forked from yuxh/gearman-go
		
	add synchronization to prevent race condition on worker shutdown (#89)
* add synchronization to prevent race condition on worker shutdown * fix incorrect synchronization and add race unit test * fix race while broadcasting outpack
This commit is contained in:
		
							parent
							
								
									b902646ce8
								
							
						
					
					
						commit
						81d00aa9ce
					
				@ -111,6 +111,9 @@ func (a *agent) work() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *agent) disconnect_error(err error) {
 | 
					func (a *agent) disconnect_error(err error) {
 | 
				
			||||||
 | 
						a.Lock()
 | 
				
			||||||
 | 
						defer a.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if a.conn != nil {
 | 
						if a.conn != nil {
 | 
				
			||||||
		err = &WorkerDisconnectError{
 | 
							err = &WorkerDisconnectError{
 | 
				
			||||||
			err:   err,
 | 
								err:   err,
 | 
				
			||||||
 | 
				
			|||||||
@ -4,9 +4,9 @@ package worker
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@ -72,7 +72,7 @@ func (worker *Worker) AddServer(net, addr string) (err error) {
 | 
				
			|||||||
// Broadcast an outpack to all Gearman server.
 | 
					// Broadcast an outpack to all Gearman server.
 | 
				
			||||||
func (worker *Worker) broadcast(outpack *outPack) {
 | 
					func (worker *Worker) broadcast(outpack *outPack) {
 | 
				
			||||||
	for _, v := range worker.agents {
 | 
						for _, v := range worker.agents {
 | 
				
			||||||
		v.write(outpack)
 | 
							v.Write(outpack)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -186,7 +186,7 @@ func (worker *Worker) Ready() (err error) {
 | 
				
			|||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Main loop, block here
 | 
					// Work start main loop (blocking)
 | 
				
			||||||
// Most of time, this should be evaluated in goroutine.
 | 
					// Most of time, this should be evaluated in goroutine.
 | 
				
			||||||
func (worker *Worker) Work() {
 | 
					func (worker *Worker) Work() {
 | 
				
			||||||
	if !worker.ready {
 | 
						if !worker.ready {
 | 
				
			||||||
@ -197,7 +197,10 @@ func (worker *Worker) Work() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worker.Lock()
 | 
				
			||||||
	worker.running = true
 | 
						worker.running = true
 | 
				
			||||||
 | 
						worker.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, a := range worker.agents {
 | 
						for _, a := range worker.agents {
 | 
				
			||||||
		a.Grab()
 | 
							a.Grab()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										59
									
								
								worker/worker_racy_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								worker/worker_racy_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					package worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestWorkerRace(t *testing.T) {
 | 
				
			||||||
 | 
						// from example worker
 | 
				
			||||||
 | 
						// An example of worker
 | 
				
			||||||
 | 
						w := New(Unlimited)
 | 
				
			||||||
 | 
						defer w.Close()
 | 
				
			||||||
 | 
						// Add a gearman job server
 | 
				
			||||||
 | 
						if err := w.AddServer(Network, "127.0.0.1:4730"); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// A function for handling jobs
 | 
				
			||||||
 | 
						foobar := func(job Job) ([]byte, error) {
 | 
				
			||||||
 | 
							// Do nothing here
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Add the function to worker
 | 
				
			||||||
 | 
						if err := w.AddFunc("foobar", foobar, 0); err != nil {
 | 
				
			||||||
 | 
							fmt.Println(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
 | 
						// A custome handler, for handling other results, eg. ECHO, dtError.
 | 
				
			||||||
 | 
						w.JobHandler = func(job Job) error {
 | 
				
			||||||
 | 
							if job.Err() == nil {
 | 
				
			||||||
 | 
								fmt.Println(string(job.Data()))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								fmt.Println(job.Err())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							wg.Done()
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// An error handler for handling worker's internal errors.
 | 
				
			||||||
 | 
						w.ErrorHandler = func(e error) {
 | 
				
			||||||
 | 
							fmt.Println(e)
 | 
				
			||||||
 | 
							// Ignore the error or shutdown the worker
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Tell Gearman job server: I'm ready!
 | 
				
			||||||
 | 
						if err := w.Ready(); err != nil {
 | 
				
			||||||
 | 
							fmt.Println(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Running main loop
 | 
				
			||||||
 | 
						go w.Work()
 | 
				
			||||||
 | 
						wg.Add(1)
 | 
				
			||||||
 | 
						// calling Echo
 | 
				
			||||||
 | 
						w.Echo([]byte("Hello"))
 | 
				
			||||||
 | 
						// Waiting results
 | 
				
			||||||
 | 
						wg.Wait()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// tear down
 | 
				
			||||||
 | 
						w.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user