[swift-server-dev] Next HTTP API meeting

Sergo Bero sergo_bero at icloud.com
Tue Mar 28 03:35:07 CDT 2017


> On 28 Mar 2017, at 01:24, Michael Chiu <hatsuneyuji at icloud.com> wrote:
> 
> 
>> That way you are just passing an argument to a closure, not capturing as I mentioned before, 
> 
> I’m not sure what you mean by capturing here, I’ll argue that you’re actually capturing a reference to the stack, as long as the stack is there the reference is always there, and for sequential operations the reference is basically there until the closure returns. Of course, the closure cannot pass the reference to a different thread that has different stack.
> 
> I’m not even against reference type at all, I just think value type has more advantage.
> 
>> How would you create a closure for this function, that does something in background queue, then moves to main queue and changes a property of original struct? Can you provide a sample ?
> 
> The real question should probably be, why would you even want to do that?

It’s very simple.
You want to retrieve data for the response from external service ( for example Redis ) asynchronously. ( Asynchronously because you don’t want to block request handlers just to wait for the data ).

These are small examples:

in Kitura you have Request, Response and next-closure for chaining.
in Perfect, you have again request and response, but here you have to call response’s ‘completed’ method after you are done with it.

Let’s say you want to connect to Redis from your request handler,

Redis libraries ( https://github.com/IBM-Swift/Kitura-redis <https://github.com/IBM-Swift/Kitura-redis> or https://github.com/PerfectlySoft/Perfect-Redis <https://github.com/PerfectlySoft/Perfect-Redis> ) both execute your request asynchronously, 

For example you want to retrieve some cached data with key

Kitura Redis:
redis.get("Redis") { (string: RedisString?, redisError: NSError?) in
	// Here you can create your response and call next()
}


Perfect Redis:
client.get(key: key) { response in
	// Here you can create response and call completed()
}

You cannot do it if Request and Response were structs. You cannot retain them inside Redis-callback closures.


> 
> First of all closure/block != multithreading.
> 
> Second for most of the web frameworks, in fact even DispatchIO, never do anything in the main thread. Main thread is usually use for the event runloop (99% the time it is either done by epoll() or kqueue(), even gcd, in fact gcd is basically high level kqueue. Most of the time all the middleware acted sequentially, so there’s no reason why you should initialize a struct in the main thread and pass it to another thread but initialize it in the worker thread instead. For example DispatchIO, you probably should initializer the struct in your DispatchIO block instead of initialize it first before even calling DispatchIO.

I called them ‘background’ and ‘main’ for simplicity.
In real app, you can have Database queue, Storage Queue, Network Queue…etc.

> 
> Even if you want to do something in background which is not sequential and move back to the main thread, one way is to block your main thread until your work is done and move back to the main thread using something live eventfd and kqueue. And TBH if you try to run something parallel for a single request,  you’re going to add a lot of overheads in a number of ways for both struct/class anyway.
> 
> Let’s say if you really have to somehow pass the request by reference to different thread, you can allocate a the struct to the heap and pass by pointer, or have a dummy class container that provides ref count interface to the struct, but the reverse is not possible. Even if you nest a class into a struct you can almost never avoid heap allocation and freeing it. Which in Swift(Darwin), according to my educated guess has time complexity of O(log n). Therefore sometimes I really doubt if things like req/res that has such short life cycle is worth for trade. (That’s why a good benchmark is important I guess) 

How do you suggest to allocate struct on the heap? using UnsafePointers ? and how is that any better than using class if you want to achieve Reference semantics ?


> 
> 
> Michael
> 
> 
> 
> 



I believe issues related to using structs mentioned by Logan were similar to your example:

fun addConstMiddleware(middleware: (Request) -> ())

Here, if request is a struct, it will be copied for each middleware, and instead of one ARC increment we will need to copy the request itself + increment all the reference counts for its  properties.

Now, if it was a class and we wanted to create a read-only function for that, we could easily create a protocol that has only { get } access to properties,

protocol ImmutableRequest {
	var headers: … { get }
}

Request: ImmutableRequest 

fun addConstMiddleware(middleware: (ImmutableRequest) -> ())






As I see from these emails,  one of the main reasons for using struct is to avoid ARC overhead and this can be understood. Still we want to use struct but at the same time want all the functions ( responder chain ) to be able to change it. That sounds like a Reference semantic to me.

If we can prove that ARC overhead is significant and there is no way to optimise it, probably the good way would be to use structs. Then, frameworks like Kitura/Perfect… can wrap the struct in a class and pass it to a responder chain.



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-server-dev/attachments/20170328/1915e70c/attachment.html>


More information about the swift-server-dev mailing list