1

Is there any way to find out if a running function was called as goroutine or not?

I've read 'go tour' and I am interested in building a websocket server with golang, so I found this tutorial https://tutorialedge.net/golang/go-websocket-tutorial/

Now I'm wondering if wsEndpoint function from the tutorial is invoked as goroutine (e.g. go wsEndpoint(...)) or not.

I've tried to read http package documentation, but did not get clear picture, just a guess that the handler will be called with go routine. Is that true?

Sergey
  • 357
  • 5
  • 16
  • 2
    Yes, HTTP handlers are called in their own goroutine. Note that *every* functions runs in a goroutine, even main. Whether one function runs in a different goroutine from another function cannot generally be determined. Some hacks exist, but they rely on implementation details of the runtime and are therefore not portable or subject to the compatibility guarantees. – Peter Jun 21 '19 at 11:17
  • Let's say I need to access some external data from my function for modification, but I don't know whether the function was called in another go routine or not. Then how do I determine if I need to use mutex to serialize access to the data? – Sergey Jun 21 '19 at 11:34
  • 1
    Whether you need synchronization or not is not a runtime decision. It's made when writing the code and when writing the code you already know. – Peter Jun 21 '19 at 11:41
  • That's the problem, I think I don't know because I don't know how my functions are going to be called from other packages... – Sergey Jun 21 '19 at 11:47
  • 1
    That's your decision to make; you can either solve a problem yourself or let the user deal with it. Just document if your function is safe for concurrent use. You should have some kind of intuition whether that's a common case or not. – Peter Jun 21 '19 at 11:53
  • No, I'm afraid it's other way around: I'm using some package which will call my function, but whether it will call my function via go or not it is not described in that package. So what shall I do? Probably read source codes of that package or find another one with better docs. – Sergey Jun 21 '19 at 11:57

1 Answers1

5

Every function is called from a goroutine, even the main() function (which is called the main goroutine).

And goroutines in Go have no identity. It does not matter which goroutine calls a function.

To answer your "original" question:

Is there any way to find out if a running function was called as goroutine or not?

If we define this as the function being called with the go statement or without that, then the answer is yes: we can check that.

But before we do: I would not use this information for anything. Don't write code that depends on this, nor on which goroutine calls a function. If you need to access a resource concurrently from multiple goroutines, just use proper synchronization.

Basically we can check the call stack: the list of functions that call each other. If the function is at the top of that list, then it was called using go (check note at the end of the answer). If there are other functions before that in the call stack, then it was called without go, from another function (that places before in the call stack).

We may use runtime.Callers() to get the calling goroutine's stack. This is how we can check if there are other functions calling "us":

func f(name string) {
    count := runtime.Callers(3, make([]uintptr, 1))
    if count == 0 {
        fmt.Printf("%q is launched as new\n", name)
    }
}

Testing it:

func main() {
    f("normal")
    go f("with-go")

    func() { f("from-anon") }()
    func() { go f("from-anon-with-go") }()

    f2("from-f2")
    go f2("with-go-from-f2")

    f3("from-f3")
    go f3("with-go-from-f3")

    time.Sleep(time.Second)
}

func f2(name string) { f(name) }
func f3(name string) { go f(name) }

This will output (try it on the Go Playground):

"with-go" is launched as new
"from-anon-with-go" is launched as new
"from-f3" is launched as new
"with-go-from-f3" is launched as new

Note: basically there is a runtime.goexit() function on "top" of all call stacks, this is the top-most function running on a goroutine and is the "exit" point for all goroutines. This is why we skip 3 frames from the stack (0. is runtime.Callers() itself, 1. is the f() function, and the last one to skip is runtime.goexit()). You can check the full call stacks with function and file names+line numbers in this Go Playground. This doesn't change the viability of this solution, it's just that we have to skip 3 frames instead of 2 to tell if f() was called from another function or with the go statement.

icza
  • 389,944
  • 63
  • 907
  • 827
  • If I'm providing my own function which accesses shared data to another function from some package (for example http package), shall I use mutex to synchronize access to the data? Always? – Sergey Jun 21 '19 at 11:46
  • @Sergey Yes, HTTP handlers are called from their own goroutines, so if they access data outside of the handler function (that includes write), synchronization is required. For details see [Process Management for the Go Webserver](https://stackoverflow.com/questions/37529511/process-management-for-the-go-webserver/37531953#37531953) – icza Jun 21 '19 at 11:47
  • Ok, maybe HTTP handlers is not a good example anymore. What if you started using some new package and you don't know the internals of the package, what will you do then? Implement serialized access in your function or look at the sources of the new package to understand what happens there? – Sergey Jun 21 '19 at 11:50
  • @Sergey Look at its documentation. If it documents that it's safe for concurrent use, then that's good. If not, don't make that assumption. – icza Jun 21 '19 at 11:55