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