Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1397 from klickverbot/fix-15104
Browse files Browse the repository at this point in the history
Fix Issue 15104 - Fiber context switch in finally blocks breaks EH
  • Loading branch information
MartinNowak committed Sep 25, 2015
2 parents 92ccb0f + f6633ab commit f0f8eda
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
56 changes: 56 additions & 0 deletions src/core/thread.d
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ private
//
extern (C) void rt_moduleTlsCtor();
extern (C) void rt_moduleTlsDtor();

/**
* Hook for whatever EH implementation is used to save/restore some data
* per stack.
*
* Params:
* newContext = The return value of the prior call to this function
* where the stack was last swapped out, or null when a fiber stack
* is switched in for the first time.
*/
extern(C) void* _d_eh_swapContext(void* newContext) nothrow;
}


Expand Down Expand Up @@ -1458,6 +1469,7 @@ private:
}
body
{
m_curr.ehContext = _d_eh_swapContext(c.ehContext);
c.within = m_curr;
m_curr = c;
}
Expand All @@ -1472,6 +1484,7 @@ private:
{
Context* c = m_curr;
m_curr = c.within;
c.ehContext = _d_eh_swapContext(m_curr.ehContext);
c.within = null;
}

Expand All @@ -1491,6 +1504,12 @@ private:
{
void* bstack,
tstack;

/// Slot for the EH implementation to keep some state for each stack
/// (will be necessary for exception chaining, etc.). Opaque as far as
/// we are concerned here.
void* ehContext;

Context* within;
Context* next,
prev;
Expand Down Expand Up @@ -4994,6 +5013,43 @@ version (Win32) {
}
}

// Test exception chaining when switching contexts in finally blocks.
unittest
{
static void throwAndYield(string msg) {
try {
throw new Exception(msg);
} finally {
Fiber.yield();
}
}

static void fiber(string name) {
try {
try {
throwAndYield(name ~ ".1");
} finally {
throwAndYield(name ~ ".2");
}
} catch (Exception e) {
assert(e.msg == name ~ ".1");
assert(e.next);
assert(e.next.msg == name ~ ".2");
assert(!e.next.next);
}
}

auto first = new Fiber(() => fiber("first"));
auto second = new Fiber(() => fiber("second"));
first.call();
second.call();
first.call();
second.call();
first.call();
second.call();
assert(first.state == Fiber.State.TERM);
assert(second.state == Fiber.State.TERM);
}

// Test Fiber resetting
unittest
Expand Down
13 changes: 12 additions & 1 deletion src/rt/deh_win32.d
Original file line number Diff line number Diff line change
Expand Up @@ -373,11 +373,22 @@ enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1);
* are shorter than D exceptions, for example.
* (3) System exceptions don't have any space for a pointer to a D object.
* So we cannot store the collision information in the exception record.
* (4) it's important that this list is thread-local.
* (4) it's important that this list is fiber-local.
*/

EXCEPTION_RECORD * inflightExceptionList = null;

/***********************************
* Switch out inflightExceptionList on fiber context switches.
*/
extern(C) void* _d_eh_swapContext(void* newContext) nothrow
{
auto old = inflightExceptionList;
inflightExceptionList = cast(EXCEPTION_RECORD*)newContext;
return old;
}


/***********************************
* Find the first non-collateral exception in the list. If the last
* entry in the list has the EXCEPTION_COLLATERAL bit set, it means
Expand Down
9 changes: 9 additions & 0 deletions src/rt/deh_win64_posix.d
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ private
}

InFlight* __inflight = null;

/// __inflight is per-stack, not per-thread, and as such needs to be
/// swapped out on fiber context switches.
extern(C) void* _d_eh_swapContext(void* newContext) nothrow
{
auto old = __inflight;
__inflight = cast(InFlight*)newContext;
return old;
}
}

void terminate()
Expand Down

0 comments on commit f0f8eda

Please sign in to comment.