| 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) |
| } |
| } |
| } |
| } |