Repository navigation
Port dependency/precedence logic when adding a mutation #223
Description
Activity
Here's an example, a fix which makes Prof. Dr. @hokeun 's Online Facility Location benchmark working:
(See diff for more info)
The corresponding runtime graph are as follow:
Not working
Loadinggraph 0["facilityLocation.0c4e7357[R0]"] 1["facilityLocation.0c4e7357[M0]"] 2["facilityLocation.0c4e7357[M1]"] 3["facilityLocation.0c4e7357.InPort"] 4["facilityLocation.0c4e7357.OutPort"] 5["facilityLocation.0c4e7357.OutPort"] 6["facilityLocation.0c4e7357.OutPort"] 7["facilityLocation.0c4e7357.OutPort"] 8["facilityLocation.0c4e7357.OutPort"] 9["facilityLocation.0c4e7357.OutPort"] 10["facilityLocation[M0]"] 11["facilityLocation.0c4e7357.866cd7ec.OutPort"] 1 --> 0 10 --> 1 0 --> 2 3 --> 2 2 --> 4 2 --> 5 2 --> 6 2 --> 7 2 --> 8 2 --> 9 11 -- "Problem here" --x 9
Error:
Exception occurred in reaction: facilityLocation.dbdc9a01[M1]: Error: Destination port is already occupied. /home/nkagamihara/reactor-ts/src/core/reactor.ts:1125 throw Error("Destination port is already occupied."); ^ Error: Destination port is already occupied. at Quadrant.canConnect (/home/nkagamihara/reactor-ts/src/core/reactor.ts:1125:13) at _mutationSandbox.connect (/home/nkagamihara/reactor-ts/src/core/reactor.ts:446:26) at partition (/home/nkagamihara/reactor-ts/src/benchmark/FacilityLocation.ts:447:41) at _mutationSandbox.<anonymous> (/home/nkagamihara/reactor-ts/src/benchmark/FacilityLocation.ts:495:33) at Mutation.doReact (/home/nkagamihara/reactor-ts/src/core/reaction.ts:155:18) at FacilityLocation._react (/home/nkagamihara/reactor-ts/src/core/reactor.ts:2352:11) at FacilityLocation._next (/home/nkagamihara/reactor-ts/src/core/reactor.ts:2485:27) at FacilityLocation.<anonymous> (/home/nkagamihara/reactor-ts/src/core/reactor.ts:2581:16) at processImmediate (node:internal/timers:478:21)Working, but there's big error because we are only writing to it:
Loadinggraph 0["facilityLocation.78c27ce7[R0]"] 1["facilityLocation.78c27ce7[M0]"] 2["facilityLocation.78c27ce7[M1]"] 3["facilityLocation.78c27ce7.InPort"] 4["facilityLocation.78c27ce7.OutPort"] 5["facilityLocation.78c27ce7.OutPort"] 6["facilityLocation.78c27ce7.OutPort"] 7["facilityLocation.78c27ce7.OutPort"] 8["facilityLocation.78c27ce7.OutPort"] 9["facilityLocation.78c27ce7.OutPort"] 10["facilityLocation[M0]"] 11["facilityLocation.78c27ce7.bdba16f9.OutPort"] 1 --> 0 10 --> 1 0 --> 2 3 --> 2 9 --> 2 2 --> 4 2 --> 5 2 --> 6 2 --> 7 2 --> 8 11 --> 9
This appears to be a bad example, as the issue is that the programme should not be written this way (that is, both could affect the
outportwhich is not good). Nevertheless this opens the possibility of writing potentially malformed code.Thanks for bringing this up!
Please note that the Facility Location example was written a very long time ago on top of the older version of reactor-ts, which might be different from what we have now.Trying to understand the issue better, could you elaborate on what you meant by "the program should not be written this way" and what you meant by "both" that could affect the port?
Thanks for bringing this up! Please note that the Facility Location example was written a very long time ago on top of the older version of reactor-ts, which might be different from what we have now.
Trying to understand the issue better, could you elaborate on what you meant by "the program should not be written this way" and what you meant by "both" that could affect the port?
Sorry for my delayed reply.
In the original code, in
Quadrant's second mutation,toAccumulator(anOutPort) was the destination of two components:
toNextAccumulatorofAccumulator:
reactor-ts/src/benchmark/FacilityLocation.ts
Lines 625 to 629 in b81e1ce
const toAccumulatorOfQuadrant = ( toAccumulator as unknown as WritablePort<Msg> ).getPort(); // Connect Accumulator's output to Quadrant's output. this.connect(accumulator.toNextAccumulator, toAccumulatorOfQuadrant); and the mutation (
[M1]itself:
reactor-ts/src/benchmark/FacilityLocation.ts
Lines 757 to 763 in b81e1ce
toAccumulator.set( new ConfirmExitMsg( 0, // facilities supportCustomers.get().length, // supportCustomers 1 // quadrantReactors ) ); I suppose, effectively, the first connection (which doesn't make much sense in the first place as it was connecting an
OutPortto anOutPort), is to make a logic that "oncetoNextAccumulatoris set, we also settoAccumulator"My current ugly fix is simply replicate this logic without making any additional connection by accessing a parent port from children: in
Accumulator:reactor-ts/src/benchmark/FacilityLocation.ts
Lines 300 to 313 in 82d6e34
[ this.fromFirstQuadrant, this.fromSecondQuadrant, this.fromThirdQuadrant, this.fromFourthQuadrant ], [ this.fromFirstQuadrant, this.fromSecondQuadrant, this.fromThirdQuadrant, this.fromFourthQuadrant, this.writable(this.toNextAccumulator), parent.writable(parent.toAccumulator), ], Then set both ports when one needs to be updated:
reactor-ts/src/benchmark/FacilityLocation.ts
Lines 359 to 374 in 82d6e34
toNextAccumulator.set( new ConfirmExitMsg( numFacilities + 1, // Add one for the facility itself. // (A quadrant with four children is considered as one facility in Akka-version implementation.) numCustomers, numQuadrantReactors + 1 // Add one for the quadrant reactor itself. ) ); parentToAccumulator.set( new ConfirmExitMsg( numFacilities + 1, // Add one for the facility itself. // (A quadrant with four children is considered as one facility in Akka-version implementation.) numCustomers, numQuadrantReactors + 1 // Add one for the quadrant reactor itself. ) ); I suppose this is not a good way to do it...... So I'm all ears
Nevertheless, outside of
facloc, the issue is that a port could (if people are usingreactor-tswith typescript, not lf+code generator) be converted to a writable port inside of a mutation, if the reactor where the mutation resides in has access to that port, without changing the causality interface and could potentially lead to causality loop, and I currently can't think of any good way to check this (unless we check the causality interface every time we doReactor::writable?)
This is a problem which troubled me for a while and I discovered it might potentially be a rather major issue.
When adding a mutation, it is necessary to declare ports. We declare a port as an argument, as things are set up currently, either as an
IOPortor as aWritablePort. They are, then, handled differently inrecordDeps: either as dependency or precedence to the mutation.reactor-ts/src/core/reactor.ts
Lines 659 to 665 in f5c195f
reactor-ts/src/core/reactor.ts
Lines 689 to 695 in f5c195f
The issue is, they are able to be converted to one another inside of the mutation (by using
this.getReactor().writable(port), or(port as unknown as WritablePort<number>).getPort()), and potentially create a causality loop. Unfortunately to do connection inside of a mutation, it appears thatIOPortversion is required, and if we want to connect AND write to a port then conversion is necessary (either pass in anWritablePortand convert toIOPortwhen connecting, or pass in anIOPortand convert toWritablePort).