lifecycle

package module
v0.0.0-...-7b4c402 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2013 License: Apache-2.0 Imports: 2 Imported by: 1

README

lifecycle

Lifecycle is a go package that helps you manage goroutines that provide services to other goroutines.

Things you can do:

  • Have one goroutine wait for another goroutine to initalize itself and be ready to work. The server goroutine calls LifeCycle.Transition(STATE_RUNNING) and the client calls LifeCycle.WaitForState(STATE_RUNNING)
  • Wait for a goroutine service to finish shutting down. This is handy for testing graceful shutdown, and it's friendlier than using WaitGroups manually.

Separately, a ShutdownRequest is a good way to quickly shut down the main loop of a server without an unbounded of iterations that can occur with the "quit channel" approach:

for !shutdownRequest.IsShutdownRequested() {
    select {
        case toAdd := <-onesChan:
            atomic.AddInt32(&sum, int32(toAdd))
        case <-shutdownRequest.GetShutdownRequestChan():
            break
        }
}

You might be wondering what is wrong with the "quit channel" approach:

// This is the wrong way to do it, for educational purposes only:
keepLooping := true
for keepLooping {
    select {
        workItem := <-workChan:
            doWork(workItem)
        <-quitChan:
            keepLooping = false
    }
}

In the "quit channel" approach, if the workChan channel is readable, the for loop might execute a few more times (actually unbounded) before the quitChan channel is read. In Go, if a select statement has multiple channels that are ready, one of them will be pseudorandomly picked. This implies that you need an exit flag outside of the select statement, which is what ShutdownRequest provides.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type LifeCycle

type LifeCycle struct {
	// contains filtered or unexported fields
}

A state machine for services which have lifecycles where they initialize, run, and stop. Provides the ability to wait for lifecycle events, such as "service has finished initializing" or "service has finished shutting down."

func NewLifeCycle

func NewLifeCycle() *LifeCycle

func (*LifeCycle) GetState

func (this *LifeCycle) GetState() State

Get the current state in the life cycle (for example, "still initializing").

func (*LifeCycle) Transition

func (this *LifeCycle) Transition(newState State)

Change to the given state in this life cycle. For example, when the service is done initializing you should probably change it's state to STATE_RUNNING. This includes unblocking any goroutines that might be waiting for the state transition.

Life cycle states must happen in order. You can skip states, but never go backward. For example, a service that is RUNNING may never go back to NEW, and a service that is STOPPED may never go back to RUNNING.

func (*LifeCycle) WaitForState

func (this *LifeCycle) WaitForState(state State) State

Wait until the service enters the given state. If the service enters another state that makes it impossible that the requested state will ever be entered, this function will also return in that case. For example, if you're waiting for STATE_RUNNING but there's an error during initialization and the service jumps to STATE_STOPPED, then this function will return STATE_STOPPED instead of blocking forever.

type ShutdownRequest

type ShutdownRequest struct {
	// contains filtered or unexported fields
}

ShutdownRequest solves the problem of quickly and cleanly shutting down a goroutine. When a goroutine is reading incoming work items from a channel and executing them, using the quit channel approach may not immediately shut down the worker:

	keepLooping := true
	for keepLooping {
		select {
    		workItem := <-workChan:
		    	doWork(workItem)
	    	<-quitChan:
		    	keepLooping = false
		}
	}

In the above example, the worker may continue processing workChan for many iterations until it happens to read from quitChan. Using ShutdownRequest you can guarantee termination within one iteration:

for !shutdownRequest.IsShutdownRequested() {
	select {
	case toAdd := <-onesChan:
		atomic.AddInt32(&sum, int32(toAdd))
	case <-shutdownRequest.GetShutdownRequestChan():
		break
	}
}

func NewShutdownRequest

func NewShutdownRequest() *ShutdownRequest

func (*ShutdownRequest) GetShutdownRequestChan

func (this *ShutdownRequest) GetShutdownRequestChan() chan interface{}

Get the channel that will be closed when shutdown is requested. Use the returned channel in the server's main loop select statement. Use the returned channel in a select statement, all inside a for loop whose predicate checks IsShutdownRequested() (see above).

func (*ShutdownRequest) IsShutdownRequested

func (this *ShutdownRequest) IsShutdownRequested() bool

Call this in the server main loop to check whether it should shut down. Use this together with the shutdown request channel.

func (*ShutdownRequest) RequestShutdown

func (this *ShutdownRequest) RequestShutdown()

Call this function to asynchronously indicate that the owning service should shut down.

type State

type State int
const (

	// The service is freshly created and has not yet finished initializing.
	STATE_NEW State = 0
	// The service has finished initializing and is operating normally.
	STATE_RUNNING State = 5
	// The service is shutting down and may not be accepting any new requests.
	STATE_STOPPING State = 10
	// The service has finished shutting down and is completely unavailable.
	STATE_STOPPED State = 15
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL