[swift-server-dev] Prototype of the discussed HTTP API Spec

Michael Chiu hatsuneyuji at icloud.com
Thu Jun 1 14:32:16 CDT 2017

Hi Johannes and Everyone

I’m sorry if I made everybody lost with all the kevent/libdrill things. I’m just getting paranoid on getting rib a synchronous api on anything involves IO, as Johannes suggested, and hence bringing up things like kevent/coroutine that people can potentially use synchronous APIs with. This will be my last email regarding this topic and I think it is better to left the IO stuff on a IO email thread.

TL;DL: synchronous api has its use cases, maybe in most situations it cannot use as nice as async api, but it preserves its semantic, hence if someone want to build it something low level enough at least they can do something.

> I do understand what happens if you call blocking system calls on coroutines but thank you.

I guess I’m just a bit paranoid about with the statement below, what I try to negate is that we_should_not_implement_synchronous_api_becasue_it_will_block__, 

>>> I'd guess that most programmers prefer an asynchronous API with callback (akin to Node.js/DispatchIO) to using the eventing mechanism directly and I was therefore assuming you wanted to build that from kevent() (which is what they're often used for). Nevertheless, kevent() won't make your programming model any nicer than asynchronous APIs and as I mentioned before you can build one from the other in a quite straightforward way. What we don't get from that is ordinary synchronous APIs that don't __block kernel threads and that happens to be what most people would prefer eventually__. Hence __libdill/mill/venice__ and Zewo :).
>> Johannes, I totally agree with you. A asynchronous API is more intuitive and I agree with that. But since we are providing low level API for ppl like Zewo, Prefect, and Kitura, it is not right for us to assume their model of programming.
>> For libdill/mill/venice, even with green threads they will block when there’s nothing to do,
> If you read in libdill/mill/venice, it will switch the user-level thread to _not_ block a kernel thread. That's the difference and that's what we can't achieve with Swift today (without using UB).


>>> Comparing this to
>>> func writeBodyChunk(_ data: Data, completion: (Error?) -> Void) -> Void
>>> we can now register the attempt to write the data and move on with the calling thread. When the data has been written we invoke the completion handler and everything's good.
>> Well the expected behavior is that is the data cannot write immediately it will throw an error ‘temporary unavailable’ and the user can write again if he want to, exactly the same approach in my cpp code.
> I'm really sorry but this is really not an API a user would want to write a web app. Your first version of the C++ example just dropped the bytes, whilst that indeed looks very straight forward in code it unfortunately doesn't work.
> If you bring the user into the situation that something's temporarily unavailable, they'll probably retry, what else can they do?
> while true {
>    do {
>        try writeBodyChunk(...)
>        break
>    } catch let e as TemporarilyUnavailable {
>        /* what to do next? retry? */
>    } catch let e {
>        /* a real error */
>    }
> }

There’s plenty of things they can do, for example collect statistics, write to another socket instead, changed the kernel buffer side, use it as a synchronous point…. 
As I said, we don’t have to know why they what to do that, but this is what they _can_ do.

I’m not quite sure how my C++ example, which happens to be just a proof of concept and as I mentioned and it supposed to be bad code (that’s what proof of concept mean), become a reason for saying such thing is bad. 
I didn’t even use more than one single buffer because I’m too lazy, which is really the reason why write is a bit more painful.

I mean I think it’s a horrible idea to code something like that as well but we’re not here to babysitting the programmers who program a web framework.

It make sense for the web framework developers babysit the back end developers not to write stupid calls.

It doesn’t make sense for us to babysit someone who suppose to be responsible on how their own framework works and how their framework should behave.

 If something can be done, someone might able to utilize it and do something with it or even take average of the semantic. 

> The important bit it that only a defined number of threads (say equal to the number of CPUs you have) call kevent/epoll/select. If you use one kevent/epoll/select call on a thread per connection you can make your life a lot easier by just sticking to synchronous IO because that's the same. 

But implementing a synchronous API doesn’t mean they have to have a kqueue for each thread. So the assumption here just not make sense.

> what this is doing is turning it into a tight busy loop. This will just burn CPU for absolutely nothing. You will still be blocking a thread. It would be much cheaper if you would just do blocking IO here.
> I just don't get what you're trying to tell me. You're always blocking a real thread, sometimes by blocking and with the swrite() by tight looping. But besides the wasted CPU cycles it's exactly equivalent to just calling a blocking write, you'll block this thread until the socket becomes writable. That could take nanoseconds, seconds, minutes, hours or longer.

We all agree on that, but again I’m just paranoid about the statement you made before, it was vague to me.
Again for the CPU part…. I’m mentioned quite a few time it is horrible and why I think it’s a ghost story.

>> That’s why I said “Ppl can always fall back to C API but that won’t do any good for serverside swift”. What the only thing I think a very simple synchronous API should at least provide is that people can use it and play nicely with the provided API without breaking anything. (read/write that's transparent to the library, messing up socket options etc).
> I can't follow you here, sorry.

What I mean about here is that if we can provide a way for the user to not interference with the library when operate on a socket.

For example, If a user is trying to read from the socket from C API, which turns out somehow if the library is constantly reading from the buffer to fill a stream, 
that a user can get a portion of the data while the library may get some other portion.

Or if the library is counting how many bytes available (not reading) but the user also read data using C API hence the checksum is wrong.

A synchronous library can prevent, or at least reduce the problem listed above, the same reason why we write setter and getter.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-server-dev/attachments/20170601/5b570cc8/attachment.html>

More information about the swift-server-dev mailing list