www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 5317] New: Assertion is not work in a function called by std.concurrency.spawn

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317

           Summary: Assertion is not work in a function called by
                    std.concurrency.spawn
           Product: D
           Version: D2
          Platform: Other
        OS/Version: Windows
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Phobos
        AssignedTo: nobody puremagic.com
        ReportedBy: rayerd.wiz gmail.com


--- Comment #0 from Haruki Shigemori <rayerd.wiz gmail.com> 2010-12-04 11:30:04
PST ---
// a.d
import std.stdio, std.concurrency;
void f()
{
//    try
//    {
        writeln("1");
        assert(false);
//    }
//    catch(Throwable t)
//    {
//      writeln(t);
//      throw t;
//    }
}
void main()
{
    auto tid = spawn(&f);
    readln();
}

$ dmd a.d
1
<=== Needs assertion failure message here!

I found that assertion is not work in a function called by spawn.
You will get the following result if you remove comment keywords.

$ dmd a.d
1
core.exception.AssertError a(7): Assertion failure

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 04 2010
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317


Sean Kelly <sean invisibleduck.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |sean invisibleduck.org


--- Comment #1 from Sean Kelly <sean invisibleduck.org> 2011-02-03 15:55:17 PST
---
This works for me using the current revision.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Feb 03 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317



--- Comment #2 from Haruki Shigemori <rayerd.wiz gmail.com> 2011-02-04 00:04:26
PST ---
Oh really?

import std.stdio, std.concurrency, std.conv;
void f()
{
    int a = to!int("2");
    assert(a == 1);
}
void main()
{
    auto tid = spawn(&f);
    readln();
}

Does this code throw AssertError?
I was not able to observe such behavior.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Feb 04 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317


Andrej Mitrovic <andrej.mitrovich gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |andrej.mitrovich gmail.com


--- Comment #3 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2011-05-24
20:15:58 PDT ---
I can't reproduce your first test case, but I can reproduce your second one. 

It seems as if the main thread eats up the exceptions. Maybe it has to do with
buffered console output which isn't flushed properly? Here's another test case:

import std.stdio, std.concurrency, std.conv, core.thread;
void f()
{
    assert(0);
}
void main()
{
    auto tid = spawn(&f);
    Thread.sleep( dur!("seconds")( 2 ) );
}

If you remove the call to sleep, the assertion will throw. So while the main
thread is locked, something is blocking the exceptions from propagating? I've
no idea..

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 24 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317



--- Comment #4 from Sean Kelly <sean invisibleduck.org> 2011-05-25 16:29:53 PDT
---
When the assertion isn't propagated, what's likely happening is that the thread
instance is being discarded because the thread is complete.  If the app
terminates quickly enough then it beats the spawned thread to termination and
thread_joinAll() finds the Error waiting to be re-thrown.

At the moment, D has fairly lax rules for propagating exceptions that caused a
thread to terminate--it's assumed that if you care about the result of a thread
you'll call join(), which will always re-throw the exception unless told not
to.  But if you don't retain a reference to the thread then its result will
remain unknown.  The exception to this rule is that a spawned thread will
receive word that its owner has terminated, and linked threads will notify one
another on termination.

I could retain these uncaught exceptions and re-throw them to the main thread
on app termination, but I don't see much utility in it.  If something horrible
happens in a thread then the user either wants to shutdown the entire app
immediately for fear of memory corruption (which increasingly unlikely with
thread-local static data), or he wants specific interested parties to be
notified that the thread terminated unexpectedly (which is handled by linking
via std.concurrency), or he doesn't care.  Perhaps I should add a hook so the
user can supply an uncaughtExceptionHandler?  I'd avoided doing this in the
past because it would interact weirdly with the current behavior of join()
re-throwing uncaught exceptions, but it's an option.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 25 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317



--- Comment #5 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2011-06-04
12:33:33 PDT ---
This might not be directly relevant to this bug report, but I thought I'd share
the story anyway. I currently have this in one of my projects:

An extern(C) callback is passed to a device driver. The user-code then calls
driver.start(), and the driver starts a new high-priority/ISR routine which
calls the callback at some predetermined rate.

Now, in my main() function I have this (pseudocode):

driver.start()
scope(exit) driver.stop();  // Calling this before app exit on started drivers
is crucial
while (engineRunning) { }   // shared bool of the engine state

That works well for the user thread, if an exception is thrown in the user
thread scope exit will be ran and the driver is released. 

But if in the callback thread an exception is thrown and is uncaught it will
shut down my application but it won't call driver.stop(), because it's in a
different thread which main doesn't directly know about.

I've had this happen to me several times with ASIO Audio drivers. Most of the
times, the app will quit but appear frozen in taskbar and the device driver
won't get released even if I do a cold-reboot of my soundcard (it's external
with its own power source). It then takes a few minutes or a pc-reboot for the
driver to actually get released.

But worse things have happened, I've had several BSODs as well. It wasn't
obvious to me what was going on until I've realized that driver.stop() never
gets called. 

So now my current workaround is to wrap any code in the callback with a
try..catch:

extern(C) callback(args..)
{
    try
    {
        realCallback(args);
    }
    catch (Throwable)
    {
        engineRunning = false;
    }
}

If an exception is thrown the app can call driver.stop() (since the user thread
keeps checking the state ofr the shared engineRunning boolean), and then the
app can quit safely.

Quitting forcefully on any errors might not be the best thing to do. But maybe
my use-case is a special one, and this doesn't happen that frequently outside
of using device drivers or other types of external resources.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 04 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317



--- Comment #6 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2011-06-04
12:34:49 PDT ---
s/taskbar/taskmanager

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 04 2011
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5317


Sean Kelly <sean invisibleduck.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |WONTFIX


--- Comment #7 from Sean Kelly <sean invisibleduck.org> 2011-08-30 10:39:03 PDT
---
If your API requires a cleanup routine to be run then you can either expect the
user to trap any errors and call the cleanup (and break if they fail to do so)
or you can provide a failsafe cleanup mechanism like the one you've outlined
below.  I'm inclined to think that your approach is the right one.

Regarding exception propagating in general... I think there are two reasonable
default behaviors.  One is to do as now where the exception is trapped and
re-thrown when join is called, based on the assumption that if a programmer
cares about the result of a thread he'll explicitly join it at some point (and
holding that reference will prevent premature cleanup of the terminated
thread's exception, if any).  The other is to terminate the app if an unhanded
exception terminates a thread.  I'm inclined to favor the former behavior
because it puts more control in the hands of the programmer, admittedly at the
risk of Bad Things if the app is just poorly written and the unhandled
exception is something important.

Since std.concurrency provides options for the owner thread to receive a
message when spawned threads terminate (via link), I'm going to consider this
issue as addressed.  If you have specific ideas for how the current behavior
could be enhanced, please open a new ticket.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Aug 30 2011