0

So, I'm creating a download manager, and I've designed every bit specifically so that it can be shut down at any second without giving it the time to clean up. However, I can't seem to find a way to kill my downloader threads. I tried using Thread.Abort (actually, that's my way of giving them time to clean up: they catch the ThreadAbortException and handle it themselves), but as you can already guess, it's no good. I'd use a flag or something, but when a thread is waiting for a network operation to complete, it's pretty much blocked completely. Any ideas on how to at least interrupt the network operation so I can shut down without waiting a noticeable amount of time?

Here's the relevant piece of code:

try
{
    Request.AddRange(StartPosition, EndPosition);
    Owner.SetDefaultRequestParameters(Request);

    Response = Request.GetResponse() as HttpWebResponse;
    ResponseStream = Response.GetResponseStream();

    var Buffer = new Byte[Downloader.BUFFER_SIZE];

    while (true)
    {
        Paused.Wait();

        if (bStopRequested)
            break;

        int Count = ResponseStream.Read(Buffer, 0, Buffer.Length); //gets stuck here
        if (Count == 0)
            break;

        MemoryStream.Write(Buffer, 0, Count);
        Downloaded += Count;
        CurrentStreak += Count;

        if (StartPosition + Downloaded >= EndPosition)
            break;

        if (MemoryStream.Capacity - CurrentStreak < Buffer.Length)
        {
            Owner.WriteToFile(MemoryStream, StartPosition + Downloaded - CurrentStreak, CurrentStreak);
            MemoryStream.Seek(0, SeekOrigin.Begin);
            CurrentStreak = 0;
        }
    }
}
catch (ThreadAbortException)
{
    try
    {
        Response.Close();
    }
    catch { }

    return; //fastest way I have found to shut the thread down, yet
}

EDIT: So, here's how I did it:

if (Request != null)
    Request.Abort();
if (Response != null)
    Response.Close();
ExecutionThread.Abort();

With this, no matter where the thread is stuck (getting a response or reading from the response stream) it will get cancelled and stop immediately.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
Arshia001
  • 1,854
  • 14
  • 19
  • 5
    Have you tried using `async`/`await`? Then the thread won't be blocking in a network operation at all. – Ben Voigt Dec 28 '13 at 21:11
  • @BenVoigt - unblocking threads and interrupting I/O are not the same thing (here). – H H Dec 28 '13 at 21:19
  • @Henk: true, cancellation is a separate issue. The resources may remain locked for however long the driver takes to process the cancellation request. Not much user mode code can do about that. Newer versions of Windows are much better. But the program can still move on and do other things instead of blocking on cancellations. – Ben Voigt Dec 28 '13 at 22:05
  • 1
    HttpWebRequest.Abort() is worth a shot. – Hans Passant Dec 28 '13 at 22:43

2 Answers2

2

You could Dispose the ResponseStream and then catch an ObjectDisposedException.

While this may work i suggest you design your application using CancellationToken to allow it to gracefully close instead of abruptedly aborting threads. You also might consider using async-await to make your application more scalable and non-blocking.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 1
    I would just `Close()` the streams and leave the Disposing to the normal thread resource management. But either way should work. And a CancellationToken can't interrupt I/O either. – H H Dec 28 '13 at 21:18
  • Wouldn't Close just call Dispose anyways? CancellationToken can be used in many network calls, but not all. But even when you can't you can add the functionality to proceed "as if" you could cancel. – i3arnon Dec 28 '13 at 21:21
  • 1
    Whether Close calls Dispose depends on the stream implementation, my suggestion is an improvement when they are different. The right responsibilities at the right place. – H H Dec 28 '13 at 21:24
  • @HenkHolterman: Will every stream type which allows `Dispose` to be called on a thread other than the one which is currently stuck in one of the stream's blocking methods do likewise with `Close`? – supercat Dec 28 '13 at 21:32
  • Won't disposing from another thread cause multithreading issues? I'll try it in the morning and post back. – Arshia001 Dec 29 '13 at 23:19
  • Technically it will... But that is pretty much what you ask for. You want one thread to abruptly stop another. – i3arnon Dec 30 '13 at 07:45
  • Not really, I was just afraid it was gonna crash something. Turns out it works fine; or at least it's not causing any troublesome exceptions. Thank for the answer! – Arshia001 Dec 30 '13 at 14:42
1

Not sure what the underlying socket does but if all fails you can directly bind to a socket in non blocking mode und use the Socket.BeginReceive method which will call your call back when data has arrived. If you do want to cancel the current operation you can call close simply the socket and thats it. See MSDN

The asynchronous BeginReceive operation must be completed by calling the EndReceive method. Typically, the method is invoked by the callback delegate.

This method does not block until the operation is complete. To block until the operation is complete, use one of the Receive method overloads.

To cancel a pending BeginReceive, call the Close method.

You should check if you can get your hands on the underlying socket and try to close it.

Alois Kraus
  • 13,229
  • 1
  • 38
  • 64