We're working with a 3rd-party legacy system that requires thread affinity for some of the tear-down logic. We're also hosting a WCF service inside IIS which, under heavy loads will do a rude unloading of our app domain. In these cases it falls to the critical finalizer to do cleanup. Unfortunately, without thread affinity in the finalizer, the 3rd-party system deadlocks.
So roughly:
public class FooEnvironment : CriticalFinalizerObject, IDisposable
{
public FooEnvironment()
{
// start up C API
}
public bool Dispose()
{
// shutdown C API (from same thread ctor was called on)
}
~FooEnvironment()
{
// try to shutdown C API but deadlock!
}
}
I've tried various things where we Run with the ExecutionContext from the initializing thread, but this doesn't work (at least in IIS) and we get an invalid operation exception stating that this execution context can't be used (ostensibly because it may have been marashalled across AppDomains, which seems likely).
I've read several things basically stating that what I'm trying to do can't be done but I figured I would ask since there isn't a lot of information on this topic.
Back in the old days I developed a library that wrapped the hideous DDEML which is a Win32 api wrapper around the DDE protocol. The DDEML has thread affinity requirements as well so I feel your pain.
The only strategy that is going to work is to create a dedicate thread that executes all of your library calls. This means biting the bullet and marshaling every single request to call into this API onto this dedicated thread and then marshaling back the result to the original thread. It sucks and its slow, but it is the only method guaranteed to work.
It can be done, but it is painful. You can see how I tackled the problem in my NDde library. Basically, the finalizer will simply post a message via static method calls to a thread that can accept and dispatch them to the appropriate API call. In my case I created a thread that called Application.Run to listen for messages because DDE required a Windows message loop anyway. In your case you will want to create the thread in a manner that monitors a custom message queue. This is not terribly difficult if you use the BlockingCollection class because the Take method blocks until an item appears the queue.
Related
I have several long-running threads in an MVC3 application that are meant to run forever.
I'm running into a problem where a ThreadAbortException is being called by some other code (not mine) and I need to recover from this gracefully and restart the thread. Right now, our only recourse is to recycle the worker process for the appDomain, which is far from ideal.
Here's some details about this code works:
A singleton service class exists for this MVC3 application. It has to be a singleton because it caches data. This service is responsible for making request to a database. A 3rd party library is used for the actual database connection code.
In this singleton class we use a collection of classes that are called "QueryRequestors". These classes identify unique package+stored_procedure names for requests to the database, so that we can queue those calls. That is the purpose of the QueryRequestor class: to make sure calls to the same package+stored_procedure (although they may have infinite different parameters) are queued, and do not happen simultaneously. This eases our database strain considerably and improves performance.
The QueryRequestor class uses an internal BlockingCollection and an internal Task (thread) to monitor its queue (blocking collection). When a request comes into the singleton service, it finds the correct QueryRequestor class via the package+stored_procedure name, and it hands the query over to that class. The query gets put in the queue (blocking collection). The QueryRequestor's Task sees there's a request in the queue and makes a call to the database (now the 3rd party library is involved). When the results come back they are cached in the singleton service. The Task continues processing requests until the blocking collection is empty, and then it waits.
Once a QueryRequestor is created and up and running, we never want it to die. Requests come in to this service 24/7 every few minutes. If the cache in the service has data, we use it. When data is stale, the very next request gets queued (and subsequent simultaneous requests continue to use the cache, because they know someone (another thread) is already making a queued request, and this is efficient).
So the issue here is what to do when the Task inside a QueryRequestor class encounters a ThreadAbortException. Ideally I'd like to recover from that and restart the thread. Or, at the very least, dispose of the QueryRequestor (it's in a "broken" state now as far as I'm concerned) and start over. Because the next request that matches the package+stored_procedure name will create a new QueryRequestor if one is not present in the service.
I suspect the thread is being killed by the 3rd party library, but I can't be certain. All I know is that nowhere do I abort or attempt to kill the thread/task. I want it to run forever. But clearly we have to have code in place for this exception. It's very annoying when the service bombs because a thread has been aborted.
What is the best way to handle this? How can we handle this gracefully?
You can stop re-throwing of ThreadAbortException by calling Thread.ResetAbort.
Note that most common case of the exception is Redirect call, and canceling thread abort may case undesired effects of execution of request code that otherwise would be ignored due to killing the thread. It is common issue in WinForms (where separation of code and rendering is less clear) than in MVC (where you can return special redirect results from controllers).
Here's what I came up with for a solution, and it works quite nicely.
The real issue here isn't preventing the ThreadAbortException, because you can't prevent it anyway, and we don't want to prevent it. It's actually a good thing if we get an error report telling us this happened. We just don't want our app coming down because of it.
So, what we really needed was a graceful way to handle this Exception without bringing down the application.
The solution I came up with was to create a bool flag property on the QueryRequestor class called "IsValid". This property is set to true in the constructor of the class.
In the DoWork() call that is run on the separate thread in the QueryRequestor class, we catch the ThreadAbortException and we set this flag to FALSE. Now we can tell other code that this class is in an Invalid (broken) state and not to use it.
So now, the singleton service that makes use of this QueryRequestor class knows to check for this IsValid property. If it's not valid, it replaces the QueryRequestor with a new one, and life moves on. The application doesn't crash and the broken QueryRequestor is thrown away, replaced with a new version that can do the job.
In testing, this worked quite well. I would intentionally call Thread.Abort() on the DoWork() thread, and watch the Debug window for output lines. The app would report that the thread had been aborted, and then the singleton service was correctly replacing the QueryRequestor. The replacement was then able to successfully handle the request.
I have an interesting problem that I haven't seen documented anywhere else (at least not this specific issue).
This issue is a combination of COM, VB6, and .NET and making them play nice.
Here's what I have:
A legacy VB6 ActiveX DLL (written by us)
A multi-threaded Windows service written in C# that processes requests from clients over the network and sends back results. It does this by creating a new STA thread to handle each request. Each request-handler thread instantiates a COM object (defined in the ActiveX DLL) to process the request and get the result (a string of XML is passed in, and it returns a string of XML back), explicitly releases the COM object, and exits. The service then sends the result back to the client.
All of the network code is handled using asynchronous networking (i.e. thread pool threads).
And yes, I know this is already a risky thing to be doing in the first place, since VB6 isn't very friendly with multi-threaded applications to begin with, but unfortunately it's what I am stuck with for the moment.
I've already fixed a number of things that were causing deadlocks in the code (for example, making sure the COM objects are actually created and called from a separate STA thread, making sure to explicitly release the COM objects before the thread exits to prevent deadlocks that were occurring between the garbage collector and the COM Interop code, etc.), but there is one deadlock scenario that I just can't seem to solve.
With some help from WinDbg, I was able to figure out what is happening, but I'm not sure how (or if) there is a way around this particular deadlock.
What's happening
If one request-handler thread is exiting, and another request-handler thread is starting at the same time, a deadlock can occur because of the way the VB6 runtime initialization and termination routines seem to work.
The deadlock occurs in the following scenario:
The new thread that is starting up is in the middle of creating a new instance of the (VB6) COM object to process an incoming request. At this point, the COM runtime is in the middle of a call to retrieve the object's class factory. The class factory implementation is in the VB6 runtime itself (MSVBVM60.dll). That is, its calling the VB6 runtime's DllGetClassObject function. This, in turn, calls an internal runtime function (MSVBVM60!CThreadPool::InitRuntime), which acquires a mutex and enters a critical section to do part of its work. At this point, it's about to call LoadLibrary to load oleaut32.dll into the process, while holding this mutex. So, now it's holding this internal VB6 runtime mutex and waiting for the OS loader lock.
The thread that is exiting is already running inside the loader lock, because it's done executing managed code and is executing inside the KERNEL32!ExitThread function. Specifically, it's in the middle of handling the DLL_THREAD_DETECH message for MSVBVM60.dll on that thread, which in turn calls a method to terminate the VB6 runtime on the thread (MSVBVM60!CThreadPool::TerminateRuntime). Now, this thread tries to acquire the same mutex that the other thread being initialized already has.
A classic deadlock. Thread A has L1 and wants L2, but Thread B has L2 and needs L1.
The problem (if you've followed me this far) is I don't have any control over what the VB6 runtime is doing in its internal thread initialization and teardown routines.
In theory, if I could force the VB6 runtime initialization code to run inside the OS loader lock, I would prevent the deadlock, because I am fairly certain the mutex the VB6 runtime is holding is specifically only used inside the initialization and termination routines.
Requirements
I can't make the COM calls from a single STA thread, because then the service won't be able to handle concurrent requests. I can't have a long-running request block other client requests either. This is why I create one STA thread per-request.
I need to create a new instance of the COM object on each thread, because I need to make sure each instance has its own copy of global variables in the VB6 code (VB6 gives each thread its own copy of all global variables).
Solutions I've tried that didn't work
Converted ActiveX DLL to ActiveX EXE
First, I tried the obvious solution and created an ActiveX EXE (out-of-process server) to handle the COM calls. Initially, I compiled it so that a new ActiveX EXE (process) was created for each incoming request, and I also tried it with the Thread Per Object compile option (one process instance is created, and it creates each object on a new thread within the ActiveX EXE).
This fixes the deadlock issue with respect to the VB6 runtime, because the VB6 runtime never gets loaded into the .NET code proper. However, this led to a different problem: if concurrent requests come into the service, the ActiveX EXE tends to fail randomly with RPC_E_SERVERFAULT errors. I assume this is because the COM marshalling and/or the VB6 runtime can't deal with concurrent object creation/destruction, or concurrent method calls, inside the ActiveX EXE.
Force the VB6 code to run inside the OS loader lock
Next, I switched back to using an ActiveX DLL for the COM class. To force the VB6 runtime to run its thread initialization code inside the OS loader lock, I created a native (Win32) C++ DLL, with code to handle DLL_THREAD_ATTACH in DllMain. The DLL_THREAD_ATTACH code calls CoInitialize and then instantiates a dummy VB6 class to force the VB6 runtime to be loaded and force the runtime initialization routine to run on the thread.
When the Windows service starts, I use LoadLibrary to load this C++ DLL into memory, so that any threads created by the service will execute that DLL's DLL_THREAD_ATTACH code.
The problem is that this code runs for every thread the service creates, including the .NET garbage collector thread and the thread-pool threads used by the async networking code, which doesn't end well (this just seems to cause the threads to never start properly, and I imagine initializing COM on the GC and thread-pool threads is in general just a very bad idea).
Addendum
I just realized why this is a bad idea (and probably part of the reason it didn't work): it isn't safe to call LoadLibrary when you are holding the loader lock. See Remarks section in this MSDN article: http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx, specifically:
Threads in DllMain hold the loader lock so no additional DLLs can be dynamically loaded or initialized.
Is there any way to workaround these issues?
So, my question is, is there any way to work around the original deadlock issue?
The only other thing I can think of is to create my own lock object and surround the code that instantiates the COM object in a .NET lock block, but then I have no way (that I know of) to put the same lock around the (operating system's) thread exit code.
Is there a more obvious solution to this issue, or am I plain out of luck here?
As long as all of your modules work in one process, you can hook Windows API by replacing some system calls with your wrappers. Then, you can wrap the calls in a single critical section to avoid deadlock.
There are several libraries and samples to achieve that, the technique is commonly known as detouring:
http://www.codeproject.com/Articles/30140/API-Hooking-with-MS-Detours
http://research.microsoft.com/en-us/projects/detours/
And of course the implementation of wrappers should be done in native code, preferably C++. .NET detours work too for high-level API functions such as MessageBox, but if you try to reimplement LoadLibrary API call in .NET then you may get a cyclic dependency issue because .NET runtime internally uses LoadLibrary function during execution and does this often.
So the solution looks like this to me: a separate .DLL module which is loaded at the very start of your application. The module fixes the deadlock problem by patching several VB and Windows API calls with your own wrappers. All wrappers do one thing: wrap the call in critical section and invoke the original API function to do the real job.
EDIT: in retrospect, I don't think this will work. The problem is that the deadlock can occur at any time that a Win32 thread exits, and since Win32 threads don't map 1:1 to .NET threads, we can't (within .NET) force Win32 threads to acquire the lock before exiting. In addition to the possibility of the .NET thread that is exiting being switched to a different OS thread, there are presumably OS threads not associated with any .NET thread (garbage collection and the like) which may start and exit at random.
The only other thing I can think of is to create my own lock object
and surround the code that instantiates the COM object in a .NET lock
block, but then I have no way (that I know of) to put the same lock
around the (operating system's) thread exit code.
That sounds like a promising approach. I gather from this that you
are able to modify the service's code, and you say each thread
explicitly releases the COM object before exiting, so presumably you
could claim a lock at this point, either just before explicitly
releasing the COM object or just after. The secret is to choose a
type of lock that is implicitly released once the thread holding it
has exited, such as a Win32 mutex.
It is likely that a Win32 mutex object does not become abandoned until
the thread has completed all DLL_THREAD_DETACH calls, although I don't
know whether this behaviour is documented. I'm not familiar with
locking in .NET but my guess is that they are unlikely to be suitable,
because even if the right kind of lock exists, it would be likely to
be considered abandoned as soon as the thread reaches the end of the
managed code section, i.e., before the calls to DLL_THREAD_DETACH.
If Win32 mutex objects don't do the trick (or if you very reasonably
prefer not to rely on undocumented behaviour) you might need to
implement the lock yourself. One way to do this would be to use
OpenThread to get a handle to the current thread and save this in your
lock object, along with an event or similar object. If the lock has
been claimed and you want to wait for it to be available, use
WaitForMultipleObjects to wait until either the thread handle or the
event is signaled. If the event is signaled this means the lock has
been explicitly released, if the thread handle is signaled it was
implicitly released by the thread exiting. Obviously implementing
this involves a lot of tricky details (for example: when a thread
explicitly releases the lock, you can't close the thread handle
because another thread might be waiting on it, so you'll have to close
it when the lock is next claimed instead) but it shouldn't be too
difficult to sort these out.
I don't see any reason why you couldn't load an extra instance of the ActiveX control in your startup code and just hang onto the reference. Presto, no more loader lock issues since the VB6 runtime never shuts down.
Since I'm still exploring my options, I wanted to still see if I could implement a solution in pure .NET code without using any native code, for the sake of simplicity. I'm not sure if this is a fool-proof solution yet, because I'm still trying to figure out whether it actually gives me the mutual exclusion I need, or if it just looks like it does.
Any thoughts or comments are welcome.
The relevant part of the code is below. Some notes:
The HandleRpcRequest method is called from a thread-pool thread when a new message is received from a remote client
This fires off a separate STA thread so that it can make the COM call safely
DbRequestProxy is a thin wrapper class around the real COM class I'm using
I used a ManualResetEvent (_safeForNewThread) to provide the mutual exclusion. The basic idea is that this event stays unsignaled (blocking other threads) if any one particular thread is about to exit (and hence potentially about to terminate the VB6 runtime). The event is only signaled again after the current thread completely terminates (after the Join call finishes). This way multiple request-handler threads can still execute concurrently unless an existing thread is exiting.
So far, I think this code is correct and guarantees that two threads can't deadlock in the VB6 runtime initialization/termination code anymore, while still allowing them to execute concurrently for most of their execution time, but I could be missing something here.
public class ClientHandler {
private static ManualResetEvent _safeForNewThread = new ManualResetEvent(true);
private void HandleRpcRequest(string request)
{
Thread rpcThread = new Thread(delegate()
{
DbRequestProxy dbRequest = null;
try
{
Thread.BeginThreadAffinity();
string response = null;
// Creates a COM object. The VB6 runtime initializes itself here.
// Other threads can be executing here at the same time without fear
// of a deadlock, because the VB6 runtime lock is re-entrant.
dbRequest = new DbRequestProxy();
// Call the COM object
response = dbRequest.ProcessDBRequest(request);
// Send response back to client
_messenger.Send(Messages.RpcResponse(response), true);
}
catch (Exception ex)
{
_messenger.Send(Messages.Error(ex.ToString()));
}
finally
{
if (dbRequest != null)
{
// Force release of COM objects and VB6 globals
// to prevent a different deadlock scenario with VB6
// and the .NET garbage collector/finalizer threads
dbRequest.Dispose();
}
// Other request threads cannot start right now, because
// we're exiting this thread, which will detach the VB6 runtime
// when the underlying native thread exits
_safeForNewThread.Reset();
Thread.EndThreadAffinity();
}
});
// Make sure we can start a new thread (i.e. another thread
// isn't in the middle of exiting...)
_safeForNewThread.WaitOne();
// Put the thread into an STA, start it up, and wait for
// it to end. If other requests come in, they'll get picked
// up by other thread-pool threads, so we won't usually be blocking anyone
// by doing this (although we are blocking a thread-pool thread, so
// hopefully we don't block for *too* long).
rpcThread.SetApartmentState(ApartmentState.STA);
rpcThread.Start();
rpcThread.Join();
// Since we've joined the thread, we know at this point
// that any DLL_THREAD_DETACH notifications have been handled
// and that the underlying native thread has completely terminated.
// Hence, other threads can safely be started.
_safeForNewThread.Set();
}
}
I had written a rather complex code using VB6,VC6 about 20 years ago and I need to port it to visual studio.net.
I simply took the functions as I had written them along with the header files corrected all the compile errors (which were MANY) and then tried to load it. got "loaderlock closed"
I then decided to redo all the files starting from those that few other files depended upon and then worked my way up and as I went I included only the header files that that particular file required. The result it loads now just fine. no more loaderlock closed.
the lesson for me is don't include any more header files in a particular cpp file than is absolutely necessary.
hope this helps
from a very happy camper!!
david
My web service has this code
lock(typeof MyWebServiceClass)
Well i call a 3rd party unchangeable code and it never returns. BOOM! BAM!
I am locked forever now and my web site crashes.
This never returns only happens once in a great while.
Is it possible to create a lock that times out? IE lock the code for 5 minutes then release the lock?
Instead of creating a timed lock, I would limit your third party request by putting it in a separate thread/task. Then, kick off the thread (or task if .NET 4.0 and TPL available to you) and join on the response with a timeout. If the join times out, then cancel the thread (or call the cancel token on the TPL task).
Is it possible to create a lock that times out?
Yes, this unpleasant situation is often refereed to as deadlock.
Usually it is good practice to lock on a static private object instead of locking on instance fields or the class itself:
private static object _syncRoot = new object();
and then:
lock(_syncRoot) {
}
If the 3rd party API has a cancellation mechanism then use that.
lock(typeof MyWebServiceClass)
{
if (ThirdPartyApiThatAcceptsTimeout(TimeSpan.FromMinutes(5)))
{
// The call was successful so proceed.
}
else
{
// The call timed out so bail out.
return;
}
}
However, I highly suspect that this API does not have a cancellation mechanism and so that is why you posed this question. If that is the case then this just got exponentially harder.
The naive approach would be to defer the API call to another thread. If the thread does not respond in a timely fashion then you can abort it.
lock(typeof MyWebServiceClass)
{
var thread = new Thread(
() =>
{
ThirdPartyApiThatCouldBlockIndefinitely();
});
thread.Start();
if (thread.Join(TimeSpan.FromMinutes(5))
{
// The call was successful so proceed.
}
else
{
// The call timed out so bail out.
thread.Abort();
return;
}
}
There are many problems with this though. First, there is no guarantee that the thread will accept the abort request. Since 2.0 there are special rules in the CLR that dictate when aborts can be injected into the thread. I believe the CLR will defer the injection while unmanaged code is executing. So if your API is unmanaged then the abort might not work. Also, aborts are voluntary since the thread could catch ThreadAbortException and ignore it. Second, aborting is dangerous since the abort can be injected asynchronously. This makes it very difficult to guard against corrupting shared state. That is why the AppDomain is normally terminated after an abort.
The safest way to handle this is to put the API call into a separate process. You would have to use interprocess communication protocols like .NET Remoting, WCF, pipes, etc. to transfer data to/from the call which would be a real pain to maintain. But, it would be the safest since you can safely kill the process without the risk of corrupting the AppDomain of the caller.
I really feel for you because this problem is really hard to solve correctly.
I found a decent looking example of how to call a delegate asynchronously with a timeout... http://www.eggheadcafe.com/tutorials/aspnet/847c94bf-4b8d-4a66-9ae5-5b61f049019f/basics-make-any-method-c.aspx. In summary it uses WaitOne with a timeout to determine if the call does not return before the timeout expires.
I also know that you should have an EndInvoke to match each BeginInvoke.
So what happens if the wait timeout expires? We (presumably) DON'T want to call EndInvoke as that will block. The code can go on to do 'other things', but have we leaked anything? Is there some poor thread someplace blocked waiting for a return that's never going to happen? Have we leaked some memory where the result-that-will-never-return was going to be placed?
I think this post talks about it very well:
From the post:
You can't terminate an executing async delegate if it's not your thread, but you can if it is. If you use the common BeginInvoke type methods, you get a thread pool thread managed by the framework. If you use the Thread() class you get you own thread to manage, start, suspend, etc. as you like.
Developing asychronously requires that one decide who will manage the threads. The many different methods that execute asynchronously are using the ThreadPool threads behind the scenes.
Since you can't/shouldn't terminate a thread pool thread then you must design you code to communicate with the thread so that it can exit. The MSDN examples for the BackgroundWorker component demonstrates this kind of communication.
Sometimes your code may have the thread blocking waiting for IO. Here you would normally use a multiple object wait instead, waiting for IO or for a ManualResetEvent.
So in short, youll need to find a way to manage the threads yourself if there is a possibility of timing out and you want the thread to end.
You need to call EndInvoke().
Here is a link talking about what happens with EndInvoke():
Is EndInvoke() optional, sort-of optional, or definitely not optional?
Here is a link to the article in the accepted answer.
We had all been talking about the 'fire and forget' technique with asynchronous delegate invocation in various public forums. Many DevelopMentor instructors had written articles and example code showing the technique, and we had all described it in class. And of course it was in Don's book by then too. So when Microsoft eventually remembered to let the outside world know that this technique is not in fact legal, it was rather astonishing.
An MSDN link on the asynchronous pattern.
You will leak the resources held by the thread. There will be various bits of .NET remoting plumbing objects like the AsyncResult. Several unmanaged handles associated with the thread. All peanuts compared to the one megabyte of virtual memory address space you'll leak, held by the thread stack.
You cannot abort the thread in any way, the leak is permanent. When you have to deal with badly behaving code like this, your only good resource is to run it in a separate process so you can get Windows to clean up the shrapnel when you shoot the process in the head with Process.Kill(). Even that is not guaranteed, these kind of freezes tend to be associated with misbehaving device drivers. Process.Kill won't terminate a device driver thread. Easy to see: trying to abort the process with Taskmgr.exe will leave it running with one Handle. You have some hope if that doesn't happen.
As part of a large automation process, we are calling a third-party API that does some work calling services on another machine. We discovered recently that every so often when the other machine is unavailable, the API call will spin away sometimes up to 40 minutes while attempting to connect to the remote server.
The API we're using doesn't offer a way to specify a timeout and we don't want our program waiting around for that long, so I thought threads would be a nice way to enforce the timeout. The resulting code looks something like:
Thread _thread = new Thread(_caller.CallServices());
_thread.Start();
_thread.Join(timeout);
if (_thread.IsAlive)
{
_thread.Abort();
throw new Exception("Timed-out attempting to connect.");
}
Basically, I want to let APICall() run, but if it is still going after timeout has elapsed, assume it is going to fail, kill it and move on.
Since I'm new to threading in C# and on the .net runtime I thought I'd ask two related questions:
Is there a better/more appropriate mechanism in the .net libraries for what I'm trying to do, and have I committed any threading gotchas in that bit of code?
Thread.Abort() is a request for the thread to abort, and gives no guarantee that it will do so in a timely manner. It is also considered bad practice (it will throw a thread abort exception in the aborted thread, but it seems like the 3rd party API offers you no other choices.
If you know (programmatically) the address of the remote service host you should ping it before you transfer control to the 3rd party API.
If not using a backgroundworker, you could set the thread's IsBackgroundThread to true, so it doesn't keep your program from terminating.
Bad idea. Thread.Abort doesn't necessarily clean up the mess left by such an interrupted API call.
If the call is expensive, consider writing a separate .exe that makes the call, and pass the arguments to/from it using the command line or temporary files. You can kill an .exe much more safely than killing a thread.
You can also just use a delegate... Create a delegate for the method that does the work, Then call BeginInvoke on the delegate, passing it the arguments, and a callback function to handle the return values (if you want)...
Immediately after the BeginInvoke you can wait a designated time for the asynch delegate to finish, and if it does not in that specified time, move on...
public delegate [ReturnType] CallerServiceDelegate
([parameter list for_caller.CallService]);
CallerServiceDelegate callSvcDel = _caller.CallService;
DateTime cutoffDate = DateTime.Now.AddSeconds(timeoutSeconds);
IAsyncResult aR = callSvcDel.BeginInvoke([here put parameters],
AsynchCallback, null);
while (!aR.IsCompleted && DateTime.Now < cutoffDate)
Thread.Sleep(500);
if (aR.IsCompleted)
{
ReturnType returnValue = callSvcDel.EndInvoke(aR);
// whatever else you need to do to handle success
}
else
{
callSvcDel.EndInvoke(aR);
// whatever you need to do to handle timeout
}
NOTE: as written AsynchCallback could be null, as the code retrieves the return value from the EndInvoke(), but if you want to you can have the CallService() method call the AsynchCallback delegate and pass it the return values instaed...
It might work, but nobody could say for sure without an understanding of the third-party API. Aborting the thread like that could leave the component in some invalid state that it might not be able to recover from, or maybe it won't free resources that it allocated (think - what if one of your routines just stopped executing half-way through. Could you make any guarantees about the state your program would be in?).
As Cicil suggested, it might be a good idea to ping the server first.
Does your application run for long periods of time or is it more of a run-as-needed application? If it's the latter, I personally would consider using the Thread.Abort() option. While it may not be the most desirable from a purist's perspective (resource management, etc.), it is certainly straightforward to implement and may foot the bill given the way your particular application works.
The idea of a separate executable makes sense. Perhaps another option would be to use AppDomains. I'm not an expert in this area (I welcome refinements/corrections to this), but as I understand it, you'd put the API call in a separate DLL and load it into a separate AppDomain. When the API call is finished or you have to abort it, you can unload the AppDomain along with the DLL. This may have the added benefit of cleaning up resources that a straightforward Thread.Abort() will not.