@softvisio/core
Version:
Softisio core
339 lines (260 loc) • 7.57 kB
JavaScript
import "#lib/result";
import Counter from "#lib/threads/counter";
import Mutex from "#lib/threads/mutex";
export default class ActivityController {
#isInitialized = false;
#isStarted = false;
#isDestroyed = false;
#initMutex = new Mutex();
#startMutex = new Mutex();
#stopMutex = new Mutex();
#destroyMutex = new Mutex();
#activeRequestsCounter = new Counter();
#startAbortController;
#stopAbortController;
#abortController = new AbortController();
#doInit;
#doStart;
#doBeforeStop;
#doStop;
#doDestroy;
constructor ( { doInit, doStart, doBeforeStop, doStop, doDestroy } = {} ) {
this.doInit = doInit;
this.doStart = doStart;
this.doBeforeStop = doBeforeStop;
this.doStop = doStop;
this.doDestroy = doDestroy;
}
// properties
get isInitializing () {
return this.#initMutex.isLocked;
}
get isInitialized () {
return this.#isInitialized;
}
get isStarted () {
return this.#isStarted;
}
get isStarting () {
return this.#startMutex.isLocked;
}
get isStopping () {
return this.#stopMutex.isLocked;
}
get isDestroying () {
return this.#destroyMutex.isLocked;
}
get isDestroyed () {
return this.#isDestroyed;
}
get activeRequestsCount () {
return this.#activeRequestsCounter.value;
}
get abortSignal () {
return this.#abortController.signal;
}
get doInit () {
return this.#doInit;
}
set doInit ( callback ) {
this.#doInit = callback;
}
get doStart () {
return this.#doStart;
}
set doStart ( callback ) {
this.#doStart = callback;
}
get doBeforeStop () {
return this.#doBeforeStop;
}
set doBeforeStop ( callback ) {
this.#doBeforeStop = callback;
}
get doStop () {
return this.#doStop;
}
set doStop ( callback ) {
this.#doStop = callback;
}
get doDestroy () {
return this.#doDestroy;
}
set doDestroy ( callback ) {
this.#doDestroy = callback;
}
// public
async init ( options ) {
// destroyed
if ( this.#isDestroyed ) return result( [ 400, "Destroyed" ] );
// initialized
if ( this.#isInitialized ) return result( 200 );
// destroying
if ( this.isDestroying ) {
if ( this.isInitializing ) {
return this.#initMutex.wait();
}
else {
return result( [ 400, "Destroying" ] );
}
}
// initializing
if ( !this.#initMutex.tryLock() ) return this.#initMutex.wait();
var res;
// init
try {
res = result.try( await this._doInit( options ), {
"allowUndefined": true,
} );
}
catch ( e ) {
res = result.catch( e, {
"log": false,
} );
}
// initialized
if ( res.ok ) {
this.#isInitialized = true;
}
this.#initMutex.unlock( res );
return res;
}
async start ( options ) {
// destroyed
if ( this.#isDestroyed ) return result( [ 400, "Destroyed" ] );
// started
if ( this.#isStarted ) return result( 200 );
// destroying
if ( this.isDestroying ) {
if ( this.isStarting ) {
return this.#startMutex.wait();
}
else {
return result( [ 400, "Destroying" ] );
}
}
// starting
if ( !this.#startMutex.tryLock() ) return this.#startMutex.wait();
// wait for stop
if ( this.isStopping ) {
this.#stopAbortController.abort();
await this.#stopMutex.wait();
}
var res;
// start
try {
this.#startAbortController = new AbortController();
const signal = this.#startAbortController.signal;
res = result.try( await this._doStart( signal, options ), {
"allowUndefined": true,
} );
}
catch ( e ) {
res = result.catch( e, {
"log": false,
} );
}
// started
if ( res.ok ) {
this.#isStarted = true;
}
this.#startMutex.unlock( res );
return res;
}
async stop ( options ) {
// stopped
if ( !this.#isStarted ) return result( 200 );
// stopping
if ( !this.#stopMutex.tryLock() ) return this.#stopMutex.wait();
// wait for start
if ( this.isStarting ) {
this.#startAbortController.abort();
await this.#startMutex.wait();
}
var res;
try {
this.#stopAbortController = new AbortController();
const signal = this.#startAbortController.signal;
// before stop
res = result.try( await this._doBeforeStop( signal, options ), {
"allowUndefined": true,
} );
if ( !res.ok ) throw res;
// wait for active requests finished
await this.#activeRequestsCounter.wait();
// stop
res = result.try( await this._doStop( signal, options ), {
"allowUndefined": true,
} );
if ( !res.ok ) throw res;
// stopped
this.#isStarted = false;
var abortController = this.#abortController;
this.#abortController = new AbortController();
}
catch ( e ) {
res = result.catch( e, {
"log": false,
} );
}
this.#stopMutex.unlock( res );
abortController?.abort();
return res;
}
async destroy ( options ) {
// destroyed
if ( this.#isDestroyed ) return result( 200 );
// destroying
if ( !this.#destroyMutex.tryLock() ) return this.#destroyMutex.wait();
// wait for start
if ( this.isStarting ) {
this.#startAbortController.abort();
await this.#startMutex.wait();
}
// wait for stop
if ( this.isStopping ) {
this.#stopAbortController.abort();
await this.#stopMutex.wait();
}
var res;
// destroy
try {
res = result.try( await this._doDestroy( options ), {
"allowUndefined": true,
} );
}
catch ( e ) {
res = result.catch( e, {
"log": false,
} );
}
// destroyed
if ( res.ok ) {
this.#isDestroyed = true;
}
this.#destroyMutex.unlock( res );
return res;
}
beginActivity () {
this.#activeRequestsCounter.value++;
}
endActivity () {
this.#activeRequestsCounter.value--;
}
// protected
async _doInit ( options ) {
return this.#doInit?.( options );
}
async _doStart ( signal, options ) {
return this.#doStart?.( signal, options );
}
async _doBeforeStop ( signal, options ) {
return this.#doBeforeStop?.( signal, options );
}
async _doStop ( signal, options ) {
return this.#doStop?.( signal, options );
}
async _doDestroy ( options ) {
return this.#doDestroy?.( options );
}
}