|
|
|
@ -19,7 +19,7 @@ func (thread * BaseThread) PropagateUpdate(ctx * GraphContext, signal GraphSigna
|
|
|
|
|
SendUpdate(ctx, thread_state.Parent(), signal)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, resource := range(thread_state.Lockables()) {
|
|
|
|
|
for _, resource := range(thread_state.Requirements()) {
|
|
|
|
|
SendUpdate(ctx, resource, signal)
|
|
|
|
|
}
|
|
|
|
|
} else if signal.Direction() == Down {
|
|
|
|
@ -27,6 +27,10 @@ func (thread * BaseThread) PropagateUpdate(ctx * GraphContext, signal GraphSigna
|
|
|
|
|
for _, child := range(thread_state.Children()) {
|
|
|
|
|
SendUpdate(ctx, child, signal)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, dep := range(thread_state.Dependencies()) {
|
|
|
|
|
SendUpdate(ctx, dep, signal)
|
|
|
|
|
}
|
|
|
|
|
} else if signal.Direction() == Direct {
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
@ -71,8 +75,6 @@ type ThreadState interface {
|
|
|
|
|
Children() []Thread
|
|
|
|
|
ChildInfo(child NodeID) ThreadInfo
|
|
|
|
|
AddChild(child Thread, info ThreadInfo) error
|
|
|
|
|
Lockables() []Lockable
|
|
|
|
|
AddLockable(resource Lockable) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BaseThreadState struct {
|
|
|
|
@ -249,8 +251,8 @@ func (state * BaseThreadState) RecordLockHolder(id NodeID, lock_holder GraphNode
|
|
|
|
|
type Thread interface {
|
|
|
|
|
GraphNode
|
|
|
|
|
|
|
|
|
|
Action(action string) (func(* GraphContext)(string, error), bool)
|
|
|
|
|
Handler(signal_type string) (func(* GraphContext, GraphSignal) (string, error), bool)
|
|
|
|
|
Action(action string) (ThreadAction, bool)
|
|
|
|
|
Handler(signal_type string) (ThreadHandler, bool)
|
|
|
|
|
|
|
|
|
|
SetTimeout(end_time time.Time, action string)
|
|
|
|
|
ClearTimeout()
|
|
|
|
@ -258,29 +260,6 @@ type Thread interface {
|
|
|
|
|
TimeoutAction() string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) TimeoutAction() string {
|
|
|
|
|
return thread.timeout_action
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) Timeout() <-chan time.Time {
|
|
|
|
|
return thread.timeout
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) ClearTimeout() {
|
|
|
|
|
thread.timeout_action = ""
|
|
|
|
|
thread.timeout = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) SetTimeout(end_time time.Time, action string) {
|
|
|
|
|
thread.timeout_action = action
|
|
|
|
|
thread.timeout = time.After(time.Until(end_time))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) Handler(signal_type string) (func(* GraphContext, GraphSignal)(string, error), bool) {
|
|
|
|
|
handler, exists := thread.Handlers[signal_type]
|
|
|
|
|
return handler, exists
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func FindChild(ctx * GraphContext, thread Thread, thread_state ThreadState, id NodeID) Thread {
|
|
|
|
|
if thread == nil {
|
|
|
|
|
panic("cannot recurse through nil")
|
|
|
|
@ -329,7 +308,7 @@ func RunThread(ctx * GraphContext, thread Thread) error {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.Log.Logf("thread", "EVENT_ACTION: %s - %s", thread.ID(), next_action)
|
|
|
|
|
next_action, err = action(ctx)
|
|
|
|
|
next_action, err = action(ctx, thread)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
@ -342,17 +321,10 @@ func RunThread(ctx * GraphContext, thread Thread) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ThreadAbort(thread Thread) func(*GraphContext, GraphSignal) (string, error) {
|
|
|
|
|
return func(ctx * GraphContext, signal GraphSignal) (string, error) {
|
|
|
|
|
return "", errors.New(fmt.Sprintf("%s aborted by signal", thread.ID()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ThreadCancel(thread Thread) func(*GraphContext, GraphSignal) (string, error) {
|
|
|
|
|
return func(ctx * GraphContext, signal GraphSignal) (string, error) {
|
|
|
|
|
return "", nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
type ThreadAction func(* GraphContext, Thread)(string, error)
|
|
|
|
|
type ThreadActions map[string]ThreadAction
|
|
|
|
|
type ThreadHandler func(* GraphContext, Thread, GraphSignal)(string, error)
|
|
|
|
|
type ThreadHandlers map[string]ThreadHandler
|
|
|
|
|
|
|
|
|
|
// Thread is the most basic thread that can exist in the thread tree.
|
|
|
|
|
// On start it automatically transitions to completion.
|
|
|
|
@ -366,8 +338,8 @@ type BaseThread struct {
|
|
|
|
|
info_lock sync.Mutex
|
|
|
|
|
parent_lock sync.Mutex
|
|
|
|
|
|
|
|
|
|
Actions map[string]func(* GraphContext) (string, error)
|
|
|
|
|
Handlers map[string]func(* GraphContext, GraphSignal) (string, error)
|
|
|
|
|
Actions ThreadActions
|
|
|
|
|
Handlers ThreadHandlers
|
|
|
|
|
|
|
|
|
|
timeout <-chan time.Time
|
|
|
|
|
timeout_action string
|
|
|
|
@ -381,40 +353,62 @@ func (thread * BaseThread) Unlock(node GraphNode, state LockableState) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) Action(action string) (func(ctx * GraphContext) (string, error), bool) {
|
|
|
|
|
func (thread * BaseThread) Action(action string) (ThreadAction, bool) {
|
|
|
|
|
action_fn, exists := thread.Actions[action]
|
|
|
|
|
return action_fn, exists
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ThreadWait(thread Thread) (func(*GraphContext) (string, error)) {
|
|
|
|
|
return func(ctx * GraphContext) (string, error) {
|
|
|
|
|
ctx.Log.Logf("thread", "EVENT_WAIT: %s TIMEOUT: %+v", thread.ID(), thread.Timeout())
|
|
|
|
|
func (thread * BaseThread) Handler(signal_type string) (ThreadHandler, bool) {
|
|
|
|
|
handler, exists := thread.Handlers[signal_type]
|
|
|
|
|
return handler, exists
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) TimeoutAction() string {
|
|
|
|
|
return thread.timeout_action
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) Timeout() <-chan time.Time {
|
|
|
|
|
return thread.timeout
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) ClearTimeout() {
|
|
|
|
|
thread.timeout_action = ""
|
|
|
|
|
thread.timeout = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (thread * BaseThread) SetTimeout(end_time time.Time, action string) {
|
|
|
|
|
thread.timeout_action = action
|
|
|
|
|
thread.timeout = time.After(time.Until(end_time))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ThreadDefaultStart = func(ctx * GraphContext, thread Thread) (string, error) {
|
|
|
|
|
ctx.Log.Logf("thread", "THREAD_DEFAUL_START: %s", thread.ID())
|
|
|
|
|
return "wait", nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ThreadWait = func(ctx * GraphContext, thread Thread) (string, error) {
|
|
|
|
|
ctx.Log.Logf("thread", "THREAD_WAIT: %s TIMEOUT: %+v", thread.ID(), thread.Timeout())
|
|
|
|
|
select {
|
|
|
|
|
case signal := <- thread.SignalChannel():
|
|
|
|
|
ctx.Log.Logf("thread", "EVENT_SIGNAL: %s %+v", thread.ID(), signal)
|
|
|
|
|
ctx.Log.Logf("thread", "THREAD_SIGNAL: %s %+v", thread.ID(), signal)
|
|
|
|
|
signal_fn, exists := thread.Handler(signal.Type())
|
|
|
|
|
if exists == true {
|
|
|
|
|
ctx.Log.Logf("thread", "EVENT_HANDLER: %s - %s", thread.ID(), signal.Type())
|
|
|
|
|
return signal_fn(ctx, signal)
|
|
|
|
|
ctx.Log.Logf("thread", "THREAD_HANDLER: %s - %s", thread.ID(), signal.Type())
|
|
|
|
|
return signal_fn(ctx, thread, signal)
|
|
|
|
|
}
|
|
|
|
|
return "wait", nil
|
|
|
|
|
case <- thread.Timeout():
|
|
|
|
|
ctx.Log.Logf("thread", "EVENT_TIMEOUT %s - NEXT_STATE: %s", thread.ID(), thread.TimeoutAction())
|
|
|
|
|
ctx.Log.Logf("thread", "THREAD_TIMEOUT %s - NEXT_STATE: %s", thread.ID(), thread.TimeoutAction())
|
|
|
|
|
return thread.TimeoutAction(), nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "wait", nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewBaseThread(ctx * GraphContext, name string) (BaseThread, error) {
|
|
|
|
|
state := NewBaseThreadState(name)
|
|
|
|
|
thread := BaseThread{
|
|
|
|
|
BaseNode: NewNode(ctx, RandID(), &state),
|
|
|
|
|
Actions: map[string]func(*GraphContext)(string, error){},
|
|
|
|
|
Handlers: map[string]func(*GraphContext,GraphSignal)(string, error){},
|
|
|
|
|
timeout: nil,
|
|
|
|
|
timeout_action: "",
|
|
|
|
|
var ThreadAbort = func(ctx * GraphContext, thread Thread, signal GraphSignal) (string, error) {
|
|
|
|
|
return "", fmt.Errorf("%s aborted by signal from %s", thread.ID(), signal.Source())
|
|
|
|
|
}
|
|
|
|
|
return thread, nil
|
|
|
|
|
|
|
|
|
|
var ThreadCancel = func(ctx * GraphContext, thread Thread, signal GraphSignal) (string, error) {
|
|
|
|
|
return "", nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewBaseThreadState(name string) BaseThreadState {
|
|
|
|
@ -428,7 +422,26 @@ func NewBaseThreadState(name string) BaseThreadState {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewThread(ctx * GraphContext, name string, requirements []Lockable) (* BaseThread, error) {
|
|
|
|
|
func NewBaseThread(ctx * GraphContext, name string) (BaseThread, error) {
|
|
|
|
|
state := NewBaseThreadState(name)
|
|
|
|
|
thread := BaseThread{
|
|
|
|
|
BaseNode: NewNode(ctx, RandID(), &state),
|
|
|
|
|
Actions: ThreadActions{
|
|
|
|
|
"wait": ThreadWait,
|
|
|
|
|
"start": ThreadDefaultStart,
|
|
|
|
|
},
|
|
|
|
|
Handlers: ThreadHandlers{
|
|
|
|
|
"abort": ThreadAbort,
|
|
|
|
|
"cancel": ThreadCancel,
|
|
|
|
|
},
|
|
|
|
|
timeout: nil,
|
|
|
|
|
timeout_action: "",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return thread, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewThread(ctx * GraphContext, name string, requirements []Lockable, actions ThreadActions, handlers ThreadHandlers) (* BaseThread, error) {
|
|
|
|
|
thread, err := NewBaseThread(ctx, name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
@ -443,12 +456,12 @@ func NewThread(ctx * GraphContext, name string, requirements []Lockable) (* Base
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_ptr.Actions["wait"] = ThreadWait(thread_ptr)
|
|
|
|
|
thread_ptr.Handlers["abort"] = ThreadAbort(thread_ptr)
|
|
|
|
|
thread_ptr.Handlers["cancel"] = ThreadCancel(thread_ptr)
|
|
|
|
|
for key, fn := range(actions) {
|
|
|
|
|
thread.Actions[key] = fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_ptr.Actions["start"] = func(ctx * GraphContext) (string, error) {
|
|
|
|
|
return "", nil
|
|
|
|
|
for key, fn := range(handlers) {
|
|
|
|
|
thread.Handlers[key] = fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return thread_ptr, nil
|
|
|
|
|