I am trying to wrap goroutines like so:
package task
import "sync"
type NoResult struct {
wait *sync.WaitGroup
}
type Result[T any] struct {
channel chan T
wait *sync.WaitGroup
}
func RunWithResult[T any](task func() T) *Result[T] {
waitGroup := sync.WaitGroup{}
channel := make(chan T)
waitGroup.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
defer close(channel)
result := task()
channel <- result
}(&waitGroup)
return &Result[T]{
channel: channel,
wait: &waitGroup,
}
}
func Run(task func()) *NoResult {
waitGroup := sync.WaitGroup{}
waitGroup.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
task()
}(&waitGroup)
return &NoResult{
wait: &waitGroup,
}
}
func (task *Result[T]) Wait() *T {
task.wait.Wait() // <- Panics here
result := <-task.channel
return &result
}
func (task *NoResult) Wait() {
task.wait.Wait() // <- Works Fine
}
Here there are two types of result and methods for them.
Here I am calling the functions:
func main() {
result := task.RunWithResult[int](func() int {
return 45
}).Wait()
fmt.Println(*result)
task.Run(func() {
fmt.Println("Hello Worldდდ")
}).Wait()
}
The problem is that, function with the result panics and throws the following error
fatal error: all goroutines are asleep - deadlock!
.
Another function without result works fine. The problem occurs when task.wait.Wait()
(also marked in the code) is called. I did not get why this function panics for the first function and works fine for another.
P.S. I know that I can change the first function and just wait for the channel. The behavior will be same, but still I wonder why the problem is caused
The deadlock is:
Fix by using a buffered channel:
func RunWithResult[T any](task func() T) *Result[T] {
waitGroup := sync.WaitGroup{}
channel := make(chan T, 1) // <-- buffered
...
https://go.dev/play/p/cZnM3eTdl_-
The buffered channel ensures that the task goroutine can complete, whether main receives from the channel or not.