www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Coroutines and exceptions

reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Hi.

I've been playing around with an implementation of coroutines in D the
last day or two, and I've got it to the point where it works pretty well
under gdc, and under dmd with a few bugs to track down.  But I've hit a
brick will with exceptions.

See, I was kinda hoping that D found exception handlers by going back up
the stack frames... although after pouring over the disassembly in gdb,
it looks like D registers the exception handlers as it meets them.

The problem with this is that the coroutine library allows you to jump
from one part of the code to another, which ends up screwing up the
order of exception handlers.  For example (simplified, mind you):

void coro_a()
{
    try
    {
    	co_call(main); // Jump back into main just after co_call
    }
    except( Exception e )
    {
        writefln("Exception in coro_a: %s", e.toString());
    }
}

void main()
{
    try
    {
        co_call(coro_a); // Jump into coro_a
        throw new Exception("Oh noes!");
    }
    catch( Exception e )
    {
        writefln("Exception in main: %s", e.toString());
    }
}

If you run that, you end up getting "Exception in coro_a: Oh noes!"
instead of "Exception in main: Oh noes!" as you would expect.

This is pretty much a show-stopper for coroutines; if exceptions don't
work with them, then the library won't be much good.

SO, the question is: assuming that you register exception handlers on to
some kind of internal stack of addresses to jump to in the event of an
exception, is there any possibility of being able to, say... swap out
that stack for a different one at runtime?

The CPU doesn't seem to mind when I do that to the program stack :P

Please and thankyou,

	-- Daniel Keep

-- 

v1sw5+8Yhw5ln4+5pr6OFma8u6+7Lw4Tm6+7l6+7D
a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP    http://hackerkey.com/
Apr 28 2006
next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Oops, forgot I was playing around with the gdc-compiled binary, not the
dmd one :P  Had a poke around a dmd-generated executable in windbg, but
couldn't actually find anything relating to exception handlers.  In any
case, the question still stands :)

	-- Daniel

Daniel Keep wrote:
 Hi.
 
 I've been playing around with an implementation of coroutines in D the
 last day or two, and I've got it to the point where it works pretty well
 under gdc, and under dmd with a few bugs to track down.  But I've hit a
 brick will with exceptions.
 
 See, I was kinda hoping that D found exception handlers by going back up
 the stack frames... although after pouring over the disassembly in gdb,
 it looks like D registers the exception handlers as it meets them.
 
 The problem with this is that the coroutine library allows you to jump
 from one part of the code to another, which ends up screwing up the
 order of exception handlers.  For example (simplified, mind you):
 
 void coro_a()
 {
     try
     {
     	co_call(main); // Jump back into main just after co_call
     }
     except( Exception e )
     {
         writefln("Exception in coro_a: %s", e.toString());
     }
 }
 
 void main()
 {
     try
     {
         co_call(coro_a); // Jump into coro_a
         throw new Exception("Oh noes!");
     }
     catch( Exception e )
     {
         writefln("Exception in main: %s", e.toString());
     }
 }
 
 If you run that, you end up getting "Exception in coro_a: Oh noes!"
 instead of "Exception in main: Oh noes!" as you would expect.
 
 This is pretty much a show-stopper for coroutines; if exceptions don't
 work with them, then the library won't be much good.
 
 SO, the question is: assuming that you register exception handlers on to
 some kind of internal stack of addresses to jump to in the event of an
 exception, is there any possibility of being able to, say... swap out
 that stack for a different one at runtime?
 
 The CPU doesn't seem to mind when I do that to the program stack :P
 
 Please and thankyou,
 
 	-- Daniel Keep
 

-- v1sw5+8Yhw5ln4+5pr6OFma8u6+7Lw4Tm6+7l6+7D a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Apr 28 2006
prev sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Daniel Keep wrote:

 
 Hi.
 
 I've been playing around with an implementation of coroutines in D the
 last day or two, and I've got it to the point where it works pretty well
 under gdc, and under dmd with a few bugs to track down.  But I've hit a
 brick will with exceptions.
 
 See, I was kinda hoping that D found exception handlers by going back up
 the stack frames... although after pouring over the disassembly in gdb,
 it looks like D registers the exception handlers as it meets them.
 
 The problem with this is that the coroutine library allows you to jump
 from one part of the code to another, which ends up screwing up the
 order of exception handlers.  For example (simplified, mind you):
 
 void coro_a()
 {
     try
     {
     co_call(main); // Jump back into main just after co_call
     }
     except( Exception e )
     {
         writefln("Exception in coro_a: %s", e.toString());
     }
 }
 
 void main()
 {
     try
     {
         co_call(coro_a); // Jump into coro_a
         throw new Exception("Oh noes!");
     }
     catch( Exception e )
     {
         writefln("Exception in main: %s", e.toString());
     }
 }
 
 If you run that, you end up getting "Exception in coro_a: Oh noes!"
 instead of "Exception in main: Oh noes!" as you would expect.

