-
Notifications
You must be signed in to change notification settings - Fork 24
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
Allows contexts to escape to parent via return #87
Conversation
When a context returns another context, that context is kept alive. Meaning that unlike other children, it won't be halted when the context exits, it is then linked to the parent. This provides a desired escape hatch to allowing contexts escape the scope in which they were created. Initially we used the `suspend` pattern for this purpose, but as documented in #86, this pattern does not compose well, and its behaviour is confusing at times. In the future, we might extend this functionality with what we have dubbed resources, where a resource basically bundles some JavaScript object, and an associated context. This PR does not implement such a feature yet. This PR also makes a few other changes which were necessary or convenient for implementing this feature: - execution contexts now track whether they are required. This simplifies things, since we want to preserve whether they are required when they are returned - execution contexts are not initialized with a parent, but rather they can be linked to another context through `link` and `unlink`. This allows for more flexibility - linked children always propagate errors to their parents. Instead of this being part of the functionality of spawn and fork. This does require some trickery to make generator work, but I think it is worth it. - `monitor` has been renamed to `spawn` (an alias is retained for now), because it does not add any functionality on top of `context.spawn`. - It is currently no longer supported to use a `fork` or `spawn` as the top level operation. Because it hurts my brain and I can't figure out what sensible behaviour actually should be.
ff838f1
to
97ef634
Compare
@@ -113,7 +112,7 @@ Thanks!`); | |||
if (this.isRunning) { | |||
this.result = value; | |||
} | |||
if (this.requiredChildren.size > 0) { | |||
if (Array.from(this.children).some((c) => (!this.parent || c !== value) && c.isRequired)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conditional is quite complicated. Not sure how to reformulate this in a better way though.
@@ -148,14 +149,43 @@ Original error:`); | |||
} | |||
} | |||
|
|||
if(this.result instanceof ExecutionContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is instanceof
the right choice here?
if (child.isBlocking) { | ||
self.context.requiredChildren.add(child); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand this conditional or what it is/was supposed to do.
@@ -24,17 +24,15 @@ describe('Order of Shutdown Operations', () => { | |||
beforeEach(() => { | |||
order = []; | |||
|
|||
let self = ({ resume, context: { parent }}) => resume(parent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I had to rewrite this is because returning the parent
here actually transfers ownership of it now 😱
|
||
describe('forking from a process', () => { | ||
let exec, resume, fail; | ||
|
||
beforeEach(() => { | ||
exec = main(fork(context => ({ resume, fail } = context))); | ||
}); | ||
|
||
it('makes the external process waiting and blocking', () => { | ||
expect(exec.isBlocking).toBe(true); | ||
expect(exec.isWaiting).toBe(true); | ||
}); | ||
|
||
describe('resuming the forked process', () => { | ||
beforeEach(() => { | ||
resume(); | ||
}); | ||
it('completes the external process', () => { | ||
expect(exec.isCompleted).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('failing the forked process', () => { | ||
beforeEach(() => { | ||
fail(new Error('boom!')); | ||
}); | ||
|
||
it('fails the process', () => { | ||
expect(exec.isErrored).toBe(true); | ||
}); | ||
it('has the error as the result', () => { | ||
expect(exec.result).toBeDefined(); | ||
expect(exec.result.message).toEqual('boom!'); | ||
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason figuring out how to make this work really hurt my brain. But then I realized that maybe this just shouldn't be allowed as a pattern? Ideally it would just work, but it turned out to be surprisingly tricky.
Closing this in favour of #89, since I did a ton of additional work which also fixed a lot of bugs in this PR. |
When a context returns another context, that context is kept alive. Meaning that unlike other children, it won't be halted when the context exits, it is then linked to the parent. This provides a desired escape hatch to allowing contexts escape the scope in which they were created.
Initially we used the
suspend
pattern for this purpose, but as documented in #86, this pattern does not compose well, and its behaviour is confusing at times.In the future, we might extend this functionality with what we have dubbed resources, where a resource basically bundles some JavaScript object, and an associated context. This PR does not implement such a feature yet.
This PR also makes a few other changes which were necessary or convenient for implementing this feature:
execution contexts now track whether they are required. This simplifies things, since we want to preserve whether they are required when they are returned
execution contexts are not initialized with a parent, but rather they can be linked to another context through
link
andunlink
. This allows for more flexibilitylinked children always propagate errors to their parents. Instead of this being part of the functionality of spawn and fork. This does require some trickery to make generator work, but I think it is worth it.
monitor
has been renamed tospawn
(an alias is retained for now), because it does not add any functionality on top ofcontext.spawn
.It is currently no longer supported to use a
fork
orspawn
as the top level operation. Because it hurts my brain and I can't figure out what sensible behaviour actually should be.