-
-
Notifications
You must be signed in to change notification settings - Fork 925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Regression in 9.1.15.0 with some ensure blocks being executed twice #4895
Comments
@jeremyevans and this definitely was not also broken for 9.1.14.0? When we process ensures we duplicate their bodies on each exception path internally and this bug looks like we might be duplicating something which has duplicated ensure already :( |
Yes, definitely sure it worked on 9.1.14.0, see the 2nd code block in the expected behavior section of the original report. I made sure to use |
Well at least we have a good place to start looking! I don't have pgsql set up on this machine at the moment, but it should be possible to bisect between 9.1.14 and 9.1.15 (only a hundred or so commits?). |
No need to setup pgsql, I'm using the mock adapter that doesn't actually do a database connection. Just checking out the sequel repository or installing the sequel gem will be enough. This should reproduce with a gem install:
|
I am guessing this nesting should just be extractable as a simple test case. Knowing we copy ensure block bodies makes guessing at the cause really easy :) |
NOTE: This is not actually a reproduction against master but against a local tree. I am leaving it as an illustration though since the description below is what must be happening (just not with this exact code). Ok this is fallout from fixing #4865. Here is a reduced snippet: begin
raise ArgumentError("DDDD")
ensure
begin
ensure
puts "EERRRRR"
end
end This will print out EERRRRR twice. Tracing through execution the raise occurs goes to a reasonable rescue ip (fairly positive it is correct cloned contents of both ensures). Then it goes to a throw instr because we are done executing the ensure and it needs to raise out to the next outer method............. HOWEVER, the rescuePC stack is dirty at this point because of the original raise (the raise jumps over an exc_end_region). So the interpreter catches the raise and then consults the dirty stack which then decides to go down a copy of the inner ensure. That finishes popping the stack and we exit having did an extra tour through the inner ensure. |
One thought is since the first time that print displays we are in UNRESCUED_REGION we could just nuke the rescuepc stack at that point? |
My commit message explains what changed and why I am reasonably confident about this fix. As an aside we basically did this new solution originally but instead of making a simple array of ints we would store it as a field on every instr (which was heavy enough we moved away from that). set on primitive array is quite a bit faster than a method call setting a field so this is no doubt quicker. |
Environment
This example requires the sequel gem, as I have not been able to create a reproducible example that doesn't require it.
Expected Behavior
CRuby
JRuby 9.1.14.0
Actual Behavior
JRuby 9.1.15.0
To make sure it isn't an OpenBSD specific issue, I also tested on Windows:
From some puts debugging I have found that an ensure block (https://github.com/jeremyevans/sequel/blob/5.3.0/lib/sequel/database/transactions.rb#L227) is being executed twice during the same method call when it should only be executed once.
This bug breaks Sequel when trying to use savepoints with JRuby 9.1.15.0.
The text was updated successfully, but these errors were encountered: