1

Reference: Implement C# Generic Timeout

I'm currently working on a project that pulls metrics from AWS and my program is just stalling on a few particular calls to ListObjects. I told my supervisor that I was considering using thread abort for this purpose and now he is asking me to write a generic timeout function rather than one specific to this case. This doesn't seem safe. Now I don't completely understand the snippet I'm using from the referenced question, so I'm a little in the dark here.

Call stack on stuck code:

mscorlib.dll!System.Threading.Thread.Sleep(int millisecondsTimeout) + 0x5 bytes AWSSDK.dll!Amazon.S3.AmazonS3Client.pauseOnRetry(int retries, int maxRetries, System.Net.HttpStatusCode status, string requestAddr, System.Net.WebHeaderCollection headers, System.Exception cause) + 0x5b bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.handleRetry(Amazon.S3.Model.S3Request userRequest, System.Net.HttpWebRequest request, System.Net.WebHeaderCollection respHdrs, long orignalStreamPosition, int retries, System.Net.HttpStatusCode statusCode, System.Exception cause) + 0x17e bytes AWSSDK.dll!Amazon.S3.AmazonS3Client.getResponseCallback(System.IAsyncResult result) + 0x57a bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.invoke(Amazon.S3.AmazonS3Client.S3AsyncResult s3AsyncResult, bool isRedirect) + 0xcde bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.getResponseCallback(System.IAsyncResult result) + 0x636 bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.invoke(Amazon.S3.AmazonS3Client.S3AsyncResult s3AsyncResult, bool isRedirect) + 0xcde bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.getResponseCallback(System.IAsyncResult result) + 0x636 bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.invoke(Amazon.S3.AmazonS3Client.S3AsyncResult s3AsyncResult, bool isRedirect) + 0xcde bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.getResponseCallback(System.IAsyncResult result) + 0x636 bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.invoke(Amazon.S3.AmazonS3Client.S3AsyncResult s3AsyncResult, bool isRedirect) + 0xcde bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.invoke(Amazon.S3.AmazonS3Client.S3AsyncResult s3AsyncResult) + 0x53 bytes
AWSSDK.dll!Amazon.S3.AmazonS3Client.invokeListObjects(Amazon.S3.Model.ListObjectsRequest request, System.AsyncCallback callback, object state, bool synchronized) + 0x102 bytes AWSSDK.dll!Amazon.S3.AmazonS3Client.ListObjects(Amazon.S3.Model.ListObjectsRequest request) + 0x31 bytes

First, is the purpose that I want to use this for (Amazon's ListObjects stalling) safe for thread abort?

Second, is there a safe way to do this in a generic function without asynchronous thread aborting?

Here is what I have:

Implementation:

public static class Timeout<TResult>
{
    private static int _timeout = 5000;

    //WARNING - This method uses asynchronous thread aborting and can result
    //in roughhousing and !@#$ hitting the fan
    public static TResult Run(Func<TResult> function)
    {
        if (function == null) throw new ArgumentNullException("function to timeout is null");

        var sync = new object();
        var isCompleted = false;

        WaitCallback watcher = obj =>
        {
            var watchedThread = obj as Thread;

            lock (sync)
            {
                if (!isCompleted)
                {
                    Monitor.Wait(sync, _timeout);
                }
            }

            if (!isCompleted)
            {
                watchedThread.Abort();
            }
        };

        try
        {
            ThreadPool.QueueUserWorkItem(watcher, Thread.CurrentThread);
            return function();
        }
        catch (ThreadAbortException)
        {
            Thread.ResetAbort();
            return default(TResult);
        }
        finally
        {
            lock (sync)
            {
                isCompleted = true;
                Monitor.Pulse(sync);
            }
        }
    }
}

The call:

response = Timeout<ListObjectsResponse>.Run(() => s3Client.ListObjects(request));
Community
  • 1
  • 1
b15
  • 2,101
  • 3
  • 28
  • 46

2 Answers2

1

First note, that a thread abort is a horrible thing to do to a long-running application. It can cause all kinds of irreversible state corruption (like aborting a static ctor, causing the whole class to be hosed forever). Show your supervisor the comments below the "reference" that you linked to.

Do not implement this with a thread abort.

Instead, start the worker function on a separate thread. Start a timer to detect the timeout condition. When it elapses, let the worker thread run but discard its result and return immediately.

The key is to let the worker run but to discard its result.

usr
  • 168,620
  • 35
  • 240
  • 369
  • the ListObjects is stalling forever though, wouldn't this result in an infinite number of threads? – b15 Jan 04 '13 at 17:48
  • @user1736218 if it is stalling forever in IO, an abort would not interrupt the IO. Why is it stalling? Can't you set a timeout? I'd strongly consider moving the stalling work to a sub-process. You can abort processes safely at any time (`Process.Kill`). – usr Jan 04 '13 at 18:12
  • it is a call to amazon's s3client to retrieve all objects from a bucket in a list. It might be a network thing but I really have no idea why it is stalling. For some reason, I see no timeout for this. I don't think they expected this problem – b15 Jan 04 '13 at 18:17
  • @user1736218 pause the debugger and look at the call stack to see what it does. Turn on "Show External Code" in the context menu. If it's IO you probably *can't* abort it. And yes, your concern that you would leak threads is valid. – usr Jan 04 '13 at 18:18
0

Well I guess I don't need to use generics, I found the cause of the root problem with amazon AWS. Apparently, if the name of the bucket has a period in it and the region isnt US(east), a specific endpoint needs to be configured for the client.

b15
  • 2,101
  • 3
  • 28
  • 46