Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,45 @@

Package application offers simple application lifecycle framework.

### Building with version
## Application's lifecycle

This package provides an Application interface to run applications in a lifecycle. The interface has four methods:
Start, Run, Terminate, Stop.

- **Start** method always runs the beginning of the lifecycle even the termination process was triggered.
- **Run** method runs immediately after Start method.
The termination process may be triggered before entering into Run method.
While in Run method, Terminate method may be running at the same time.
- **Terminate** method runs immediately after the termination process is triggered.
Terminate method always runs after Start method.
While in Terminate method, Run method may be running at the same time.
- **Stop** method runs immediately after Run and Terminate methods were returned.
The lifecycle ends with Stop method was returned.

## Application Context

Start and Run methods have **ctx** argument that is *Context* from *CancelableContext* of *xcontext*.
These contextes are the exactly same and **ctx** can be named as ***application context***.
All application contextes are the exactly same at all of Applications in a single lifecycle.
The calling *Cancel* method of the application context cancels the context, and starts the termination process.

## Termination process

Termination process can be triggered with the canceling of application contexts or termination signals.
When termination process started, Terminate method always will be called with a *Context* has the given terminate timeout.
If termination process started before Run method, it waits the ending of Start method to call Terminate method.
Otherwise it calls Terminate method immediately.

## Timeouts

Once the termination process has been started, there is no way back to initial state.
It waits methods of Application interface or timeouts.
Terminate method's context will be cancelled after the given terminate timeout or, when Run and Terminate methods were ended.
The waiting of the quit timeout is started after Terminate method's context were cancelled.

## Building with version

You can save your application's name, version and build into this package below this.

go build -ldflags "\
-X=github.com/goinsane/application.name=$NAME \
Expand Down
38 changes: 19 additions & 19 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,56 +13,55 @@ import (

// Application is an interface for handling application lifecycle.
type Application interface {
Start(ctx Context)
Run(ctx Context)
Start(ctx xcontext.CancelableContext)
Run(ctx xcontext.CancelableContext)
Terminate(ctx context.Context)
Stop()
}

// Context is a custom implementation of context.Context with Terminate() method to terminate application.
type Context = xcontext.TerminateContext

// Run runs an Application by the application lifecycle with timeouts and terminate signals.
// Run runs an instance of Application by the application lifecycle with timeouts and terminate signals.
// It returns false if the quit timeout occurs.
func Run(app Application, terminateTimeout, quitTimeout time.Duration, terminateSignals ...os.Signal) bool {
return RunAll([]Application{app}, terminateTimeout, quitTimeout, terminateSignals...)
}

// RunAll runs all Application's in common Context by the application lifecycle with timeouts and terminate signals.
// RunAll runs all instances of Application in common Context by the application lifecycle with timeouts and terminate signals.
// It returns false if the quit timeout occurs.
func RunAll(apps []Application, terminateTimeout, quitTimeout time.Duration, terminateSignals ...os.Signal) bool {
ctx := xcontext.WithTerminate2(context.Background())
defer ctx.Terminate()
terminateCtx, terminateCtxCancel := xcontext.DelayAfterContext(ctx, terminateTimeout)
defer terminateCtxCancel()
appCtx := xcontext.WithCancelable2(context.Background())
defer appCtx.Cancel()

termCtx := xcontext.WithCancelable2(xcontext.DelayAfterContext2(appCtx, terminateTimeout))
defer termCtx.Cancel()

go func() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, terminateSignals...)
<-ch
ctx.Terminate()
appCtx.Cancel()
}()

quittedCh := make(chan struct{})
go func() {
lifecycle(ctx, apps, terminateCtx)
lifecycle(appCtx, termCtx, apps)
close(quittedCh)
}()
select {
case <-quittedCh:
return true
case <-xcontext.DelayAfterContext2(terminateCtx, quitTimeout).Done():
case <-xcontext.DelayAfterContext2(termCtx, quitTimeout).Done():
return false
}
}

func lifecycle(ctx Context, apps []Application, terminateCtx context.Context) {
func lifecycle(appCtx, termCtx xcontext.CancelableContext, apps []Application) {
var wg sync.WaitGroup

for _, app := range apps {
wg.Add(1)
go func(app Application) {
defer wg.Done()
app.Start(ctx)
app.Start(appCtx)
}(app)
}
wg.Wait()
Expand All @@ -71,22 +70,23 @@ func lifecycle(ctx Context, apps []Application, terminateCtx context.Context) {
wg.Add(1)
go func(app Application) {
defer wg.Done()
app.Run(ctx)
app.Run(appCtx)
}(app)
}
wg.Add(1)
go func() {
defer wg.Done()
<-ctx.Done()
<-appCtx.Done()
for _, app := range apps {
wg.Add(1)
go func(app Application) {
defer wg.Done()
app.Terminate(terminateCtx)
app.Terminate(termCtx)
}(app)
}
}()
wg.Wait()
termCtx.Cancel()

for _, app := range apps {
wg.Add(1)
Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@ module github.com/goinsane/application

go 1.9

require (
github.com/goinsane/xcontext v1.6.1
)
require github.com/goinsane/xcontext v1.7.0
22 changes: 2 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,20 +1,2 @@
github.com/goinsane/xcontext v1.4.1 h1:3h5Wl//AerANDdsOFfoOtXd1+UKH8xnZ0SYFnqzturw=
github.com/goinsane/xcontext v1.4.1/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.5.0 h1:DdVfHipJuhIIfhaQMGVRQ3rs5XOlg5+VKFixEXbuTyk=
github.com/goinsane/xcontext v1.5.0/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.5.1 h1:fQwJoWAP61bqsh1JAP42d6HMRFBYCdFw57TXlUToB8c=
github.com/goinsane/xcontext v1.5.1/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.5.2 h1:ocUAm9xdNmbb2fnx8sn1XplruwrtEPdsJtLrEW56CI8=
github.com/goinsane/xcontext v1.5.2/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.5.3 h1:tcQ66M7f2U9mLdIS02nvpJOj6gKylAGvp/V5IXl1LKc=
github.com/goinsane/xcontext v1.5.3/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.5.4 h1:sOBI7GgaXxeJjhKAZMJ++YZyf04YYdU+pjMt+UGCiX0=
github.com/goinsane/xcontext v1.5.4/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.5.5 h1:S1d94ThH1luqOEQzEaYaYLHcOpXpCVu/dly52NC01ew=
github.com/goinsane/xcontext v1.5.5/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.5.6 h1:Iyqede4/XFqP+zAbELvHO3Zjo6rogRg2X77pL1Ft2bQ=
github.com/goinsane/xcontext v1.5.6/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.6.0 h1:hp2XA9WLOUZRmWRBeUANAsVtPld+xbJp7SQYfTnpA84=
github.com/goinsane/xcontext v1.6.0/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.6.1 h1:WBuffQmlulCA8mzs4oXS8XDH3gYuUMWfbdcHmlEFlS4=
github.com/goinsane/xcontext v1.6.1/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=
github.com/goinsane/xcontext v1.7.0 h1:Jz6Gh2WCOu/u84d2iCrPUMfOwFAKLfCDs4mLGTunN+8=
github.com/goinsane/xcontext v1.7.0/go.mod h1:y+fK+cXBf9T6MBP7XFXQbYrsyhzlhRVVIIRVTxc6QjY=