1
votes

I am going to write a program processing requests coming from a TCP/IP connection and from a shared memory queue at the same time. This means that the program shall block until there is either a request in the queue or an input on a socket. Then it will process the request and continue. Is there a way to do this in a single thread? I mean some kind of select working with a semaphore and socket at the same time. Latency is important in my case and I do not want to do a busy wait neither. The program will run on Windows. Thanks.

3

3 Answers

3
votes

One way is to use overlapped I/O and using the hEvent mechanism to signal I/O completion. You can then wait on both the queue semaphore and the hEvent/s with the WaitForMultipleObjects() API.

Another way is to use overlapped I/O and completion routines. You could then wait on the semaphore in a loop with the WaitForSingleObectEx() API with the bAlertable argument set true so that the thread can process the queued completion routines, eg:

while(WAIT_IO_COMPLETION!=WaitForSingleObjectEx(queueSema,INFINITE,true)){
  [deque object and handle it];
};

Both schemes allow you to set timeouts to poll the server to keep the connection open and/or check if it's down.

2
votes

Libevent is a way to abstract out I/O events, and it has a way to manually trigger an event.

Setting up file-based events with libevents looks like this (copied from the documentation):

void callback_func(evutil_socket_t fd, short what, void *arg) { ... }

struct event *ev1, *ev2;
struct event_base *base = event_base_new();

/* The caller has already set up fd1, fd2 somehow, and make them
   nonblocking. */

ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, callback_func,
   (char*)"Reading event");
ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, callback_func,
   (char*)"Writing event");

event_add(ev1, NULL);
event_add(ev2, NULL);

If you want to create an event not associated with any file descriptor, pass -1 intead of an fd:

ev = event_new(base, -1, EV_PERSIST | EV_READ, callback_func, NULL);
event_add(ev, NULL);

Now, instead of raising a semaphore, trigger the event:

event_active(ev, EV_WRITE, 0);
1
votes

The Windows way of waiting for one of several possible things to happen is WaitForMultipleObjectsEx. You give it an array of Windows "handles" which can become "signaled" (these kinds of handles are called Synchronization Objects), and when any of them is signaled, the function returns and tells you which one it was.

The problem is that a socket, as implemented in the WinSock library, is not a Windows handle. You can't put it into the WaitForMultipleObjectsEx array.

Luckily, WinSock provides a function WSAEventSelect that can link a socket to a Windows event object. An event object is the simplest type of synchronization object. In this case, you would ask it to signal the event object when the socket is ready to be read (FD_READ). Then you would put the event object into the array alongside the semaphore.

The Windows Semaphore is already a synchronization object -- see CreateSemaphore in MSDN. It is signaled when its count is greater than 0. (This is what you expect from a semaphore.)

If you don't understand the whole idea of Synchronization Objects and reading MSDN doesn't help, I'd recommend Jim Beveridge's book Multithreading Applications In Win32. It's an old book, so it doesn't have sockets or semaphores in it, but it explains how to work with event objects, mutexes, and WaitForMultipleObjects in general; the sockets and semaphores will be easy to understand then.