<div dir="ltr"><div><div>My guess is you can almost do what you want with asynchronous exceptions but some changes to the RTS would be needed.<br></div><div><br></div>There's a bit of code in the IO library that literally looks like this (<a href="https://gitlab.haskell.org/ghc/ghc/blob/master/libraries%2Fbase%2FGHC%2FIO%2FHandle%2FInternals.hs#L175">https://gitlab.haskell.org/ghc/ghc/blob/master/libraries%2Fbase%2FGHC%2FIO%2FHandle%2FInternals.hs#L175</a>):</div><div><br></div><div>            t <- myThreadId<br>            throwTo t e<br></div><div>            ... carry on ...</div><div><br></div><div>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. <br></div><div><br></div><div>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.<br></div><div><br></div><div>I suppose if you want to pass a value to the thread after resumption you could do it via an IORef.</div><div><br></div><div>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#.<br></div><div><br></div><div>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.<br></div><div><br></div><div>Hope this is helpful.</div><div><br></div><div>Cheers</div><div>Simon<br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 30 Jan 2020 at 00:55, Alexis King <<a href="mailto:lexi.lambda@gmail.com">lexi.lambda@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">> On Jan 29, 2020, at 03:32, Simon Peyton Jones <<a href="mailto:simonpj@microsoft.com" target="_blank">simonpj@microsoft.com</a>> wrote:<br>
> <br>
> Suppose a thread happens to be evaluating a pure thunk for (factorial 200). […] This stack-freezing stuff is definitely implemented.<br>
<br>
That’s fascinating! I had no idea, but your explanation makes sense (as do the papers you linked). That is definitely promising, as it seems like many of the tricky cases may already be accounted for? I’ll see if I can follow the Cmm code well enough to hunt down how it’s implemented.<br>
<br>
One other thing I have been thinking about: this is completely incompatible with the state hack, isn’t it? That is not a showstopper, of course—I do not intend to suggest that continuations be capturable in ordinary IO—but it does mean I probably want a way to selectively opt out. (But I’ll worry about that if I ever get that far.)<br>
<br>
Alexis</blockquote></div>