5

Background

We have a piece of programmable hardware here at work that we integrate into almost all of our systems. This piece of hardware came with a native Windows DLL (for which we do not have the source code) as well as a wrapper to said library. This wrapper is responsible only for marshalling the calls to the native API up to the .NET layer and nothing more. Therefore, we use this wrapper in order to make the calls to the native API.

This piece of programmable hardware is physically integrated into our product in a very problematic way. The piece of hardware sits in the product in such a way that every once in a while the USB cable gets cut in half... (don't ask) The problem is that the cable tends to get cut in half when we are deep into the native API. When this happens, the caller will not return from the native API function. Below is a snippet of what the call looks like via the wrapper:

    public static UInt32 Wrap(UInt32 configval, Int32 channum, IntPtr handleval)
    {
        if (IntPtr.Size == 4)
            return Native32(configval, channum, handleval);
        else
            return Native64(configval, channum, handleval);
    }

Question

Is there a general method of making sure we don't hang inside of the API?

I already know that I can just start a task, and wait for it with a timeout (just resume if it times out). This method works, but is there any other way of doing this? I have no control whatsoever of what goes on inside the API.

  • 5
    Create a second process that does the native interop. Talk to it via IPC. Shoot it, if it hangs. – CodesInChaos Mar 10 '16 at 17:53
  • 2
    I find this question fascinating. You have this problem where the cable is cut, and apparently fubars everything and you are looking for a solution to not hang. I'm baffled as to why you wouldn't just solve the problem where the cord is getting cut. Is the goal to simply return back a message saying: sorry, "it appears an internal cable has been severed catastrophically"? – JimmyJames Mar 21 '16 at 17:30
  • @JimmyJames No, all of that is out of my hands. My job is to write a "work-around" and that's where the buck stops. – Snoop Mar 21 '16 at 17:31
  • that's tough, please hold. – JimmyJames Mar 21 '16 at 17:33
  • 2
    Better solution would definitely be avoiding the cutting of the cable. But what is wrong with the solution you already stated you know abou, start a task and wait for timeout? Why don't you use that solution? – Trilarion Mar 22 '16 at 12:16
7
+50

Basically the problem is blocking on a call that will never return. This is a solved problem in that every network call is highly likely to fail so we have things like sockets that have timeouts built-in to them. In one sense, the problem is that your code assumes this won't fail to respond when it can. The only way to get around that is to stop blocking on that call. You'll need some sort of asynchronous capability. Whether you use some sort of non-blocking IO or spawn a thread, the solution will have the same kind of high-level design.

The solution depends a lot on the overall architecture. If you are structured around a work-queue, then the nicest solution is when the API returns (or times out) you push the response to the work queue and deal with it. On the other hand, if your application is highly sequential, you probably have to block the main thread until the response comes back (or times out.)

In either approach, one issue is how long the API can be expected to take. If the response time is highly regular (low variance) then you can simply accept the timeout as a failure. If you can't put a reasonable upper-limit on the response time, you might want to take one more step on timeout to send another request to the API (preferably something fast) to check whether you are still connected. If you are still able to connect, the cord isn't cut and you can wait some more and repeat as necessary.

  • 1
    My upper limit is roughly 50ms, beyond that and I know my API timed out. I am already using a task start/wait with a timeout. This works just fine. To be fair, the work queue thing is an idea though. Will have to give it a try and get back to you. – Snoop Mar 21 '16 at 19:30
1

The only way to return control from a function call that never returns, is to watch it from the side and smack it if it doesn't respond quickly. There is no other way to get a non-returning call to return.

So a task would work, but if you want to isolate the issue, you could start a thread inside the wrapper to make the call, so the main wrapper method can then handle a timeout on the thread's response. This would make your .NET client code very easy - you make a call and it might return after 50ms with a "no data" response, at the expense of putting the async call inside the wrapper (which isn't too much trouble to be fair, start a thread and WaitForSingleObject on its handle and a timeout)

  • Have you personally had to do this for something? – Snoop Mar 22 '16 at 17:24
  • not for .NET, but I have done something similar for running external processes that might hang - if it doesn't respond, it can be killed and restarted without the client application even noticing. – gbjbaanb Mar 22 '16 at 19:23
1

I've seen several solutions to this kind of problems, all involving separate processes, which are usually connected through pipes, or use the Microsoft AddIn Framework MAF. The last is somehow complex, with a ton of intermediate classes between the main and the isolated process, and it can't have a unique identifying name for the dependent process.

It seems the only option to deal with native components which have potentially severe bugs, such as

  • not returning from a call (can also be handled on thread level)
  • memory leaks
  • severe runtime errors, leading to process termination

or where you have to bridge architecture gaps, such as 32bit DLL under 64bit .NET process.

1

Since you're using .Net, why don't you put your call into a Task, with a timeout configured ? MSDN has a nice example about this, using Task.Wait:

https://msdn.microsoft.com/en-us/library/dd235606(v=vs.110).aspx

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Task t = Task.Run( () => {
                            Random rnd = new Random();
                            long sum = 0;
                            int n = 5000000;
                            for (int ctr = 1; ctr <= n; ctr++) {
                               int number = rnd.Next(0, 101);
                               sum += number;
                            }
                            Console.WriteLine("Total:   {0:N0}", sum);
                            Console.WriteLine("Mean:    {0:N2}", sum/n);
                            Console.WriteLine("N:       {0:N0}", n);   
                         } );
     TimeSpan ts = TimeSpan.FromMilliseconds(150);
     if (! t.Wait(ts))
        Console.WriteLine("The timeout interval elapsed.");
   }
}
// The example displays output similar to the following:
//       Total:   50,015,714
//       Mean:    50.02
//       N:       1,000,000
// Or it displays the following output:
//      The timeout interval elapsed.
  • Thanks for the answer, I thought of this myself and so did two other people so far. This seems to be what people are saying, will wait a while longer to see what others suggest. – Snoop Mar 22 '16 at 19:24
  • I guess you're on the right track. The workaround for your case is indeed to add a timeout to the hardware query, and there are many ways to do that. Tasks are kind of a heavy way to do it, but since you're constrained to the vendor API, it seems to be the easiest way to accomplish that. Good luck! :-) – Machado Mar 22 '16 at 19:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.