Skip to content

Instantly share code, notes, and snippets.

@hekmon
Last active February 1, 2021 17:39
Show Gist options
  • Save hekmon/7c9c666e7267c0a1090395ee649ebdbf to your computer and use it in GitHub Desktop.
Save hekmon/7c9c666e7267c0a1090395ee649ebdbf to your computer and use it in GitHub Desktop.
Golang "autostop" design pattern: controller with clean context stop (avoid Start() Stop() workflow)
package foobar
import (
"context"
"errors"
)
// Controller is a base pattern controller
type Controller struct {
ctx context.Context
workers sync.WaitGroup
stopped chan struct{}
}
// New returns a initialized & started controller
func New(ctx context.Context) (c *Controller, err error) {
if ctx == nil {
err = errors.New("context can't be nill")
return
}
// Init
c = &Controller{
ctx: ctx,
stopped: make(chan struct{}),
}
// Start workers
c.workers.Add(1)
go func() {
c.workerA()
c.workers.Done()
}()
c.workers.Add(1)
go func() {
c.workerB()
c.workers.Done()
}()
// Create the auto-stopper (must be launch after the worker(s) in case ctx is cancelled while launching workers)
go c.autostop()
// Return the initialized & started controller
return
}
func (c *Controller) autostop() {
// Wait for signal
<-c.ctx.Done()
// Begin the stopping proceedure
c.workers.Wait()
// TODO: save some state ?
// Close the stopped chan to indicate we are fully stopped
close(c.stopped)
}
// WaitStopped will block until c is fully stopped.
// To be stopped, c needs to have its context cancelled.
// WaitStopped is safe to be called from multiples goroutines.
func (c *Controller) WaitStopped() {
<-c.stopped
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment