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
Invalid code generated for funcs with unnamed return parameters that defer blocking code. #603
Comments
I can reproduce in Chrome 57.0.2987.98. This is definitely a bug and shouldn't be happening. I've also updated the playground to latest version of GopherJS just now, and it still continues to occur there. So this is a valid issue in master. |
It seems the value of https://gopherjs.github.io/playground/#/Q5xYbr2_yf That prints |
It definitely seems to be related to |
Here's a simplified reproduce case: package main
import "time"
func f() error {
defer func() {
time.Sleep(time.Second)
}()
return nil
}
func main() {
err := f()
print(err) // prints "undefined"
} |
Interestingly, it works fine if the return parameter has a name, even if that name is blank identifier. Both these work ok: package main
import "fmt"
import "time"
func f() (err error) {
defer func() {
time.Sleep(time.Second)
}()
return nil
}
func main() {
err := f()
fmt.Println(err) // prints "<nil>" which is correct
} package main
import "fmt"
import "time"
func f() (_ error) {
defer func() {
time.Sleep(time.Second)
}()
return nil
}
func main() {
err := f()
fmt.Println(err) // prints "<nil>" which is correct
} So, this bug should be easy to fix, it's just an unhandled edge case. |
I ran into this bug too and I think I know what's the issue here. Consider the following program: func TestReturnWithBlockingDefer(t *testing.T) {
f := func() int {
defer time.Sleep(0)
return 42
}
got := f()
if got != 42 {
t.Errorf("Unexpected return value %v. Want: 42.", got)
}
} Here's the code f = (function $b() {
var $s, $deferred, $r;
/* */ $s = 0; var $f, $c = false; if (this !== undefined && this.$blk !== undefined) { $f = this; $c = true; $s = $f.$s; $deferred = $f.$deferred; $r = $f.$r; } var $err = null; try { s: while (true) { switch ($s) { case 0: $deferred = []; $deferred.index = $curGoroutine.deferStack.length; $curGoroutine.deferStack.push($deferred);
$deferred.push([time.Sleep, [new time.Duration(0, 0)]]);
$s = -1; return 42;
/* */ } return; } } catch(err) { $err = err; $s = -1; return 0; } finally { $callDeferred($deferred, $err); if($curGoroutine.asleep) { if ($f === undefined) { $f = { $blk: $b }; } $f.$s = $s; $f.$deferred = $deferred; $f.$r = $r; return $f; } }
}); When the function executes the first time, it successfully passes through the |
Thinking a bit more about this, the right fix is to treat the return statement as blocking in case any of the deferred functions are blocking. And for a blocking return statement we need to compute the return value, store it in a temporary variable, generate a new resumption case and then returned the stored value. |
This doesn't make any changes to the existing logic, but makes the code a bit easier to read (or so I hope) and explains some of the logic I had to reverse-engineer in the comments. Overall this should make fixing gopherjs#603 a bit easier.
This doesn't make any changes to the existing logic, but makes the code a bit easier to read (or so I hope) and explains some of the logic I had to reverse-engineer in the comments. Overall this should make fixing gopherjs#603 a bit easier.
Fixes gopherjs#603. The fix consists of two parts: 1. If a function has a deferred call, we assume that call might be blocking and therefore return statements in the function may behave like a blocking call even though the returned expression may not be blocking itself. 2. When generating code for return statements that were marked as blocking we add a resumption point to make sure that before resuming the deferred function we re-return the correct value.
https://gopherjs.github.io/playground/#/JcN1xwSTbp
If I run this Playground link in Firefox 52.0, the
should get here
alert dialog appears. If I run the same link in Chrome 56.0.2924.87, it printscalling alert(err)
but notreturned from alert(err)
and the following stack trace is generated:Removing the
defer
statement afterhttp.Get
resolves the issue in Chrome:https://gopherjs.github.io/playground/#/DK-J3eMXYr
Is using defer like this incorrect? If it is I would expect to either see a compilation error or see the same issue in both Firefox and Chrome.
The text was updated successfully, but these errors were encountered: