digitalmars.D - Any way to peek at the exception currently being thrown?
- Andrei Alexandrescu (4/4) Jun 01 2021 One possible idiom is to use scope(failure) and take a look at the
- Steven Schveighoffer (5/10) Jun 01 2021 I've wanted that too! Only thing I think you can do is the awkward
- Andrei Alexandrescu (3/8) Jun 02 2021 The runtime certainly has that information, so the only matter is
- Steven Schveighoffer (3/12) Jun 02 2021 Yeah, OK. I thought you meant a solution could be had solely with drunti...
- Steven Schveighoffer (9/11) Jun 02 2021 Seems obvious for an API:
- Imperatorn (3/14) Jun 02 2021 +1 for scope(failure, ex)
- Andrei Alexandrescu (5/13) Jun 03 2021 I am hoping for an API that would allow picking the current exception
- Ola Fosheim Grostad (3/16) Jun 04 2021 Making exceptions a runtime feature instead of a language feature
- Steven Schveighoffer (5/23) Jun 04 2021 Doesn't that mean that throwing an exception has to stuff the exception
- deadalnix (3/7) Jun 04 2021 Where would that local variable be, on stack? It has to be on TLS.
- Steven Schveighoffer (9/17) Jun 04 2021 Why wouldn't it be on the stack?
- H. S. Teoh (18/21) Jun 04 2021 AFAIK, stack unwinding happens on the runtime stack, which is TLS
- Elronnd (6/8) Jun 08 2021 Reminds me of a technique used by some early java VMs: they
- kinke (8/10) Jun 17 2021 Yep. It's probably simply a matter of exposing the Throwable in
- Francesco Mecca (3/7) Jun 01 2021 https://forum.dlang.org/thread/k6epn0$2ms6$1@digitalmars.com
- WebFreak001 (3/7) Jun 02 2021 I would love this too! recently really needed that
- Steven Schveighoffer (28/33) Jun 17 2021 Thought of a great usecase for this (or something like it). This is
- kdevel (8/25) Jun 17 2021 On Thursday, 17 June 2021 at 20:35:42 UTC, Steven Schveighoffer
- Steven Schveighoffer (8/35) Jun 17 2021 Yeah, that's possible, but then I still have to do:
- kdevel (11/20) Jun 17 2021 On Thursday, 17 June 2021 at 20:59:06 UTC, Steven Schveighoffer
- Steven Schveighoffer (18/46) Jun 18 2021 Transactions don't nest (this is mysql). So calling "START TRANSACTION"
- kdevel (13/27) Jun 18 2021 On Friday, 18 June 2021 at 12:37:15 UTC, Steven Schveighoffer
- Sebastiaan Koppe (16/21) Jun 17 2021 I typically add a transaction method to the connection that takes
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 2021
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 2021
On 6/1/21 12:46 PM, Steven Schveighoffer wrote:On 6/1/21 12:36 PM, Andrei Alexandrescu wrote:The runtime certainly has that information, so the only matter is exposing it via an API.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.
Jun 02 2021
On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:On 6/1/21 12:46 PM, Steven Schveighoffer wrote:Yeah, OK. I thought you meant a solution could be had solely with druntime. -SteveOn 6/1/21 12:36 PM, Andrei Alexandrescu wrote:The runtime certainly has that information, so the only matter is exposing it via an API.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.
Jun 02 2021
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 2021
On Wednesday, 2 June 2021 at 16:47:56 UTC, Steven Schveighoffer wrote:On 6/2/21 6:03 AM, Andrei Alexandrescu wrote:+1 for scope(failure, ex)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 2021
On 6/2/21 12:47 PM, Steven Schveighoffer wrote:On 6/2/21 6:03 AM, Andrei Alexandrescu wrote: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.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
Jun 03 2021
On Friday, 4 June 2021 at 06:02:27 UTC, Andrei Alexandrescu wrote:On 6/2/21 12:47 PM, Steven Schveighoffer wrote:Making exceptions a runtime feature instead of a language feature is not a good design.On 6/2/21 6:03 AM, Andrei Alexandrescu wrote: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.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
Jun 04 2021
On 6/4/21 2:02 AM, Andrei Alexandrescu wrote:On 6/2/21 12:47 PM, 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. -SteveOn 6/2/21 6:03 AM, Andrei Alexandrescu wrote: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.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
Jun 04 2021
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. -SteveWhere would that local variable be, on stack? It has to be on TLS.
Jun 04 2021
On 6/4/21 5:24 PM, deadalnix wrote:On Friday, 4 June 2021 at 13:42:31 UTC, Steven Schveighoffer wrote: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. -SteveDoesn'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.
Jun 04 2021
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 2021
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 2021
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 2021
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 2021
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 2021
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 2021
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 2021
On 6/17/21 4:49 PM, kdevel wrote:On Thursday, 17 June 2021 at 20:35:42 UTC, Steven Schveighoffer wrote: [...]The connection is not terminated (it's kept in a reusable pool)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?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. -SteveWhile 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 2021
On Thursday, 17 June 2021 at 20:59:06 UTC, Steven Schveighoffer wrote: [...]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?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.
Jun 17 2021
On 6/17/21 5:51 PM, kdevel wrote:On Thursday, 17 June 2021 at 20:59:06 UTC, Steven Schveighoffer wrote: [...]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. -SteveYou 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?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.
Jun 18 2021
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 2021
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). -SteveI 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 2021