Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Commit c8443ad

Browse files
committed
Fix Issue 15104 - Fiber context switch in finally blocks breaks EH
This is a backport of dlang#1397.
1 parent 5cfa460 commit c8443ad

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

src/core/thread.d

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,17 @@ private
113113
//
114114
extern (C) void rt_moduleTlsCtor();
115115
extern (C) void rt_moduleTlsDtor();
116+
117+
/**
118+
* Hook for whatever EH implementation is used to save/restore some data
119+
* per stack.
120+
*
121+
* Params:
122+
* newContext = The return value of the prior call to this function
123+
* where the stack was last swapped out, or null when a fiber stack
124+
* is switched in for the first time.
125+
*/
126+
extern(C) void* _d_eh_swapContext(void* newContext) nothrow;
116127
}
117128

118129

@@ -1478,6 +1489,7 @@ private:
14781489
}
14791490
body
14801491
{
1492+
m_curr.ehContext = _d_eh_swapContext(c.ehContext);
14811493
c.within = m_curr;
14821494
m_curr = c;
14831495
}
@@ -1492,6 +1504,7 @@ private:
14921504
{
14931505
Context* c = m_curr;
14941506
m_curr = c.within;
1507+
c.ehContext = _d_eh_swapContext(m_curr.ehContext);
14951508
c.within = null;
14961509
}
14971510

@@ -1511,6 +1524,12 @@ private:
15111524
{
15121525
void* bstack,
15131526
tstack;
1527+
1528+
/// Slot for the EH implementation to keep some state for each stack
1529+
/// (will be necessary for exception chaining, etc.). Opaque as far as
1530+
/// we are concerned here.
1531+
void* ehContext;
1532+
15141533
Context* within;
15151534
Context* next,
15161535
prev;
@@ -5326,6 +5345,43 @@ version (Win32) {
53265345
}
53275346
}
53285347

5348+
// Test exception chaining when switching contexts in finally blocks.
5349+
unittest
5350+
{
5351+
static void throwAndYield(string msg) {
5352+
try {
5353+
throw new Exception(msg);
5354+
} finally {
5355+
Fiber.yield();
5356+
}
5357+
}
5358+
5359+
static void fiber(string name) {
5360+
try {
5361+
try {
5362+
throwAndYield(name ~ ".1");
5363+
} finally {
5364+
throwAndYield(name ~ ".2");
5365+
}
5366+
} catch (Exception e) {
5367+
assert(e.msg == name ~ ".1");
5368+
assert(e.next);
5369+
assert(e.next.msg == name ~ ".2");
5370+
assert(!e.next.next);
5371+
}
5372+
}
5373+
5374+
auto first = new Fiber(() => fiber("first"));
5375+
auto second = new Fiber(() => fiber("second"));
5376+
first.call();
5377+
second.call();
5378+
first.call();
5379+
second.call();
5380+
first.call();
5381+
second.call();
5382+
assert(first.state == Fiber.State.TERM);
5383+
assert(second.state == Fiber.State.TERM);
5384+
}
53295385

53305386
// Test Fiber resetting
53315387
unittest

src/ldc/eh/common.d

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ struct ActiveCleanupBlock {
327327
/// currently done.
328328
ActiveCleanupBlock* innermostCleanupBlock = null;
329329

330+
/// innermostCleanupBlock is per-stack, not per-thread, and as such needs to be
331+
/// swapped out on fiber context switches.
332+
extern(C) void* _d_eh_swapContext(void* newContext) nothrow
333+
{
334+
auto old = innermostCleanupBlock;
335+
innermostCleanupBlock = cast(ActiveCleanupBlock*)newContext;
336+
return old;
337+
}
338+
330339
/// During the search phase of unwinding, points to the currently active cleanup
331340
/// block (i.e. somewhere in the innermostCleanupBlock linked list, but possibly
332341
/// not at the beginning if the search for the catch block has already continued

src/rt/deh_win32.d

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,22 @@ enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1);
372372
* are shorter than D exceptions, for example.
373373
* (3) System exceptions don't have any space for a pointer to a D object.
374374
* So we cannot store the collision information in the exception record.
375-
* (4) it's important that this list is thread-local.
375+
* (4) it's important that this list is fiber-local.
376376
*/
377377

378378
EXCEPTION_RECORD * inflightExceptionList = null;
379379

380+
/***********************************
381+
* Switch out inflightExceptionList on fiber context switches.
382+
*/
383+
extern(C) void* _d_eh_swapContext(void* newContext) nothrow
384+
{
385+
auto old = inflightExceptionList;
386+
inflightExceptionList = cast(EXCEPTION_RECORD*)newContext;
387+
return old;
388+
}
389+
390+
380391
/***********************************
381392
* Find the first non-collateral exception in the list. If the last
382393
* entry in the list has the EXCEPTION_COLLATERAL bit set, it means

src/rt/deh_win64_posix.d

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ private
9090
}
9191

9292
InFlight* __inflight = null;
93+
94+
/// __inflight is per-stack, not per-thread, and as such needs to be
95+
/// swapped out on fiber context switches.
96+
extern(C) void* _d_eh_swapContext(void* newContext) nothrow
97+
{
98+
auto old = __inflight;
99+
__inflight = cast(InFlight*)newContext;
100+
return old;
101+
}
93102
}
94103

95104
void terminate()

0 commit comments

Comments
 (0)