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"
)
// Update the threads listeners, and notify the parent to do the same
2023-07-09 15:59:41 -06:00
func ( thread * SimpleThread ) Signal ( ctx * Context , signal GraphSignal , nodes NodeMap ) error {
err := thread . SimpleLockable . Signal ( ctx , signal , nodes )
if err != nil {
return err
}
2023-07-04 18:45:23 -06:00
if signal . Direction ( ) == Up {
// Child->Parent, thread updates parent and connected requirement
2023-07-09 15:59:41 -06:00
if thread . parent != nil {
UseMoreStates ( ctx , [ ] Node { thread . parent } , nodes , func ( nodes NodeMap ) error {
thread . parent . Signal ( ctx , signal , nodes )
2023-07-04 18:45:23 -06:00
return nil
} )
}
} else if signal . Direction ( ) == Down {
// Parent->Child, updates children and dependencies
2023-07-09 15:59:41 -06:00
UseMoreStates ( ctx , NodeList ( thread . children ) , nodes , func ( nodes NodeMap ) error {
for _ , child := range ( thread . children ) {
child . Signal ( ctx , signal , nodes )
2023-06-23 21:21:14 -06:00
}
2023-07-04 18:45:23 -06:00
return nil
} )
} else if signal . Direction ( ) == Direct {
} else {
panic ( fmt . Sprintf ( "Invalid signal direction: %d" , signal . Direction ( ) ) )
}
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 {
return fmt . Errorf ( "info type mismatch, expecting %+v" , thread . InfoType )
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-09 15:59:41 -06:00
func checkIfChild ( ctx * Context , target Thread , cur Thread , nodes NodeMap ) bool {
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-09 15:59:41 -06:00
UpdateMoreStates ( ctx , [ ] Node { child } , nodes , func ( nodes NodeMap ) error {
is_child = checkIfChild ( ctx , target , child , nodes )
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-09 15:59:41 -06:00
// Requires thread and childs thread to be locked for write
func LinkThreads ( ctx * Context , thread Thread , child Thread , info ThreadInfo , nodes NodeMap ) error {
2023-06-23 20:56:09 -06:00
if ctx == nil || thread == nil || child == nil {
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-09 15:59:41 -06:00
if child . Parent ( ) != nil {
2023-07-03 16:37:54 -06:00
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-09 15:59:41 -06:00
if checkIfChild ( ctx , thread , child , nodes ) == true {
2023-07-03 16:37:54 -06:00
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-09 15:59:41 -06:00
if checkIfChild ( ctx , child , thread , nodes ) == true {
2023-07-03 16:37:54 -06:00
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-09 15:59:41 -06:00
err := thread . AddChild ( child , info )
2023-07-03 16:37:54 -06:00
if err != nil {
return fmt . Errorf ( "EVENT_LINK_ERR: error adding %s as child to %s: %e" , child . ID ( ) , thread . ID ( ) , err )
}
2023-07-09 15:59:41 -06:00
child . SetParent ( thread )
2023-06-23 20:56:09 -06:00
if err != nil {
return err
}
return nil
}
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
}
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 {
Parent * NodeID ` json:"parent" `
Children map [ NodeID ] interface { } ` json:"children" `
Timeout time . Time ` json:"timeout" `
TimeoutAction string ` json:"timeout_action" `
StateName string ` json:"state_name" `
SimpleLockableJSON
}
func NewSimpleThreadJSON ( thread * SimpleThread ) SimpleThreadJSON {
children := map [ NodeID ] interface { } { }
for _ , child := range ( thread . children ) {
children [ child . ID ( ) ] = thread . child_info [ child . ID ( ) ]
}
var parent_id * NodeID = nil
if thread . parent != nil {
new_str := thread . parent . ID ( )
parent_id = & new_str
}
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
}
// SimpleThread as no associated info with children
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 {
thread . SetTimeout ( j . Timeout , j . TimeoutAction )
if j . Parent != nil {
p , err := LoadNodeRecurse ( ctx , * j . Parent , nodes )
if err != nil {
return err
}
p_t , ok := p . ( Thread )
if ok == false {
return err
}
thread . SetParent ( p_t )
}
// TODO: Call different loading functions(to return different ThreadInfo types, based on the j.Type,
// Will probably have to add another set of callbacks to the context for this, and since there's now 3 sets that need to be matching it could be useful to move them to a struct so it's easier to keep in sync
i := 0
for id , info_raw := range ( j . Children ) {
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 )
}
info_ser , err := json . Marshal ( info_raw )
if err != nil {
return err
}
parsed_info , err := thread . DeserializeInfo ( ctx , info_ser )
if err != nil {
return err
}
thread . AddChild ( child_t , parsed_info )
i ++
}
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 ) ,
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-03 18:08:32 -06:00
// Requires that thread is already locked for read in UseStates
2023-07-09 15:59:41 -06:00
func FindChild ( ctx * Context , thread Thread , id NodeID , nodes NodeMap ) 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-09 15:59:41 -06:00
UseMoreStates ( ctx , [ ] Node { child } , nodes , func ( nodes NodeMap ) error {
result = FindChild ( ctx , child , id , nodes )
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-06 16:57:51 -06:00
// Main Loop for Threads
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-09 15:59:41 -06:00
err = UpdateStates ( ctx , [ ] Node { thread } , func ( nodes NodeMap ) error {
err := thread . SetState ( "finished" )
2023-07-06 16:57:51 -06:00
if err != nil {
return err
}
2023-07-03 17:13:00 -06:00
return UnlockLockables ( ctx , [ ] Lockable { thread } , thread , nodes )
2023-06-28 23:51:44 -06:00
} )
2023-07-06 16:57:51 -06:00
2023-06-26 23:15:40 -06:00
if err != nil {
2023-07-06 16:57:51 -06:00
ctx . Log . Logf ( "thread" , "THREAD_LOOP_UNLOCK_ERR: %e" , err )
2023-06-26 23:15:40 -06:00
return err
}
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-06-23 21:21:14 -06:00
}
2023-07-09 15:59:41 -06:00
var ThreadStart = func ( ctx * Context , thread Thread ) error {
err := UpdateStates ( ctx , [ ] Node { thread } , func ( nodes NodeMap ) error {
2023-07-06 16:57:51 -06:00
owner_id := NodeID ( "" )
2023-07-09 15:59:41 -06:00
if thread . Owner ( ) != nil {
owner_id = thread . Owner ( ) . ID ( )
2023-07-06 16:57:51 -06:00
}
if owner_id != thread . ID ( ) {
err := LockLockables ( ctx , [ ] Lockable { thread } , thread , nodes )
if err != nil {
return err
}
}
2023-07-09 15:59:41 -06:00
return thread . SetState ( "started" )
2023-07-06 16:57:51 -06:00
} )
if err != nil {
return err
}
return nil
}
2023-07-09 15:59:41 -06:00
var ThreadDefaultStart = func ( 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-09 15:59:41 -06:00
var ThreadDefaultRestore = func ( 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-09 15:59:41 -06:00
var ThreadWait = func ( 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 ( ) :
if signal . Source ( ) == thread . ID ( ) {
ctx . Log . Logf ( "thread" , "THREAD_SIGNAL_INTERNAL" )
2023-06-25 21:00:00 -06:00
} else {
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-09 15:59:41 -06:00
err := UpdateStates ( ctx , [ ] Node { thread } , func ( nodes NodeMap ) error {
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-06 16:57:51 -06:00
type ThreadAbortedError NodeID
func ( e ThreadAbortedError ) Is ( target error ) bool {
error_type := reflect . TypeOf ( ThreadAbortedError ( "" ) )
target_type := reflect . TypeOf ( target )
return error_type == target_type
}
func ( e ThreadAbortedError ) Error ( ) string {
return fmt . Sprintf ( "Aborted by %s" , string ( e ) )
}
func NewThreadAbortedError ( aborter NodeID ) ThreadAbortedError {
return ThreadAbortedError ( aborter )
}
// Default thread abort is to return a ThreadAbortedError
2023-07-09 15:59:41 -06:00
func ThreadAbort ( ctx * Context , thread Thread , signal GraphSignal ) ( string , error ) {
UseStates ( ctx , [ ] Node { thread } , func ( nodes NodeMap ) error {
thread . Signal ( ctx , NewSignal ( thread , "thread_aborted" ) , nodes )
2023-07-06 16:57:51 -06:00
return nil
} )
return "" , NewThreadAbortedError ( signal . Source ( ) )
2023-06-23 21:21:14 -06:00
}
2023-07-06 16:57:51 -06:00
// Default thread cancel is to finish the thread
2023-07-09 15:59:41 -06:00
func ThreadCancel ( ctx * Context , thread Thread , signal GraphSignal ) ( string , error ) {
UseStates ( ctx , [ ] Node { thread } , func ( nodes NodeMap ) error {
thread . Signal ( ctx , NewSignal ( thread , "thread_cancelled" ) , nodes )
2023-07-06 16:57:51 -06:00
return nil
} )
2023-06-23 21:21:14 -06:00
return "" , nil
2023-06-23 20:56:09 -06:00
}
2023-07-02 12:14:04 -06:00
func NewThreadActions ( ) ThreadActions {
actions := ThreadActions { }
for k , v := range ( BaseThreadActions ) {
actions [ k ] = v
}
return actions
}
func NewThreadHandlers ( ) ThreadHandlers {
handlers := ThreadHandlers { }
for k , v := range ( BaseThreadHandlers ) {
handlers [ k ] = v
}
return handlers
}
var BaseThreadActions = ThreadActions {
"wait" : ThreadWait ,
"start" : ThreadDefaultStart ,
2023-07-02 12:47:45 -06:00
"restore" : ThreadDefaultRestore ,
2023-07-02 12:14:04 -06:00
}
var BaseThreadHandlers = ThreadHandlers {
"abort" : ThreadAbort ,
"cancel" : ThreadCancel ,
}