-1
package main

var fooRunning = false
var barRunning = false

func foo() {
    fooRunning = true
    defer func() { fooRunning = false }()
    if barRunning {
        // wait for bar() to finish
    }
    ...
}

func bar() {
    barRunning = true
    defer func() { barRunning = false }()
    if fooRunning {
        // wait for foo() to finish
    }
    ...
}

In my case if we run go foo() it should wait for bar() to finish and vice versa. What is the best way to do it? Note that they also can be executed independently.

seh
  • 14,999
  • 2
  • 48
  • 58
homeboy
  • 171
  • 12
  • 1
    Related / Possible duplicate of [What's wrong with this golang code?](https://stackoverflow.com/questions/28958192/whats-wrong-with-this-golang-code?noredirect=1&lq=1) – icza Sep 15 '17 at 08:37
  • 2
    Those requirements lead to a huge number of race conditions that must be properly handled (What if the other routine ends right after your postitive check ? What if someone else starts the other routine right after a negative check etc. What if someone runs 2 routines of foo() ) I would strongly urge you to create a better design where you don't have 2 goroutines that are mutually dependent on the otherwise independent running state of the other. – nos Sep 15 '17 at 08:39
  • 1
    The code in the question has a designed deadlock as @nos noted. There is no solution that will work to the given scenario. – Adrian Sep 15 '17 at 15:28

2 Answers2

0

Your requirements cannot be satisfied safely by any concrete design. As prescribed, you say that foo and bar can run in concurrent goroutines, and that if either or both of them have started, the other should wait for them both to finish. That is too weak of a prescription, though; what happens if foo starts and then would finish, but bar hasn't started running yet? What if bar never runs at all? Or what if bar runs, but foo never does?

Are you mandating that both foo and bar must start and complete in order for your program to be correct? If so, I can guess as to what you meant to prescribe: You want a barrier that waits for them both to complete before proceeding.

package main

import (
    "fmt"
    "sync"
    "time"
)

func foo() {
    fmt.Println("foo")
}

func bar() {
    fmt.Println("bar")
}

func within(wg *sync.WaitGroup, f func()) {
    wg.Add(1)
    go func() {
        defer wg.Done()
        f()
    }()
}

func main() {
    var wg sync.WaitGroup
    within(&wg, foo)
    within(&wg, bar)
    wg.Wait()
    fmt.Println("Both foo and bar completed.")
}

(That same example in the Playground)

Note that here, neither foo nor bar are mutually aware; only their callers are, in the interest of coordinating the two calls.

Your original attempt might lead you down the road of making foo and bar each close over or accept as a parameter a sync.WaitGroup, with each function first adding itself to the group and waiting on it before exiting. That way lies madness.

If foo starts and completes before bar has a chance to add itself to the WaitGroup, foo will exit before bar, even though you could claim they had been running concurrently, or the converse with bar running before foo can register its active state. Again, since this is a poorly specified aspect of your program, I suggest you instead focus on a higher-level barrier and not on the mutual dependence of these two functions.

seh
  • 14,999
  • 2
  • 48
  • 58
-1

You can use a channel! As i remember from my rusty go, this would give:

func foo() {
    c := make(chan int)
    go bar(c)
    <-c
}

and in bar

func bar(c chan int) {
    // do stuff here
    c <- 0
}
TDk
  • 1,019
  • 6
  • 18
  • What if bar() not running? Channel will block further execution, right? – homeboy Sep 15 '17 at 08:43
  • Yes, unless another goroutine pushes an integer in the channel, the `<-c` will block until further integers are given. If you forget to give an integer to the channel in `bar` then your program will run into a deadlock – TDk Sep 15 '17 at 08:51
  • 1
    This only provides unidirectional blocking, where the question wanted bidirectional; it doesn't address the designed deadlock in the question; and it uses channels unnecessarily. [`sync.WaitGroup`](https://golang.org/pkg/sync/#WaitGroup) is built for exactly this use case, but a solution can't be proposed until the deadlock is removed from the design. – Adrian Sep 15 '17 at 15:29