D: Fix exception handling support.

The original code was ported from the C# module. It looks like it
tried to avoid reading TLS data by using a shared counter. However,
without also synchronizing on the counter check (or using atomics)
the code is racy. While the races might be benign (the thread that
sets the exception also increments the counter, so when there is
actually an exception, the visible value will always be non-zero
even if it is outdated), they are still undefined behavior,
strictly speaking. Additionally, just using TLS isn't expensive
either.
This commit is contained in:
David Nadlinger 2014-11-01 17:39:42 +01:00
commit 278308cfd3

View file

@ -93,31 +93,26 @@ private class SwigExceptionHelper {
static void setException(char* message) { static void setException(char* message) {
auto exception = new object.Exception(tango.stdc.stringz.fromStringz(message).dup); auto exception = new object.Exception(tango.stdc.stringz.fromStringz(message).dup);
exception.next = SwigPendingException.retrieve();
SwigPendingException.set(exception); SwigPendingException.set(exception);
} }
static void setIllegalArgumentException(char* message) { static void setIllegalArgumentException(char* message) {
auto exception = new tango.core.Exception.IllegalArgumentException(tango.stdc.stringz.fromStringz(message).dup); auto exception = new tango.core.Exception.IllegalArgumentException(tango.stdc.stringz.fromStringz(message).dup);
exception.next = SwigPendingException.retrieve();
SwigPendingException.set(exception); SwigPendingException.set(exception);
} }
static void setIllegalElementException(char* message) { static void setIllegalElementException(char* message) {
auto exception = new tango.core.Exception.IllegalElementException(tango.stdc.stringz.fromStringz(message).dup); auto exception = new tango.core.Exception.IllegalElementException(tango.stdc.stringz.fromStringz(message).dup);
exception.next = SwigPendingException.retrieve();
SwigPendingException.set(exception); SwigPendingException.set(exception);
} }
static void setIOException(char* message) { static void setIOException(char* message) {
auto exception = new tango.core.Exception.IOException(tango.stdc.stringz.fromStringz(message).dup); auto exception = new tango.core.Exception.IOException(tango.stdc.stringz.fromStringz(message).dup);
exception.next = SwigPendingException.retrieve();
SwigPendingException.set(exception); SwigPendingException.set(exception);
} }
static void setNoSuchElementException(char* message) { static void setNoSuchElementException(char* message) {
auto exception = new tango.core.Exception.NoSuchElementException(tango.stdc.stringz.fromStringz(message).dup); auto exception = new tango.core.Exception.NoSuchElementException(tango.stdc.stringz.fromStringz(message).dup);
exception.next = SwigPendingException.retrieve();
SwigPendingException.set(exception); SwigPendingException.set(exception);
} }
} }
@ -125,51 +120,31 @@ private class SwigExceptionHelper {
package class SwigPendingException { package class SwigPendingException {
public: public:
static this() { static this() {
m_sPendingCount = 0;
m_sPendingException = new ThreadLocalData(null); m_sPendingException = new ThreadLocalData(null);
} }
static bool isPending() { static bool isPending() {
bool pending = false; return m_sPendingException.val !is null;
if (m_sPendingCount > 0) {
if (m_sPendingException.val !is null) {
pending = true;
}
}
return pending;
} }
static void set(object.Exception e) { static void set(object.Exception e) {
if (m_sPendingException.val !is null) { auto pending = m_sPendingException.val;
throw new object.Exception("FATAL: An earlier pending exception from C/C++ code " ~ if (pending !is null) {
"was missed and thus not thrown (" ~ m_sPendingException.val.classinfo.name ~ e.next = pending;
": " ~ m_sPendingException.val.msg ~ ")!", e); throw new object.Exception("FATAL: An earlier pending exception from C/C++ " ~
"code was missed and thus not thrown (" ~ pending.classinfo.name ~ ": " ~
pending.msg ~ ")!", e);
} }
m_sPendingException.val = e; m_sPendingException.val = e;
synchronized {
++m_sPendingCount;
}
} }
static object.Exception retrieve() { static object.Exception retrieve() {
object.Exception e = null; auto e = m_sPendingException.val;
if (m_sPendingCount > 0) { m_sPendingException.val = null;
if (m_sPendingException.val !is null) {
e = m_sPendingException.val;
m_sPendingException.val = null;
synchronized {
--m_sPendingCount;
}
}
}
return e; return e;
} }
private: private:
// The pending exception counter is stored thread-global.
static int m_sPendingCount;
// The reference to the pending exception (if any) is stored thread-local. // The reference to the pending exception (if any) is stored thread-local.
alias tango.core.Thread.ThreadLocal!(object.Exception) ThreadLocalData; alias tango.core.Thread.ThreadLocal!(object.Exception) ThreadLocalData;
static ThreadLocalData m_sPendingException; static ThreadLocalData m_sPendingException;
@ -195,8 +170,7 @@ private class SwigExceptionHelper {
} }
static void setException(const char* message) { static void setException(const char* message) {
auto exception = new object.Exception(std.conv.to!string(message).idup); auto exception = new object.Exception(std.conv.to!string(message));
exception.next = SwigPendingException.retrieve();
SwigPendingException.set(exception); SwigPendingException.set(exception);
} }
} }
@ -204,53 +178,31 @@ private class SwigExceptionHelper {
package struct SwigPendingException { package struct SwigPendingException {
public: public:
static this() { static this() {
m_sPendingCount = 0;
m_sPendingException = null; m_sPendingException = null;
} }
static bool isPending() { static bool isPending() {
bool pending = false; return m_sPendingException !is null;
if (m_sPendingCount > 0) {
if (m_sPendingException !is null) {
pending = true;
}
}
return pending;
} }
static void set(object.Exception e) { static void set(object.Exception e) {
if (m_sPendingException !is null) { if (m_sPendingException !is null) {
e.next = m_sPendingException;
throw new object.Exception("FATAL: An earlier pending exception from C/C++ code " ~ throw new object.Exception("FATAL: An earlier pending exception from C/C++ code " ~
"was missed and thus not thrown (" ~ m_sPendingException.classinfo.name ~ "was missed and thus not thrown (" ~ m_sPendingException.classinfo.name ~
": " ~ m_sPendingException.msg ~ ")!", e); ": " ~ m_sPendingException.msg ~ ")!", e);
} }
m_sPendingException = e; m_sPendingException = e;
synchronized {
import core.atomic;
core.atomic.atomicOp!"+="(m_sPendingCount, 1);
}
} }
static object.Exception retrieve() { static object.Exception retrieve() {
object.Exception e = null; auto e = m_sPendingException;
if (m_sPendingCount > 0) { m_sPendingException = null;
if (m_sPendingException !is null) {
e = m_sPendingException;
m_sPendingException = null;
synchronized {
import core.atomic;
core.atomic.atomicOp!"-="(m_sPendingCount, 1);
}
}
}
return e; return e;
} }
private: private:
// The pending exception counter is stored thread-global.
static shared int m_sPendingCount;
// The reference to the pending exception (if any) is stored thread-local. // The reference to the pending exception (if any) is stored thread-local.
static object.Exception m_sPendingException; static object.Exception m_sPendingException;
} }