t <- myThreadId
throwTo t e
... carry on ...
that is, it throws an exception to the current thread using throwTo, and then there is code to handle what happens if the enclosing thunk is evaluated after the exception has been thrown.
That is, throwing an exception to the current thread is an IO operation that returns later! This only works with throwTo, not with throwIO, because throwIO is a *synchronous* exception that destructively tears down the stack.
I suppose if you want to pass a value to the thread after resumption you could do it via an IORef.
But the issue with this is that you can only apply the continuation once: GHC treats the captured continuation like a thunk, which means that after evaluating it, it will be updated with its value. But for your purposes you need to be able to apply it at least twice - once because we want to continue after shift#, and again when we apply the continuation later. Somehow the thunks we build this way would need to be marked non-updatable. Perhaps this could be done with a new primitive `throwToNonUpdatable` (hopefully with a better name) that creates non-updatable thunks. Also you might want to optimise the implementation so that it doesn't actually tear down the stack as it copies it into the heap, so that you could avoid the need to copy it back from the heap again in shift#.
So that's shift#. What about reset#? I expect it's something like `unsafeInterleaveIO`, that is it creates a thunk to name the continuation. You probably also want a `catch` in there, so that we don't tear down more of the stack than we need to.
Hope this is helpful.
Cheers
Simon