| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -5,11 +5,16 @@ import (
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  "encoding/json"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Any struct that wants to hold a lock must implement this interface
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// LockHolderState is the interface that any node that wants to posses locks must implement
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				//
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// ReturnLock returns the node that held the resource pointed to by ID before this node,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// or nil if the resource was unlocked previously
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				//
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// AllowedToTakeLock returns true if the node pointed to by ID is allowed to take a lock from this node
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				type LockHolderState interface {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  OriginalLockHolder(id NodeID) GraphNode
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  AllowedToTakeLock(id NodeID) bool
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  RecordLockHolder(id NodeID, lock_holder GraphNode)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  ReturnLock(resource_id NodeID) GraphNode
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  AllowedToTakeLock(node_id NodeID, resource_id NodeID) bool
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  RecordLockHolder(resource_id NodeID, lock_holder GraphNode)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Any node that wants to be connected to the lockable DAG must implement this interface
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -29,6 +34,7 @@ type BaseLockableState struct {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  owner GraphNode
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  requirements []Lockable
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  dependencies []Lockable
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  delegation_map map[NodeID]GraphNode
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) MarshalJSON() ([]byte, error) {
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -42,6 +48,16 @@ func (state * BaseLockableState) MarshalJSON() ([]byte, error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    dependency_ids[i] = dependency.ID()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  delegations := map[NodeID]*NodeID{}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  for resource_id, node := range(state.delegation_map) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if node == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      delegations[resource_id] = nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      str := node.ID()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      delegations[resource_id] = &str
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  var owner_id *NodeID = nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if state.owner != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    new_str := state.owner.ID()
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -53,11 +69,13 @@ func (state * BaseLockableState) MarshalJSON() ([]byte, error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Owner *NodeID `json:"owner"`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Dependencies []NodeID `json:"dependencies"`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Requirements []NodeID `json:"requirements"`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Delegations map[NodeID]*NodeID `json:"delegations"`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Name: state.name,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Owner: owner_id,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Dependencies: dependency_ids,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Requirements: requirement_ids,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Delegations: delegations,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -67,19 +85,31 @@ func (state * BaseLockableState) Name() string {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Locks cannot be passed between base lockables, so the answer to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// "who used to own this lock held by a base lockable" is always "nobody"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) OriginalLockHolder(id NodeID) GraphNode {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) ReturnLock(resource_id NodeID) GraphNode {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  node, exists := state.delegation_map[resource_id]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if exists == false {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    panic("Attempted to take a get the original lock holder of a resource we don't own")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  delete(state.delegation_map, resource_id)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return node
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Nothing can take a lock from a base lockable either
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) AllowedToTakeLock(id NodeID) bool {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) AllowedToTakeLock(node_id NodeID, resource_id NodeID) bool {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  _, exists := state.delegation_map[resource_id]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if exists == false {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    panic ("Trying to give away lock we don't own")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) RecordLockHolder(id NodeID, lock_holder GraphNode) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if lock_holder != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    panic("Attempted to delegate a lock to a lockable")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) RecordLockHolder(resource_id NodeID, lock_holder GraphNode) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  _, exists := state.delegation_map[resource_id]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if exists == true {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    panic("Attempted to lock a resource we're already holding(lock cycle)")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  state.delegation_map[resource_id] = lock_holder
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (state * BaseLockableState) Owner() GraphNode {
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -119,6 +149,7 @@ func NewLockableState(name string) BaseLockableState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    owner: nil,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    requirements: []Lockable{},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    dependencies: []Lockable{},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    delegation_map: map[NodeID]GraphNode{},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -240,7 +271,7 @@ func UnlockLockable(ctx * GraphContext, lockable Lockable, node GraphNode, node_
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return nil, nil, fmt.Errorf("Lockable %s failed to unlock: %e", lockable.ID(), lock_err)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    new_owner := node_state.OriginalLockHolder(lockable.ID())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    new_owner := node_state.ReturnLock(lockable.ID())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    lockable_state.SetOwner(new_owner)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    err := lockable.Unlock(node, lockable_state)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if err != nil {
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -263,6 +294,7 @@ func LockLockable(ctx * GraphContext, lockable Lockable, node GraphNode, node_st
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if node == nil || lockable == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    panic("Cannot lock without a specified node and lockable")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  ctx.Log.Logf("resource", "LOCKING: %s from %s", lockable.ID(), node.ID())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  _, err := UpdateStates(ctx, []GraphNode{lockable}, func(states []NodeState) ([]NodeState, interface{}, error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if lockable.ID() == node.ID() {
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -276,10 +308,10 @@ func LockLockable(ctx * GraphContext, lockable Lockable, node GraphNode, node_st
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      var lock_pass_allowed bool = false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if lockable_state.Owner().ID() == lockable.ID() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lock_pass_allowed = lockable_state.AllowedToTakeLock(node.ID())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lock_pass_allowed = lockable_state.AllowedToTakeLock(node.ID(), lockable.ID())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        tmp, _ := UseStates(ctx, []GraphNode{lockable_state.Owner()}, func(states []NodeState)(interface{}, error){
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          return states[0].(LockHolderState).AllowedToTakeLock(node.ID()), nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          return states[0].(LockHolderState).AllowedToTakeLock(node.ID(), lockable.ID()), nil
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lock_pass_allowed = tmp.(bool)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -318,7 +350,7 @@ func LockLockable(ctx * GraphContext, lockable Lockable, node GraphNode, node_st
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    old_owner := lockable_state.Owner()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    lockable_state.SetOwner(node)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    node_state.RecordLockHolder(node.ID(), old_owner)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    node_state.RecordLockHolder(lockable.ID(), old_owner)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if old_owner == nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ctx.Log.Logf("lockable", "LOCKABLE_LOCK: %s locked %s", node.ID(), lockable.ID())
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |