Time-Out Usage

Use time-outs to specify the maximum time a caller is willing to wait for completion of a method call.

A time-out might take the form of a parameter to the method call as follows.

server.PerformOperation(timeout)
[C#]
server.PerformOperation(timeout);

Alternately, a time-out can be used as a property on the server class as follows.

server.Timeout = timeout
server.PerformOperation()
[C#]
server.Timeout = timeout;
server.PerformOperation();   

You should favor the first approach, because the association between the operation and the time-out is clearer. The property-based approach might be better if the server class is designed to be a component used with visual designers.

Historically, time-outs have been represented by integers. Integer time-outs can be hard to use because it is not obvious what the unit of the time-out is, and it is difficult to translate units of time into the commonly used millisecond.

A better approach is to use the TimeSpan structure as the time-out type. TimeSpan solves the problems with integer time-outs mentioned above. The following code example shows how to use a time-out of type TimeSpan.

Public Class Server
   Public Sub PerformOperation(timeout As TimeSpan timeout) 
      ' Insert code for the method here.
   End Sub
End Class

Public Class TestClass
   Dim server As New Server();
   server.PerformOperation(New TimeSpan(0,15,0))
End Class
[C#]
public class Server
{
   void PerformOperation(TimeSpan timeout)
   {
      // Insert code for the method here.
   } 
}

public class TestClass
{
   public Server server = new Server();
   server.PerformOperation(new TimeSpan(0,15,0));
}

If the time-out is set to TimeSpan(0), the method should throw an exception if the operation is not immediately completed. If the time-out is TimeSpan.MaxValue, the operation should wait forever without timing out, as if there were no time-out set. A server class is not required to support either of these values, but it should throw an InvalidArgumentException if an unsupported time-out value is specified.

If a time-out expires and an exception is thrown, the server class should cancel the underlying operation.

If a default time-out is used, the server class should include a static defaultTimeout property to be used if the user does not specify one. The following code example includes a static OperationTimeout property of type TimeSpan that returns defaultTimeout.

Class Server
   Private defaultTimeout As New TimeSpan(1000)
   
   Overloads Sub PerformOperation()
      Me.PerformOperation(OperationTimeout)
   End Sub 
   
   Overloads Sub PerformOperation(timeout As TimeSpan)
      ' Insert code here.
   End Sub 
   
   ReadOnly Property OperationTimeout() As TimeSpan
      Get
         Return defaultTimeout
      End Get
   End Property
End Class 
[C#]
class Server
{
   TimeSpan defaultTimeout = new TimeSpan(1000); 

   void PerformOperation()
   {
      this.PerformOperation(OperationTimeout);
   }

   void PerformOperation(TimeSpan timeout)
   {
      // Insert code here.
   }

   TimeSpan OperationTimeout
   {
      get
      {
         return defaultTimeout;
      }
   }
}

Types that are not able to resolve time-outs to the resolution of a TimeSpan should round the time-out to the nearest interval that can be accommodated. For example, a type that can only wait in one-second increments should round to the nearest second. An exception to this rule is when a value is rounded down to zero. In this case, the time-out should be rounded up to the minimum time-out possible. Rounding away from zero prevents "busy-wait" loops where a zero time-out value causes 100 percent processor utilization.

In addition, it is recommended that you throw an exception when a time-out expires instead of returning an error code. Expiration of a time-out means that the operation could not complete successfully and therefore should be treated and handled as any other run-time error. For more information, see Error Raising and Handling Guidelines.

In the case of an asynchronous operation with a time-out, the callback function should be called and an exception thrown when the results of the operation are first accessed. This is illustrated in the following code example.

Sub OnReceiveCompleted(ByVal sender As System.Object, ByVal asyncResult As ReceiveCompletedEventArgs)
   Dim queue As MessageQueue = CType(sender, MessageQueue)
   ' The following code will throw an exception
   ' if BeginReceive has timed out.
   Dim message As Message = queue.EndReceive(asyncResult.AsyncResult)
   Console.WriteLine(("Message: " + CStr(message.Body)))
   queue.BeginReceive(New TimeSpan(1, 0, 0))
End Sub 
[C#]
void OnReceiveCompleted(Object sender, ReceiveCompletedEventArgs asyncResult)
{
   MessageQueue queue = (MessageQueue) sender;
   // The following code will throw an exception
   // if BeginReceive has timed out.
   Message message = queue.EndReceive(asyncResult.AsyncResult);
   Console.WriteLine("Message: " + (string)message.Body);
queue.BeginReceive(new TimeSpan(1,0,0));
}

For related information, see Guidelines for Asynchronous Programming.

See Also

Design Guidelines for Class Library Developers | Error Raising and Handling Guidelines