package main import ( "context" "time" "github.com/golang/glog" ) type lockCtrl struct { getCurrent *lockCtrlGetCurrent take *lockCtrlTake release *lockCtrlRelease subscribe *lockCtrlSubscribe bump *lockCtrlBump } type lockCtrlGetCurrent struct { res chan *lockResCurrent } type lockCtrlTake struct { note string addr string prev string res chan bool } type lockCtrlRelease struct { addr string force bool res chan struct{} } type lockCtrlSubscribe struct { subscriber chan *lockUpdate } type lockCtrlBump struct { addr string } type lockResCurrent struct { note string addr string deadline time.Time } type lockUpdate struct { note string addr string } func (s *service) runLocker(ctx context.Context, ctrlC chan *lockCtrl, ownershipDuration time.Duration) { glog.Infof("Locker starting...") var curNote string var curAddr string var curDeadline time.Time var subscribers []chan *lockUpdate notify := func() { for _, sub := range subscribers { go func() { sub <- &lockUpdate{ note: curNote, addr: curAddr, } }() } } t := time.NewTicker(5 * time.Second) defer t.Stop() for { select { case <-ctx.Done(): err := ctx.Err() glog.Errorf("Locker stoppped: %v", err) return case <-t.C: if curAddr != "" && time.Now().After(curDeadline) { glog.Infof("Expiring lock") curAddr = "" curNote = "" notify() } case ctrl := <-ctrlC: switch { case ctrl.take != nil: won := false if curAddr == ctrl.take.prev { won = true curNote = ctrl.take.note curAddr = ctrl.take.addr curDeadline = time.Now().Add(ownershipDuration) notify() glog.Infof("Lock taken by %q %q", curNote, curAddr) } go func() { ctrl.take.res <- won }() case ctrl.release != nil: if curAddr == ctrl.release.addr || ctrl.release.force { curAddr = "" curNote = "" notify() glog.Infof("Lock relased") } go func() { ctrl.release.res <- struct{}{} }() case ctrl.getCurrent != nil: go func() { ctrl.getCurrent.res <- &lockResCurrent{ note: curNote, addr: curAddr, deadline: curDeadline, } }() case ctrl.bump != nil: if curAddr != "" { curDeadline = time.Now().Add(ownershipDuration) } case ctrl.subscribe != nil: subscribers = append(subscribers, ctrl.subscribe.subscriber) } } } }