views:

158

answers:

4

In my program there is one thread (receiving thread) that is responsible for receiving requests from a TCP socket and there are many threads (worker threads) that are responsible for processing the received requests. Once a request is processed I need to send an answer over TCP.

And here is a question. I would like to send TCP data in the same thread that I use for receiving data. This thread after receiving data usually waits for new data in select(). So once a worker thread finished processing a request and put an answer in the output queue it has to signal the receiving thread that there are data to send. The problem is that I don't know how to cancel waiting in select() in order to get out of waiting and to call send() .

Or shall I use another thread solely for sending data over TCP?

Updated

MSalters, Artyom thank you for you answers!

MSalters, having read your answer I found this site: Winsock 2 I/O Methods and read about WSAWaitForMultipleEvents(). My program in fact must work both on HP-UX and Windows I finally decided to use the approach that had been suggested by Artyom.

+4  A: 

select is not the native API for Windows. The native way is WSAWaitForMultipleEvents. If you use this to create an alertable wait, you can use QueueUserAPC to instruct the waiting thread to send data. (This might also mean you don't have to implement your own output queue)

MSalters
What you are suggesting is to use IOCP that have many drawbacks and... unless you handle 10000 connections it is better to avoid them.
Artyom
Well, IOCP's are one way to break out of a WSAWaitForMultipleEvents. You can also add an Event to the list of objects waited on, and signal the event when there's data to send. I don't recall problems with the combination of WaitForMultipleEvents/QueueUserAPC, though, that's why I suggested it.
MSalters
Artyom, what are the 'many drawbacks' of IOCP then? Not that I'm suggesting that they're the solution to either the original question or this particular issue; I'm just curious as to what you think is hard about IOCP based communication..
Len Holgate
@Len Holgate, the problem that IOCP generally does not cover many aspects of socket programming, for example, you can't do asynchronous connect with IOCP and you can do it with select, so in complex TCP/IP cases you may find yourself using threads, IOCP and select in order to solve such kind of problems, so unless you have very big number of sockets to manage, select is more appropriate.
Artyom
@Artyom: you're missing the point then. The IOPC is used only behind the scenes, for the cross-thread `QueueUserAPC` interrupting; `WSAWaitForMultipleEvents()` waits for sockets in the same way as `select()` does.
MSalters
@Artyom - er, yes you can, `ConnectEx()` is fully async and operates with IOCP, likewise `AcceptEx()` is also fully async and allows you to accept connections on your IOCP threads and so removes the need for a listen thread. I respectfully suggest you take another look as it sounds like you knowledge of IOCP is either out of date or is just incorrect. IMHO select is only more appropriate if you feel you need some level of cross platform portability.
Len Holgate
@Len Holgate, I see your point, indeed ConnectEx exist from Windows XP and above, I just wonder, why Boost.Asio still uses select for asynchronous connect implementation? It has very good support of Windows and even supports new features of Vista/Winodows 7 but it still prefers using additional thread+select over using ConnectEx+IOCP when it uses IOCP for everything but connect: http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/overview/implementation.html
Artyom
Lazy backwards compatibility (if they support XP and don't bother to selectively enable more advanced functionality when it's available)? Ignorance? Failure to update working code when new functionality becomes available? Who knows.
Len Holgate
@Len Holgate, it is unlikely, as Boost.Asio usually implements best techniques, so I assume there was some reason.
Artyom
@Artyom - assume makes an ass, etc. If they do have a reason then it would be sensible for them to document it. Personally, I've not had any problems with `ConnectEx()` (except of my own making) and I've been using it for years.
Len Holgate
@MSalters, thanks a lot
skwllsp
A: 

The typical model is for the worker to handle its own writing. Is there a reason why you want to send all the output-IO through selecting thread?

If you're sure of this model, you could have your workers send data back to the master thread using file descriptors as well (pipe(2)) and simply add those descriptors to your select() call.

And, if you're especially sure that you're not going to use pipes to send data back to your master process, the select call allows you to specify a timeout. You can busy-wait while checking your worker threads, and periodically call select to figure out which TCP sockets to read from.

sarnold
windows does not have selectable pipes
Artyom
Ah. I had assumed windows implemented the POSIX spec closely. Thanks for the note.
sarnold
@sarnold "windows implemented the POSIX spec closely" Ohhhhh, you made me laugh. :)
Artyom
@artyom, glad to lighten your day :) Thanks for educating mine. Hehe.
sarnold
+4  A: 

You need to use something similar to safe-pipe trick, but in your case you need to use a pair of connected TCP sockets.

  1. Create a pair of sockets.
  2. Add one to the select and wait on it as well
  3. Notify by writing to other socket from other threads.
  4. Select is immediately waken-up as one of the sockets is readable, reads all the data in this special socket and check all data in queues to send/recv

How to create pair of sockets under Windows?

inline void pair(SOCKET fds[2])
{
    struct sockaddr_in inaddr;
    struct sockaddr addr;
    SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
    memset(&inaddr, 0, sizeof(inaddr));
    memset(&addr, 0, sizeof(addr));
    inaddr.sin_family = AF_INET;
    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    inaddr.sin_port = 0;
    int yes=1;
    setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes));
    bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr));
    listen(lst,1);
    int len=sizeof(inaddr);
    getsockname(lst, &addr,&len);
    fds[0]=::socket(AF_INET, SOCK_STREAM,0);
    connect(fds[0],&addr,len);
    fds[1]=accept(lst,0,0);
    closesocket(lst);
}

Of course some checks should be added for return values.

Artyom
sarnold
@sarnold Unlike POSIX setsockopt, windows one receives `char const *` as value instead of `void const *` as on POSIX platforms.
Artyom
You don't really need a pair of sockets, you can use a single UDP socket, "connected" to itself.
Hasturkun
@Artyom, thank you!
skwllsp
A: 

Another quick&dirty solution is to add localhost sockets to the set. Now use those sockets as the inter-thread communication queues. Each worker thread simply sends something to its socket, which ends up on the corresponding socket in your receiving thread. This wakes up the select(), and your receiving thread can then echo the message on the appropriate outgoing socket.

MSalters
Actually, WSAWaitForMultipleEvents seems to be more or less sutable but I have to read some manuals about using it before making a decision
skwllsp