server: Simplify gating of state transition waiters
parent
105f8b0ccb
commit
5b327b63ac
|
@ -63,6 +63,14 @@ const State = {
|
||||||
|
|
||||||
let state = State.INITIAL;
|
let state = State.INITIAL;
|
||||||
|
|
||||||
|
class Gate extends Promise {
|
||||||
|
constructor() {
|
||||||
|
let res;
|
||||||
|
super((resolve) => { res = resolve; });
|
||||||
|
this.resolve = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const removeSignalListener = (signal, listener) => {
|
const removeSignalListener = (signal, listener) => {
|
||||||
logger.debug(`Removing ${signal} listener because it might interfere with shutdown tasks. ` +
|
logger.debug(`Removing ${signal} listener because it might interfere with shutdown tasks. ` +
|
||||||
`Function code:\n${listener.toString()}\n` +
|
`Function code:\n${listener.toString()}\n` +
|
||||||
|
@ -70,13 +78,13 @@ const removeSignalListener = (signal, listener) => {
|
||||||
process.off(signal, listener);
|
process.off(signal, listener);
|
||||||
};
|
};
|
||||||
|
|
||||||
const runningCallbacks = [];
|
let startDoneGate;
|
||||||
exports.start = async () => {
|
exports.start = async () => {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case State.INITIAL:
|
case State.INITIAL:
|
||||||
break;
|
break;
|
||||||
case State.STARTING:
|
case State.STARTING:
|
||||||
await new Promise((resolve) => runningCallbacks.push(resolve));
|
await startDoneGate;
|
||||||
// fall through
|
// fall through
|
||||||
case State.RUNNING:
|
case State.RUNNING:
|
||||||
return express.server;
|
return express.server;
|
||||||
|
@ -89,6 +97,7 @@ exports.start = async () => {
|
||||||
throw new Error(`unknown State: ${state.toString()}`);
|
throw new Error(`unknown State: ${state.toString()}`);
|
||||||
}
|
}
|
||||||
logger.info('Starting Etherpad...');
|
logger.info('Starting Etherpad...');
|
||||||
|
startDoneGate = new Gate();
|
||||||
state = State.STARTING;
|
state = State.STARTING;
|
||||||
|
|
||||||
// Check if Etherpad version is up-to-date
|
// Check if Etherpad version is up-to-date
|
||||||
|
@ -135,13 +144,13 @@ exports.start = async () => {
|
||||||
|
|
||||||
logger.info('Etherpad is running');
|
logger.info('Etherpad is running');
|
||||||
state = State.RUNNING;
|
state = State.RUNNING;
|
||||||
while (runningCallbacks.length > 0) setImmediate(runningCallbacks.pop());
|
startDoneGate.resolve();
|
||||||
|
|
||||||
// Return the HTTP server to make it easier to write tests.
|
// Return the HTTP server to make it easier to write tests.
|
||||||
return express.server;
|
return express.server;
|
||||||
};
|
};
|
||||||
|
|
||||||
const stoppedCallbacks = [];
|
let stopDoneGate;
|
||||||
exports.stop = async () => {
|
exports.stop = async () => {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case State.STARTING:
|
case State.STARTING:
|
||||||
|
@ -151,7 +160,7 @@ exports.stop = async () => {
|
||||||
case State.RUNNING:
|
case State.RUNNING:
|
||||||
break;
|
break;
|
||||||
case State.STOPPING:
|
case State.STOPPING:
|
||||||
await new Promise((resolve) => stoppedCallbacks.push(resolve));
|
await stopDoneGate;
|
||||||
// fall through
|
// fall through
|
||||||
case State.INITIAL:
|
case State.INITIAL:
|
||||||
case State.STOPPED:
|
case State.STOPPED:
|
||||||
|
@ -162,6 +171,7 @@ exports.stop = async () => {
|
||||||
throw new Error(`unknown State: ${state.toString()}`);
|
throw new Error(`unknown State: ${state.toString()}`);
|
||||||
}
|
}
|
||||||
logger.info('Stopping Etherpad...');
|
logger.info('Stopping Etherpad...');
|
||||||
|
let stopDoneGate = new Gate();
|
||||||
state = State.STOPPING;
|
state = State.STOPPING;
|
||||||
let timeout = null;
|
let timeout = null;
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
|
@ -173,10 +183,10 @@ exports.stop = async () => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
logger.info('Etherpad stopped');
|
logger.info('Etherpad stopped');
|
||||||
state = State.STOPPED;
|
state = State.STOPPED;
|
||||||
while (stoppedCallbacks.length > 0) setImmediate(stoppedCallbacks.pop());
|
stopDoneGate.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
const exitCallbacks = [];
|
let exitGate;
|
||||||
let exitCalled = false;
|
let exitCalled = false;
|
||||||
exports.exit = async (err = null) => {
|
exports.exit = async (err = null) => {
|
||||||
/* eslint-disable no-process-exit */
|
/* eslint-disable no-process-exit */
|
||||||
|
@ -206,7 +216,7 @@ exports.exit = async (err = null) => {
|
||||||
case State.STOPPED:
|
case State.STOPPED:
|
||||||
break;
|
break;
|
||||||
case State.EXITING:
|
case State.EXITING:
|
||||||
await new Promise((resolve) => exitCallbacks.push(resolve));
|
await exitGate;
|
||||||
// fall through
|
// fall through
|
||||||
case State.WAITING_FOR_EXIT:
|
case State.WAITING_FOR_EXIT:
|
||||||
return;
|
return;
|
||||||
|
@ -214,8 +224,9 @@ exports.exit = async (err = null) => {
|
||||||
throw new Error(`unknown State: ${state.toString()}`);
|
throw new Error(`unknown State: ${state.toString()}`);
|
||||||
}
|
}
|
||||||
logger.info('Exiting...');
|
logger.info('Exiting...');
|
||||||
|
exitGate = new Gate();
|
||||||
state = State.EXITING;
|
state = State.EXITING;
|
||||||
while (exitCallbacks.length > 0) setImmediate(exitCallbacks.pop());
|
exitGate.resolve();
|
||||||
// Node.js should exit on its own without further action. Add a timeout to force Node.js to exit
|
// Node.js should exit on its own without further action. Add a timeout to force Node.js to exit
|
||||||
// just in case something failed to get cleaned up during the shutdown hook. unref() is called on
|
// just in case something failed to get cleaned up during the shutdown hook. unref() is called on
|
||||||
// the timeout so that the timeout itself does not prevent Node.js from exiting.
|
// the timeout so that the timeout itself does not prevent Node.js from exiting.
|
||||||
|
|
Loading…
Reference in New Issue