Shutting down thread with Socket blocking for connection

Dmitry Olshansky dmitry.olsh at gmail.com
Mon Mar 5 00:38:02 PST 2012


On 05.03.2012 1:46, Vidar Wahlberg wrote:
> Coming from a C++/Java world I find D's approach to concurrency slightly
> difficult to grasp, perhaps someone could help me out a bit on this
> problem:

>
> I'd like to have a method that spawns a new thread which sets up a
> socket and listens for connections (in a blocking fashion). This part is
> easy, the hard part is nicely shutting down the thread listening for
> connections.

> Here's some quick code (ignore the fact that even if "sock" was shared,
> you would not be guaranteed that it was initialized correctly by the
> time you call "shutdown()" and "close()"):



> *************
> import std.concurrency;
> import std.socket;
> /*shared*/ Socket sock;
//yeah so this socket is
> void main() {
> spawn(&listen);
>
> /* do stuff */
>
> sock.shutdown(SocketShutdown.BOTH);
> sock.close();
> }
> void listen() {
> sock = new TcpSocket();
> sock.blocking = true;
> sock.bind(new InternetAddress(8080));
> sock.listen(10);
> sock.accept(); // assume no connection was made, thus still blocking
> }
> *************
>
> If I make "sock" shared, then I'm not allowed to call any methods on
> "sock" in "listen()" ("is not callable using argument types (...) shared").

Everything is thread-local by default. And thus there is extra 
protection with shared, for instance you *can* do atomic op on them 
(core.atomic), but sockets API wasn't designed with shared in mind 
(obviously). In any case, this oldschool sharing like that is a recipe 
for race conditions and bad scalability.

> I came across a post about a similar issue in this mail group from
> Andrew Wiley (2011-02-15 23:59) which had some example code, but I could
> not get that code to compile. Neither could I find an example in TDPL
> that shows how to deal with threads that are blocked.
>
> Basically, how would I go on about nicely stopping a thread that's
> waiting for connections?

Even in C I would put all responsibility on the listen thread.
In D, you have message passing in std(!), so do a select-pooling loop
and check for messages from main thread:

(*not tested*)
It may be that e.g. null are not accepted for select as an empty set, 
then just create a new empty SocketSet.

void main() {
  Tid listenT = spawn(&listen);

  /* do stuff */

  send(listenT, 42); //usually messages have extra info ;)
}

void listen() {
  bool working = true;
  Socket sock = new TcpSocket();
  sock.blocking = true;
  sock.bind(new InternetAddress(8080));
  scope(exit) {
    sock.shutdown(SocketShutdown.BOTH);
    sock.close();
  }
  sock.listen(10);
  SocketSet set = new SocketSet();
  set.add(sock);
  while(working){

   if(select(set, null, null, 10) > 0){ //10 usec wait on a socket, may 
do plain 0
	 sock.accept(); // no blocking here
   }
   set.reset();
   set.add(sock);
   receiveTimeout(dur!"us"(1), (int code){ working = false; });
  }

}

What the heck in such convenient manner you can send it message to  bind 
to another port, etc.

-- 
Dmitry Olshansky


More information about the Digitalmars-d-learn mailing list