June 04, 2021
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
June 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.
>
> -Steve

Where would that local variable be, on stack? It has to be on TLS.
June 04, 2021
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
June 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.
June 09, 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.)
June 17, 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
June 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?
June 17, 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).

-Steve

I typically add a transaction method to the connection that takes a delegate and executes the delegate in a try catch.

auto transaction(Conn conn, void delegate(Conn) dg) {
  conn.begin();
  try {
    dg(conn);
    conn.commit();
  } catch (Exception e) {
    conn.rollback();
    throw e;
  }
}
June 17, 2021
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
June 17, 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.