Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutation connect test #107

Merged
merged 7 commits into from
Jul 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions __tests__/InvalidMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,49 @@ class R1 extends Reactor {
this.addMutation(
new Triggers(this.in1),
new Args(this.in1, this.out2),
function(this, __in, __out) {
// test('expect error to be thrown on mutation creating loop', () => {
function(this, __in1, __out2) {
test('expect error to be thrown on mutation creating loop', () => {
expect(() => {
this.connect(__out2, __in1)
}).toThrowError("New connection introduces cycle.")
expect(() => {
this.connect(__in1, __out2)
}).toThrowError("New connection introduces direct feed through.") // dist port already in use

// let R2 = new R1(this.getReactor())
// expect(() => {
// // this.connect(R2.out1, R2.in1)
// // this.connect(R2.in1, R2.out1)
// // this.connect(R2.in1, R2.out1)
// this.connect(R2.out1, R2.in1)
// }).toThrowError("New connection introduces cycle.")
})
// console.log("TEST START!")
// test('expect error on mutation creating race condition', () => {
// expect(() => {
// this.connect(__out, __in)
// }).toThrowError("ERROR connecting " + __out + " to " + __in)
// //this.canConnect(__in1)
// // console.log("Can Connect: ", this.getReactor().canConnect(__in1, __out2))

// this.connect(__in1, __out2)
// }).toThrowError("New connection introduces direct feed through.")
// })
// let R2 = new R1(this.getReactor())
// test('expect error on spawning and creating loop within a reactor', () => {
// expect(() => {
// this.connect(__out2, __in1)
// }).toThrowError("New connection introduces cycle.")
// })
test('expect error on mutation creating race condition', () => {
expect(() => {
this.connect(__in, __out)
}).toThrowError("ERROR connecting " + __out + " to " + __in)
this.connect(__in1, __out2)
}).toThrowError("New connection introduces direct feed through.")
})
let R2 = new R1(this.getReactor())
test('expect error on spawning and creating loop within a reactor', () => {
expect(() => {
this.connect(R2.in1, R2.out1)
this.connect(R2.out1, R2.in1)
}).toThrowError("ERROR connecting " + R2.out1 + " to " + R2.in1)
}).toThrowError("New connection introduces cycle.")
})
}
)
Expand All @@ -82,4 +108,3 @@ class testApp extends App {

var app = new testApp()
app._start()

19 changes: 3 additions & 16 deletions src/core/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,27 +184,14 @@ export class DependencyGraph<T> {
* @param current The current node being visited.
*/
function search(current: T) {
visited.add(current)
if (origins.has(current)) reachable.add(current)
for (let next of self.getEdges(current)) {
if (!visited.has(current)) {
// Do not visit a node twice.
if (origins.has(current)) {
// If the current node is among the origins searched
// for, add it to the reachable set.
reachable.add(current)
}
// Continue search, depth first.
if (reachable.size == origins.size) {
search(next)
} else {
// Preempt search of all origins are reachable.
return
}
}
if (!visited.has(next)) search(next)
}
}

search(effect)

return reachable
}

Expand Down
64 changes: 47 additions & 17 deletions src/core/reactor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ protected _getFirstReactionOrMutation(): Reaction<any> | undefined {
(src: IOPort<S>, dst: IOPort<R>) {
// Immediate rule out trivial self loops.
if (src === dst) {
return false
throw Error("Source port and destination port are the same.")
}

if (this._runtime.isRunning() == false) {
Expand All @@ -956,15 +956,15 @@ protected _getFirstReactionOrMutation(): Reaction<any> | undefined {
// Rule out write conflicts.
// - (between reactors)
if (this._dependencyGraph.getBackEdges(dst).size > 0) {
return false;
throw Error("Destination port is already occupied.")
}

// - between reactors and reactions (NOTE: check also needs to happen
// in addReaction)
var deps = this._dependencyGraph.getEdges(dst) // FIXME this will change with multiplex ports
if (deps != undefined && deps.size > 0) {
return false;
}
// // - between reactors and reactions (NOTE: check also needs to happen
// // in addReaction)
// var deps = this._dependencyGraph.getEdges(dst) // FIXME this will change with multiplex ports
// if (deps != undefined && deps.size > 0) {
// return false;
// }

return this._isInScope(src, dst)

Expand All @@ -983,25 +983,57 @@ protected _getFirstReactionOrMutation(): Reaction<any> | undefined {
}

// Add the new edge.
// Add edge when we are able to add graph
// if(isAbleToAdd(dst, src)) graph.addEdge(dst, src)
graph.addEdge(dst, src)

// 1) check for loops
let hasCycle = false
if (graph.hasCycle()) {
return false
hasCycle = true
// throw Error("New connection introduces cycle.")
}

// 2) check for direct feed through.
let hasDirectFeedThrough = false
let inputs = this._findOwnInputs()
for (let output of this._findOwnOutputs()) {
let outputs = this._findOwnOutputs()


for (let output of outputs) {
let newReachable = graph.reachableOrigins(output, inputs)
let oldReachable = this._causalityGraph.reachableOrigins(output, inputs)

// Print newReachable and oldReachable origin
for (let new_ori of newReachable) {
if (new_ori instanceof Port) {
console.log("New Origin: ", new_ori._getFullyQualifiedName())
}
}
for (let old_ori of oldReachable) {
if (old_ori instanceof Port) {
console.log("Old Origin: ", old_ori._getFullyQualifiedName())
}
}
console.log("!!!")

for (let origin of newReachable) {
if (origin instanceof Port && !oldReachable.has(origin)) {
return false
hasDirectFeedThrough = true
// throw Error("New connection introduces direct feed through.")
}
}
}

// Throw error three cases: 1. cycle / 2. direct feed through / 3. both
if (hasDirectFeedThrough && hasCycle) {
throw Error("New connection introduces direct feed through and cycle.")
} else if (hasCycle) {
throw Error("New connection introduces cycle.")
} else if (hasDirectFeedThrough) {
throw Error("New connection introduces direct feed through.")
}

return true
}
}
Expand Down Expand Up @@ -1078,11 +1110,9 @@ protected _getFirstReactionOrMutation(): Reaction<any> | undefined {
if (dst === undefined || dst === null) {
throw new Error("Cannot connect unspecified destination");
}
if (this.canConnect(src, dst)) {
this._uncheckedConnect(src, dst);
} else {
throw new Error("ERROR connecting " + src + " to " + dst);
}
this.canConnect(src, dst);
console.log("Performing connect.")
this._uncheckedConnect(src, dst);
}

protected _connectMulti<R extends Present, S extends R>(
Expand Down Expand Up @@ -1251,7 +1281,7 @@ protected _getFirstReactionOrMutation(): Reaction<any> | undefined {
private _findOwnOutputs() {
let outputs = new Set<OutPort<Present>>()
for (let component of this._keyChain.keys()) {
if (component instanceof InPort) {
if (component instanceof OutPort) {
outputs.add(component)
}
}
Expand Down