3

I'm new to linux programming and not entirely familiar with all the synchronization facilities so I'd like to ask more knowledgeable people how they might go about solving this problem.

I have a single thread that I would like to run through a loop. The stopping point in the loop will be a read operation on a socket. I want the read operation to block for some period of time and then timeout. However, I need a way unblock the thread from the read, if some event needs attention. The "event" could be any one of a number of different things so I need some way to tell the thread what cause the read to unblock.

I know that you can unblock a blocked read with a signal but I'm not sure how that's done.

fotanus
  • 19,618
  • 13
  • 77
  • 111
DaveR
  • 1,295
  • 1
  • 13
  • 35
  • 2
    Why not have one thread only handling the blocking read and another thread doing whatever else is interrupting you? It seems like you want to have a threaded architecture and avoid polling and then you ask how to multiplex other work using another paradigm. – Joe Aug 09 '13 at 15:36
  • It's an XY problem: OP is under the impression that thread X cannot write to a socket if thread Y is stuck on a blocking read :) – Martin James Aug 09 '13 at 15:59

5 Answers5

3

See the select() system call.

This is especially useful for waiting for multiple file channels.

wallyk
  • 56,922
  • 16
  • 83
  • 148
  • I'm not waiting on multiple file channels. Only a single socket. Other events may need attention so I have to unblock the read and have the thread go off and handle whatever it is that needs attention. – DaveR Aug 09 '13 at 15:13
  • @user1143970: Depending on the nature of the other events, maybe they can provide i/o readiness as a synchronization mechanism. *select()* is a very mature mechanism. Otherwise, it does seem like multiple threads or even multiple processes might be a better fit. – wallyk Aug 09 '13 at 16:14
3

You can set timeout of socket operation. Example:

struct timeval timeout;    
timeout.tv_sec = TIMEOUT_SEC;
timeout.tv_usec = TIMEOUT_MSEC;

setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

/* now receive msg */
recvmsg(sock_fd, &msg, 0);

When you want to make your socket blocking, do:

timeout.tv_sec = 0;
timeout.tv_usec = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
Gyan Gupta
  • 986
  • 8
  • 15
2

epoll seems to be the way to go:

The epoll API performs a similar task to poll(2): monitoring multiple file descriptors to see if I/O is possible on any of them. The epoll API can be used either as an edge-triggered or a level-triggered inter‐ face and scales well to large numbers of watched file descriptors. The following system calls are provided to create and manage an epoll instance:

man epoll for more info. You might want to see "Example for Suggested Usage" section on manual.

See also epoll vs select

Community
  • 1
  • 1
fotanus
  • 19,618
  • 13
  • 77
  • 111
  • Here's a scenario: I am blocking on a socket read operation and a message appears in a queue. By queue, I mean a one maintained by my program. I need the thread to unblock, get the message, send it over the socket, and then go back to the read. If my thread is blocked on a read, how to I unblock it and tell it to check the queue? – DaveR Aug 09 '13 at 15:16
  • @user1143970 Not sure I understood right. Lets see: your thread should read from two different places, a queue and a socked. If both are file descriptors, you can use epoll to read on both descriptors, and the first one that have something ready to your thread work with, it will get and work. Is that correct/possible? – fotanus Aug 09 '13 at 15:40
  • 1
    What are your messages? Can they be handled by other threads or must you interrupt your read? If you *must* interrupt your read you have to "poll" in some way, poll, select, epoll or use signals (go async, which is trickier) – Joe Aug 09 '13 at 15:40
  • @user1143970 - just send the message over the socket from a write thread. The IP stack is fine with one thread reading and another writing on the same socket. – Martin James Aug 09 '13 at 15:58
1

Sounds like you want to use select() as others have mentioned, but you also want a way to interrupt it when a "message" of some sort is available. A typical way of interrupting a select() is to use the self pipe trick. Basically you create a pipe() and also select() on the read file descriptor of the pipe. When a message arrives in the queue maintained by your program, write a byte to the pipe. This will cause your select call to return and you'll be able to check to see if your pipe is ready for reading. If it is then you know you have a message to process (whatever that is in your context), so you process it and then go back to select(). Better yet, you could have your pipe actually be your message queue. If you just use the pipe as a way to signal that messages are on your queue, make sure you actually read() the bytes out of your pipe each time through, or it will fill up eventually and block you from writing more notifications to it.

Although, as others have mentioned, why not just have one thread service your queue and do your writes to the socket, while another thread does the reads? Probably a lot simpler.

mshildt
  • 8,782
  • 3
  • 34
  • 41
  • Is it possible to use pselect and have another thread deliver a signal to interrupt the pselect? If so, what signal would get sent by the second thread? – DaveR Aug 12 '13 at 17:46
0

Perhaps these two libraries may be of use to you:

They both use the event-driven paradigm on one or more threads (if so desired). Of course, you can implement your own event-driven framework using already mentioned APIs and conditional variables, but that might be more work than necessary.

Ioan
  • 2,382
  • 18
  • 32