<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div><blockquote type="cite" class=""><div class="">On Mar 23, 2017, at 9:01 AM, Joe Groff via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">setjmp and longjmp do *not* work well with Swift since they need compiler support to implement their semantics, and since Swift does not provide this support, setjmp-ing from Swift is undefined behavior. Empirical evidence that small examples appear to work is not a good way of evaluating UB, since any changes to Swift or LLVM optimizations may break it. Ease of implementation is also not a good criterion for designing things. *Supporting* a trap hook is not easy; it still has serious language semantics and runtime design issues, and may limit our ability to do something better.</span><br class="Apple-interchange-newline"></div></blockquote></div><div class=""><br class=""></div><div class="">Could we do something useful here without setjmp/longjmp? Suppose we had a function in the standard library that was roughly equivalent to this:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>typealias UnsafeFatalErrorCleanupHandler = () -&gt; Void</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// Note the imaginary @_thread_local property behavior</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>@_thread_local var _fatalErrorCleanupHandlers: [UnsafeFatalErrorCleanupHandler] = []</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>func withUnsafeFatalErrorCleanupHandler&lt;R&gt;(_ handler: UnsafeFatalErrorCleanupHandler, do body: () throws -&gt; R) rethrows -&gt; R {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>_fatalErrorCleanupHandlers.append(handler)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>defer { _fatalErrorCleanupHandlers.removeLast() }</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>return try&nbsp;body()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class="">And then, just before we trap, we do something like this:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// Mutating it this way ensures that, if we reenter fatalError() in one of the handlers,&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// it will pick up with the next handler.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>while let handler = _fatalErrorCleanupHandlers.popLast() {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>handler()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class="">I think that would allow frameworks to do something like:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class Worker {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>let queue = DispatchQueue(label: "Worker")</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>typealias RequestCompletion = (RequestStatus) -&gt; Void</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>enum RequestStatus {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>case responded</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>case crashing</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func beginRequest(from&nbsp;conn: IOHandle, then completion: RequestCompletion) {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>queue.async {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                                </span>withUnsafeFatalErrorCleanupHandler(fatalErrorHandler(completion)) {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// Handle the request, potentially crashing</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func fatalErrorHandler(_ completion: RequestCompletion) -&gt; UnsafeFatalErrorCleanupHandler {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>return {&nbsp;completion(.crashing) }</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class Supervisor {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>let queue = DispatchQueue(label: "Supervisor")</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>var workerPool: Pool&lt;Worker&gt;</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func startListening(to sockets: [IOHandle]) { … }</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func stopListening() { … }</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func bootReplacement() { … }</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func connected(by conn: IOHandle) {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>dispatchPrecondition(condition: .onQueue(queue))</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>let worker = workerPool.reserve()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>worker.beginRequest(from: conn) { status in</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                </span>switch status {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                                </span>case .responded:</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>conn.close()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>self.queue.sync {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>self.workerPool.relinquish(worker)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                </span>case .crashing:</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// Uh oh, we're in trouble.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>//&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// This process is toast; it will not survive long beyond this stack frame.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// We want to close our listening socket, start a replacement server,&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// and then just try to hang on until the other workers have finished their&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// current requests.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>//&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// It'd be nice to send an error message and close the connection,&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>//&nbsp;but we shouldn't. We don't know what state the connection or the&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>//&nbsp;worker are in, so we don't want to do anything to them.We risk a&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>//&nbsp;`!==` check because it only involves a memory address stored in&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>//&nbsp;our closure context, not the actual object being referenced by it.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// Go exclusive on the supervisor's queue so we don't try to handle&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// two crashes at once (or a crash and something else, for that matter).</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>self.queue.sync {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>self.stopListening()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>self.bootReplacement()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>// Try to keep the process alive until all of the surviving workers have&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>// finished their current requests. To do this, we'll perform barrier blocks&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>// on all of their queues, attached to a single dispatch group, and then&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>// wait for the group to complete.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                                                </span>let group = DispatchGroup()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>for otherWorker in self.workerPool.all where otherWorker !== worker {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                        </span>// We run this as `.background` to try to let anything else the request might&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                        </span>// enqueue run first, and `.barrier` to make sure we're the only thing running.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                        </span>otherWorker.queue.async(group: group, qos: .background, flags: .barrier) {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                                </span>// Make sure we don't do anything else.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                                </span>otherWorker.queue.suspend()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>// We do not use `notify` because we need this stack frame to keep&nbsp;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>// running so we don't trap yet.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                                </span>group.wait(timeout: .now() + .seconds(15))</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                        </span>// Okay, we can now return, and probably crash.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class="">It's definitely not a full actor model, and you have to be very careful, but it might be a useful subset.</div><br class=""><div class="">
<span class="Apple-style-span" style="border-collapse: separate; font-variant-ligatures: normal; font-variant-position: normal; font-variant-numeric: normal; font-variant-alternates: normal; font-variant-east-asian: normal; line-height: normal; border-spacing: 0px;"><div class=""><div style="font-size: 12px; " class="">--&nbsp;</div><div style="font-size: 12px; " class="">Brent Royal-Gordon</div><div style="font-size: 12px; " class="">Architechies</div></div></span>

</div>
<br class=""></body></html>