C # Task.WaitAll () in .Net and Mono

Why does this code behave differently on Windows and Linux (using Mono)?

static void Main(string[] args)
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    Task[] tasks = new Task[1];

    tasks[0] = Task.Run(() =>
    {
        IPHostEntry iphe = Dns.GetHostEntry("8.8.8.8.dnsrbl.org");
    });
    Task.WaitAll(tasks, 2000);
    Console.WriteLine("Done in " + stopwatch.ElapsedMilliseconds + " ms");
}

      

8.8.8.8.dnsrbl.ru

is an example of a request that will eventually expire. I believe there is no working DNS server (or its firewall is blocking me).

Anyway, the point is not getting the result from the DNS server, the point is how it Task.WaitAll()

behaves in Windows and Mono while waiting for a task containing a call Dns.GetHostEntry()

.

On Windows, the program takes more or less 2 seconds to start when the query does not return a result within the timeout period (2 seconds). That is, Task.WaitAll with a timeout seems to work. Running this program on Linux with Mono takes 2 seconds to get an exit, but the program does not exit until the task completes. Why is this?

It seems that I get the same execution time regardless of whether I use Time.WaitAll the timeout or not.

The key is in Dns.GetHostEntry()

because it Task.WaitAll()

works as expected if I run a task with a Thread.Sleep()

simulated long running task. This works as expected:

tasks[0] = Task.Run(() => Thread.Sleep(10000));

      

Is there a way to force shutdown Task.WaitAll(Task[] tasks, int millisecondsTimeout)

on startup in Mono?

Edit: Task.WaitAll () does return after a timeout period, but the program does not terminate when running in mono (before Dns.GetHostEntry

times).

And it's not a compiler. I get the same result whether I compile with Visual Studio or the Mono C # compiler.

+3


source to share


1 answer


I am answering my own question, although the loan should go to Evk, who led me on the right path (thanks mate!)

The topic on this issue is bad, to say the least. The problem has nothing to do with Task.WaitAll

, but rather the implementation of Mono Dns.GetHostEntry

. As Evk said in a comment:

This means (most likely) that Dns.GetHostEntry

a new nonphonic thread is starting on linux. The program cannot be executed until all nonphonic streams have completed.

The method GetHostEntry()

is in the original Dns.cs file, and when called with a string, it calls GetHostByName

, which then calls GetHostByName_internal

, which is an external C function located in w32socket.c. Finally, mono_get_address_info

(on net-posix.c) gets called and we are lagging behind the libc function getaddrinfo

. Phew!

I don't see any new nonphonic threads that have been started, but I found this:

MONO_ENTER_GC_SAFE;
ret = getaddrinfo (hostname, service_name, &hints, &info);
MONO_EXIT_GC_SAFE;

      

MONO_ENTER_GC_SAFE

and MONO_EXIT_GC_SAFE

are macros defined in mono-threads-api.h



#define MONO_ENTER_GC_SAFE  \
    do {    \
        gpointer __gc_safe_dummy;   \
        gpointer __gc_safe_cookie = mono_threads_enter_gc_safe_region (&__gc_safe_dummy)

#define MONO_EXIT_GC_SAFE   \
        mono_threads_exit_gc_safe_region (__gc_safe_cookie, &__gc_safe_dummy);  \
    } while (0)

      

I didn't dig further, but I believe that Euk is right.

So the answer to my question is: Dns.GetHostEntry()

cannot be interrupted or canceled in Mono. The program that calls this method will not complete until all requests have been processed or completed. This is true. My guess is that this is due to the garbage collector (GC), which is probably running on a nephon thread and therefore cannot be canceled / terminated.

Edit: (A few days later) The reason for this was obvious as soon as I went to the man page for getaddrinfo

. This function returns a linked list to the result. This list is, of course, allocated on the heap and must be freed at some point to avoid a memory leak. Function for this freeaddrinfo

.

Anyway, thanks again Evk!

So how can we run multiple DNS queries in parallel with a timeout (using Mono)? Easy! This function can be called in a Task, and it will be happy to submit to WaitAll with a timeout:

private static string host(string query)
{
    ProcessStartInfo psi = new ProcessStartInfo("host", query);
    psi.UseShellExecute = false;
    psi.RedirectStandardOutput = true;
    Process p = Process.Start(psi);
    return p.StandardOutput.ReadToEnd();
}

      

+3


source







All Articles