You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A MachineRunner instance contains a living Actyx subscription before it is destroyed. Currently, the programmer has to make sure that either for-await loop is used or destroy to make sure an orphan subscription is not running. There are a lot of cases where an instance is used temporarily without a 'for-await' loop; it is created, extracted, and then destroyed. Such cases have a risk---a missed destroy call.
Missing a destroy call is caused by an early return or a throw. But its root cause is the lifetime ownership of MachineRunner. For this issue, I am going to borrow some terms:
Lifetime of a MachineRunner: The period when a MachineRunner lives and its destruction
Ownership of a MachineRunner; The responsibility of a function or an object over a MachineRunner's lifetime --- the need to explicitly call 'destroy' after an instance is not used anymore
For example, an early return creates an orphaned machine.
constextractSomeData=async(): Promise<SomePayload|null>=>{constmachine=createMachineRunner(actyx,tags,Initial,payload);// a machine is createdconststate=(awaitmachine?.peek())?.value;// An early return. Here, the program forgets to call `machine.destroy()`, thus creating a living orphan machineif(!state){returnnull;}constpayload=state.payload;machine.destroy();returnpayload;}
A manual review is required to see the mistake in the piece of code above. The proper fix is:
if(!state){machine.destroy();returnnull;}
This is only one example of a missable destroy call. Another class of missable is less visible to a code review, which is a thrown Exception.
Solution
The proposed API aims to take over the ownership of a MachineRunner and instead allow the programmer to borrow an instance.
The API set will expose two things:
a function usingMachineRunner which accepts a similar set of arguments to createMachineRunner.
a type MachineRunnerUseFn<Factory, ReturnType, Payload = unknown> which enables the user to write a 'dependency-injection' style.
With this API, the problematic example above can be rewritten as:
usingMachineRunner does what createMachineRunner does and then execute the function passed by the user. With this pattern, the library now can destroy the machine after the function execution is done or if it throws, therefore eliminating the need for an explicit call to the destroy method.
Dependency injection can be achieved by the provided type. For example, a class wants to allow parametrization to only tags and the function body.
import{Initial}from"protocol/someRole";classSomeClass{actyx: Actyx,// Dependency injection happens hereuseMachine<T>(tags: Tags,useFn: MachineRunnerUseFn<Initial,T>){returnusingMachineRunner(this.actyx,tags,Initial,useFn);}}// subsequently the class can be used this wayconstsomeClass=newSomeClass(...);constresult=awaitsomeClass.useMachine(createSomeTags(),(machine)=>{forawait(conststateofmachine){
...
}});
The text was updated successfully, but these errors were encountered:
Kelerchian
changed the title
usingMachineRunner
[API] usingMachineRunner to borrow instead of own a MachineRunner
Aug 4, 2023
Kelerchian
changed the title
[API] usingMachineRunner to borrow instead of own a MachineRunner
[API] usingMachineRunner borrowing a machine (as opposed to owning)
Aug 10, 2023
Kelerchian
changed the title
[API] usingMachineRunner borrowing a machine (as opposed to owning)
[API] usingMachineRunner to borrow a machine (as opposed to owning)
Aug 10, 2023
Problem
A
MachineRunner
instance contains a living Actyx subscription before it is destroyed. Currently, the programmer has to make sure that eitherfor-await
loop is used ordestroy
to make sure an orphan subscription is not running. There are a lot of cases where an instance is used temporarily without a 'for-await' loop; it is created, extracted, and then destroyed. Such cases have a risk---a misseddestroy
call.Missing a destroy call is caused by an early return or a throw. But its root cause is the lifetime ownership of
MachineRunner
. For this issue, I am going to borrow some terms:For example, an early return creates an orphaned machine.
A manual review is required to see the mistake in the piece of code above. The proper fix is:
This is only one example of a missable
destroy
call. Another class of missable is less visible to a code review, which is a thrown Exception.Solution
The proposed API aims to take over the ownership of a
MachineRunner
and instead allow the programmer to borrow an instance.The API set will expose two things:
usingMachineRunner
which accepts a similar set of arguments tocreateMachineRunner
.MachineRunnerUseFn<Factory, ReturnType, Payload = unknown>
which enables the user to write a 'dependency-injection' style.With this API, the problematic example above can be rewritten as:
usingMachineRunner
does whatcreateMachineRunner
does and then execute the function passed by the user. With this pattern, the library now can destroy the machine after the function execution is done or if it throws, therefore eliminating the need for an explicit call to thedestroy
method.for-await
loop can also be used in the same way.Dependency injection can be achieved by the provided type. For example, a class wants to allow parametrization to only
tags
and the function body.The text was updated successfully, but these errors were encountered: