Skip to content
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

sync/WaitGroup: new feature WaitWithTimeout #69285

Closed
gozoro opened this issue Sep 5, 2024 · 5 comments · May be fixed by #69286
Closed

sync/WaitGroup: new feature WaitWithTimeout #69285

gozoro opened this issue Sep 5, 2024 · 5 comments · May be fixed by #69286

Comments

@gozoro
Copy link

gozoro commented Sep 5, 2024

Hi!
I would like to have a WaitWithTimeout function in the standard sync/WaitGroup structure.

Example:

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

func main() {

	wg := &WaitGroupTimeout{}
	ctx, cancel := context.WithCancel(context.Background())

	var gracefulStopChan = make(chan os.Signal, 1)

	signal.Notify(gracefulStopChan, syscall.SIGTERM, syscall.SIGINT)

	go func() {
		sig := <-gracefulStopChan

		log.Printf("Caught sig: %+v\n", sig)
		log.Println("Application graceful shutdown begin...")

		// Shutdown

		cancel()
		if ok := wg.WaitWithTimeout(10 * time.Second); !ok {
			log.Println("Force shutdown by timeout")
		}
		log.Println("Application graceful shutdown finished")
		os.Exit(1)

	}()

	log.Println("APP start")

	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-ctx.Done():
				//time.Sleep(4 * time.Second)
				for {
					// failed shutdown process or closes something resource
				}
				log.Println("Break the process 1")
				return
			case <-time.After(1 * time.Second):
				log.Println("Process 1")
			}
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-ctx.Done():
				time.Sleep(5 * time.Second)
				log.Println("Break the process 2")
				return
			case <-time.After(1 * time.Second):
				log.Println("Process 2")
			}
		}
	}()

	wg.Wait()
}

// I would not like to transfer this code from one project to another
type WaitGroupTimeout struct {
	sync.WaitGroup
}

// WaitWithTimeout returns the value "true" when  the [WaitGroup] counter is zero.
// And returns the value "false" when the wait is completed by timeout.
func (wg *WaitGroupTimeout) WaitWithTimeout(timeout time.Duration) bool {

	timeoutChan := time.After(timeout)
	waitChan := make(chan struct{})

	go func() {

		wg.Wait()
		close(waitChan)
	}()

	select {
	case <-timeoutChan:
		return false
	case <-waitChan:
		return true
	}
}
@seankhliao
Copy link
Member

Duplicate of #40916

@seankhliao seankhliao marked this as a duplicate of #40916 Sep 5, 2024
@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Sep 5, 2024
@zigo101
Copy link

zigo101 commented Sep 5, 2024

It is easy to do this with an additional time.Timer.

@gozoro
Copy link
Author

gozoro commented Sep 6, 2024

I do this in one line wg.WaitWithTimeout(10 * time.Second). How to put your time.Timer into one function?

@zigo101
Copy link

zigo101 commented Sep 6, 2024

Certainly, you can't do it in one line.
Programming with channels is fun, why to lose the fun? :D
I mean it is fun to write the code in your first comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants