diff --git a/lib/units/device/plugins/screen/stream.js b/lib/units/device/plugins/screen/stream.js index 6452c654..f5993b2b 100644 --- a/lib/units/device/plugins/screen/stream.js +++ b/lib/units/device/plugins/screen/stream.js @@ -37,6 +37,7 @@ module.exports = syrup.serial() this.frameConfig = config this.readable = false this.needsReadable = false + this.starter = Promise.resolve(true) } util.inherits(FrameProducer, EventEmitter) @@ -47,6 +48,10 @@ module.exports = syrup.serial() FrameProducer.STATE_STOPPING = 4 FrameProducer.prototype._ensureState = function() { + if (this.desiredState.empty()) { + return + } + switch (this.runningState) { case FrameProducer.STATE_STARTING: case FrameProducer.STATE_STOPPING: @@ -55,7 +60,7 @@ module.exports = syrup.serial() case FrameProducer.STATE_STOPPED: if (this.desiredState.next() === FrameProducer.STATE_STARTED) { this.runningState = FrameProducer.STATE_STARTING - this._startService().bind(this) + this.starter = this._startService().bind(this) .then(function(out) { this.output = new RiskyStream(out) .on('unexpectedEnd', this._outputEnded.bind(this)) @@ -78,42 +83,31 @@ module.exports = syrup.serial() this.runningState = FrameProducer.STATE_STARTED this.emit('start') }) + .catch(Promise.CancellationError, function() { + return this._stop() + }) .catch(function(err) { - this.runningState = FrameProducer.STATE_STOPPED - this.emit('error', err) + return this._stop().finally(function() { + this.emit('error', err) + }) }) .finally(function() { this._ensureState() }) } + else { + setImmediate(this._ensureState.bind(this)) + } break case FrameProducer.STATE_STARTED: if (this.desiredState.next() === FrameProducer.STATE_STOPPED) { this.runningState = FrameProducer.STATE_STOPPING - this._disconnectService(this.socket).bind(this) - .timeout(2000) - .then(function() { - return this._stopService(this.output).timeout(10000) - }) - .then(function() { - this.runningState = FrameProducer.STATE_STOPPED - this.emit('stop') - }) - .catch(function(err) { - // In practice we _should_ never get here due to _stopService() - // being quite aggressive. But if we do, well... assume it - // stopped anyway for now. - this.runningState = FrameProducer.STATE_STOPPED - this.emit('error', err) - this.emit('stop') - }) - .finally(function() { - this.output = null - this.socket = null - this.banner = null - this.parser = null - this._ensureState() - }) + this._stop().finally(function() { + this._ensureState() + }) + } + else { + setImmediate(this._ensureState.bind(this)) } break } @@ -135,6 +129,7 @@ module.exports = syrup.serial() switch (this.runningState) { case FrameProducer.STATE_STARTED: case FrameProducer.STATE_STARTING: + this.starter.cancel() this.desiredState.push(FrameProducer.STATE_STOPPED) this.desiredState.push(FrameProducer.STATE_STARTED) this._ensureState() @@ -248,6 +243,32 @@ module.exports = syrup.serial() return tryConnect(5, 100) } + FrameProducer.prototype._stop = function() { + return this._disconnectService(this.socket).bind(this) + .timeout(2000) + .then(function() { + return this._stopService(this.output).timeout(10000) + }) + .then(function() { + this.runningState = FrameProducer.STATE_STOPPED + this.emit('stop') + }) + .catch(function(err) { + // In practice we _should_ never get here due to _stopService() + // being quite aggressive. But if we do, well... assume it + // stopped anyway for now. + this.runningState = FrameProducer.STATE_STOPPED + this.emit('error', err) + this.emit('stop') + }) + .finally(function() { + this.output = null + this.socket = null + this.banner = null + this.parser = null + }) + } + FrameProducer.prototype._disconnectService = function(socket) { log.info('Disconnecting from minicap service') diff --git a/lib/units/device/plugins/screen/util/statequeue.js b/lib/units/device/plugins/screen/util/statequeue.js index da3a8337..d8b8d187 100644 --- a/lib/units/device/plugins/screen/util/statequeue.js +++ b/lib/units/device/plugins/screen/util/statequeue.js @@ -6,6 +6,10 @@ StateQueue.prototype.next = function() { return this.queue.shift() } +StateQueue.prototype.empty = function() { + return this.queue.length === 0 +} + StateQueue.prototype.push = function(state) { var found = false