Network server design question

Dmitry Olshansky dmitry.olsh at gmail.com
Mon Aug 5 10:58:37 PDT 2013


05-Aug-2013 00:59, Marek Janukowicz пишет:
> Dmitry Olshansky wrote:
> There are more things specific to this particular application that would
> play a role here. One is that such "real workers" would operate on a common
> data structure and I would have to introduce some synchronization. Single
> worker thread was not my first approach, but after some woes with other
> solutions I decided to take it, because the problem is really not in
> processing (where a single thread does just fine so far), but in socket
> read/write operations.

Then what will make it simple is the following scenario
X Input threads feed 1 worker thread by putting requests into one shared 
queue.

You would have to use lock around it or get some decent concurrent queue 
code (but better start with simple lock + queue)...

Got carried away ... you can just easily use std.concurrency message 
passing (as *it is* an implicit message queue).

Then just throw in another writer thread that recieves pairs of 
responses + sockets (or shared void* e-hm) from "real worker".

The pipeline is then  roughly:
Acceptor
--CREATES--> InputWorkers (xN)
--SEND REQ--> Real Worker
--SOCK/RESP--> Writer

>>> 2. Create separate thread per each client connection. I think this could
>>> result in a nice, clean setup, but I see some problems:
>>> - I'm not sure how ~50 threads will do resource-wise (although they will
>>> probably be mostly waiting on Socket.select)
>>
>> 50 threads is not that big a problem. Around 100+ could be, 1000+ is a
>> killer.
>
> Thanks for those numbers, it's great to know at least the ranges here.
>
>> The benefit with thread per client is that you don't even need
>> Socket.select, just use blocking I/O and do the work per each parsed
>> request in the same thread.
>
> Not really. This is something that Go (the language I also originally
> considered for the project) has solved in much better way - you can "select"
> on a number of "channels" and have both I/O and message passing covered by
> those.

They multiplex stuff in their runtime. In fact AFAIK they don't even 
have clean-cut native threads. It would be interesting to see how they 
handle it but I guess either self-pipe or event-driven + async I/O to 
begin with.

> In D I must react both to network data or message from worker
> incoming, which means either self-pipe trick (which leads to Socket.select
> again) or some quirky stuff with timeouts on socket read and message receive
> (but this is basically a busy loop).

Sadly like others said with std.socket you get to witness the gory glory 
of BSD sockets API that shows its age. Regardless it's what all major OS 
directly provide.

>  Btw.
> would it work if I pass a socket to 2 threads - reader and writer (by
> working I mean - not running into race conditions and other scary concurrent
> stuff)?

Should be just fine.
See also
http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid


> Also I'm really puzzled by the fact this common idiom doesn't work in some
> elegant way in D. I tried to Google a solution, but only found some weird
> tricks. Can anyone really experienced in D tell me why there is no nice
> solution for this (or correct me if I'm mistaken)?

The trick is that Socket/std.socket was designed way back before 
std.concurrency. It's a class as everything back then liked to be.
The catch is that classes by default are mutable and thread-local and 
thus can't be automatically _safely_ transfered across threads.

There were/are talks about adding some kind of Unique helper to 
facilitate such move in a clean way. So at the moment - nope.


-- 
Dmitry Olshansky


More information about the Digitalmars-d mailing list