laserproxy: init

Change-Id: I1900be6eea2d702548279176d796c58c34952dc8
diff --git a/hswaw/laserproxy/locker.go b/hswaw/laserproxy/locker.go
new file mode 100644
index 0000000..044e832
--- /dev/null
+++ b/hswaw/laserproxy/locker.go
@@ -0,0 +1,131 @@
+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) {
+	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(15 * time.Minute)
+					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(15 * time.Minute)
+				}
+			case ctrl.subscribe != nil:
+				subscribers = append(subscribers, ctrl.subscribe.subscriber)
+			}
+		}
+	}
+}