4

We have a Windows service that calls a 3rd party method that can hang when improperly configured by the end user, which is difficult to test for beforehand. We're handling this risk by calling the method in a Task with a timeout:

private int? FooWithTimeout()
{
    var timeout = TimeSpan.FromMinutes(1);
    var task = Task.Run(Foo);

    if (!task.Wait(timeout))
    {
        Log("Foo timed out...");
        return null;
    }

    if (task.IsFaulted)
    {
        Log("Foo threw an exception...");
        return null;
    }

    return task.Result;
}

The 3rd party method blocks forever waiting for input from a resource that cannot respond. It does not support cancellation in any way, shape, or form, nor does it have a built-in timeout. Our concern is that as the service runs these tasks will continue blocking and slowly accumulate, eventually consuming a large amount of resources.

Is that a valid concern? Do we need to abort/dispose the tasks in some way? If so, what is the correct way to do so?

Addendum: The 3rd party in question is Crystal Reports. It hangs when asked to print to a printer that requires some sort of additional input from the user (for example, Microsoft XPS Document Writer will prompt you for where to save the file if you print to it). And by hang, I mean it attempts to show a user prompt to get the additional input, but it's in a Windows Service so nobody ever sees the user prompt and it waits forever for a human being to tell it how to print. We allow end users to configure which printer the service attempts to print to, and there isn't really any way to tell if a given printer requires additional input short of attempting to print to it.

Oblivious Sage
  • 3,326
  • 6
  • 37
  • 58
  • what is `Foo`? a normal method that blocks the thread? This might help if you need to abort a Task: http://stackoverflow.com/a/19311606/1657476 – Meirion Hughes Apr 07 '16 at 15:05
  • 3
    Your question is basically "I have taken a dependency on poorly implemented, randomly hanging third party software, what do I do?" It seems to me that the problem ought to be solved by *not doing that in the first place*. If your third party is doing a bad job of solving the problem then get them to fix their poor design, or find an alternative. Dangerous workarounds like killing threads seems like solving the problem too late in the process. – Eric Lippert Apr 07 '16 at 18:29
  • Oh - common ! I have done a lot of dll's which shows some UI prompts. Of course it needs to be eventually fixed, but I don't always have a time to do right solution. Then everything goes - registry hacking, temporary files, batches, so on. It's not a "poor design", just a bug which needs to be fixed. Difficult to locate all UI prompt occurrences when your application is complex. – TarmoPikaro Apr 07 '16 at 19:41

2 Answers2

3

Our concern is that as the service runs these tasks will continue blocking and slowly accumulate

This is a valid concern. You can reduce the consequences of that by starting those tasks on a special thread-pool that uses small stacks. That way there is less memory usage. But it's not a complete fix. If you app must function for a long time (not a GUI app intended for a few hours of use) then this solution will prove unacceptable because eventually the app will suffer from resource exhaustion.

.NET has no way to terminate an uncooperative thread. You need to run these actions in their own processes. You can then terminate those processes safely.

Using AppDomains might be safe as well but it's less certain. It's possible that per-process state is being corrupted when an AppDomain and threads in it are aborted. Also, not all thread can be aborted. In particular IO operations.

Using separate processes is also not a guarantee that no state corruption will result from termination. But in practice most corruptible state live in memory. Process termination likely clears all inconsistent state.

usr
  • 168,620
  • 35
  • 240
  • 369
  • There is thread.abort which is used by iis as well. And iis runs for weeks or months. – Riki Apr 07 '16 at 18:27
  • 1
    IIS uses Thread.Abort only for the *current* thread at a well-defined point. Thread.Abort is known to be "evil", it cannot be used. – usr Apr 07 '16 at 18:37
0

One approach could be to create separate thread which would monitor for any newly created windows - if any new window is created - thread would try to forcefully to close them.

Enumerate windows:

How to enumerate all windows belonging to a particular process using .NET?

And close non wanted windows:

How to use WM_Close in C#?

May be will not work, just a proposal... :-)

Community
  • 1
  • 1
TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62