Bug 21682 - AutoResetEvent nonexpected behaviour
Summary: AutoResetEvent nonexpected behaviour
Status: RESOLVED NOT_ON_ROADMAP
Alias: None
Product: Class Libraries
Classification: Mono
Component: General ()
Version: 3.2.x
Hardware: PC Linux
: --- normal
Target Milestone: Untriaged
Assignee: marcos.henrich
URL:
Depends on:
Blocks:
 
Reported: 2014-07-30 03:02 UTC by damirainullin
Modified: 2014-08-10 10:12 UTC (History)
2 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED NOT_ON_ROADMAP

Description damirainullin 2014-07-30 03:02:55 UTC
I use HttpListener class for simple multitheading HTTP server for Long Polling actions. I use AutoResetEvents and WaitHandle.WaitAny for signal messaging between threads. Application works fine on .NET 4.5. But we have some problem with multithreading after porting application on Mono (version 3.2.8 (Debian 3.2.8+dfsg-4ubuntu1)).

We expected when second HTTP request is received by server handling of first HTTP request should be finish. And so on. But in Mono handling of first request finish only by timeout. The simplified code of my application is below.

class Program
{
    private static AutoResetEvent autoEvent = new AutoResetEvent(false);
    private static AutoResetEvent autoEvent2 = new AutoResetEvent(false);

    public static void Main()
    {
        HttpListener listener = new HttpListener();

        listener.Prefixes.Add("http://*:2999/");

        listener.Start();
        Console.WriteLine("Listening...");
        BeginGetContext(listener);
        Console.ReadLine();
    }

    public static void BeginGetContext(HttpListener listener)
    {
        Console.WriteLine("BeginGetContext...");
        while (listener.IsListening)
        {
            var asyncResult = listener.BeginGetContext(ListenerCallback, listener);
            asyncResult.AsyncWaitHandle.WaitOne();
        }
    }

    public static void ListenerCallback(IAsyncResult result)
    {
        Console.WriteLine("ListenerCallback...");

        autoEvent.Set();
        autoEvent.Reset();

        HttpListener listener = (HttpListener)result.AsyncState;
        HttpListenerContext context = listener.EndGetContext(result);

        // some code
        //...

        int res = WaitHandleNext();

        if (res == 0 || res == 1)
        {
            // Never get this line
            Console.WriteLine("Done...");
        }
        else if (res == WaitHandle.WaitTimeout)
        {
            // Always here
            Console.WriteLine("Done by timeout");
        }
    }

    private static int WaitHandleNext()
    {
        Console.WriteLine("WaitHandleNext...");
        int waitResult = WaitHandle.WaitAny(new[] { autoEvent, autoEvent2 }, TimeSpan.FromSeconds(30), false);
        return waitResult;
    }
}

If I put Thread.Sleep(10) between autoEvent.Set and autoEvent.Reset I get expected behaviour but not always.

autoEvent.Set();
Thread.Sleep(10);
autoEvent.Reset();

Stackoverflow link:
http://stackoverflow.com/questions/25017946/mono-autoresetevent-nonexpected-behaviour?noredirect=1#comment38904778_25017946
Comment 1 marcos.henrich 2014-08-06 06:15:19 UTC
Hi Damir.

Thank you for the bug report.

AutoResetEvent.Set does not guarantee that a waiting thread is resumed immediately, this is the case both on .NET and Mono.

When AutoResetEvent.Reset is called before another thread has a chance to resume, AutoResetEnvent.Set will have no effect.

Here is an adaptation of your example showing that it also fails in .NET.
https://gist.github.com/esdrubal/01969cb78601f3e8d965
Comment 2 marcos.henrich 2014-08-06 06:37:32 UTC
Although Mono should not fail on every call.
Comment 3 marcos.henrich 2014-08-06 09:23:27 UTC
Here is the test case for this issue.
https://gist.github.com/esdrubal/8ac96d62daf955ff07aa
Comment 4 marcos.henrich 2014-08-06 10:20:22 UTC
Damir your code seems to be working fine on .NET.

Nonetheless doing AutoResetEvent.Set and AutoResetEvent.Reset before WaitHandle.WaitAny is prone to race conditions so we decided not to reproduce this behavior.
Comment 5 damirainullin 2014-08-10 10:12:56 UTC
Hi Marcos.

Thank you for the answer.

I can't see "Done by timeout" line in your adaptation for .NET (https://gist.github.com/esdrubal/01969cb78601f3e8d965).
Also I run test-case https://gist.github.com/esdrubal/8ac96d62daf955ff07aa and I see that variable "i" always equals 19 or 18 in .NET and 8, 10, 18 etc in Mono. I suppose that realizations of AutoResetEvent/EventWaitHandle in Mono and . NET are strongly differ and it is very poorly for cross-platforming.

May be my code is not correct and leads to race condtition but I think that so different behaviour with Mono and .NET is very strange and can lead to misunderstanding by programmers in the future. When we use Mono we want to expect the same behaviour with Mono and .NET for cross-platform reasons.