Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better shutdown #1

Merged
merged 6 commits into from
Oct 4, 2011
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
30 changes: 26 additions & 4 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,10 @@ about to daemonize). 'pid' will be the id of the running process, and
A function to be called if the start action cannot be performed. Error will be
some sort of stringifiable error object. Defaults to init.startFailed.

### init.stop(pidfile, cb)
### init.stop(pidfile, cb, killer)

Sends your service TERM, INT, QUIT, in that order (with 2 second delays) and
then KILL until the process is no longer running, then calls cb (defaults to
init.stopped). If the process was running, cb's first argument will be true.
Stops your service with one of shutdown functions. Default is
`init.hardKiller(2000)`, but you may pass your own.

### init.status(pidfile, cb)

Expand All @@ -80,11 +79,34 @@ following keyword arguments:
#### logfile
As in init.start()

### killer
As in init.stop()

#### command
A string on which to dispatch. Defaults to your program's first argument
(process.argv[2]). Recognized actions are "start", "stop", "restart",
"try-restart", "force-reload", and "status".

#### killer
As in init.stop()

Shutdown functions
-----------------

### init.hardKiller(delay = 2000)

Sends your service TERM, INT, QUIT, in that order (with 2000 ms delays) and
then KILL until the process is no longer running, then calls cb (defaults to
init.stopped). If the process was running, cb's first argument will be true.
This is default shutdown function.

### init.softKiller(delay = 2000)

Sends your service TERM and wait until it die with 2000 ms delays. This is
preferred choice for graceful shutdown. Your service may decide when it want
to stop with no data loss.


Default Actions
---------------
These functions are the defaults for various callbacks, but you can call them
Expand Down
51 changes: 36 additions & 15 deletions init.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -68,32 +68,53 @@ exports.stopped = (killed) ->
console.log 'Not running.'
process.exit 0

exports.stop = (pidfile, cb = exports.stopped) ->
exports.hardKiller = (timeout = 2000) ->
(pid, cb) ->
signals = ['TERM', 'INT', 'QUIT', 'KILL']
tryKill = ->
sig = "SIG#{ signals[0] }"
try
# throws when the process no longer exists
process.kill pid, sig
signals.shift() if signals.length > 1
setTimeout (-> tryKill sig), timeout
catch e
cb(signals.length < 4)
tryKill()

exports.softKiller = (timeout = 2000) ->
(pid, cb) ->
sig = "SIGTERM"
tryKill = ->
try
# throws when the process no longer exists
process.kill pid, sig
console.log "Waiting for pid " + pid
sig = 0 if sig != 0
first = false
setTimeout tryKill, timeout
catch e
cb(sig == 0)
tryKill()

exports.stop = (pidfile, cb = exports.stopped, killer = exports.hardKiller(2000)) ->
exports.status pidfile, ({pid}) ->
if pid
signals = ['TERM', 'INT', 'QUIT', 'KILL']
tryKill = ->
sig = "SIG#{ signals[0] }"
try
# throws when the process no longer exists
process.kill pid, sig
signals.shift() if signals.length > 1
setTimeout (-> tryKill sig), 2000
catch e
fs.unlink pidfile, -> cb(signals.length < 4)
tryKill()
killer pid, (killed) ->
fs.unlink pidfile, -> cb(killed)
else
cb false

exports.simple = ({pidfile, logfile, command, run}) ->
exports.simple = ({pidfile, logfile, command, run, killer}) ->
command or= process.argv[2]
killer or= null
start = -> exports.start { pidfile, logfile, run }
switch command
when 'start' then start()
when 'stop' then exports.stop pidfile
when 'stop' then exports.stop pidfile, null, killer
when 'status' then exports.status pidfile
when 'restart', 'force-reload'
exports.stop pidfile, start
exports.stop pidfile, start, killer
when 'try-restart'
exports.stop pidfile, (killed) ->
if killed
Expand Down