Allow ready decorator to opt in to being callable with allowed status "exceptions"
CMCDragonkai opened this issue · 1 comments
Specification
The ready
decorator right now prevents execution unless the class is ready, that is after start
is finished, and before stop
is called.
The problem here is that there are many methods that need to be callable publically and therefore has ready
applied, but also needs to be called in the start
method.
Of course the reason for this is to prevent inadvertent usage of the public method when the side-effects of start
hasn't completed. However in some situations, the programmer can be responsible for ordering the side-effects in the start
method.
So the ready
method should have an additional parameter to allow it to run in the other statuses other than _running
.
Note that this parameter would make block
not work. Because block
ensures that the function would wait until the initLock
is freed, as the ready methods acquire a read lock.
This parameter can be called:
// ready(errorNotRunning, block, allowedStatuses: Array<Status>)
ready(error, false, ['starting'])
Then you can allow a ready method to also be called when it is 'destroying'
, 'starting'
or 'stopping'
or null
.
Note that null
means that the none of the statuses are occurring. So generally speaking, allowing null
means that it is also allowed when the class itself isn't starting, stopping or destroying, and whether or not is running.
Right now the methods are doing this:
if (this[initLock].isLocked()) {
throw errorNotRunning;
}
if (!this[_running]) {
throw errorNotRunning;
}
The above would need to change to allow the statuses as exceptions, even if the init lock is locked, or that running isn't true.
Additional context
- MatrixAI/Polykey#310 - affects
syncNodeGraph
, if this can be done, this call be part of thestart()
call ofNodeConnectionManager
rather than being a separate call.
Tasks
- ...
- ...
- ...
A bug discovered in MatrixAI/Polykey#381 (comment) lead us to change the check right now from:
if (this[initLock].isLocked()) {
throw errorNotRunning;
}
if (!this[_running]) {
throw errorNotRunning;
}
To:
if (this[_status] !== null) {
errorNotRunning.stack = new Error().stack ?? '';
throw errorNotRunning;
}
if (!this[_running]) {
throw errorNotRunning;
}
The reason for this is because:
So the problem turns out to be that the ready decorated functions without
block=true
is checking whether theinitLock
is locked. Now theinitLock
may be locked in the case wherestart
,stop
, anddestroy
are being called, but it can also be locked if another method is decorated withready(..., true)
. This means the functions may throw an exception indicating that the class is not running, but that's not true. So the solution is to just change the check forinitLock
to check instead forthis[_status] !== null
.
This issue explains also that:
Note that
null
means that the none of the statuses are occurring. So generally speaking, allowingnull
means that it is also allowed when the class itself isn't starting, stopping or destroying, and whether or not is running.
To solve this particular issue, we would need to change both of these checks again to be more general. The default is to throw an exception IF:
- The
_status
is notnull
- The
_running
isfalse
This means it can only run when the class is running and neither start
nor stop
nor destroy
is executing.
If allowable statuses come into play, then that means we have to remove the running check, but instead solely focus on the _status
.
But we would need distinguish the status of null
from generally running and and not running.
The check has to be simplified somehow.