@ -68,6 +68,7 @@ type ThreadState interface {
RemoveChild ( child Thread )
RemoveChild ( child Thread )
Start ( ) error
Start ( ) error
Stop ( ) error
Stop ( ) error
State ( ) string
TimeoutAction ( ) string
TimeoutAction ( ) string
SetTimeout ( end_time time . Time , action string )
SetTimeout ( end_time time . Time , action string )
@ -75,11 +76,11 @@ type ThreadState interface {
type BaseThreadState struct {
type BaseThreadState struct {
BaseLockableState
BaseLockableState
state_name string
parent Thread
parent Thread
children [ ] Thread
children [ ] Thread
child_info map [ NodeID ] ThreadInfo
child_info map [ NodeID ] ThreadInfo
InfoType reflect . Type
InfoType reflect . Type
running bool
timeout time . Time
timeout time . Time
timeout_action string
timeout_action string
}
}
@ -89,6 +90,7 @@ type BaseThreadStateJSON struct {
Children map [ NodeID ] interface { } ` json:"children" `
Children map [ NodeID ] interface { } ` json:"children" `
Timeout time . Time ` json:"timeout" `
Timeout time . Time ` json:"timeout" `
TimeoutAction string ` json:"timeout_action" `
TimeoutAction string ` json:"timeout_action" `
StateName string ` json:"state_name" `
BaseLockableStateJSON
BaseLockableStateJSON
}
}
@ -111,6 +113,7 @@ func SaveBaseThreadState(state * BaseThreadState) BaseThreadStateJSON {
Children : children ,
Children : children ,
Timeout : state . timeout ,
Timeout : state . timeout ,
TimeoutAction : state . timeout_action ,
TimeoutAction : state . timeout_action ,
StateName : state . state_name ,
BaseLockableStateJSON : lockable_state ,
BaseLockableStateJSON : lockable_state ,
}
}
@ -124,6 +127,8 @@ func RestoreBaseThread(ctx * GraphContext, id NodeID, actions ThreadActions, han
Actions : actions ,
Actions : actions ,
Handlers : handlers ,
Handlers : handlers ,
child_waits : & sync . WaitGroup { } ,
child_waits : & sync . WaitGroup { } ,
active : false ,
active_lock : & sync . Mutex { } ,
}
}
return thread
return thread
@ -146,7 +151,7 @@ func RestoreBaseThreadState(ctx * GraphContext, j BaseThreadStateJSON, loaded_no
children : make ( [ ] Thread , len ( j . Children ) ) ,
children : make ( [ ] Thread , len ( j . Children ) ) ,
child_info : map [ NodeID ] ThreadInfo { } ,
child_info : map [ NodeID ] ThreadInfo { } ,
InfoType : nil ,
InfoType : nil ,
running: fals e,
state_name: j . StateNam e,
timeout : j . Timeout ,
timeout : j . Timeout ,
timeout_action : j . TimeoutAction ,
timeout_action : j . TimeoutAction ,
}
}
@ -228,19 +233,38 @@ func (state * BaseThreadState) MarshalJSON() ([]byte, error) {
return json . Marshal ( & thread_state )
return json . Marshal ( & thread_state )
}
}
func ( state * BaseThreadState ) State ( ) string {
return state . state_name
}
func ( state * BaseThreadState ) SetState ( new_state string ) error {
if new_state == "init" {
return fmt . Errorf ( "Cannot set a thread to 'init' with SetState" )
} else if new_state == "finished" {
return fmt . Errorf ( "Cannot set a thread to 'finished' with SetState" )
} else if new_state == "started" {
return fmt . Errorf ( "Cannot set a thread to 'started' with SetState" )
}
state . state_name = new_state
return nil
}
func ( state * BaseThreadState ) Start ( ) error {
func ( state * BaseThreadState ) Start ( ) error {
if state . running == true {
if state . state_name != "init" {
return fmt . Errorf ( "Cannot start a running thread" )
return fmt . Errorf ( "Cannot start a thread that's already starte d")
}
}
state . running = true
state . state_name = "started"
return nil
return nil
}
}
func ( state * BaseThreadState ) Stop ( ) error {
func ( state * BaseThreadState ) Stop ( ) error {
if state . running == false {
if state . state_name == "finished" {
return fmt . Errorf ( "Cannot stop a thread that's not running" )
return fmt . Errorf ( "Cannot stop a finished thread" )
} else if state . state_name == "init" {
return fmt . Errorf ( "Cannot stop a thread that hasn't been started" )
}
}
state . running = false
state . state_name = "finished"
return nil
return nil
}
}
@ -404,6 +428,8 @@ type Thread interface {
ClearTimeout ( )
ClearTimeout ( )
ChildWaits ( ) * sync . WaitGroup
ChildWaits ( ) * sync . WaitGroup
Start ( ) error
Stop ( ) error
}
}
// Requires that thread is already locked for read in UseStates
// Requires that thread is already locked for read in UseStates
@ -430,16 +456,12 @@ func FindChild(ctx * GraphContext, thread Thread, id NodeID, states NodeStateMap
return nil
return nil
}
}
func ChildGo ( ctx * GraphContext , thread_state ThreadState , thread Thread , child_id NodeID , first_action string ) {
func ChildGo ( ctx * GraphContext , thread Thread , child Thread , first_action string ) {
child := thread_state . Child ( child_id )
if child == nil {
panic ( fmt . Errorf ( "Child not in thread, can't start %s" , child_id ) )
}
thread . ChildWaits ( ) . Add ( 1 )
thread . ChildWaits ( ) . Add ( 1 )
go func ( child Thread ) {
go func ( child Thread ) {
ctx . Log . Logf ( "thread" , "THREAD_START_CHILD: %s from %s" , thread . ID ( ) , child . ID ( ) )
ctx . Log . Logf ( "thread" , "THREAD_START_CHILD: %s from %s" , thread . ID ( ) , child . ID ( ) )
defer thread . ChildWaits ( ) . Done ( )
defer thread . ChildWaits ( ) . Done ( )
err := Run Thread( ctx , child , first_action )
err := ThreadLoop ( ctx , child , first_action )
if err != nil {
if err != nil {
ctx . Log . Logf ( "thread" , "THREAD_CHILD_RUN_ERR: %s %e" , child . ID ( ) , err )
ctx . Log . Logf ( "thread" , "THREAD_CHILD_RUN_ERR: %s %e" , child . ID ( ) , err )
} else {
} else {
@ -448,39 +470,15 @@ func ChildGo(ctx * GraphContext, thread_state ThreadState, thread Thread, child_
} ( child )
} ( child )
}
}
func RunThread ( ctx * GraphContext , thread Thread , first_action string ) error {
// Main Loop for Threads
ctx . Log . Logf ( "thread" , "THREAD_RUN: %s" , thread . ID ( ) )
func ThreadLoop ( ctx * GraphContext , thread Thread , first_action string ) error {
// Start the thread, error if double-started
err := UpdateStates ( ctx , [ ] GraphNode { thread } , func ( nodes NodeMap ) ( error ) {
ctx . Log . Logf ( "thread" , "THREAD_LOOP_START: %s - %s" , thread . ID ( ) , first_action )
thread_state := thread . State ( ) . ( ThreadState )
err := thread . Start ( )
owner_id := NodeID ( "" )
if thread_state . Owner ( ) != nil {
owner_id = thread_state . Owner ( ) . ID ( )
}
if owner_id != thread . ID ( ) {
return LockLockables ( ctx , [ ] Lockable { thread } , thread , nodes )
}
return nil
} )
if err != nil {
return err
}
err = UseStates ( ctx , [ ] GraphNode { thread } , func ( states NodeStateMap ) ( error ) {
thread_state := states [ thread . ID ( ) ] . ( ThreadState )
if thread_state . Owner ( ) == nil {
return fmt . Errorf ( "THREAD_RUN_NOT_LOCKED: %s" , thread_state . Name ( ) )
} else if thread_state . Owner ( ) . ID ( ) != thread . ID ( ) {
return fmt . Errorf ( "THREAD_RUN_RESOURCE_ALREADY_LOCKED: %s, %s" , thread_state . Name ( ) , thread_state . Owner ( ) . ID ( ) )
} else if err := thread_state . Start ( ) ; err != nil {
return fmt . Errorf ( "THREAD_START_ERR: %e" , err )
}
return nil
} )
if err != nil {
if err != nil {
ctx . Log . Logf ( "thread" , "THREAD_LOOP_START_ERR: %e" , err )
return err
return err
}
}
next_action := first_action
next_action := first_action
for next_action != "" {
for next_action != "" {
action , exists := thread . Action ( next_action )
action , exists := thread . Action ( next_action )
@ -496,31 +494,27 @@ func RunThread(ctx * GraphContext, thread Thread, first_action string) error {
}
}
}
}
err = UseStates ( ctx , [ ] GraphNode { thread } , func ( states NodeStateMap ) ( error ) {
err = thread . Stop ( )
thread_state := states [ thread . ID ( ) ] . ( ThreadState )
err := thread_state . Stop ( )
return err
} )
if err != nil {
if err != nil {
ctx . Log . Logf ( "thread" , "THREAD_ RUN _STOP_ERR: %e", err )
ctx . Log . Logf ( "thread" , "THREAD_LOOP_STOP_ERR: %e" , err )
return err
return err
}
}
err = UpdateStates ( ctx , [ ] GraphNode { thread } , func ( nodes NodeMap ) ( error ) {
err = UpdateStates ( ctx , [ ] GraphNode { thread } , func ( nodes NodeMap ) ( error ) {
thread_state := thread . State ( ) . ( ThreadState )
err := thread_state . Stop ( )
if err != nil {
return err
}
return UnlockLockables ( ctx , [ ] Lockable { thread } , thread , nodes )
return UnlockLockables ( ctx , [ ] Lockable { thread } , thread , nodes )
} )
} )
if err != nil {
if err != nil {
ctx . Log . Logf ( "thread" , "THREAD_RUN_UNLOCK_ERR: %e" , err )
ctx . Log . Logf ( "thread" , "THREAD_ LOOP _UNLOCK_ERR: %e", err )
return err
return err
}
}
err = UseStates ( ctx , [ ] GraphNode { thread } , func ( states NodeStateMap ) error {
ctx . Log . Logf ( "thread" , "THREAD_LOOP_DONE: %s" , thread . ID ( ) )
SendUpdate ( ctx , thread , NewSignal ( thread , "thread_done" ) , states )
return nil
} )
ctx . Log . Logf ( "thread" , "THREAD_RUN_DONE: %s" , thread . ID ( ) )
return nil
return nil
}
}
@ -542,12 +536,34 @@ type BaseThread struct {
timeout_chan <- chan time . Time
timeout_chan <- chan time . Time
child_waits * sync . WaitGroup
child_waits * sync . WaitGroup
active bool
active_lock * sync . Mutex
}
}
func ( thread * BaseThread ) ChildWaits ( ) * sync . WaitGroup {
func ( thread * BaseThread ) ChildWaits ( ) * sync . WaitGroup {
return thread . child_waits
return thread . child_waits
}
}
func ( thread * BaseThread ) Start ( ) error {
thread . active_lock . Lock ( )
defer thread . active_lock . Unlock ( )
if thread . active == true {
return fmt . Errorf ( "%s is active, cannot start" , thread . ID ( ) )
}
thread . active = true
return nil
}
func ( thread * BaseThread ) Stop ( ) error {
thread . active_lock . Lock ( )
defer thread . active_lock . Unlock ( )
if thread . active == false {
return fmt . Errorf ( "%s is not active, cannot stop" , thread . ID ( ) )
}
thread . active = false
return nil
}
func ( thread * BaseThread ) CanLock ( node GraphNode , state LockableState ) error {
func ( thread * BaseThread ) CanLock ( node GraphNode , state LockableState ) error {
return nil
return nil
}
}
@ -586,8 +602,35 @@ func (thread * BaseThread) SetTimeout(end time.Time) {
thread . timeout_chan = time . After ( time . Until ( end ) )
thread . timeout_chan = time . After ( time . Until ( end ) )
}
}
var ThreadStart = func ( ctx * GraphContext , thread Thread ) error {
err := UpdateStates ( ctx , [ ] GraphNode { thread } , func ( nodes NodeMap ) ( error ) {
thread_state := thread . State ( ) . ( ThreadState )
owner_id := NodeID ( "" )
if thread_state . Owner ( ) != nil {
owner_id = thread_state . Owner ( ) . ID ( )
}
if owner_id != thread . ID ( ) {
err := LockLockables ( ctx , [ ] Lockable { thread } , thread , nodes )
if err != nil {
return err
}
}
return thread_state . Start ( )
} )
if err != nil {
return err
}
return nil
}
var ThreadDefaultStart = func ( ctx * GraphContext , thread Thread ) ( string , error ) {
var ThreadDefaultStart = func ( ctx * GraphContext , thread Thread ) ( string , error ) {
ctx . Log . Logf ( "thread" , "THREAD_DEFAUL_START: %s" , thread . ID ( ) )
ctx . Log . Logf ( "thread" , "THREAD_DEFAULT_START: %s" , thread . ID ( ) )
err := ThreadStart ( ctx , thread )
if err != nil {
return "" , err
}
return "wait" , nil
return "wait" , nil
}
}
@ -630,11 +673,35 @@ var ThreadWait = func(ctx * GraphContext, thread Thread) (string, error) {
}
}
}
}
var ThreadAbort = func ( ctx * GraphContext , thread Thread , signal GraphSignal ) ( string , error ) {
type ThreadAbortedError NodeID
return "" , fmt . Errorf ( "%s aborted by signal from %s" , thread . ID ( ) , signal . Source ( ) )
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
func ThreadAbort ( ctx * GraphContext , thread Thread , signal GraphSignal ) ( string , error ) {
UseStates ( ctx , [ ] GraphNode { thread } , func ( states NodeStateMap ) error {
SendUpdate ( ctx , thread , NewSignal ( thread , "thread_aborted" ) , states )
return nil
} )
return "" , NewThreadAbortedError ( signal . Source ( ) )
}
}
var ThreadCancel = func ( ctx * GraphContext , thread Thread , signal GraphSignal ) ( string , error ) {
// Default thread cancel is to finish the thread
func ThreadCancel ( ctx * GraphContext , thread Thread , signal GraphSignal ) ( string , error ) {
UseStates ( ctx , [ ] GraphNode { thread } , func ( states NodeStateMap ) error {
SendUpdate ( ctx , thread , NewSignal ( thread , "thread_cancelled" ) , states )
return nil
} )
return "" , nil
return "" , nil
}
}
@ -646,6 +713,7 @@ func NewBaseThreadState(name string, _type string) BaseThreadState {
parent : nil ,
parent : nil ,
timeout : time . Time { } ,
timeout : time . Time { } ,
timeout_action : "wait" ,
timeout_action : "wait" ,
state_name : "init" ,
}
}
}
}
@ -689,6 +757,8 @@ func NewBaseThread(ctx * GraphContext, actions ThreadActions, handlers ThreadHan
Actions : actions ,
Actions : actions ,
Handlers : handlers ,
Handlers : handlers ,
child_waits : & sync . WaitGroup { } ,
child_waits : & sync . WaitGroup { } ,
active : false ,
active_lock : & sync . Mutex { } ,
}
}
return thread , nil
return thread , nil