Browse Source

refactor: use sync.Cond to signal state availablity

sync.Cond is used to signal state availablity between goroutines.
Mustafa Arici 8 years ago
parent
commit
4cf9ba6fce
1 changed files with 29 additions and 13 deletions
  1. 29 13
      supervisor/supervisor.go

+ 29 - 13
supervisor/supervisor.go

@@ -69,16 +69,17 @@ var tt = []transition{
 
 // Process represents a unix process to be supervised.
 type Process struct {
-	lock       *sync.RWMutex
-	state      State
-	maxRetry   uint
-	cmd        *exec.Cmd
-	executable string
-	wdir       string
-	args       []string
-	done       chan error
-	stop       chan bool
-	out        *bytes.Buffer
+	lock            *sync.RWMutex
+	state           State
+	maxRetry        uint
+	cmd             *exec.Cmd
+	executable      string
+	wdir            string
+	args            []string
+	done            chan error
+	stop            chan bool
+	out             *bytes.Buffer
+	stateChangeCond *sync.Cond
 	// stdin     io.WriteCloser
 	// stdoutLog Logger
 	// stderrLog Logger
@@ -96,6 +97,7 @@ func NewProcess(executable string, dir string, args []string) (*Process, error)
 	p.wdir = dir
 	p.args = args
 	p.lock = &sync.RWMutex{}
+	p.stateChangeCond = sync.NewCond(&sync.RWMutex{})
 	p.state = STOPPED
 	p.done = make(chan error)
 	p.stop = make(chan bool)
@@ -112,6 +114,15 @@ func isExist(executable string) bool {
 	return false
 }
 
+// waitFor blocks until the FSM transitions to the given state.
+func (p *Process) waitFor(state State) {
+	for p.state != state {
+		p.stateChangeCond.L.Lock()
+		p.stateChangeCond.Wait()
+		p.stateChangeCond.L.Unlock()
+	}
+}
+
 // Start will run the process.
 func (p *Process) Start() {
 	p.transitionTo(STARTING)
@@ -127,6 +138,7 @@ func (p *Process) Restart() {
 	if p.state == RUNNING {
 		p.Stop()
 	}
+	p.waitFor(STOPPED)
 	p.Start()
 }
 
@@ -141,6 +153,8 @@ func (p *Process) IsRunning() bool {
 }
 
 func (p *Process) permittable(state State) bool {
+	p.lock.Lock()
+	defer p.lock.Unlock()
 	for _, t := range tt {
 		if p.state == t.currState && t.nextState == state {
 			return true
@@ -157,10 +171,13 @@ func (p *Process) setState(state State) {
 
 func (p *Process) transitionTo(state State) {
 	if p.permittable(state) {
+		p.stateChangeCond.L.Lock()
 		logrus.Infof("transition: '%s' -> '%s'", p.state, state)
 		logrus.Debug(p.out.String())
 		p.setState(state)
-		go p.process(state)()
+		go p.run(state)()
+		p.stateChangeCond.L.Unlock()
+		p.stateChangeCond.Broadcast()
 		return
 	}
 	logrus.Errorf("transition to '%s' from '%s' is not permitted!", p.state, state)
@@ -187,11 +204,10 @@ func (p *Process) newCommand() *exec.Cmd {
 	if err != nil {
 		panic(fmt.Sprintf("can not convert string to int %s: %v", currUsr.Gid, err))
 	}
-	fmt.Printf("%++v", cmd)
 	cmd.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}}
 	return cmd
 }
-func (p *Process) process(state State) func() {
+func (p *Process) run(state State) func() {
 	switch state {
 	case STOPPED:
 		return func() {