cglfsm.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. Tideland Common Go Library - Finite State Machine
  3. Copyright (C) 2010-2011 Frank Mueller / Oldenburg / Germany
  4. Redistribution and use in source and binary forms, with or
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. Redistributions of source code must retain the above copyright notice, this
  8. list of conditions and the following disclaimer.
  9. Redistributions in binary form must reproduce the above copyright notice,
  10. this list of conditions and the following disclaimer in the documentation
  11. and/or other materials provided with the distribution.
  12. Neither the name of Tideland nor the names of its contributors may be
  13. used to endorse or promote products derived from this software without
  14. specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  19. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  25. THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. package cgl
  28. //--------------------
  29. // IMPORTS
  30. //--------------------
  31. import (
  32. "log"
  33. "reflect"
  34. "strings"
  35. "time"
  36. )
  37. //--------------------
  38. // HELPER TYPES
  39. //--------------------
  40. // Condition type.
  41. type Condition struct {
  42. Now int64
  43. Payload interface{}
  44. }
  45. // Transition type.
  46. type transition struct {
  47. payload interface{}
  48. resultChan chan interface{}
  49. }
  50. // Timeout type.
  51. type Timeout int64
  52. //--------------------
  53. // FINITE STATE MACHINE
  54. //--------------------
  55. // Handler interface.
  56. type Handler interface {
  57. Init() string
  58. Terminate(string, interface{}) string
  59. }
  60. // State machine type.
  61. type FSM struct {
  62. Handler Handler
  63. handlerValue reflect.Value
  64. handlerFuncs map[string]reflect.Value
  65. state string
  66. transitionChan chan *transition
  67. timeoutChan <-chan int64
  68. }
  69. // Create a new finite state machine.
  70. func NewFSM(h Handler, timeout int64) *FSM {
  71. var bufferSize int
  72. if timeout > 0 {
  73. bufferSize = int(timeout / 1e3)
  74. } else {
  75. bufferSize = 10
  76. }
  77. fsm := &FSM{
  78. Handler: h,
  79. handlerFuncs: make(map[string]reflect.Value),
  80. state: h.Init(),
  81. transitionChan: make(chan *transition, bufferSize),
  82. }
  83. if timeout > 0 {
  84. fsm.timeoutChan = time.After(timeout)
  85. }
  86. fsm.analyze()
  87. go fsm.backend()
  88. return fsm
  89. }
  90. // Send a payload to handle and return the result.
  91. func (fsm *FSM) SendWithResult(payload interface{}) interface{} {
  92. t := &transition{payload, make(chan interface{})}
  93. fsm.transitionChan <- t
  94. return <-t.resultChan
  95. }
  96. // Send a payload with no result.
  97. func (fsm *FSM) Send(payload interface{}) {
  98. t := &transition{payload, nil}
  99. fsm.transitionChan <- t
  100. }
  101. // Send a payload with no result after a given time.
  102. func (fsm *FSM) SendAfter(payload interface{}, ns int64) {
  103. saf := func() {
  104. time.Sleep(ns)
  105. fsm.Send(payload)
  106. }
  107. go saf()
  108. }
  109. // Return the current state.
  110. func (fsm *FSM) State() string {
  111. return fsm.state
  112. }
  113. // Return the supervisor.
  114. func (fsm *FSM) Supervisor() *Supervisor {
  115. return GlobalSupervisor()
  116. }
  117. // Recover after an error.
  118. func (fsm *FSM) Recover(recoverable Recoverable, err interface{}) {
  119. log.Printf("[cgl] recovering finite state machine server backend after error '%v'!", err)
  120. go fsm.backend()
  121. }
  122. // Analyze the event handler and prepare the state table.
  123. func (fsm *FSM) analyze() {
  124. prefix := "HandleState"
  125. fsm.handlerValue = reflect.ValueOf(fsm.Handler)
  126. num := fsm.handlerValue.Type().NumMethod()
  127. for i := 0; i < num; i++ {
  128. meth := fsm.handlerValue.Type().Method(i)
  129. if (meth.PkgPath == "") && (strings.HasPrefix(meth.Name, prefix)) {
  130. if (meth.Type.NumIn() == 2) && (meth.Type.NumOut() == 2) {
  131. state := meth.Name[len(prefix):len(meth.Name)]
  132. fsm.handlerFuncs[state] = meth.Func
  133. }
  134. }
  135. }
  136. }
  137. // State machine backend.
  138. func (fsm *FSM) backend() {
  139. defer func() {
  140. HelpIfNeeded(fsm, recover())
  141. }()
  142. // Message loop.
  143. for {
  144. select {
  145. case t := <-fsm.transitionChan:
  146. // Regular transition.
  147. if nextState, ok := fsm.handle(t); ok {
  148. // Continue.
  149. fsm.state = nextState
  150. } else {
  151. // Stop processing.
  152. fsm.state = fsm.Handler.Terminate(fsm.state, nextState)
  153. return
  154. }
  155. case to := <-fsm.timeoutChan:
  156. // Timeout signal resent to let it be handled.
  157. t := &transition{Timeout(to), nil}
  158. fsm.transitionChan <- t
  159. }
  160. }
  161. }
  162. // Handle a transition.
  163. func (fsm *FSM) handle(t *transition) (string, bool) {
  164. condition := &Condition{time.Nanoseconds(), t.payload}
  165. handlerFunc := fsm.handlerFuncs[fsm.state]
  166. handlerArgs := make([]reflect.Value, 2)
  167. handlerArgs[0] = fsm.handlerValue
  168. handlerArgs[1] = reflect.ValueOf(condition)
  169. results := handlerFunc.Call(handlerArgs)
  170. nextState := results[0].Interface().(string)
  171. result := results[1].Interface()
  172. // Return a result if wanted.
  173. if t.resultChan != nil {
  174. t.resultChan <- result
  175. }
  176. // Check for termination.
  177. if nextState == "Terminate" {
  178. return nextState, false
  179. }
  180. return nextState, true
  181. }
  182. /*
  183. EOF
  184. */