Go: proposal: Go 2: defer go func()

Created on 23 Apr 2018  ·  17Comments  ·  Source: golang/go

defer go f()

This should be permitted for Go 2

Currently you have to do this:

defer func() {
        go f()
    }()
FrozenDueToAge Go2 LanguageChange Proposal

Most helpful comment

Why would you do this? You’ve lost track of the groutine you just started.

Can you please show a real world example where the code is so short that saving two lines makes a significant impact on the readability of the function.

All 17 comments

Why would you do this? You’ve lost track of the groutine you just started.

Can you please show a real world example where the code is so short that saving two lines makes a significant impact on the readability of the function.

just saves 2 lines.

But it doesn't, that's the thing, to write defer go func() ... would spawn a goroutine with no relationship to the function that just exited. You wouldn't write this code if you cared about the result of the goroutine that was spun off. Instead you'd be doing the waitgroup dance, or passing around some result channel.

My point is, this saving of two lines in a 3 line function is immaterial when you're talking about saving two lines in a properly written function that tracks the lifetime of the goroutine that was fired off on the way out.

I use it all the time.

At the end of a http request, I like to save some statistical data to a database that has no bearing on the response.

I don't want to delay the client because they should not have to wait for me to write to database.

func foo() { go bar() }

...

defer foo()

As I said, saves 1-2 lines with no cluttering of the language spec.

Note that defer func() { go f() }() on a single line is Valid Go and left alone by gofmt.

@pjebs What do you expect with below? You want to spawn goroutine even if doAnotherThing return error?

func doSomething() error {
    defer go doLater()
    return doAnotherThing()
}

I guess, in most of cases, it should check err and return if err != nil. (before spawning goroutine). And if there are several conditions and next condition doesn't related on whether spawn the goroutine, defer go doLater() possibly become go doLater().

@mattn Yes. It needs to be run irrespective of outcome.

In reality, my proposal is not really about my particular use case. It's more about making the language spec _feel_ more internally consistent, even if there is minimal benefit, and particularly if there is zero costs or ambiguity associated with it.

@pjebs Then, you can do it like below in caller of doSomething().

doSomething()
go doLater()

I'm thinking current spec have consistent.

I don't understand your suggestion?
There seems to be even more lines of code + I want to be able to apply defer at the very top, irrespective of the happy path or alternative paths. One of the benefits of defer is we don't need to think about when it's going to run. We just have the intention that we WANT IT TO RUN at some point.

We just have the intention that we WANT IT TO RUN at some point.

Are you ok with the goroutine never being scheduled?

Are you ok with the goroutine never completing and your program exhausting the heap?

I don’t think you want either of those things, so you want to have some control over the lifetime of that goroutine, and that will negate any LOC reduction this proposal asserts. .

On 24 Apr 2018, at 15:58, pjebs notifications@github.com wrote:

We just have the intention that we WANT IT TO RUN at some point.

At least for the contrived example you mention, below code seems like fulfills your needs

func Foo() {
   go doLater()
   return doSomething()
}

Because:

  1. doLater is run "irrespective of the happy path or alternative paths"
  2. It fulfills "we don't need to think about when it's going to run"
  3. It save s a few lines of code than defer func() { go f() }

And,

  1. It doesn't require a language spec alteration

So we should all be happy :)

@davecheney

Are you ok with the goroutine never being scheduled?

The defer means it will get scheduled (eventually).

Are you ok with the goroutine never completing and your program exhausting the heap?

How is that different from current status quo?
If we don't write defer and just use go f(), all those problems are still existent.
If you were to manually write go f() in every path before the function returns (to simualate what defer would do), your concerns are still existent, but it will be operating within the current spec.

Sorry, when I wrote scheduled in the first paragraph I meant something closer to “being given a chance to execute”

WRT go f() is not guaranteed to run irrespective of defer, you are correct. There is no difference. Which is my point, both of those patterns of fire and forgetting a goroutine are potential resource leaks.

I restate my assertion that optimising the syntax for this operation with a view to reducing the size of the code on the page is a non goal when the code written correctly to track the lifetime of a goroutine would dwarf any saving in lines from optimising for the trivial case.

On 24 Apr 2018, at 16:43, pjebs notifications@github.com wrote:

@davecheney

Are you ok with the goroutine never being scheduled?
The defer means it will get scheduled.

Are you ok with the goroutine never completing and your program exhausting the heap?
How is that different from current?
If we don't write defer and just use go f(), all those problems are still existent.
If you were to manually write go f() in every path before the function returns, your concerns are still valid, but it will be operating within the current spec.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

At the end of a http request, I like to save some statistical data to a database that has no bearing on the response.

If you don't want to run out of memory and crash when the stats database is slow, you still need some kind of synchronization (e.g., a semaphore). That seems like it would disrupt the one-liner:

func logStats(f func()) {
    acquireStatsSem()  // or: if !acquireStatsSem() { return }
    go func() {
        defer releaseStatsSem()
        f()
    }()
}

func … {
    defer logStats(func() {
        …
    })

    …
}

This is minor syntactic sugar, and people have given good explanations for why it is rarely a good idea. We aren't going to make this change.

Was this page helpful?
0 / 5 - 0 ratings