Fix potential race conditions for starting/stopping server add-ons
Reviewed By: mweststrate Differential Revision: D34301593 fbshipit-source-id: 2950de8a8567318cd3e87eff176657df5ba8fd1b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
bdbf79e3e1
commit
81d0057a8d
147
desktop/flipper-server-core/src/plugins/ServerAddManager.tsx
Normal file
147
desktop/flipper-server-core/src/plugins/ServerAddManager.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
ClientResponseType,
|
||||
ExecuteMessage,
|
||||
FlipperServerForServerAddOn,
|
||||
ServerAddOnStartDetails,
|
||||
} from 'flipper-common';
|
||||
import {assertNotNull} from '../comms/Utilities';
|
||||
import {StateMachine} from '../utils/StateMachine';
|
||||
import {ServerAddOn} from './ServerAddOn';
|
||||
|
||||
type TState =
|
||||
| 'inactive'
|
||||
| 'starting'
|
||||
| 'active'
|
||||
| 'fatal'
|
||||
| 'stopping'
|
||||
| 'zombie';
|
||||
|
||||
export class ServerAddOnManager {
|
||||
public readonly state = new StateMachine<TState, 'fatal'>('inactive');
|
||||
private _serverAddOn?: ServerAddOn;
|
||||
constructor(
|
||||
public readonly pluginName: string,
|
||||
details: ServerAddOnStartDetails,
|
||||
initialOwner: string,
|
||||
flipperServer: FlipperServerForServerAddOn,
|
||||
) {
|
||||
this.startServerAddOn(details, initialOwner, flipperServer);
|
||||
}
|
||||
|
||||
sendExpectResponse(message: ExecuteMessage): Promise<ClientResponseType> {
|
||||
if (!this.state.is('active')) {
|
||||
console.info(
|
||||
'StateAddOnManager.sendExpectResponse -> error: server add-on is not active, Current state:',
|
||||
this.state.currentState,
|
||||
);
|
||||
throw new Error(
|
||||
'StateAddOnManager.sendExpectResponse -> error: server add-on is not active',
|
||||
);
|
||||
}
|
||||
assertNotNull(
|
||||
this._serverAddOn,
|
||||
'StateAddOnManager.sendExpectResponse -> _serverAddOn is undefined',
|
||||
);
|
||||
return this._serverAddOn.connection.sendExpectResponse(message);
|
||||
}
|
||||
|
||||
async addOwner(owner: string) {
|
||||
if (this.state.is('starting')) {
|
||||
await this.state.wait(['active', 'fatal']);
|
||||
}
|
||||
|
||||
if (!this.state.is('active')) {
|
||||
console.info(
|
||||
'StateAddOnManager.addOwner -> error: server add-on is not active, Current state:',
|
||||
this.state.currentState,
|
||||
);
|
||||
throw new Error(
|
||||
'StateAddOnManager.addOwner -> error: server add-on is not active',
|
||||
);
|
||||
}
|
||||
assertNotNull(
|
||||
this._serverAddOn,
|
||||
'StateAddOnManager.addOwner -> _serverAddOn is undefined',
|
||||
);
|
||||
|
||||
this._serverAddOn.addOwner(owner);
|
||||
}
|
||||
|
||||
async removeOwner(owner: string) {
|
||||
if (this.state.is(['stopping', 'inactive'])) {
|
||||
return this.state.wait(['zombie', 'inactive']);
|
||||
}
|
||||
|
||||
if (this.state.is('starting')) {
|
||||
await this.state.wait(['active', 'fatal']);
|
||||
}
|
||||
|
||||
if (!this.state.is('active')) {
|
||||
console.debug(
|
||||
'StateAddOnManager.removeOwner -> error: server add-on failed to start, Current state:',
|
||||
this.state.currentState,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
assertNotNull(
|
||||
this._serverAddOn,
|
||||
'StateAddOnManager.addOwner -> _serverAddOn is undefined',
|
||||
);
|
||||
|
||||
const stopping = this._serverAddOn.removeOwner(owner);
|
||||
|
||||
if (stopping) {
|
||||
this.state.set('stopping');
|
||||
try {
|
||||
await stopping;
|
||||
this.state.set('inactive');
|
||||
} catch (e) {
|
||||
this.state.set('zombie');
|
||||
console.error(
|
||||
'ServerAddOnManager.removeOwner -> server add-on failed to clean up',
|
||||
this.pluginName,
|
||||
e,
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async startServerAddOn(
|
||||
details: ServerAddOnStartDetails,
|
||||
initialOwner: string,
|
||||
flipperServer: FlipperServerForServerAddOn,
|
||||
) {
|
||||
try {
|
||||
this.state.set('starting');
|
||||
|
||||
this._serverAddOn = await ServerAddOn.start(
|
||||
this.pluginName,
|
||||
details,
|
||||
initialOwner,
|
||||
flipperServer,
|
||||
);
|
||||
|
||||
this.state.set('active');
|
||||
} catch (e) {
|
||||
this.state.set('fatal', e);
|
||||
console.error(
|
||||
'StateAddOnManager.startServerAddOn -> error',
|
||||
this.pluginName,
|
||||
details,
|
||||
initialOwner,
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user