|  |  |  | @ -2,17 +2,21 @@ package graphvent | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | import ( | 
		
	
		
			
				|  |  |  |  |   "encoding/binary" | 
		
	
		
			
				|  |  |  |  |   "github.com/google/uuid" | 
		
	
		
			
				|  |  |  |  | ) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | type ReqState int | 
		
	
		
			
				|  |  |  |  | type ReqState byte | 
		
	
		
			
				|  |  |  |  | const ( | 
		
	
		
			
				|  |  |  |  |   Unlocked = ReqState(0) | 
		
	
		
			
				|  |  |  |  |   Unlocking = ReqState(1) | 
		
	
		
			
				|  |  |  |  |   Locked = ReqState(2) | 
		
	
		
			
				|  |  |  |  |   Locking = ReqState(3) | 
		
	
		
			
				|  |  |  |  |   AbortingLock = ReqState(4) | 
		
	
		
			
				|  |  |  |  | ) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | type LockableExt struct{ | 
		
	
		
			
				|  |  |  |  |   State ReqState | 
		
	
		
			
				|  |  |  |  |   ReqID uuid.UUID | 
		
	
		
			
				|  |  |  |  |   Owner *NodeID | 
		
	
		
			
				|  |  |  |  |   PendingOwner *NodeID | 
		
	
		
			
				|  |  |  |  |   Requirements map[NodeID]ReqState | 
		
	
	
		
			
				
					|  |  |  | @ -37,7 +41,7 @@ func (ext *LockableExt) Type() ExtType { | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func (ext *LockableExt) Serialize() ([]byte, error) { | 
		
	
		
			
				|  |  |  |  |   ret := make([]byte, 8 + (16 * 2) + (17 * len(ext.Requirements))) | 
		
	
		
			
				|  |  |  |  |   ret := make([]byte, 9 + (16 * 2) + (17 * len(ext.Requirements))) | 
		
	
		
			
				|  |  |  |  |   if ext.Owner != nil { | 
		
	
		
			
				|  |  |  |  |     bytes, err := ext.Owner.MarshalBinary() | 
		
	
		
			
				|  |  |  |  |     if err != nil { | 
		
	
	
		
			
				
					|  |  |  | @ -55,8 +59,8 @@ func (ext *LockableExt) Serialize() ([]byte, error) { | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   binary.BigEndian.PutUint64(ret[32:40], uint64(len(ext.Requirements))) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   cur := 40 | 
		
	
		
			
				|  |  |  |  |   ret[40] = byte(ext.State) | 
		
	
		
			
				|  |  |  |  |   cur := 41 | 
		
	
		
			
				|  |  |  |  |   for req, state := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |     bytes, err := req.MarshalBinary() | 
		
	
		
			
				|  |  |  |  |     if err != nil { | 
		
	
	
		
			
				
					|  |  |  | @ -105,6 +109,9 @@ func (ext *LockableExt) Deserialize(ctx *Context, data []byte) error { | 
		
	
		
			
				|  |  |  |  |   num_requirements := int(binary.BigEndian.Uint64(data[cur:cur+8])) | 
		
	
		
			
				|  |  |  |  |   cur += 8 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   ext.State = ReqState(data[cur]) | 
		
	
		
			
				|  |  |  |  |   cur += 1 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if num_requirements != 0 { | 
		
	
		
			
				|  |  |  |  |     ext.Requirements = map[NodeID]ReqState{} | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
	
		
			
				
					|  |  |  | @ -130,6 +137,7 @@ func NewLockableExt(requirements []NodeID) *LockableExt { | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   return &LockableExt{ | 
		
	
		
			
				|  |  |  |  |     State: Unlocked, | 
		
	
		
			
				|  |  |  |  |     Owner: nil, | 
		
	
		
			
				|  |  |  |  |     PendingOwner: nil, | 
		
	
		
			
				|  |  |  |  |     Requirements: reqs, | 
		
	
	
		
			
				
					|  |  |  | @ -137,162 +145,163 @@ func NewLockableExt(requirements []NodeID) *LockableExt { | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // Send the signal to unlock a node from itself
 | 
		
	
		
			
				|  |  |  |  | func UnlockLockable(ctx *Context, node *Node) error { | 
		
	
		
			
				|  |  |  |  | func UnlockLockable(ctx *Context, owner *Node, target NodeID) (uuid.UUID, error) { | 
		
	
		
			
				|  |  |  |  |   msgs := Messages{} | 
		
	
		
			
				|  |  |  |  |   msgs = msgs.Add(node.ID, node.Key, NewLockSignal("unlock"), node.ID) | 
		
	
		
			
				|  |  |  |  |   return ctx.Send(msgs) | 
		
	
		
			
				|  |  |  |  |   signal := NewLockSignal("unlock") | 
		
	
		
			
				|  |  |  |  |   msgs = msgs.Add(owner.ID, owner.Key, signal, target) | 
		
	
		
			
				|  |  |  |  |   return signal.ID(), ctx.Send(msgs) | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // Send the signal to lock a node from itself
 | 
		
	
		
			
				|  |  |  |  | func LockLockable(ctx *Context, node *Node) error { | 
		
	
		
			
				|  |  |  |  | func LockLockable(ctx *Context, owner *Node, target NodeID) (uuid.UUID, error) { | 
		
	
		
			
				|  |  |  |  |   msgs := Messages{} | 
		
	
		
			
				|  |  |  |  |   msgs = msgs.Add(node.ID, node.Key, NewLockSignal("lock"), node.ID) | 
		
	
		
			
				|  |  |  |  |   return ctx.Send(msgs) | 
		
	
		
			
				|  |  |  |  |   signal := NewLockSignal("lock") | 
		
	
		
			
				|  |  |  |  |   msgs = msgs.Add(owner.ID, owner.Key, signal, target) | 
		
	
		
			
				|  |  |  |  |   return signal.ID(), ctx.Send(msgs) | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // Handle a LockSignal and update the extensions owner/requirement states
 | 
		
	
		
			
				|  |  |  |  | func (ext *LockableExt) HandleLockSignal(log Logger, node *Node, source NodeID, signal *StringSignal) Messages { | 
		
	
		
			
				|  |  |  |  |   state := signal.Str | 
		
	
		
			
				|  |  |  |  |   log.Logf("lockable", "LOCK_SIGNAL: %s->%s %+v", source, node.ID, signal) | 
		
	
		
			
				|  |  |  |  | func (ext *LockableExt) HandleErrorSignal(log Logger, node *Node, source NodeID, signal *ErrorSignal) Messages { | 
		
	
		
			
				|  |  |  |  |   str := signal.Error | 
		
	
		
			
				|  |  |  |  |   log.Logf("lockable", "ERROR_SIGNAL: %s->%s %+v", source, node.ID, str) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   messages := Messages{} | 
		
	
		
			
				|  |  |  |  |   switch state { | 
		
	
		
			
				|  |  |  |  |   case "unlock": | 
		
	
		
			
				|  |  |  |  |     if ext.Owner == nil { | 
		
	
		
			
				|  |  |  |  |       messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "already_unlocked"), source) | 
		
	
		
			
				|  |  |  |  |     } else if source != *ext.Owner { | 
		
	
		
			
				|  |  |  |  |       messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_owner"), source) | 
		
	
		
			
				|  |  |  |  |     } else if ext.PendingOwner == nil { | 
		
	
		
			
				|  |  |  |  |       messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "already_unlocking"), source) | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |       if len(ext.Requirements) == 0 { | 
		
	
		
			
				|  |  |  |  |         ext.Owner = nil | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = nil | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewLockSignal("unlocked"), source) | 
		
	
		
			
				|  |  |  |  |       } else { | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = nil | 
		
	
		
			
				|  |  |  |  |         for id, state := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |           if state != Locked { | 
		
	
		
			
				|  |  |  |  |             panic("NOT_LOCKED") | 
		
	
		
			
				|  |  |  |  |           } | 
		
	
		
			
				|  |  |  |  |   msgs := Messages {} | 
		
	
		
			
				|  |  |  |  |   switch str { | 
		
	
		
			
				|  |  |  |  |   case "not_unlocked": | 
		
	
		
			
				|  |  |  |  |     if ext.State == Locking { | 
		
	
		
			
				|  |  |  |  |       ext.State = AbortingLock | 
		
	
		
			
				|  |  |  |  |       ext.Requirements[source] = Unlocked | 
		
	
		
			
				|  |  |  |  |       for id, state := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |         if state == Locked { | 
		
	
		
			
				|  |  |  |  |           ext.Requirements[id] = Unlocking | 
		
	
		
			
				|  |  |  |  |           messages = messages.Add(node.ID, node.Key, NewLockSignal("unlock"), id) | 
		
	
		
			
				|  |  |  |  |           msgs = msgs.Add(node.ID, node.Key, NewLockSignal("unlock"), id) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         if source != node.ID { | 
		
	
		
			
				|  |  |  |  |           messages = messages.Add(node.ID, node.Key, NewLockSignal("unlocking"), source) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   case "unlocking": | 
		
	
		
			
				|  |  |  |  |     if ext.Requirements != nil { | 
		
	
		
			
				|  |  |  |  |       state, exists := ext.Requirements[source] | 
		
	
		
			
				|  |  |  |  |       if exists == false { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_requirement"), source) | 
		
	
		
			
				|  |  |  |  |       } else if state != Unlocking { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_unlocking"), source) | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   case "not_locked": | 
		
	
		
			
				|  |  |  |  |     panic("RECEIVED not_locked, meaning a node thought it held a lock it didn't") | 
		
	
		
			
				|  |  |  |  |   case "not_requirement": | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   case "unlocked": | 
		
	
		
			
				|  |  |  |  |     if source == node.ID { | 
		
	
		
			
				|  |  |  |  |       return nil | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if ext.Requirements != nil { | 
		
	
		
			
				|  |  |  |  |       state, exists := ext.Requirements[source] | 
		
	
		
			
				|  |  |  |  |       if exists == false { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_requirement"), source) | 
		
	
		
			
				|  |  |  |  |       } else if state != Unlocking { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_unlocking"), source) | 
		
	
		
			
				|  |  |  |  |       } else { | 
		
	
		
			
				|  |  |  |  |         ext.Requirements[source] = Unlocked | 
		
	
		
			
				|  |  |  |  |   return msgs | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         if ext.PendingOwner == nil { | 
		
	
		
			
				|  |  |  |  |           unlocked := 0 | 
		
	
		
			
				|  |  |  |  |           for _, s := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |             if s == Unlocked { | 
		
	
		
			
				|  |  |  |  |               unlocked += 1 | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |           } | 
		
	
		
			
				|  |  |  |  | // Handle a LockSignal and update the extensions owner/requirement states
 | 
		
	
		
			
				|  |  |  |  | func (ext *LockableExt) HandleLockSignal(log Logger, node *Node, source NodeID, signal *StringSignal) Messages { | 
		
	
		
			
				|  |  |  |  |   state := signal.Str | 
		
	
		
			
				|  |  |  |  |   log.Logf("lockable", "LOCK_SIGNAL: %s->%s %+v", source, node.ID, state) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |           if len(ext.Requirements) == unlocked { | 
		
	
		
			
				|  |  |  |  |             previous_owner := *ext.Owner | 
		
	
		
			
				|  |  |  |  |             ext.Owner = nil | 
		
	
		
			
				|  |  |  |  |             messages = messages.Add(node.ID, node.Key, NewLockSignal("unlocked"), previous_owner) | 
		
	
		
			
				|  |  |  |  |           } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   msgs := Messages{} | 
		
	
		
			
				|  |  |  |  |   switch state { | 
		
	
		
			
				|  |  |  |  |   case "locked": | 
		
	
		
			
				|  |  |  |  |     if source == node.ID { | 
		
	
		
			
				|  |  |  |  |       return nil | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if ext.Requirements != nil { | 
		
	
		
			
				|  |  |  |  |       state, exists := ext.Requirements[source] | 
		
	
		
			
				|  |  |  |  |       if exists == false { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_requirement"), source) | 
		
	
		
			
				|  |  |  |  |       } else if state != Locking { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_locking"), source) | 
		
	
		
			
				|  |  |  |  |       } else { | 
		
	
		
			
				|  |  |  |  |     state, found := ext.Requirements[source] | 
		
	
		
			
				|  |  |  |  |     if found == false { | 
		
	
		
			
				|  |  |  |  |       msgs = msgs.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_requirement"), source) | 
		
	
		
			
				|  |  |  |  |     } else if state == Locking { | 
		
	
		
			
				|  |  |  |  |       if ext.State == Locking { | 
		
	
		
			
				|  |  |  |  |         ext.Requirements[source] = Locked | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         if ext.PendingOwner != nil { | 
		
	
		
			
				|  |  |  |  |           locked := 0 | 
		
	
		
			
				|  |  |  |  |           for _, s := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |             if s == Locked { | 
		
	
		
			
				|  |  |  |  |               locked += 1 | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         reqs := 0 | 
		
	
		
			
				|  |  |  |  |         locked := 0 | 
		
	
		
			
				|  |  |  |  |         for _, s := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |           reqs += 1 | 
		
	
		
			
				|  |  |  |  |           if s == Locked { | 
		
	
		
			
				|  |  |  |  |             locked += 1 | 
		
	
		
			
				|  |  |  |  |           } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |           if len(ext.Requirements) == locked { | 
		
	
		
			
				|  |  |  |  |             ext.Owner = ext.PendingOwner | 
		
	
		
			
				|  |  |  |  |             messages = messages.Add(node.ID, node.Key, NewLockSignal("locked"), *ext.Owner) | 
		
	
		
			
				|  |  |  |  |           } | 
		
	
		
			
				|  |  |  |  |         if locked == reqs { | 
		
	
		
			
				|  |  |  |  |           ext.State = Locked | 
		
	
		
			
				|  |  |  |  |           ext.Owner = ext.PendingOwner | 
		
	
		
			
				|  |  |  |  |           msgs = msgs.Add(node.ID, node.Key, NewLockSignal("locked"), *ext.Owner) | 
		
	
		
			
				|  |  |  |  |         } else { | 
		
	
		
			
				|  |  |  |  |           log.Logf("lockable", "PARTIAL LOCK: %s - %d/%d", node.ID, locked, reqs) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       } else if ext.State == AbortingLock { | 
		
	
		
			
				|  |  |  |  |         ext.Requirements[source] = Unlocking | 
		
	
		
			
				|  |  |  |  |         msgs = msgs.Add(node.ID, node.Key, NewLockSignal("unlock"), source) | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   case "locking": | 
		
	
		
			
				|  |  |  |  |     if ext.Requirements != nil { | 
		
	
		
			
				|  |  |  |  |       state, exists := ext.Requirements[source] | 
		
	
		
			
				|  |  |  |  |       if exists == false { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_requirement"), source) | 
		
	
		
			
				|  |  |  |  |       } else if state != Locking { | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_locking"), source) | 
		
	
		
			
				|  |  |  |  |   case "unlocked": | 
		
	
		
			
				|  |  |  |  |     state, found := ext.Requirements[source] | 
		
	
		
			
				|  |  |  |  |     if found == false { | 
		
	
		
			
				|  |  |  |  |       msgs = msgs.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_requirement"), source) | 
		
	
		
			
				|  |  |  |  |     } else if state == Unlocking { | 
		
	
		
			
				|  |  |  |  |       ext.Requirements[source] = Unlocked | 
		
	
		
			
				|  |  |  |  |       reqs := 0 | 
		
	
		
			
				|  |  |  |  |       unlocked := 0 | 
		
	
		
			
				|  |  |  |  |       for _, s := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |         reqs += 1 | 
		
	
		
			
				|  |  |  |  |         if s == Unlocked { | 
		
	
		
			
				|  |  |  |  |           unlocked += 1 | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       if unlocked == reqs { | 
		
	
		
			
				|  |  |  |  |         old_state := ext.State | 
		
	
		
			
				|  |  |  |  |         ext.State = Unlocked | 
		
	
		
			
				|  |  |  |  |         if old_state == Unlocking { | 
		
	
		
			
				|  |  |  |  |           ext.Owner = ext.PendingOwner | 
		
	
		
			
				|  |  |  |  |           msgs = msgs.Add(node.ID, node.Key, NewLockSignal("unlocked"), *ext.Owner) | 
		
	
		
			
				|  |  |  |  |         } else if old_state == AbortingLock { | 
		
	
		
			
				|  |  |  |  |           msgs = msgs.Add(node.ID, node.Key, NewErrorSignal(ext.ReqID, "not_unlocked"), *ext.PendingOwner) | 
		
	
		
			
				|  |  |  |  |           ext.PendingOwner = ext.Owner | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       } else { | 
		
	
		
			
				|  |  |  |  |         log.Logf("lockable", "PARTIAL UNLOCK: %s - %d/%d", node.ID, unlocked, reqs) | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   case "lock": | 
		
	
		
			
				|  |  |  |  |     if ext.Owner != nil { | 
		
	
		
			
				|  |  |  |  |       messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "already_locked"), source) | 
		
	
		
			
				|  |  |  |  |     } else if ext.PendingOwner != nil { | 
		
	
		
			
				|  |  |  |  |       messages = messages.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "already_locking"), source) | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |       owner := source | 
		
	
		
			
				|  |  |  |  |     if ext.State == Unlocked { | 
		
	
		
			
				|  |  |  |  |       if len(ext.Requirements) == 0 { | 
		
	
		
			
				|  |  |  |  |         ext.Owner = &owner | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = ext.Owner | 
		
	
		
			
				|  |  |  |  |         messages = messages.Add(node.ID, node.Key, NewLockSignal("locked"), source) | 
		
	
		
			
				|  |  |  |  |         ext.State = Locked | 
		
	
		
			
				|  |  |  |  |         new_owner := source | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = &new_owner | 
		
	
		
			
				|  |  |  |  |         ext.Owner = &new_owner | 
		
	
		
			
				|  |  |  |  |         msgs = msgs.Add(node.ID, node.Key, NewLockSignal("locked"), new_owner) | 
		
	
		
			
				|  |  |  |  |       } else { | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = &owner | 
		
	
		
			
				|  |  |  |  |         ext.State = Locking | 
		
	
		
			
				|  |  |  |  |         ext.ReqID = signal.ID() | 
		
	
		
			
				|  |  |  |  |         new_owner := source | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = &new_owner | 
		
	
		
			
				|  |  |  |  |         for id, state := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |           log.Logf("lockable_detail", "LOCK_REQ: %s sending 'lock' to %s", node.ID, id) | 
		
	
		
			
				|  |  |  |  |           if state != Unlocked { | 
		
	
		
			
				|  |  |  |  |             panic("NOT_UNLOCKED") | 
		
	
		
			
				|  |  |  |  |             log.Logf("lockable", "REQ_NOT_UNLOCKED_WHEN_LOCKING") | 
		
	
		
			
				|  |  |  |  |           } | 
		
	
		
			
				|  |  |  |  |           ext.Requirements[id] = Locking | 
		
	
		
			
				|  |  |  |  |           messages = messages.Add(node.ID, node.Key, NewLockSignal("lock"), id) | 
		
	
		
			
				|  |  |  |  |           lock_signal := NewLockSignal("lock") | 
		
	
		
			
				|  |  |  |  |           msgs = msgs.Add(node.ID, node.Key, lock_signal, id) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         log.Logf("lockable", "LOCK_REQ: %s sending 'lock' to %d requirements", node.ID, len(ext.Requirements)) | 
		
	
		
			
				|  |  |  |  |         if source != node.ID { | 
		
	
		
			
				|  |  |  |  |           messages = messages.Add(node.ID, node.Key, NewLockSignal("locking"), source) | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |       msgs = msgs.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_unlocked"), source) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   case "unlock": | 
		
	
		
			
				|  |  |  |  |     if ext.State == Locked { | 
		
	
		
			
				|  |  |  |  |       if len(ext.Requirements) == 0 { | 
		
	
		
			
				|  |  |  |  |         ext.State = Unlocked | 
		
	
		
			
				|  |  |  |  |         new_owner := source | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = nil | 
		
	
		
			
				|  |  |  |  |         ext.Owner = nil | 
		
	
		
			
				|  |  |  |  |         msgs = msgs.Add(node.ID, node.Key, NewLockSignal("unlocked"), new_owner) | 
		
	
		
			
				|  |  |  |  |       } else if source == *ext.Owner { | 
		
	
		
			
				|  |  |  |  |         ext.State = Unlocking | 
		
	
		
			
				|  |  |  |  |         ext.ReqID = signal.ID() | 
		
	
		
			
				|  |  |  |  |         ext.PendingOwner = nil | 
		
	
		
			
				|  |  |  |  |         for id, state := range(ext.Requirements) { | 
		
	
		
			
				|  |  |  |  |           if state != Locked { | 
		
	
		
			
				|  |  |  |  |             log.Logf("lockable", "REQ_NOT_LOCKED_WHEN_UNLOCKING") | 
		
	
		
			
				|  |  |  |  |           } | 
		
	
		
			
				|  |  |  |  |           ext.Requirements[id] = Unlocking | 
		
	
		
			
				|  |  |  |  |           lock_signal := NewLockSignal("unlock") | 
		
	
		
			
				|  |  |  |  |           msgs = msgs.Add(node.ID, node.Key, lock_signal, id) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |       msgs = msgs.Add(node.ID, node.Key, NewErrorSignal(signal.ID(), "not_locked"), source) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   default: | 
		
	
		
			
				|  |  |  |  |     log.Logf("lockable", "LOCK_ERR: unkown state %s", state) | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   return messages | 
		
	
		
			
				|  |  |  |  |   return msgs | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // LockableExts process Up/Down signals by forwarding them to owner, dependency, and requirement nodes
 | 
		
	
	
		
			
				
					|  |  |  | @ -314,6 +323,8 @@ func (ext *LockableExt) Process(ctx *Context, node *Node, source NodeID, signal | 
		
	
		
			
				|  |  |  |  |     switch signal.Type() { | 
		
	
		
			
				|  |  |  |  |     case LockSignalType: | 
		
	
		
			
				|  |  |  |  |       messages = ext.HandleLockSignal(ctx.Log, node, source, signal.(*StringSignal)) | 
		
	
		
			
				|  |  |  |  |     case ErrorSignalType: | 
		
	
		
			
				|  |  |  |  |       messages = ext.HandleErrorSignal(ctx.Log, node, source, signal.(*ErrorSignal)) | 
		
	
		
			
				|  |  |  |  |     default: | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   default: | 
		
	
	
		
			
				
					|  |  |  | 
 |