1 | 'use strict';
|
2 |
|
3 | var hooks = [];
|
4 | var errHooks = [];
|
5 | var called = false;
|
6 | var waitingFor = 0;
|
7 | var asyncTimeoutMs = 10000;
|
8 |
|
9 | var events = [];
|
10 | var filters = [];
|
11 |
|
12 | function exit(exit, code, err) {
|
13 |
|
14 | if (called) {
|
15 | return;
|
16 | }
|
17 |
|
18 | called = true;
|
19 |
|
20 |
|
21 | if (err) {
|
22 |
|
23 | errHooks.map(runHook.bind(null, 1, err));
|
24 | }
|
25 | hooks.map(runHook.bind(null, 0, null));
|
26 |
|
27 | if (!waitingFor) {
|
28 |
|
29 | doExit();
|
30 | } else {
|
31 |
|
32 | setTimeout(function() {
|
33 | doExit();
|
34 | }, asyncTimeoutMs);
|
35 | }
|
36 |
|
37 |
|
38 | function runHook(syncArgCount, err, hook) {
|
39 |
|
40 | if (exit && hook.length > syncArgCount) {
|
41 |
|
42 | waitingFor++;
|
43 |
|
44 | if (err) {
|
45 |
|
46 | return hook(err, stepTowardExit);
|
47 | }
|
48 | return hook(stepTowardExit);
|
49 | }
|
50 |
|
51 |
|
52 | if (err) {
|
53 |
|
54 | return hook(err);
|
55 | }
|
56 | return hook();
|
57 | }
|
58 |
|
59 |
|
60 | function stepTowardExit() {
|
61 | process.nextTick(function() {
|
62 | if (--waitingFor === 0) {
|
63 | doExit();
|
64 | }
|
65 | });
|
66 | }
|
67 |
|
68 | var doExitDone = false;
|
69 | function doExit() {
|
70 | if (doExitDone) {
|
71 | return;
|
72 | }
|
73 | doExitDone = true;
|
74 |
|
75 | if (exit === true) {
|
76 |
|
77 | process.nextTick(process.exit.bind(null, code));
|
78 | }
|
79 | }
|
80 | }
|
81 |
|
82 |
|
83 | function add(hook) {
|
84 | hooks.push(hook);
|
85 |
|
86 | if (hooks.length === 1) {
|
87 | add.hookEvent('exit');
|
88 | add.hookEvent('beforeExit', 0);
|
89 | add.hookEvent('SIGHUP', 128 + 1);
|
90 | add.hookEvent('SIGINT', 128 + 2);
|
91 | add.hookEvent('SIGTERM', 128 + 15);
|
92 | add.hookEvent('SIGBREAK', 128 + 21);
|
93 |
|
94 |
|
95 |
|
96 |
|
97 | add.hookEvent('message', 0, function(msg) {
|
98 | if (msg !== 'shutdown') {
|
99 | return true;
|
100 | }
|
101 | });
|
102 | }
|
103 | }
|
104 |
|
105 |
|
106 | add.hookEvent = function(event, code, filter) {
|
107 | events[event] = function() {
|
108 | for (var i = 0; i < filters.length; i++) {
|
109 | if (filters[i].apply(this, arguments)) {
|
110 | return;
|
111 | }
|
112 | }
|
113 | exit(code != null, code);
|
114 | };
|
115 |
|
116 | if (!filters[event]) {
|
117 | filters[event] = [];
|
118 | }
|
119 |
|
120 | if (filter) {
|
121 | filters[event].push(filter);
|
122 | }
|
123 | process.on(event, events[event]);
|
124 | };
|
125 |
|
126 |
|
127 | add.unhookEvent = function(event) {
|
128 | console.log('unhookevent: ' + event);
|
129 | process.removeListener(event, events[event]);
|
130 | delete events[event];
|
131 | delete filters[event];
|
132 | };
|
133 |
|
134 |
|
135 | add.hookedEvents = function() {
|
136 | var ret = [];
|
137 | for (var name in events) {
|
138 | if (events.hasOwnProperty(name)) {
|
139 | ret.push(name);
|
140 | }
|
141 | }
|
142 | return ret;
|
143 | };
|
144 |
|
145 |
|
146 | add.uncaughtExceptionHandler = function(hook) {
|
147 | errHooks.push(hook);
|
148 |
|
149 | if (errHooks.length === 1) {
|
150 | process.once('uncaughtException', exit.bind(null, true, 1));
|
151 | }
|
152 | };
|
153 |
|
154 |
|
155 | add.forceExitTimeout = function(ms) {
|
156 | asyncTimeoutMs = ms;
|
157 | };
|
158 |
|
159 |
|
160 | module.exports = add;
|