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