2023-06-23 20:56:09 -06:00
|
|
|
package graphvent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"encoding/json"
|
|
|
|
)
|
|
|
|
|
2023-06-23 21:57:26 -06:00
|
|
|
// LockHolderState is the interface that any node that wants to posses locks must implement
|
|
|
|
//
|
2023-06-24 19:48:59 -06:00
|
|
|
// ReturnLock returns the node that held the lockable pointed to by ID before this node and
|
|
|
|
// removes the mapping from it's state, or nil if the lockable was unlocked previously
|
2023-06-23 21:57:26 -06:00
|
|
|
//
|
|
|
|
// AllowedToTakeLock returns true if the node pointed to by ID is allowed to take a lock from this node
|
2023-06-23 22:19:43 -06:00
|
|
|
//
|
2023-06-24 19:48:59 -06:00
|
|
|
// RecordLockHolder records that lockable_id needs to be passed back to lock_holder
|
2023-06-23 20:56:09 -06:00
|
|
|
type LockHolderState interface {
|
2023-06-24 19:48:59 -06:00
|
|
|
ReturnLock(lockable_id NodeID) GraphNode
|
|
|
|
AllowedToTakeLock(node_id NodeID, lockable_id NodeID) bool
|
|
|
|
RecordLockHolder(lockable_id NodeID, lock_holder GraphNode)
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-06-23 22:19:43 -06:00
|
|
|
// LockableState is the interface that a lockables state must have to allow it to connect to the DAG
|
2023-06-23 20:56:09 -06:00
|
|
|
type LockableState interface {
|
|
|
|
LockHolderState
|
|
|
|
Name() string
|
|
|
|
Requirements() []Lockable
|
|
|
|
AddRequirement(requirement Lockable)
|
|
|
|
Dependencies() []Lockable
|
|
|
|
AddDependency(dependency Lockable)
|
|
|
|
Owner() GraphNode
|
|
|
|
SetOwner(owner GraphNode)
|
|
|
|
}
|
|
|
|
|
2023-06-23 22:19:43 -06:00
|
|
|
type BaseLockHolderState struct {
|
|
|
|
delegation_map map[NodeID] GraphNode
|
|
|
|
}
|
|
|
|
|
|
|
|
type BaseLockHolderStateJSON struct {
|
|
|
|
Delegations map[NodeID]*NodeID `json:"delegations"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockHolderState) MarshalJSON() ([]byte, error) {
|
|
|
|
delegations := map[NodeID]*NodeID{}
|
|
|
|
for lockable_id, node := range(state.delegation_map) {
|
|
|
|
if node == nil {
|
|
|
|
delegations[lockable_id] = nil
|
|
|
|
} else {
|
|
|
|
str := node.ID()
|
|
|
|
delegations[lockable_id] = &str
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return json.Marshal(&BaseLockHolderStateJSON{
|
|
|
|
Delegations: delegations,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// BaseLockableStates are a minimum collection of variables for a basic implementation of a LockHolder
|
|
|
|
// Include in any state structs that should be lockable
|
2023-06-23 20:56:09 -06:00
|
|
|
type BaseLockableState struct {
|
2023-06-23 22:19:43 -06:00
|
|
|
BaseLockHolderState
|
2023-06-23 20:56:09 -06:00
|
|
|
name string
|
|
|
|
owner GraphNode
|
|
|
|
requirements []Lockable
|
|
|
|
dependencies []Lockable
|
2023-06-23 22:19:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type BaseLockableStateJSON struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Owner *NodeID `json:"owner"`
|
|
|
|
Dependencies []NodeID `json:"dependencies"`
|
|
|
|
Requirements []NodeID `json:"requirements"`
|
|
|
|
HolderState *BaseLockHolderState `json:"holder_state"`
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) MarshalJSON() ([]byte, error) {
|
|
|
|
requirement_ids := make([]NodeID, len(state.requirements))
|
|
|
|
for i, requirement := range(state.requirements) {
|
|
|
|
requirement_ids[i] = requirement.ID()
|
|
|
|
}
|
|
|
|
|
|
|
|
dependency_ids := make([]NodeID, len(state.dependencies))
|
|
|
|
for i, dependency := range(state.dependencies) {
|
|
|
|
dependency_ids[i] = dependency.ID()
|
|
|
|
}
|
|
|
|
|
|
|
|
var owner_id *NodeID = nil
|
|
|
|
if state.owner != nil {
|
|
|
|
new_str := state.owner.ID()
|
|
|
|
owner_id = &new_str
|
|
|
|
}
|
|
|
|
|
2023-06-23 22:19:43 -06:00
|
|
|
return json.Marshal(&BaseLockableStateJSON{
|
2023-06-23 20:56:09 -06:00
|
|
|
Name: state.name,
|
|
|
|
Owner: owner_id,
|
|
|
|
Dependencies: dependency_ids,
|
|
|
|
Requirements: requirement_ids,
|
2023-06-23 22:19:43 -06:00
|
|
|
HolderState: &state.BaseLockHolderState,
|
2023-06-23 20:56:09 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) Name() string {
|
|
|
|
return state.name
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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"
|
2023-06-24 19:48:59 -06:00
|
|
|
func (state * BaseLockHolderState) ReturnLock(lockable_id NodeID) GraphNode {
|
|
|
|
node, exists := state.delegation_map[lockable_id]
|
2023-06-23 21:57:26 -06:00
|
|
|
if exists == false {
|
2023-06-24 19:48:59 -06:00
|
|
|
panic("Attempted to take a get the original lock holder of a lockable we don't own")
|
2023-06-23 21:57:26 -06:00
|
|
|
}
|
2023-06-24 19:48:59 -06:00
|
|
|
delete(state.delegation_map, lockable_id)
|
2023-06-23 21:57:26 -06:00
|
|
|
return node
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing can take a lock from a base lockable either
|
2023-06-24 19:48:59 -06:00
|
|
|
func (state * BaseLockHolderState) AllowedToTakeLock(node_id NodeID, lockable_id NodeID) bool {
|
|
|
|
_, exists := state.delegation_map[lockable_id]
|
2023-06-23 21:57:26 -06:00
|
|
|
if exists == false {
|
|
|
|
panic ("Trying to give away lock we don't own")
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-06-24 19:48:59 -06:00
|
|
|
func (state * BaseLockHolderState) RecordLockHolder(lockable_id NodeID, lock_holder GraphNode) {
|
|
|
|
_, exists := state.delegation_map[lockable_id]
|
2023-06-23 21:57:26 -06:00
|
|
|
if exists == true {
|
2023-06-24 19:48:59 -06:00
|
|
|
panic("Attempted to lock a lockable we're already holding(lock cycle)")
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
2023-06-23 21:57:26 -06:00
|
|
|
|
2023-06-24 19:48:59 -06:00
|
|
|
state.delegation_map[lockable_id] = lock_holder
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) Owner() GraphNode {
|
|
|
|
return state.owner
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) SetOwner(owner GraphNode) {
|
|
|
|
state.owner = owner
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) Requirements() []Lockable {
|
|
|
|
return state.requirements
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) AddRequirement(requirement Lockable) {
|
|
|
|
if requirement == nil {
|
|
|
|
panic("Will not connect nil to the DAG")
|
|
|
|
}
|
|
|
|
state.requirements = append(state.requirements, requirement)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) Dependencies() []Lockable {
|
|
|
|
return state.dependencies
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state * BaseLockableState) AddDependency(dependency Lockable) {
|
|
|
|
if dependency == nil {
|
|
|
|
panic("Will not connect nil to the DAG")
|
|
|
|
}
|
|
|
|
|
|
|
|
state.dependencies = append(state.dependencies, dependency)
|
|
|
|
}
|
|
|
|
|
2023-06-23 22:19:43 -06:00
|
|
|
func NewLockHolderState() BaseLockHolderState {
|
|
|
|
return BaseLockHolderState{
|
|
|
|
delegation_map: map[NodeID]GraphNode{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
func LinkLockables(ctx * GraphContext, lockable Lockable, requirements []Lockable) error {
|
|
|
|
if lockable == nil {
|
|
|
|
return fmt.Errorf("LOCKABLE_LINK_ERR: Will not link Lockables to nil as requirements")
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
for _, requirement := range(requirements) {
|
|
|
|
if requirement == nil {
|
|
|
|
return fmt.Errorf("LOCKABLE_LINK_ERR: Will not link nil to a Lockable as a requirement")
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
if lockable.ID() == requirement.ID() {
|
|
|
|
return fmt.Errorf("LOCKABLE_LINK_ERR: cannot link %s to itself", lockable.ID())
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
nodes := make([]GraphNode, len(requirements) + 1)
|
|
|
|
nodes[0] = lockable
|
|
|
|
for i, node := range(requirements) {
|
|
|
|
nodes[i+1] = node
|
|
|
|
}
|
|
|
|
_, err := UpdateStates(ctx, nodes, func(states []NodeState) ([]NodeState, interface{}, error) {
|
|
|
|
// Check that all the requirements can be added
|
2023-06-23 20:56:09 -06:00
|
|
|
lockable_state := states[0].(LockableState)
|
2023-06-25 13:39:00 -06:00
|
|
|
for i, requirement := range(requirements) {
|
|
|
|
requirement_state := states[i+1].(LockableState)
|
2023-06-23 20:56:09 -06:00
|
|
|
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
if checkIfRequirement(ctx, lockable.ID(), requirement_state, requirement.ID()) == true {
|
|
|
|
return nil, nil, fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependency of %s so cannot link as requirement", requirement.ID(), lockable.ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
if checkIfRequirement(ctx, requirement.ID(), lockable_state, lockable.ID()) == true {
|
|
|
|
return nil, nil, fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependency of %s so cannot link as dependency again", lockable.ID(), requirement.ID())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Update the states of the requirements
|
|
|
|
for i, requirement := range(requirements) {
|
|
|
|
requirement_state := states[i+1].(LockableState)
|
|
|
|
requirement_state.AddDependency(lockable)
|
|
|
|
lockable_state.AddRequirement(requirement)
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
// Return no error
|
|
|
|
return states, nil, nil
|
2023-06-23 20:56:09 -06:00
|
|
|
})
|
2023-06-25 13:39:00 -06:00
|
|
|
|
2023-06-23 20:56:09 -06:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
func NewBaseLockableState(name string) BaseLockableState {
|
|
|
|
state := BaseLockableState{
|
|
|
|
BaseLockHolderState: NewLockHolderState(),
|
|
|
|
name: name,
|
|
|
|
owner: nil,
|
|
|
|
requirements: []Lockable{},
|
|
|
|
dependencies: []Lockable{},
|
|
|
|
}
|
|
|
|
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2023-06-23 20:56:09 -06:00
|
|
|
type Lockable interface {
|
|
|
|
GraphNode
|
|
|
|
// Called when locking the node to allow for custom lock behaviour
|
|
|
|
Lock(node GraphNode, state LockableState) error
|
|
|
|
// Called when unlocking the node to allow for custom lock behaviour
|
|
|
|
Unlock(node GraphNode, state LockableState) error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lockable * BaseLockable) PropagateUpdate(ctx * GraphContext, signal GraphSignal) {
|
|
|
|
UseStates(ctx, []GraphNode{lockable}, func(states []NodeState) (interface{}, error){
|
|
|
|
lockable_state := states[0].(LockableState)
|
|
|
|
if signal.Direction() == Up {
|
|
|
|
// Child->Parent, lockable updates dependency lockables
|
2023-06-24 19:48:59 -06:00
|
|
|
owner_sent := false
|
2023-06-23 20:56:09 -06:00
|
|
|
for _, dependency := range lockable_state.Dependencies() {
|
|
|
|
SendUpdate(ctx, dependency, signal)
|
2023-06-24 19:48:59 -06:00
|
|
|
if lockable_state.Owner() != nil {
|
|
|
|
if dependency.ID() != lockable_state.Owner().ID() {
|
|
|
|
owner_sent = true
|
|
|
|
}
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
2023-06-24 19:48:59 -06:00
|
|
|
if lockable_state.Owner() != nil && owner_sent == false {
|
2023-06-23 20:56:09 -06:00
|
|
|
SendUpdate(ctx, lockable_state.Owner(), signal)
|
|
|
|
}
|
2023-06-24 19:48:59 -06:00
|
|
|
} else if signal.Direction() == Down {
|
|
|
|
// Parent->Child, lockable updates lock holder
|
2023-06-23 20:56:09 -06:00
|
|
|
for _, requirement := range(lockable_state.Requirements()) {
|
|
|
|
SendUpdate(ctx, requirement, signal)
|
|
|
|
}
|
2023-06-24 19:48:59 -06:00
|
|
|
|
2023-06-23 20:56:09 -06:00
|
|
|
} else if signal.Direction() == Direct {
|
|
|
|
} else {
|
|
|
|
panic(fmt.Sprintf("Invalid signal direction: %d", signal.Direction()))
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
func checkIfRequirement(ctx * GraphContext, r_id NodeID, cur LockableState, cur_id NodeID) bool {
|
2023-06-23 20:56:09 -06:00
|
|
|
for _, c := range(cur.Requirements()) {
|
|
|
|
if c.ID() == r_id {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
val, _ := UseStates(ctx, []GraphNode{c}, func(states []NodeState) (interface{}, error) {
|
|
|
|
requirement_state := states[0].(LockableState)
|
2023-06-25 13:39:00 -06:00
|
|
|
return checkIfRequirement(ctx, cur_id, requirement_state, c.ID()), nil
|
2023-06-23 20:56:09 -06:00
|
|
|
})
|
|
|
|
|
|
|
|
is_requirement := val.(bool)
|
|
|
|
if is_requirement {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func UnlockLockable(ctx * GraphContext, lockable Lockable, node GraphNode, node_state LockHolderState) error {
|
|
|
|
if node == nil || lockable == nil{
|
|
|
|
panic("Cannot unlock without a specified node and lockable")
|
|
|
|
}
|
|
|
|
_, err := UpdateStates(ctx, []GraphNode{lockable}, func(states []NodeState) ([]NodeState, interface{}, error) {
|
|
|
|
if lockable.ID() == node.ID() {
|
|
|
|
if node_state != nil {
|
|
|
|
panic("node_state must be nil if unlocking lockable from itself")
|
|
|
|
}
|
|
|
|
node_state = states[0].(LockHolderState)
|
|
|
|
}
|
|
|
|
lockable_state := states[0].(LockableState)
|
|
|
|
|
|
|
|
if lockable_state.Owner() == nil {
|
|
|
|
return nil, nil, fmt.Errorf("Lockable already unlocked")
|
|
|
|
}
|
|
|
|
|
|
|
|
if lockable_state.Owner().ID() != node.ID() {
|
|
|
|
return nil, nil, fmt.Errorf("Lockable %s not locked by %s", lockable.ID(), node.ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
var lock_err error = nil
|
|
|
|
for _, requirement := range(lockable_state.Requirements()) {
|
|
|
|
var err error = nil
|
|
|
|
err = UnlockLockable(ctx, requirement, node, node_state)
|
|
|
|
if err != nil {
|
|
|
|
lock_err = err
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if lock_err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("Lockable %s failed to unlock: %e", lockable.ID(), lock_err)
|
|
|
|
}
|
|
|
|
|
2023-06-23 21:57:26 -06:00
|
|
|
new_owner := node_state.ReturnLock(lockable.ID())
|
2023-06-23 20:56:09 -06:00
|
|
|
lockable_state.SetOwner(new_owner)
|
|
|
|
err := lockable.Unlock(node, lockable_state)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("Lockable %s failed custom Unlock: %e", lockable.ID(), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if lockable_state.Owner() == nil {
|
|
|
|
ctx.Log.Logf("lockable", "LOCKABLE_UNLOCK: %s unlocked %s", node.ID(), lockable.ID())
|
|
|
|
} else {
|
|
|
|
ctx.Log.Logf("lockable", "LOCKABLE_UNLOCK: %s passed lock of %s back to %s", node.ID(), lockable.ID(), lockable_state.Owner().ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
return []NodeState{lockable_state}, nil, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func LockLockable(ctx * GraphContext, lockable Lockable, node GraphNode, node_state LockHolderState) error {
|
|
|
|
if node == nil || lockable == nil {
|
2023-06-24 19:48:59 -06:00
|
|
|
return fmt.Errorf("Cannot lock without a specified node and lockable")
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
2023-06-24 19:48:59 -06:00
|
|
|
ctx.Log.Logf("lockable", "LOCKING: %s from %s", lockable.ID(), node.ID())
|
2023-06-23 20:56:09 -06:00
|
|
|
|
|
|
|
_, err := UpdateStates(ctx, []GraphNode{lockable}, func(states []NodeState) ([]NodeState, interface{}, error) {
|
|
|
|
if lockable.ID() == node.ID() {
|
|
|
|
if node_state != nil {
|
2023-06-24 19:48:59 -06:00
|
|
|
return nil, nil, fmt.Errorf("node_state must be nil if locking lockable from itself")
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
node_state = states[0].(LockHolderState)
|
|
|
|
}
|
|
|
|
lockable_state := states[0].(LockableState)
|
|
|
|
if lockable_state.Owner() != nil {
|
|
|
|
var lock_pass_allowed bool = false
|
|
|
|
|
|
|
|
if lockable_state.Owner().ID() == lockable.ID() {
|
2023-06-23 21:57:26 -06:00
|
|
|
lock_pass_allowed = lockable_state.AllowedToTakeLock(node.ID(), lockable.ID())
|
2023-06-23 20:56:09 -06:00
|
|
|
} else {
|
|
|
|
tmp, _ := UseStates(ctx, []GraphNode{lockable_state.Owner()}, func(states []NodeState)(interface{}, error){
|
2023-06-23 21:57:26 -06:00
|
|
|
return states[0].(LockHolderState).AllowedToTakeLock(node.ID(), lockable.ID()), nil
|
2023-06-23 20:56:09 -06:00
|
|
|
})
|
|
|
|
lock_pass_allowed = tmp.(bool)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if lock_pass_allowed == false {
|
|
|
|
return nil, nil, fmt.Errorf("%s is not allowed to take a lock from %s", node.ID(), lockable_state.Owner().ID())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := lockable.Lock(node, lockable_state)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("Failed to lock lockable: %e", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var lock_err error = nil
|
|
|
|
locked_requirements := []Lockable{}
|
|
|
|
for _, requirement := range(lockable_state.Requirements()) {
|
|
|
|
err = LockLockable(ctx, requirement, node, node_state)
|
|
|
|
if err != nil {
|
|
|
|
lock_err = err
|
|
|
|
break
|
|
|
|
}
|
|
|
|
locked_requirements = append(locked_requirements, requirement)
|
|
|
|
}
|
|
|
|
|
|
|
|
if lock_err != nil {
|
|
|
|
for _, locked_lockable := range(locked_requirements) {
|
|
|
|
err = UnlockLockable(ctx, locked_lockable, node, node_state)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil, fmt.Errorf("Lockable failed to lock: %e", lock_err)
|
|
|
|
}
|
|
|
|
|
|
|
|
old_owner := lockable_state.Owner()
|
|
|
|
lockable_state.SetOwner(node)
|
2023-06-23 21:57:26 -06:00
|
|
|
node_state.RecordLockHolder(lockable.ID(), old_owner)
|
2023-06-23 20:56:09 -06:00
|
|
|
|
|
|
|
if old_owner == nil {
|
|
|
|
ctx.Log.Logf("lockable", "LOCKABLE_LOCK: %s locked %s", node.ID(), lockable.ID())
|
|
|
|
} else {
|
|
|
|
ctx.Log.Logf("lockable", "LOCKABLE_LOCK: %s took lock of %s from %s", node.ID(), lockable.ID(), old_owner.ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
return []NodeState{lockable_state}, nil, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// BaseLockables represent simple lockables in the DAG that can be used to create a hierarchy of locks that store names
|
|
|
|
type BaseLockable struct {
|
|
|
|
BaseNode
|
|
|
|
}
|
|
|
|
|
|
|
|
//BaseLockables don't check anything special when locking/unlocking
|
|
|
|
func (lockable * BaseLockable) Lock(node GraphNode, state LockableState) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lockable * BaseLockable) Unlock(node GraphNode, state LockableState) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewLockable(ctx * GraphContext, name string, requirements []Lockable) (* BaseLockable, error) {
|
2023-06-25 13:39:00 -06:00
|
|
|
state := NewBaseLockableState(name)
|
2023-06-23 20:56:09 -06:00
|
|
|
lockable := &BaseLockable{
|
|
|
|
BaseNode: NewNode(ctx, RandID(), &state),
|
|
|
|
}
|
|
|
|
|
2023-06-25 13:39:00 -06:00
|
|
|
err := LinkLockables(ctx, lockable, requirements)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return lockable, nil
|
|
|
|
}
|