www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Any way to peek at the exception currently being thrown?

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
One possible idiom is to use scope(failure) and take a look at the 
exception being thrown.

Is there a way to do that? If not, I assume it shouldn't be difficult to 
add to druntime?
Jun 01
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/1/21 12:36 PM, Andrei Alexandrescu wrote:
 One possible idiom is to use scope(failure) and take a look at the 
 exception being thrown.
I've wanted that too! Only thing I think you can do is the awkward try/catch and rethrow.
 
 Is there a way to do that? If not, I assume it shouldn't be difficult to 
 add to druntime?
Not sure what you mean. -Steve
Jun 01
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/1/21 12:46 PM, Steven Schveighoffer wrote:
 On 6/1/21 12:36 PM, Andrei Alexandrescu wrote:
 Is there a way to do that? If not, I assume it shouldn't be difficult 
 to add to druntime?
Not sure what you mean.
The runtime certainly has that information, so the only matter is exposing it via an API.
Jun 02
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:
 On 6/1/21 12:46 PM, Steven Schveighoffer wrote:
 On 6/1/21 12:36 PM, Andrei Alexandrescu wrote:
 Is there a way to do that? If not, I assume it shouldn't be difficult 
 to add to druntime?
Not sure what you mean.
The runtime certainly has that information, so the only matter is exposing it via an API.
Yeah, OK. I thought you meant a solution could be had solely with druntime. -Steve
Jun 02
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:

 The runtime certainly has that information, so the only matter is 
 exposing it via an API.
Seems obvious for an API: scope(failure, e) // use exception e Could also extend to scope success scope(success, retval) ... Not sure scope(exit) would work, as only one of the two parameters would be valid. But then again, just do scope(success) and scope(failure) at the same time. -Steve
Jun 02
next sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Wednesday, 2 June 2021 at 16:47:56 UTC, Steven Schveighoffer 
wrote:
 On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:

 The runtime certainly has that information, so the only matter 
 is exposing it via an API.
Seems obvious for an API: scope(failure, e) // use exception e Could also extend to scope success scope(success, retval) ... Not sure scope(exit) would work, as only one of the two parameters would be valid. But then again, just do scope(success) and scope(failure) at the same time. -Steve
+1 for scope(failure, ex)
Jun 02
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/2/21 12:47 PM, Steven Schveighoffer wrote:
 On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:
 
 The runtime certainly has that information, so the only matter is 
 exposing it via an API.
Seems obvious for an API: scope(failure, e) // use exception e
I am hoping for an API that would allow picking the current exception from anywhere: Throwable currentThrowable(); Returns null if none, or the current exception being thrown.
Jun 03
next sibling parent Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Friday, 4 June 2021 at 06:02:27 UTC, Andrei Alexandrescu wrote:
 On 6/2/21 12:47 PM, Steven Schveighoffer wrote:
 On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:
 
 The runtime certainly has that information, so the only 
 matter is exposing it via an API.
Seems obvious for an API: scope(failure, e) // use exception e
I am hoping for an API that would allow picking the current exception from anywhere: Throwable currentThrowable(); Returns null if none, or the current exception being thrown.
Making exceptions a runtime feature instead of a language feature is not a good design.
Jun 04
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/4/21 2:02 AM, Andrei Alexandrescu wrote:
 On 6/2/21 12:47 PM, Steven Schveighoffer wrote:
 On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:

 The runtime certainly has that information, so the only matter is 
 exposing it via an API.
Seems obvious for an API: scope(failure, e) // use exception e
I am hoping for an API that would allow picking the current exception from anywhere: Throwable currentThrowable(); Returns null if none, or the current exception being thrown.
Doesn't that mean that throwing an exception has to stuff the exception somewhere in TLS while in flight? I assumed it was just something stored as a local variable. -Steve
Jun 04
parent reply deadalnix <deadalnix gmail.com> writes:
On Friday, 4 June 2021 at 13:42:31 UTC, Steven Schveighoffer 
wrote:
 Doesn't that mean that throwing an exception has to stuff the 
 exception somewhere in TLS while in flight?

 I assumed it was just something stored as a local variable.

 -Steve
Where would that local variable be, on stack? It has to be on TLS.
Jun 04
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/4/21 5:24 PM, deadalnix wrote:
 On Friday, 4 June 2021 at 13:42:31 UTC, Steven Schveighoffer wrote:
 Doesn't that mean that throwing an exception has to stuff the 
 exception somewhere in TLS while in flight?

 I assumed it was just something stored as a local variable.
Where would that local variable be, on stack? It has to be on TLS.
Why wouldn't it be on the stack? catch(Exception e) { // is e on the stack or TLS? } Honestly though, stack unwinding is black magic to me. I don't know how it works, but I'm pretty sure it worked before TLS was a thing in D. -Steve
Jun 04
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jun 04, 2021 at 06:06:32PM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
[...]
 Honestly though, stack unwinding is black magic to me. I don't know
 how it works, but I'm pretty sure it worked before TLS was a thing in
 D.
AFAIK, stack unwinding happens on the runtime stack, which is TLS (because each thread by definition has its own stack). It's not *that* magic; it's just done at a lower level than usual language constructs. It basically consists of starting with the current stack pointer (pointing to the top of the stack) and popping off stack frames until it finds a stack frame that registered a catch block. At each stack frame, it runs any dtors that may be present in the frame in order to clean up any local objects that need destruction. Where the reference to the exception object is held during this process is generally implementation detail, but I'd expect it would be either in CPU registers or else in TLS somewhere, since it wouldn't make sense for other threads to be able to stomp on an exception-in-flight (accidentally or otherwise). T -- This sentence is false.
Jun 04
parent Elronnd <elronnd elronnd.net> writes:
On Friday, 4 June 2021 at 22:28:02 UTC, H. S. Teoh wrote:
 AFAIK, stack unwinding happens on the runtime stack, which is 
 TLS (because each thread by definition has its own stack).
Reminds me of a technique used by some early java VMs: they aligned all stacks to 2MB, so to get the 'thread id', all you had to do was check sp&-2MB. (Much faster than a load through tls segment to a ptr probably not in cache.)
Jun 08
prev sibling parent kinke <noone nowhere.com> writes:
On Wednesday, 2 June 2021 at 10:03:56 UTC, Andrei Alexandrescu 
wrote:
 The runtime certainly has that information, so the only matter 
 is exposing it via an API.
Yep. It's probably simply a matter of exposing the Throwable in * for Posix: https://github.com/dlang/druntime/blob/60a214942ae2b88fffd43b0fd3301f1e7ddca02f/src/rt/dwarfeh.d#L149 * for Win64 (DMD; LDC is different): https://github.com/dlang/druntime/blob/60a214942ae2b88fffd43b0fd3301f1e7ddca02f/src/rt/deh_win64_posix.d#L100 That probably doesn't account for foreign (C++) exceptions though.
Jun 17
prev sibling next sibling parent Francesco Mecca <me francescomecca.eu> writes:
On Tuesday, 1 June 2021 at 16:36:31 UTC, Andrei Alexandrescu 
wrote:
 One possible idiom is to use scope(failure) and take a look at 
 the exception being thrown.

 Is there a way to do that? If not, I assume it shouldn't be 
 difficult to add to druntime?
https://forum.dlang.org/thread/k6epn0$2ms6$1 digitalmars.com
Jun 01
prev sibling next sibling parent WebFreak001 <d.forum webfreak.org> writes:
On Tuesday, 1 June 2021 at 16:36:31 UTC, Andrei Alexandrescu 
wrote:
 One possible idiom is to use scope(failure) and take a look at 
 the exception being thrown.

 Is there a way to do that? If not, I assume it shouldn't be 
 difficult to add to druntime?
