Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.5 KiB
121 lines
3.5 KiB
// Copyright 2013, Google Inc. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package sync2 |
|
|
|
import ( |
|
"sync" |
|
) |
|
|
|
// These are the three predefined states of a service. |
|
const ( |
|
SERVICE_STOPPED = iota |
|
SERVICE_RUNNING |
|
SERVICE_SHUTTING_DOWN |
|
) |
|
|
|
var stateNames = []string{ |
|
"Stopped", |
|
"Running", |
|
"ShuttingDown", |
|
} |
|
|
|
// ServiceManager manages the state of a service through its lifecycle. |
|
type ServiceManager struct { |
|
mu sync.Mutex |
|
wg sync.WaitGroup |
|
err error // err is the error returned from the service function. |
|
state AtomicInt64 |
|
// shutdown is created when the service starts and is closed when the service |
|
// enters the SERVICE_SHUTTING_DOWN state. |
|
shutdown chan struct{} |
|
} |
|
|
|
// Go tries to change the state from SERVICE_STOPPED to SERVICE_RUNNING. |
|
// |
|
// If the current state is not SERVICE_STOPPED (already running), it returns |
|
// false immediately. |
|
// |
|
// On successful transition, it launches the service as a goroutine and returns |
|
// true. The service function is responsible for returning on its own when |
|
// requested, either by regularly checking svc.IsRunning(), or by waiting for |
|
// the svc.ShuttingDown channel to be closed. |
|
// |
|
// When the service func returns, the state is reverted to SERVICE_STOPPED. |
|
func (svm *ServiceManager) Go(service func(svc *ServiceContext) error) bool { |
|
svm.mu.Lock() |
|
defer svm.mu.Unlock() |
|
if !svm.state.CompareAndSwap(SERVICE_STOPPED, SERVICE_RUNNING) { |
|
return false |
|
} |
|
svm.wg.Add(1) |
|
svm.err = nil |
|
svm.shutdown = make(chan struct{}) |
|
go func() { |
|
svm.err = service(&ServiceContext{ShuttingDown: svm.shutdown}) |
|
svm.state.Set(SERVICE_STOPPED) |
|
svm.wg.Done() |
|
}() |
|
return true |
|
} |
|
|
|
// Stop tries to change the state from SERVICE_RUNNING to SERVICE_SHUTTING_DOWN. |
|
// If the current state is not SERVICE_RUNNING, it returns false immediately. |
|
// On successul transition, it waits for the service to finish, and returns true. |
|
// You are allowed to Go() again after a Stop(). |
|
func (svm *ServiceManager) Stop() bool { |
|
svm.mu.Lock() |
|
defer svm.mu.Unlock() |
|
if !svm.state.CompareAndSwap(SERVICE_RUNNING, SERVICE_SHUTTING_DOWN) { |
|
return false |
|
} |
|
// Signal the service that we've transitioned to SERVICE_SHUTTING_DOWN. |
|
close(svm.shutdown) |
|
svm.shutdown = nil |
|
svm.wg.Wait() |
|
return true |
|
} |
|
|
|
// Wait waits for the service to terminate if it's currently running. |
|
func (svm *ServiceManager) Wait() { |
|
svm.wg.Wait() |
|
} |
|
|
|
// Join waits for the service to terminate and returns the value returned by the |
|
// service function. |
|
func (svm *ServiceManager) Join() error { |
|
svm.wg.Wait() |
|
return svm.err |
|
} |
|
|
|
// State returns the current state of the service. |
|
// This should only be used to report the current state. |
|
func (svm *ServiceManager) State() int64 { |
|
return svm.state.Get() |
|
} |
|
|
|
// StateName returns the name of the current state. |
|
func (svm *ServiceManager) StateName() string { |
|
return stateNames[svm.State()] |
|
} |
|
|
|
// ServiceContext is passed into the service function to give it access to |
|
// information about the running service. |
|
type ServiceContext struct { |
|
// ShuttingDown is a channel that the service can select on to be notified |
|
// when it should shut down. The channel is closed when the state transitions |
|
// from SERVICE_RUNNING to SERVICE_SHUTTING_DOWN. |
|
ShuttingDown chan struct{} |
|
} |
|
|
|
// IsRunning returns true if the ServiceContext.ShuttingDown channel has not |
|
// been closed yet. |
|
func (svc *ServiceContext) IsRunning() bool { |
|
select { |
|
case <-svc.ShuttingDown: |
|
return false |
|
default: |
|
return true |
|
} |
|
}
|
|
|