[swift-server-dev] Next HTTP API meeting

Johannes Weiß johannesweiss at apple.com
Thu Mar 30 09:28:26 CDT 2017

Hi Helge,

> On 29 Mar 2017, at 18:53, Helge Heß via swift-server-dev <swift-server-dev at swift.org> wrote:
> On 29. Mar 2017, at 19:14, Johannes Weiß <johannesweiss at apple.com> wrote:
>>> And we didn’t even discuss body streams yet, which I still find much more interesting due to the implications they can have on the I/O model :-) Paulo already mentioned the possible need for two APIs (async/sync), which matches my experience.
>> As per my email where I sketch what we use internally, we only have an async API. We have higher-level stuff on that and some does actually have sync APIs but why do you think we need both a sync and an async API? If someone wants a sync web framework, that's pretty simple to put on top of an async API.
> I’ve been thinking a lot about that lately (since well, one of my projects Noze.io is async to the core while my other, mod_swift, is completely sync for practical purposes). I do not think that it is easy at all to put a sync API on top of an async one. But maybe you can enlighten me :-)
> Yes, you can use a DispatchSemaphore to serialise the access and make GCD stuff work within sync environments, but as far as I can see this can’t be done in an automagic way right now. It would be cool if GCD would support that, something like a DispatchMain(), but more like WaitForEverythingScheduledFromThisThread().

I meant the DispatchSemaphore way. Said that, GCD really doesn't like you mixing synchronous and asynchronous programming. It's very easy to run into deadlocks when blocking a GCD provided queue with a semaphore. If you want to have one thread per request the only option I see is spawning the threads manually outside of GCD.

GCD has a finite thread pool size and it won't increase it even if everything is blocked waiting for maybe just one more thread.

> The other thing is that async (not NIO) essentially requires @escaping closures while sync stuff often doesn’t. @escaping is a pretty big big overhead.

< handled in another reply to my mail>

> You can have the same user-facing API, like:
>  readFile(“/etc/passwd”) { data in 
>  }
> but in a sync framework that would be
>  func readFile(_ p: String, cb: (Data)->Void)
> in an async framework it would be
>  func readFile(_ p: String, cb: @escaping (Data)->Void)
> Would be trivial with a #define in C, but due to the excellence of Swift, this is not that easy to do w/o copying large amounts of code ;-> (apart from always doing @escaping and live with copying the stack to the heap, adding ARC etc, which is plain lame).

I meant trivial as in easy to create once, not in easy to create the code. A function

func synchronise<T>(_ func: ((T) -> Void) -> Void) -> T

which is easy to write might help though.

> Plus the error reporting issue (throws vs (error, result)).
>> So I'd argue we should only have an async API for the HTTP stuff this group proposes.
> That sounds OK to *me*. If people want to do other stuff, they still can (libmill and such), it will just perform worse ...

AFAIK, libmill and friends all use setjmp/longjmp which is unsupported in Swift anyway and might/will break. Quote from Joe Groff:

<quote href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034519.html">
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.


More information about the swift-server-dev mailing list