I would love this too! recently really needed that
Jun 02
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/1/21 12:36 PM, Andrei Alexandrescu wrote:
 One possible idiom is to use scope(failure) and take a look at the 
 exception being thrown.
 
 Is there a way to do that? If not, I assume it shouldn't be difficult to 
 add to druntime?
Thought of a great usecase for this (or something like it). This is pretty much code from my application, I do this all over: ```d auto conn = DB.getConnection; conn.exec("START TRANSACTION"); scope(success) conn.exec("COMMIT"); scope(failure) conn.exec("ROLLBACK"); ... // stuff ``` While this works, and isn't too horrible, what is horrible is the fact that I can only do this ONCE. So I can't defensively use a reentrant transaction troika as above. I have to rely on the top level doing the transaction (which means this code gets repeated everywhere, and I can't do it in helper functions). I thought of making a type that automatically either commits or rolls back based on whether we are throwing an exception or not. But there's no way to figure that out in the destructor of the type. But with some mechanism to say "what exception is in flight?" it would be trivial. I don't want to necessarily make it dependent on only SQL errors, because other errors can happen, I want to rollback if ANY exceptions are thrown. Does someone know how to do this with current code? Essentially, I want a reentrant transaction that either does commit or rollback whenever the reentrance count goes to 0 and either an exception is being thrown (rollback) or not (commit). -Steve
Jun 17
next sibling parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 17 June 2021 at 20:35:42 UTC, Steven Schveighoffer 
wrote:
[...]
 Thought of a great usecase for this (or something like it). 
 This is pretty much code from my application, I do this all 
 over:

 ```d
 auto conn = DB.getConnection;

 conn.exec("START TRANSACTION");
 scope(success) conn.exec("COMMIT");
 scope(failure) conn.exec("ROLLBACK");
Is the explicit rollback really necessary? Which DBMS does not roll a running transaction back if the connection terminates?
 While this works, and isn't too horrible, what is horrible is 
 the fact that I can only do this ONCE. So I can't defensively 
 use a reentrant transaction troika as above. I have to rely on 
 the top level doing the transaction (which means this code gets 
 repeated everywhere, and I can't do it in helper functions).

 I thought of making a type that automatically either commits or 
 rolls back based on whether we are throwing an exception or 
 not. But there's no way to figure that out in the destructor of 
 the type.
Why don't you make the COMMIT explicit (by implementing commit method) and let the destructor ROLLBACK the transactino unconditionally?
Jun 17
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/17/21 4:49 PM, kdevel wrote:
 On Thursday, 17 June 2021 at 20:35:42 UTC, Steven Schveighoffer wrote:
 [...]
 Thought of a great usecase for this (or something like it). This is 
 pretty much code from my application, I do this all over:

 ```d
 auto conn = DB.getConnection;

 conn.exec("START TRANSACTION");
 scope(success) conn.exec("COMMIT");
 scope(failure) conn.exec("ROLLBACK");
Is the explicit rollback really necessary? Which DBMS does not roll a running transaction back if the connection terminates?
The connection is not terminated (it's kept in a reusable pool)
 
 While this works, and isn't too horrible, what is horrible is the fact 
 that I can only do this ONCE. So I can't defensively use a reentrant 
 transaction troika as above. I have to rely on the top level doing the 
 transaction (which means this code gets repeated everywhere, and I 
 can't do it in helper functions).

 I thought of making a type that automatically either commits or rolls 
 back based on whether we are throwing an exception or not. But there's 
 no way to figure that out in the destructor of the type.
Why don't you make the COMMIT explicit (by implementing commit method) and let the destructor ROLLBACK the transactino unconditionally?
Yeah, that's possible, but then I still have to do: auto txn = conn.beginTransaction; scope(success) txn.commit; In which the `commit` call only does something on the outer-most txn. Not much better. And a bit awkward. -Steve
Jun 17
parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 17 June 2021 at 20:59:06 UTC, Steven Schveighoffer 
wrote:

[...]

 Why don't you make the COMMIT explicit (by implementing commit 
 method) and let the destructor ROLLBACK the transactino 
 unconditionally?
Yeah, that's possible, but then I still have to do: auto txn = conn.beginTransaction; scope(success) txn.commit; In which the `commit` call only does something on the outer-most txn. Not much better. And a bit awkward.
You have nested transactions txn1, ... txnN. Then there is a path thru the code having a line when all transactions are to be commited. Why don't you explicitly write tnxN.commit; : tnx1.commit; or whatever the appropriate order is? Why do you hide the commit (side effect!) in a scope exit?
Jun 17
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/17/21 5:51 PM, kdevel wrote:
 On Thursday, 17 June 2021 at 20:59:06 UTC, Steven Schveighoffer wrote:
 
 [...]
 
 Why don't you make the COMMIT explicit (by implementing commit 
 method) and let the destructor ROLLBACK the transactino unconditionally?
Yeah, that's possible, but then I still have to do: auto txn = conn.beginTransaction; scope(success) txn.commit; In which the `commit` call only does something on the outer-most txn. Not much better. And a bit awkward.
You have nested transactions txn1, ... txnN. Then there is a path thru the code having a line when all transactions are to be commited. Why don't you explicitly write    tnxN.commit;    :    tnx1.commit; or whatever the appropriate order is? Why do you hide the commit (side effect!) in a scope exit?
Transactions don't nest (this is mysql). So calling "START TRANSACTION" actually commits any in-progress transaction. This is why I need something like a counter, and I increment and decrement the counter instead of actually beginning and committing the transaction. A transaction is an atomic change to the database. Let's say I have 2 library types that have a `saveToDB` function. Each of those might have multiple SQL statements they use, which I want to be atomic. But I also might want to save both of them at once and have *that* be atomic. Right now, I just put the transactions at the highest level, and the types cannot enforce atomicity of their own storage. Really, there are 2 problems here. One is that I want to avoid having to type out those 3 lines. The other is that I want to implement some sort of re-entrant transactions, which can be done already without language help. But something like a `scope(failure) ~this()` mechanism might be useful for the first problem. -Steve
Jun 18
parent kdevel <kdevel vogtner.de> writes:
On Friday, 18 June 2021 at 12:37:15 UTC, Steven Schveighoffer 
wrote:
[...]
 A transaction is an atomic change to the database. Let's say I 
 have 2 library types that have a `saveToDB` function. Each of 
 those might have multiple SQL statements they use, which I want 
 to be atomic.
What do you want to be atomic? The collection of SQL statements or the individual SQL statements?
 But I also might want to save both of them at once and have 
 *that* be atomic.
Okay, you cannot commit within your saveToDB method. So you must tell the computer when it should commit: obj1.saveToDB; obj2.saveToDB; tx.commit;
 Right now, I just put the transactions at the highest level, 
 and the types cannot enforce atomicity of their own storage.
They can, but they cannot enforce "*that*" atomicity from obove.
 Really, there are 2 problems here. One is that I want to avoid 
 having to type out those 3 lines.
??
 The other is that I want to implement some sort of re-entrant 
 transactions, which can be done already without language help.

 But something like a `scope(failure) ~this()` mechanism might 
 be useful for the first problem.
Sure.
Jun 18
prev sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Thursday, 17 June 2021 at 20:35:42 UTC, Steven Schveighoffer 
wrote:
 Does someone know how to do this with current code? 
 Essentially, I want a reentrant transaction that either does 
 commit or rollback whenever the reentrance count goes to 0 and 
 either an exception is being thrown (rollback) or not (commit).

 -Steve
I typically add a transaction method to the connection that takes a delegate and executes the delegate in a try catch. ```dlang auto transaction(Conn conn, void delegate(Conn) dg) { conn.begin(); try { dg(conn); conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } } ```
Jun 17