2023-06-23 20:56:09 -06:00
|
|
|
package graphvent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
2023-06-26 21:20:04 -06:00
|
|
|
"sync"
|
2023-06-23 20:56:09 -06:00
|
|
|
"errors"
|
|
|
|
"reflect"
|
|
|
|
"encoding/json"
|
|
|
|
)
|
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
// Assumed that thread is already locked for signal
|
2023-07-23 17:57:47 -06:00
|
|
|
func (thread *SimpleThread) Signal(context *StateContext, signal GraphSignal) error {
|
2023-07-22 20:21:17 -06:00
|
|
|
err := thread.SimpleLockable.Signal(context, signal)
|
2023-07-09 15:59:41 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-07-22 20:21:17 -06:00
|
|
|
|
|
|
|
switch signal.Direction() {
|
|
|
|
case Up:
|
2023-07-23 17:57:47 -06:00
|
|
|
err = UseStates(context, thread, NewLockInfo(thread, []string{"parent"}), func(context *StateContext) error {
|
2023-07-22 20:21:17 -06:00
|
|
|
if thread.parent != nil {
|
2023-07-23 17:57:47 -06:00
|
|
|
return UseStates(context, thread, NewLockInfo(thread.parent, []string{"signal"}), func(context *StateContext) error {
|
2023-07-22 20:21:17 -06:00
|
|
|
return thread.parent.Signal(context, signal)
|
|
|
|
})
|
|
|
|
} else {
|
2023-07-04 18:45:23 -06:00
|
|
|
return nil
|
2023-07-22 20:21:17 -06:00
|
|
|
}
|
|
|
|
})
|
|
|
|
case Down:
|
2023-07-23 17:57:47 -06:00
|
|
|
err = UseStates(context, thread, NewLockMap(
|
2023-07-22 21:24:54 -06:00
|
|
|
NewLockInfo(thread, []string{"children"}),
|
|
|
|
LockList(thread.children, []string{"signal"}),
|
2023-07-23 17:57:47 -06:00
|
|
|
), func(context *StateContext) error {
|
2023-07-09 15:59:41 -06:00
|
|
|
for _, child := range(thread.children) {
|
2023-07-22 20:21:17 -06:00
|
|
|
err := child.Signal(context, signal)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-06-23 21:21:14 -06:00
|
|
|
}
|
2023-07-04 18:45:23 -06:00
|
|
|
return nil
|
|
|
|
})
|
2023-07-22 20:21:17 -06:00
|
|
|
case Direct:
|
|
|
|
err = nil
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Invalid signal direction %d", signal.Direction())
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2023-07-04 18:45:23 -06:00
|
|
|
}
|
2023-07-22 20:21:17 -06:00
|
|
|
|
2023-06-23 20:56:09 -06:00
|
|
|
thread.signal <- signal
|
2023-07-09 15:59:41 -06:00
|
|
|
return nil
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
// Interface to represent any type of thread information
|
2023-06-23 20:56:09 -06:00
|
|
|
type ThreadInfo interface {
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) SetTimeout(timeout time.Time, action string) {
|
|
|
|
thread.timeout = timeout
|
|
|
|
thread.timeout_action = action
|
|
|
|
thread.timeout_chan = time.After(time.Until(timeout))
|
2023-06-23 22:19:43 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) TimeoutAction() string {
|
|
|
|
return thread.timeout_action
|
2023-06-30 13:25:35 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) State() string {
|
|
|
|
return thread.state_name
|
2023-07-01 13:03:28 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) SetState(new_state string) error {
|
|
|
|
if new_state == "" {
|
|
|
|
return fmt.Errorf("Cannot set state to '' with SetState")
|
2023-07-01 13:03:28 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
thread.state_name = new_state
|
2023-06-26 21:20:04 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) Parent() Thread {
|
|
|
|
return thread.parent
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) SetParent(parent Thread) {
|
|
|
|
thread.parent = parent
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) Children() []Thread {
|
|
|
|
return thread.children
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) Child(id NodeID) Thread {
|
|
|
|
for _, child := range(thread.children) {
|
2023-06-26 21:20:04 -06:00
|
|
|
if child.ID() == id {
|
|
|
|
return child
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) ChildInfo(child NodeID) ThreadInfo {
|
|
|
|
return thread.child_info[child]
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
// Requires thread and childs thread to be locked for write
|
|
|
|
func UnlinkThreads(ctx * Context, thread Thread, child Thread) error {
|
|
|
|
var found Node = nil
|
|
|
|
for _, c := range(thread.Children()) {
|
2023-07-03 16:37:54 -06:00
|
|
|
if child.ID() == c.ID() {
|
|
|
|
found = c
|
|
|
|
break
|
2023-07-03 16:03:56 -06:00
|
|
|
}
|
2023-07-03 16:37:54 -06:00
|
|
|
}
|
2023-07-03 16:03:56 -06:00
|
|
|
|
2023-07-03 16:37:54 -06:00
|
|
|
if found == nil {
|
|
|
|
return fmt.Errorf("UNLINK_THREADS_ERR: %s is not a child of %s", child.ID(), thread.ID())
|
|
|
|
}
|
2023-07-03 16:03:56 -06:00
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
child.SetParent(nil)
|
|
|
|
thread.RemoveChild(child)
|
2023-07-03 16:37:54 -06:00
|
|
|
|
|
|
|
return nil
|
2023-07-03 16:03:56 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) RemoveChild(child Thread) {
|
2023-07-03 16:03:56 -06:00
|
|
|
idx := -1
|
2023-07-09 15:59:41 -06:00
|
|
|
for i, c := range(thread.children) {
|
2023-07-03 16:03:56 -06:00
|
|
|
if c.ID() == child.ID() {
|
|
|
|
idx = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if idx == -1 {
|
2023-07-09 15:59:41 -06:00
|
|
|
panic(fmt.Sprintf("%s is not a child of %s", child.ID(), thread.Name()))
|
2023-07-03 16:03:56 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
child_len := len(thread.children)
|
|
|
|
thread.children[idx] = thread.children[child_len-1]
|
|
|
|
thread.children = thread.children[0:child_len-1]
|
2023-07-03 16:03:56 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) AddChild(child Thread, info ThreadInfo) error {
|
2023-06-23 20:56:09 -06:00
|
|
|
if child == nil {
|
|
|
|
return fmt.Errorf("Will not connect nil to the thread tree")
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
_, exists := thread.child_info[child.ID()]
|
2023-06-23 20:56:09 -06:00
|
|
|
if exists == true {
|
|
|
|
return fmt.Errorf("Will not connect the same child twice")
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
if info == nil && thread.InfoType != nil {
|
2023-06-23 20:56:09 -06:00
|
|
|
return fmt.Errorf("nil info passed when expecting info")
|
|
|
|
} else if info != nil {
|
2023-07-09 15:59:41 -06:00
|
|
|
if reflect.TypeOf(info) != thread.InfoType {
|
2023-07-09 20:30:19 -06:00
|
|
|
return fmt.Errorf("info type mismatch, expecting %+v - %+v", thread.InfoType, reflect.TypeOf(info))
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
thread.children = append(thread.children, child)
|
|
|
|
thread.child_info[child.ID()] = info
|
2023-06-23 20:56:09 -06:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
func checkIfChild(context *StateContext, target Thread, cur Thread) bool {
|
2023-07-09 15:59:41 -06:00
|
|
|
for _, child := range(cur.Children()) {
|
|
|
|
if child.ID() == target.ID() {
|
2023-06-23 20:56:09 -06:00
|
|
|
return true
|
|
|
|
}
|
2023-06-28 00:48:49 -06:00
|
|
|
is_child := false
|
2023-07-23 17:57:47 -06:00
|
|
|
UpdateStates(context, cur, NewLockMap(
|
2023-07-22 21:24:54 -06:00
|
|
|
NewLockInfo(child, []string{"children"}),
|
2023-07-23 17:57:47 -06:00
|
|
|
), func(context *StateContext) error {
|
2023-07-22 20:21:17 -06:00
|
|
|
is_child = checkIfChild(context, target, child)
|
2023-06-28 00:48:49 -06:00
|
|
|
return nil
|
2023-06-23 20:56:09 -06:00
|
|
|
})
|
|
|
|
if is_child {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Links child to parent with info as the associated info
|
|
|
|
// Continues the write context with princ, getting children for thread and parent for child
|
|
|
|
func LinkThreads(context *StateContext, princ Node, thread Thread, child Thread, info ThreadInfo) error {
|
2023-07-22 21:24:54 -06:00
|
|
|
if context == nil || thread == nil || child == nil {
|
2023-06-23 20:56:09 -06:00
|
|
|
return fmt.Errorf("invalid input")
|
|
|
|
}
|
|
|
|
|
|
|
|
if thread.ID() == child.ID() {
|
|
|
|
return fmt.Errorf("Will not link %s as a child of itself", thread.ID())
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
return UpdateStates(context, princ, LockMap{
|
|
|
|
child.ID(): LockInfo{Node: child, Resources: []string{"parent"}},
|
|
|
|
thread.ID(): LockInfo{Node: thread, Resources: []string{"children"}},
|
|
|
|
}, func(context *StateContext) error {
|
2023-07-22 20:21:17 -06:00
|
|
|
if child.Parent() != nil {
|
|
|
|
return fmt.Errorf("EVENT_LINK_ERR: %s already has a parent, cannot link as child", child.ID())
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
if checkIfChild(context, thread, child) == true {
|
|
|
|
return fmt.Errorf("EVENT_LINK_ERR: %s is a child of %s so cannot add as parent", thread.ID(), child.ID())
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
if checkIfChild(context, child, thread) == true {
|
|
|
|
return fmt.Errorf("EVENT_LINK_ERR: %s is already a parent of %s so will not add again", thread.ID(), child.ID())
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
err := thread.AddChild(child, info)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("EVENT_LINK_ERR: error adding %s as child to %s: %e", child.ID(), thread.ID(), err)
|
|
|
|
}
|
|
|
|
child.SetParent(thread)
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
return nil
|
|
|
|
})
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
type ThreadAction func(* Context, Thread)(string, error)
|
|
|
|
type ThreadActions map[string]ThreadAction
|
|
|
|
type ThreadHandler func(* Context, Thread, GraphSignal)(string, error)
|
|
|
|
type ThreadHandlers map[string]ThreadHandler
|
|
|
|
|
2023-06-23 20:56:09 -06:00
|
|
|
type Thread interface {
|
2023-07-09 15:59:41 -06:00
|
|
|
// All Threads are Lockables
|
2023-06-24 19:48:59 -06:00
|
|
|
Lockable
|
2023-07-09 15:59:41 -06:00
|
|
|
/// State Modification Functions
|
|
|
|
SetParent(parent Thread)
|
|
|
|
AddChild(child Thread, info ThreadInfo) error
|
|
|
|
RemoveChild(child Thread)
|
|
|
|
SetState(new_thread string) error
|
|
|
|
SetTimeout(end_time time.Time, action string)
|
|
|
|
/// State Reading Functions
|
|
|
|
Parent() Thread
|
|
|
|
Children() []Thread
|
|
|
|
Child(id NodeID) Thread
|
|
|
|
ChildInfo(child NodeID) ThreadInfo
|
|
|
|
State() string
|
|
|
|
TimeoutAction() string
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
/// Functions that dont read/write thread
|
|
|
|
// Deserialize the attribute map from json.Unmarshal
|
|
|
|
DeserializeInfo(ctx *Context, data []byte) (ThreadInfo, error)
|
|
|
|
SetActive(active bool) error
|
2023-06-23 21:21:14 -06:00
|
|
|
Action(action string) (ThreadAction, bool)
|
|
|
|
Handler(signal_type string) (ThreadHandler, bool)
|
2023-06-23 20:56:09 -06:00
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
// Internal timeout channel for thread
|
2023-06-23 20:56:09 -06:00
|
|
|
Timeout() <-chan time.Time
|
2023-07-09 15:59:41 -06:00
|
|
|
// Internal signal channel for thread
|
|
|
|
SignalChannel() <-chan GraphSignal
|
2023-07-02 12:14:04 -06:00
|
|
|
ClearTimeout()
|
2023-06-26 21:20:04 -06:00
|
|
|
|
|
|
|
ChildWaits() *sync.WaitGroup
|
2023-07-09 15:59:41 -06:00
|
|
|
}
|
|
|
|
|
2023-07-12 22:31:13 -06:00
|
|
|
type ParentInfo interface {
|
|
|
|
Parent() *ParentThreadInfo
|
|
|
|
}
|
2023-07-11 16:54:09 -06:00
|
|
|
|
|
|
|
// Data required by a parent thread to restore it's children
|
|
|
|
type ParentThreadInfo struct {
|
|
|
|
Start bool `json:"start"`
|
|
|
|
StartAction string `json:"start_action"`
|
|
|
|
RestoreAction string `json:"restore_action"`
|
|
|
|
}
|
|
|
|
|
2023-07-12 22:31:13 -06:00
|
|
|
func (info * ParentThreadInfo) Parent() *ParentThreadInfo{
|
|
|
|
return info
|
|
|
|
}
|
|
|
|
|
2023-07-11 16:54:09 -06:00
|
|
|
func NewParentThreadInfo(start bool, start_action string, restore_action string) ParentThreadInfo {
|
|
|
|
return ParentThreadInfo{
|
|
|
|
Start: start,
|
|
|
|
StartAction: start_action,
|
|
|
|
RestoreAction: restore_action,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
type SimpleThread struct {
|
|
|
|
SimpleLockable
|
|
|
|
|
|
|
|
actions ThreadActions
|
|
|
|
handlers ThreadHandlers
|
|
|
|
|
|
|
|
timeout_chan <-chan time.Time
|
|
|
|
signal chan GraphSignal
|
|
|
|
child_waits *sync.WaitGroup
|
|
|
|
active bool
|
|
|
|
active_lock *sync.Mutex
|
|
|
|
|
|
|
|
state_name string
|
|
|
|
parent Thread
|
|
|
|
children []Thread
|
|
|
|
child_info map[NodeID] ThreadInfo
|
|
|
|
InfoType reflect.Type
|
|
|
|
timeout time.Time
|
|
|
|
timeout_action string
|
|
|
|
}
|
|
|
|
|
2023-07-09 16:03:42 -06:00
|
|
|
func (thread * SimpleThread) Type() NodeType {
|
|
|
|
return NodeType("simple_thread")
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) Serialize() ([]byte, error) {
|
|
|
|
thread_json := NewSimpleThreadJSON(thread)
|
|
|
|
return json.MarshalIndent(&thread_json, "", " ")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (thread * SimpleThread) SignalChannel() <-chan GraphSignal {
|
|
|
|
return thread.signal
|
|
|
|
}
|
|
|
|
|
|
|
|
type SimpleThreadJSON struct {
|
2023-07-21 00:06:11 -06:00
|
|
|
Parent string `json:"parent"`
|
2023-07-19 20:03:13 -06:00
|
|
|
Children map[string]interface{} `json:"children"`
|
2023-07-09 15:59:41 -06:00
|
|
|
Timeout time.Time `json:"timeout"`
|
|
|
|
TimeoutAction string `json:"timeout_action"`
|
|
|
|
StateName string `json:"state_name"`
|
|
|
|
SimpleLockableJSON
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSimpleThreadJSON(thread *SimpleThread) SimpleThreadJSON {
|
2023-07-19 20:03:13 -06:00
|
|
|
children := map[string]interface{}{}
|
2023-07-09 15:59:41 -06:00
|
|
|
for _, child := range(thread.children) {
|
2023-07-19 20:03:13 -06:00
|
|
|
children[child.ID().String()] = thread.child_info[child.ID()]
|
2023-07-09 15:59:41 -06:00
|
|
|
}
|
|
|
|
|
2023-07-21 00:06:11 -06:00
|
|
|
parent_id := ""
|
2023-07-09 15:59:41 -06:00
|
|
|
if thread.parent != nil {
|
2023-07-21 00:06:11 -06:00
|
|
|
parent_id = thread.parent.ID().String()
|
2023-07-09 15:59:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
lockable_json := NewSimpleLockableJSON(&thread.SimpleLockable)
|
|
|
|
|
|
|
|
return SimpleThreadJSON{
|
|
|
|
Parent: parent_id,
|
|
|
|
Children: children,
|
|
|
|
Timeout: thread.timeout,
|
|
|
|
TimeoutAction: thread.timeout_action,
|
|
|
|
StateName: thread.state_name,
|
|
|
|
SimpleLockableJSON: lockable_json,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadSimpleThread(ctx *Context, id NodeID, data []byte, nodes NodeMap) (Node, error) {
|
|
|
|
var j SimpleThreadJSON
|
|
|
|
err := json.Unmarshal(data, &j)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
thread := NewSimpleThread(id, j.Name, j.StateName, nil, BaseThreadActions, BaseThreadHandlers)
|
|
|
|
nodes[id] = &thread
|
|
|
|
|
|
|
|
err = RestoreSimpleThread(ctx, &thread, j, nodes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &thread, nil
|
|
|
|
}
|
|
|
|
|
2023-07-20 00:24:22 -06:00
|
|
|
// SimpleThread has no associated info with children
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) DeserializeInfo(ctx *Context, data []byte) (ThreadInfo, error) {
|
|
|
|
if len(data) > 0 {
|
|
|
|
return nil, fmt.Errorf("SimpleThread expected to deserialize no info but got %d length data: %s", len(data), string(data))
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func RestoreSimpleThread(ctx *Context, thread Thread, j SimpleThreadJSON, nodes NodeMap) error {
|
2023-07-10 01:07:56 -06:00
|
|
|
if j.TimeoutAction != "" {
|
|
|
|
thread.SetTimeout(j.Timeout, j.TimeoutAction)
|
|
|
|
}
|
2023-07-09 15:59:41 -06:00
|
|
|
|
2023-07-21 00:06:11 -06:00
|
|
|
if j.Parent != "" {
|
|
|
|
parent_id, err := ParseID(j.Parent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p, err := LoadNodeRecurse(ctx, parent_id, nodes)
|
2023-07-09 15:59:41 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p_t, ok := p.(Thread)
|
|
|
|
if ok == false {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
thread.SetParent(p_t)
|
|
|
|
}
|
|
|
|
|
2023-07-19 20:03:13 -06:00
|
|
|
for id_str, info_raw := range(j.Children) {
|
|
|
|
id, err := ParseID(id_str)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-07-09 15:59:41 -06:00
|
|
|
child_node, err := LoadNodeRecurse(ctx, id, nodes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
child_t, ok := child_node.(Thread)
|
|
|
|
if ok == false {
|
|
|
|
return fmt.Errorf("%+v is not a Thread as expected", child_node)
|
|
|
|
}
|
|
|
|
|
2023-07-11 16:39:47 -06:00
|
|
|
var info_ser []byte
|
2023-07-11 00:23:07 -06:00
|
|
|
if info_raw != nil {
|
2023-07-11 16:39:47 -06:00
|
|
|
info_ser, err = json.Marshal(info_raw)
|
2023-07-11 00:23:07 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-07-11 16:39:47 -06:00
|
|
|
}
|
2023-07-09 15:59:41 -06:00
|
|
|
|
2023-07-11 16:39:47 -06:00
|
|
|
parsed_info, err := thread.DeserializeInfo(ctx, info_ser)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2023-07-09 15:59:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
thread.AddChild(child_t, parsed_info)
|
|
|
|
}
|
|
|
|
|
|
|
|
return RestoreSimpleLockable(ctx, thread, j.SimpleLockableJSON, nodes)
|
|
|
|
}
|
|
|
|
|
|
|
|
const THREAD_SIGNAL_BUFFER_SIZE = 128
|
|
|
|
func NewSimpleThread(id NodeID, name string, state_name string, info_type reflect.Type, actions ThreadActions, handlers ThreadHandlers) SimpleThread {
|
|
|
|
return SimpleThread{
|
|
|
|
SimpleLockable: NewSimpleLockable(id, name),
|
2023-07-09 20:30:19 -06:00
|
|
|
InfoType: info_type,
|
2023-07-09 15:59:41 -06:00
|
|
|
state_name: state_name,
|
|
|
|
signal: make(chan GraphSignal, THREAD_SIGNAL_BUFFER_SIZE),
|
|
|
|
children: []Thread{},
|
|
|
|
child_info: map[NodeID]ThreadInfo{},
|
|
|
|
actions: actions,
|
|
|
|
handlers: handlers,
|
|
|
|
child_waits: &sync.WaitGroup{},
|
|
|
|
active_lock: &sync.Mutex{},
|
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
// Requires the read permission of threads children
|
2023-07-23 17:57:47 -06:00
|
|
|
func FindChild(context *StateContext, princ Node, thread Thread, id NodeID) Thread {
|
2023-06-23 20:56:09 -06:00
|
|
|
if thread == nil {
|
|
|
|
panic("cannot recurse through nil")
|
|
|
|
}
|
|
|
|
if id == thread.ID() {
|
|
|
|
return thread
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
for _, child := range thread.Children() {
|
2023-06-28 00:48:49 -06:00
|
|
|
var result Thread = nil
|
2023-07-23 17:57:47 -06:00
|
|
|
UseStates(context, princ, NewLockInfo(child, []string{"children"}), func(context *StateContext) error {
|
2023-07-22 21:24:54 -06:00
|
|
|
result = FindChild(context, princ, child, id)
|
2023-06-28 00:48:49 -06:00
|
|
|
return nil
|
2023-06-23 20:56:09 -06:00
|
|
|
})
|
|
|
|
if result != nil {
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func ChildGo(ctx * Context, thread Thread, child Thread, first_action string) {
|
2023-06-26 21:20:04 -06:00
|
|
|
thread.ChildWaits().Add(1)
|
|
|
|
go func(child Thread) {
|
2023-07-03 13:14:48 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_START_CHILD: %s from %s", thread.ID(), child.ID())
|
2023-06-26 21:20:04 -06:00
|
|
|
defer thread.ChildWaits().Done()
|
2023-07-06 16:57:51 -06:00
|
|
|
err := ThreadLoop(ctx, child, first_action)
|
2023-06-26 21:20:04 -06:00
|
|
|
if err != nil {
|
2023-07-03 13:14:48 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_CHILD_RUN_ERR: %s %e", child.ID(), err)
|
2023-06-26 21:20:04 -06:00
|
|
|
} else {
|
2023-07-03 13:14:48 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_CHILD_RUN_DONE: %s", child.ID())
|
2023-06-26 21:20:04 -06:00
|
|
|
}
|
|
|
|
}(child)
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Main Loop for Threads, starts a write context, so cannot be called from a write or read context
|
2023-07-09 15:59:41 -06:00
|
|
|
func ThreadLoop(ctx * Context, thread Thread, first_action string) error {
|
2023-07-06 16:57:51 -06:00
|
|
|
// Start the thread, error if double-started
|
|
|
|
ctx.Log.Logf("thread", "THREAD_LOOP_START: %s - %s", thread.ID(), first_action)
|
2023-07-09 15:59:41 -06:00
|
|
|
err := thread.SetActive(true)
|
2023-06-28 00:48:49 -06:00
|
|
|
if err != nil {
|
2023-07-06 16:57:51 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_LOOP_START_ERR: %e", err)
|
2023-06-28 00:48:49 -06:00
|
|
|
return err
|
|
|
|
}
|
2023-07-02 12:47:45 -06:00
|
|
|
next_action := first_action
|
2023-06-23 20:56:09 -06:00
|
|
|
for next_action != "" {
|
|
|
|
action, exists := thread.Action(next_action)
|
|
|
|
if exists == false {
|
|
|
|
error_str := fmt.Sprintf("%s is not a valid action", next_action)
|
|
|
|
return errors.New(error_str)
|
|
|
|
}
|
|
|
|
|
2023-06-26 21:20:04 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_ACTION: %s - %s", thread.ID(), next_action)
|
2023-06-23 21:21:14 -06:00
|
|
|
next_action, err = action(ctx, thread)
|
2023-06-23 20:56:09 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
err = thread.SetActive(false)
|
2023-06-26 21:20:04 -06:00
|
|
|
if err != nil {
|
2023-07-06 16:57:51 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_LOOP_STOP_ERR: %e", err)
|
2023-06-26 21:20:04 -06:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-07-06 16:57:51 -06:00
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
|
2023-06-26 23:15:40 -06:00
|
|
|
|
2023-07-06 16:57:51 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_LOOP_DONE: %s", thread.ID())
|
2023-06-23 20:56:09 -06:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) ChildWaits() *sync.WaitGroup {
|
2023-07-05 14:50:21 -06:00
|
|
|
return thread.child_waits
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) SetActive(active bool) error {
|
2023-07-06 16:57:51 -06:00
|
|
|
thread.active_lock.Lock()
|
|
|
|
defer thread.active_lock.Unlock()
|
2023-07-09 15:59:41 -06:00
|
|
|
if thread.active == true && active == true {
|
|
|
|
return fmt.Errorf("%s is active, cannot set active", thread.ID())
|
|
|
|
} else if thread.active == false && active == false {
|
|
|
|
return fmt.Errorf("%s is already inactive, canot set inactive", thread.ID())
|
2023-07-06 16:57:51 -06:00
|
|
|
}
|
2023-07-09 15:59:41 -06:00
|
|
|
thread.active = active
|
2023-07-06 16:57:51 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) Action(action string) (ThreadAction, bool) {
|
|
|
|
action_fn, exists := thread.actions[action]
|
2023-06-23 20:56:09 -06:00
|
|
|
return action_fn, exists
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) Handler(signal_type string) (ThreadHandler, bool) {
|
|
|
|
handler, exists := thread.handlers[signal_type]
|
2023-06-23 21:21:14 -06:00
|
|
|
return handler, exists
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) Timeout() <-chan time.Time {
|
2023-07-02 12:14:04 -06:00
|
|
|
return thread.timeout_chan
|
2023-06-23 21:21:14 -06:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:59:41 -06:00
|
|
|
func (thread * SimpleThread) ClearTimeout() {
|
2023-07-02 12:14:04 -06:00
|
|
|
thread.timeout_chan = nil
|
2023-07-12 21:12:29 -06:00
|
|
|
thread.timeout_action = ""
|
2023-06-23 21:21:14 -06:00
|
|
|
}
|
|
|
|
|
2023-07-11 16:13:57 -06:00
|
|
|
func (thread * SimpleThread) AllowedToTakeLock(new_owner Lockable, lockable Lockable) bool {
|
|
|
|
for _, child := range(thread.children) {
|
|
|
|
if new_owner.ID() == child.ID() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Helper function to start a child from a thread during a signal handler
|
|
|
|
// Starts a write context, so cannot be called from either a write or read context
|
2023-07-21 14:05:39 -06:00
|
|
|
func ThreadStartChild(ctx *Context, thread Thread, signal StartChildSignal) error {
|
2023-07-23 17:57:47 -06:00
|
|
|
context := NewWriteContext(ctx)
|
|
|
|
return UpdateStates(context, thread, NewLockInfo(thread, []string{"children"}), func(context *StateContext) error {
|
2023-07-22 20:21:17 -06:00
|
|
|
child := thread.Child(signal.ID)
|
2023-07-21 14:05:39 -06:00
|
|
|
if child == nil {
|
2023-07-22 20:21:17 -06:00
|
|
|
return fmt.Errorf("%s is not a child of %s", signal.ID, thread.ID())
|
2023-07-21 14:05:39 -06:00
|
|
|
}
|
2023-07-23 17:57:47 -06:00
|
|
|
return UpdateStates(context, thread, NewLockInfo(child, []string{"start"}), func(context *StateContext) error {
|
2023-07-21 14:05:39 -06:00
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
info := thread.ChildInfo(signal.ID).(*ParentThreadInfo)
|
|
|
|
info.Start = true
|
|
|
|
ChildGo(ctx, thread, child, signal.Action)
|
2023-07-21 14:05:39 -06:00
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
return nil
|
|
|
|
})
|
2023-07-21 14:05:39 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Helper function to restore threads that should be running from a parents restore action
|
|
|
|
// Starts a write context, so cannot be called from either a write or read context
|
2023-07-21 14:05:39 -06:00
|
|
|
func ThreadRestore(ctx * Context, thread Thread) {
|
2023-07-23 17:57:47 -06:00
|
|
|
context := NewWriteContext(ctx)
|
|
|
|
UpdateStates(context, thread, NewLockMap(
|
2023-07-22 21:24:54 -06:00
|
|
|
NewLockInfo(thread, []string{"children"}),
|
|
|
|
LockList(thread.Children(), []string{"start"}),
|
2023-07-23 17:57:47 -06:00
|
|
|
), func(context *StateContext)(error) {
|
2023-07-22 21:24:54 -06:00
|
|
|
for _, child := range(thread.Children()) {
|
|
|
|
should_run := (thread.ChildInfo(child.ID())).(ParentInfo).Parent()
|
|
|
|
ctx.Log.Logf("thread", "THREAD_RESTORE: %s -> %s: %+v", thread.ID(), child.ID(), should_run)
|
|
|
|
if should_run.Start == true && child.State() != "finished" {
|
|
|
|
ctx.Log.Logf("thread", "THREAD_RESTORED: %s -> %s", thread.ID(), child.ID())
|
|
|
|
ChildGo(ctx, thread, child, should_run.RestoreAction)
|
2023-07-11 16:54:09 -06:00
|
|
|
}
|
2023-07-22 21:24:54 -06:00
|
|
|
}
|
|
|
|
return nil
|
2023-07-11 16:54:09 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Helper function to be called during a threads start action, sets the thread state to started
|
|
|
|
// Starts a write context, so cannot be called from either a write or read context
|
2023-07-21 14:05:39 -06:00
|
|
|
func ThreadStart(ctx * Context, thread Thread) error {
|
2023-07-23 17:57:47 -06:00
|
|
|
context := NewWriteContext(ctx)
|
|
|
|
return UpdateStates(context, thread, NewLockInfo(thread, []string{"state"}), func(context *StateContext) error {
|
|
|
|
err := LockLockables(context, []Lockable{thread}, thread)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2023-07-06 16:57:51 -06:00
|
|
|
}
|
2023-07-09 15:59:41 -06:00
|
|
|
return thread.SetState("started")
|
2023-07-06 16:57:51 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-07-21 14:05:39 -06:00
|
|
|
func ThreadDefaultStart(ctx * Context, thread Thread) (string, error) {
|
2023-07-06 16:57:51 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_DEFAULT_START: %s", thread.ID())
|
|
|
|
err := ThreadStart(ctx, thread)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2023-06-23 21:21:14 -06:00
|
|
|
return "wait", nil
|
|
|
|
}
|
|
|
|
|
2023-07-21 14:05:39 -06:00
|
|
|
func ThreadDefaultRestore(ctx * Context, thread Thread) (string, error) {
|
2023-07-02 12:47:45 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_DEFAULT_RESTORE: %s", thread.ID())
|
|
|
|
return "wait", nil
|
|
|
|
}
|
|
|
|
|
2023-07-21 14:05:39 -06:00
|
|
|
func ThreadWait(ctx * Context, thread Thread) (string, error) {
|
2023-06-23 21:21:14 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_WAIT: %s TIMEOUT: %+v", thread.ID(), thread.Timeout())
|
2023-06-24 19:48:59 -06:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case signal := <- thread.SignalChannel():
|
2023-07-22 20:21:17 -06:00
|
|
|
ctx.Log.Logf("thread", "THREAD_SIGNAL: %s %+v", thread.ID(), signal)
|
2023-06-24 19:48:59 -06:00
|
|
|
signal_fn, exists := thread.Handler(signal.Type())
|
|
|
|
if exists == true {
|
|
|
|
ctx.Log.Logf("thread", "THREAD_HANDLER: %s - %s", thread.ID(), signal.Type())
|
|
|
|
return signal_fn(ctx, thread, signal)
|
2023-06-25 21:00:00 -06:00
|
|
|
} else {
|
|
|
|
ctx.Log.Logf("thread", "THREAD_NOHANDLER: %s - %s", thread.ID(), signal.Type())
|
2023-06-24 19:48:59 -06:00
|
|
|
}
|
|
|
|
case <- thread.Timeout():
|
2023-07-02 12:14:04 -06:00
|
|
|
timeout_action := ""
|
2023-07-23 17:57:47 -06:00
|
|
|
context := NewWriteContext(ctx)
|
|
|
|
err := UpdateStates(context, thread, NewLockMap(NewLockInfo(thread, []string{"timeout"})), func(context *StateContext) error {
|
2023-07-09 15:59:41 -06:00
|
|
|
timeout_action = thread.TimeoutAction()
|
2023-07-02 12:14:04 -06:00
|
|
|
thread.ClearTimeout()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
ctx.Log.Logf("thread", "THREAD_TIMEOUT_ERR: %s - %e", thread.ID(), err)
|
|
|
|
}
|
|
|
|
ctx.Log.Logf("thread", "THREAD_TIMEOUT %s - NEXT_STATE: %s", thread.ID(), timeout_action)
|
|
|
|
return timeout_action, nil
|
2023-06-24 19:48:59 -06:00
|
|
|
}
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
func ThreadDefaultFinish(ctx *Context, thread Thread) (string, error) {
|
|
|
|
ctx.Log.Logf("thread", "THREAD_DEFAULT_FINISH: %s", thread.ID().String())
|
|
|
|
return "", ThreadFinish(ctx, thread)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ThreadFinish(ctx *Context, thread Thread) error {
|
|
|
|
context := NewWriteContext(ctx)
|
|
|
|
return UpdateStates(context, thread, NewLockInfo(thread, []string{"state"}), func(context *StateContext) error {
|
|
|
|
err := thread.SetState("finished")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return UnlockLockables(context, []Lockable{thread}, thread)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-07-22 20:21:17 -06:00
|
|
|
var ThreadAbortedError = errors.New("Thread aborted by signal")
|
2023-07-06 16:57:51 -06:00
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Default thread action function for "abort", sends a signal and returns a ThreadAbortedError
|
2023-07-09 15:59:41 -06:00
|
|
|
func ThreadAbort(ctx * Context, thread Thread, signal GraphSignal) (string, error) {
|
2023-07-23 17:57:47 -06:00
|
|
|
context := NewReadContext(ctx)
|
|
|
|
err := UseStates(context, thread, NewLockInfo(thread, []string{"signal"}), func(context *StateContext) error {
|
2023-07-22 21:24:54 -06:00
|
|
|
return thread.Signal(context, NewStatusSignal("aborted", thread.ID()))
|
2023-07-06 16:57:51 -06:00
|
|
|
})
|
2023-07-22 20:21:17 -06:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2023-07-23 17:57:47 -06:00
|
|
|
return "finish", ThreadAbortedError
|
2023-06-23 21:21:14 -06:00
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Default thread action for "cancel", sends a signal and returns no error
|
2023-07-09 15:59:41 -06:00
|
|
|
func ThreadCancel(ctx * Context, thread Thread, signal GraphSignal) (string, error) {
|
2023-07-23 17:57:47 -06:00
|
|
|
context := NewReadContext(ctx)
|
|
|
|
err := UseStates(context, thread, NewLockInfo(thread, []string{"signal"}), func(context *StateContext) error {
|
2023-07-22 21:24:54 -06:00
|
|
|
return thread.Signal(context, NewSignal("cancelled"))
|
2023-07-06 16:57:51 -06:00
|
|
|
})
|
2023-07-23 17:57:47 -06:00
|
|
|
return "finish", err
|
2023-06-23 20:56:09 -06:00
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Copy the default thread actions to a new ThreadActions map
|
2023-07-02 12:14:04 -06:00
|
|
|
func NewThreadActions() ThreadActions{
|
|
|
|
actions := ThreadActions{}
|
|
|
|
for k, v := range(BaseThreadActions) {
|
|
|
|
actions[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return actions
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Copy the defult thread handlers to a new ThreadAction map
|
2023-07-02 12:14:04 -06:00
|
|
|
func NewThreadHandlers() ThreadHandlers{
|
|
|
|
handlers := ThreadHandlers{}
|
|
|
|
for k, v := range(BaseThreadHandlers) {
|
|
|
|
handlers[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return handlers
|
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Default thread actions
|
2023-07-02 12:14:04 -06:00
|
|
|
var BaseThreadActions = ThreadActions{
|
|
|
|
"wait": ThreadWait,
|
|
|
|
"start": ThreadDefaultStart,
|
2023-07-23 17:57:47 -06:00
|
|
|
"finish": ThreadDefaultFinish,
|
2023-07-02 12:47:45 -06:00
|
|
|
"restore": ThreadDefaultRestore,
|
2023-07-02 12:14:04 -06:00
|
|
|
}
|
|
|
|
|
2023-07-23 17:57:47 -06:00
|
|
|
// Default thread signal handlers
|
2023-07-02 12:14:04 -06:00
|
|
|
var BaseThreadHandlers = ThreadHandlers{
|
|
|
|
"abort": ThreadAbort,
|
|
|
|
"cancel": ThreadCancel,
|
|
|
|
}
|