I have been working on getting Mikola Lysenko's StackThread working on Linux, and I just tested the exception handling (which I did not have to do anything with, it just worked the way he had implemented it. Unless this way is not what you want, and your couroutines might not be altogether the same thing as this StackThread (I tried to send you a mail, but don't know if it will reach you...). Anyway, the test code goes like this (in the unittest): try { void exceptions() { writefln("Testing exception handling..."); throw new Exception(std.string.format("Test exception, line %s", __LINE__)); StackThread.yield(); } StackThread t = new StackThread(&exceptions); t.resume(); StackThread.run(); assert(false); } catch(Exception e) { writefln(e); } And the result is this: Testing exception handling... Test exception, line 412 The assert is not triggered. I do agree that your result seems somewhat strange, though... -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsivi
Apr 29 2006
parent reply mclysenk mtu.edu writes:
In article <e2vh64$2vhe$1 digitaldaemon.com>, Lars Ivar Igesund says...
Daniel Keep wrote:

 
 Hi.
 
 I've been playing around with an implementation of coroutines in D the
 last day or two, and I've got it to the point where it works pretty well
 under gdc, and under dmd with a few bugs to track down.  But I've hit a
 brick will with exceptions.
 
 See, I was kinda hoping that D found exception handlers by going back up
 the stack frames... although after pouring over the disassembly in gdb,
 it looks like D registers the exception handlers as it meets them.
 
 The problem with this is that the coroutine library allows you to jump
 from one part of the code to another, which ends up screwing up the
 order of exception handlers.  For example (simplified, mind you):
 
 void coro_a()
 {
     try
     {
     co_call(main); // Jump back into main just after co_call
     }
     except( Exception e )
     {
         writefln("Exception in coro_a: %s", e.toString());
     }
 }
 
 void main()
 {
     try
     {
         co_call(coro_a); // Jump into coro_a
         throw new Exception("Oh noes!");
     }
     catch( Exception e )
     {
         writefln("Exception in main: %s", e.toString());
     }
 }
 
 If you run that, you end up getting "Exception in coro_a: Oh noes!"
 instead of "Exception in main: Oh noes!" as you would expect.

I have been working on getting Mikola Lysenko's StackThread working on Linux, and I just tested the exception handling (which I did not have to do anything with, it just worked the way he had implemented it. Unless this way is not what you want, and your couroutines might not be altogether the same thing as this StackThread (I tried to send you a mail, but don't know if it will reach you...). Anyway, the test code goes like this (in the unittest): try { void exceptions() { writefln("Testing exception handling..."); throw new Exception(std.string.format("Test exception, line %s", __LINE__)); StackThread.yield(); } StackThread t = new StackThread(&exceptions); t.resume(); StackThread.run(); assert(false); } catch(Exception e) { writefln(e); } And the result is this: Testing exception handling... Test exception, line 412 The assert is not triggered. I do agree that your result seems somewhat strange, though... -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsivi

That is because on windows you need to save the stack top & bottom when you change contexts. They are stored at FS[4] and FS[8]. Also it would be a good idea to relink the SEH chain when you create a new coroutine, so the top level is tied to the SEH that originally called the coroutine. As far as I can tell, this behaviour is not very well documented, but for more info on structured exception handling, you can check out http://www.microsoft.com/msj/0197/exception/exception.aspx . I'm currently working on extending the StackThread system to support priority based scheduling and Linux. At the moment, I have a specification drawn up, along with a basic architecture/implementation. -Mikola Lysenko
May 01 2006
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Cool; thanks for that.  Guess I'll have some reading to do when I get
home :)

	-- Daniel

mclysenk mtu.edu wrote:
 In article <e2vh64$2vhe$1 digitaldaemon.com>, Lars Ivar Igesund says...
 Daniel Keep wrote:

 Hi.

 I've been playing around with an implementation of coroutines in D the
 last day or two, and I've got it to the point where it works pretty well
 under gdc, and under dmd with a few bugs to track down.  But I've hit a
 brick will with exceptions.

 See, I was kinda hoping that D found exception handlers by going back up
 the stack frames... although after pouring over the disassembly in gdb,
 it looks like D registers the exception handlers as it meets them.

 The problem with this is that the coroutine library allows you to jump
 from one part of the code to another, which ends up screwing up the
 order of exception handlers.  For example (simplified, mind you):

 void coro_a()
 {
     try
     {
     co_call(main); // Jump back into main just after co_call
     }
     except( Exception e )
     {
         writefln("Exception in coro_a: %s", e.toString());
     }
 }

 void main()
 {
     try
     {
         co_call(coro_a); // Jump into coro_a
         throw new Exception("Oh noes!");
     }
     catch( Exception e )
     {
         writefln("Exception in main: %s", e.toString());
     }
 }

 If you run that, you end up getting "Exception in coro_a: Oh noes!"
 instead of "Exception in main: Oh noes!" as you would expect.

Linux, and I just tested the exception handling (which I did not have to do anything with, it just worked the way he had implemented it. Unless this way is not what you want, and your couroutines might not be altogether the same thing as this StackThread (I tried to send you a mail, but don't know if it will reach you...). Anyway, the test code goes like this (in the unittest): try { void exceptions() { writefln("Testing exception handling..."); throw new Exception(std.string.format("Test exception, line %s", __LINE__)); StackThread.yield(); } StackThread t = new StackThread(&exceptions); t.resume(); StackThread.run(); assert(false); } catch(Exception e) { writefln(e); } And the result is this: Testing exception handling... Test exception, line 412 The assert is not triggered. I do agree that your result seems somewhat strange, though... -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsivi

That is because on windows you need to save the stack top & bottom when you change contexts. They are stored at FS[4] and FS[8]. Also it would be a good idea to relink the SEH chain when you create a new coroutine, so the top level is tied to the SEH that originally called the coroutine. As far as I can tell, this behaviour is not very well documented, but for more info on structured exception handling, you can check out http://www.microsoft.com/msj/0197/exception/exception.aspx . I'm currently working on extending the StackThread system to support priority based scheduling and Linux. At the moment, I have a specification drawn up, along with a basic architecture/implementation. -Mikola Lysenko

-- v1sw5+8Yhw5ln4+5pr6OFma8u6+7Lw4Tm6+7l6+7D a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
May 02 2006