accept() interrupted on a signal and epoll_wait() - c

If I am epoll_wait()ing on a listening socket and, when epoll_wait() returns indicating it has activity (in this case, a connection waiting to be accept()ed), then if the accept() call fails with errno=EINTR, will epoll_wait() indicate that the same connection is waiting on the listening socket the next time it returns?
i.e., will I need to do something along the lines of:
while(1){
epoll_wait(epfd, &events, maxevents, timeout);
if (events.data.fd == listener){
connsock = accept(listener, &addr, &addrlen);
while (connsock != -1){
if (errno == EINTR){
accept(listener, &addr, &addrlen);
}
}
}
}
in order to make sure that the connection gets accepted, or will this work and still ensure that the connection for which accept() was interrupted by a signal gets accepted:
while(1){
epoll_wait(epfd, &events, maxevents, timeout);
if (events.data.fd == listener){
connsock = accept(listener, &addr, &addrlen);
}
}
where in this case if accept() is interrupted by a signal it'll just pick up the same connection the next time through the loop after epoll_wait returns again.
Obviously, in both these examples I'm making some assumptions (that only one event on one socket is returned in a given call to epoll_wait, for instance) and leaving out error checking (except for EINTR on accept(), since it's the whole point here) to simplify things

This is the difference between edge triggering and level triggering. Use level triggering, the default, and you don't have to worry about it.
The tradeoff with level triggering is that you can't have one thread handle the detected event while another thread goes back to call epoll_wait -- it would just detect the same event again. But in most cases, you don't need to do this anyway, and the tradeoff of it being impossible to lose an event is worth it.

Related

one thread to exit them all

I have a main program that generates a few threads (using a while loop with accept() to get clients), and one that all it has to do is "listen to the keyboard" and when the user enters the word exit it will close the entire program.
first, the main program create the listening thread, then it enters a while loop that accept the clients. even if the listening thread get the exit input the loop is still stuck on accept.
i don't have to use a seperate thread to listen to the keyboard but i could'nt find a none blocking way that would work.
the listening thread:
DWORD WINAPI ListenService(LPVOID lpParam)
{
char buffer[5];
if (EOF == scanf("%s", buffer))
{
printf("faile get word from keyboard\n");
}
if (buffer[4] != '\0')
strcat(buffer, "\0");
if (STRINGS_ARE_EQUAL(buffer, "exit"))
{
return 999;
}
return -1;
}
in the main code:
ThreadListen = CreateThread(NULL,0,ListenService,NULL,0,&(ThreadId));
while(1)
{
SOCKET AcceptSocket = accept(MainSocket, NULL, NULL);
if (AcceptSocket == INVALID_SOCKET)
{
printf("Accepting connection with client failed, error %ld\n", WSAGetLastError());
CleanupWorkerThreads();
WSACleanup();
}
printf("Client Connected.\n");
}
There are many different ways you can handle this.
You can abort a blocked accept() by simply closing the listening socket.
Or, you can use select() with a short timeout to detect when a new client is waiting before then calling accept(). You can check your exit condition in between calls to select(). Just be aware that there is a small race condition where a client may disconnect between the time select() and accect() are called, so accept() may still block, if there are no more clients waiting.
Or, you can get rid of your threads and just use non-blocking sockets in a single thread, checking your exit condition periodically in between socket operations.
Or, you can use asynchronous sockets, using WSACreateEvent(), WSAEventSelect(), and WSAWaitForMultipleEvents() to detect socket activity. Then you can create an addition event to wait on for when the exit condition happens.
Or, you can use an I/O Completion Port to handle socket activity, and then you can post a custom exit packet into the IOCP queue using PostQueuedCompletionStatus() to "wake up" any waiting threads.

Adding new FDs to fd_Set while blocking on select

I have a question regarding adding new socket file descriptors to an FDSET. Lets say we've already connected to a socket s1:
fd_set readfds;
//s1 = socket(...);
//connect(s1, ...)...
FD_ZERO(&readfds);
FD_SET(s1, &readfds);
and we are waiting for data to come down the socket, by calling select in a thread:
socket_reader_thread() {
for (;;)
{
int rv = select(n, &readfds, NULL, NULL, &tv);
if (rv == -1) {
perror("select"); // error occurred in select()
}
else if (rv == 0) {
printf("Timeout occurred! No data after 10.5 seconds.\n");
}
else {
// one the descriptors have data
.....
}
}
}
If I now wanted to add another socket (or may be two more socket etc) to the readfds set, given that select is blocking, how should I proceed? how can I interrupt select
Is the trick to add a zero timeout and use select like poll?
You need to use the "pipe-trick".
This is where an additional socket or pipe is created add it to the fd_set.
Then to interrupt a running or pending select, send a 1 byte message to it via another thread.
The select will then return and if the special pipe FD is one of the ones that are ready in the set, that means you need to say look at a list or something "do work" - like add any new FDs to the fd_set before returning to the select call.
You can interrupt select by sending (and catching) a signal to your process, for example using raise. select will return in this case with -1 and errno set to EINTR. You can then change the events you want to wait for and call select again.
Is the trick to add a zero timeout and use select like poll?
One can simply use a timeout of 0 in which case it will just do a non-blocking check if any of the events got triggered, i.e. polling. But this should only be done in a few cases since busy polling instead of a blocking wait uses lots of resources of machine. And I would even consider the interrupting of a blocking select a questionable design, although probably not as bad as busy polling.

Recover after "accept" interruption

Was writing program with server capability.
Here is short snippet demonstrating accept:
for (;;) {
if ((c = accept(s, (struct sockaddr *)&sa, &b)) == -1) {
if (EINTR == errno) {
syslog(LOG_INFO, "recovering after system call interruption");
continue;
} else
err_exit(strerror(errno));
}
/* forking, client handling code */
}
Also I handle HUP by rereading configuration file so that signal could happen when accept is being blocked waiting for client requests.
Is the approach I use correct or there is another proper method for doing accept and handling signals?
Does interruption by a signal flushes clients queue waiting to be serviced, that is my main concern regarding the question.
Does interruption by a signal flushes clients queue
No, it doesn't.
Is the approach I use correct
It is a common approach, yes.
or there is another proper method for doing accept and handling signals?
Not answerable, as you do not show the related code, how you actually do the signal handling.

Understanding gap between socket creation and select() system call

I am aware that select() will be triggered whenever there is a data in the registered socket buffer.
what will happen if there is a delay between these two statements.
FD_SET(listener, &read_fds); // &
(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
what will happen if packet arrives between these two statements?
//create socket and listen for packets &
FD_SET(listener, &read_fds);
Assume that recv() is done once select is triggered.
What will happen if a packet arrives before the select() call is
made.?
does FD_ISSET still detects the packet which is already in
socket buffer or it will be detected only if new packet arrives and
select gets triggered?
Sample code:
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
Understanding gap between socket creation and select() system call
There is no gap between socket creation and select() in your question.
I am aware that select() will be triggered whenever there is a data in the registered socket buffer.
That's true for read events and it applies to the socket receive buffer of connected sockets. It also triggers when there is an inbound connection on a listening socket, or room in the socket send buffer for send events.
what will happen if there is a delay between these two statements.
FD_SET(listener, &read_fds); // &
(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
Nothing bad. Any event that occurs between them will still be signalled. But the first statement isn't a socket creation, contrary to your title.
what will happen if packet arrives between these two statements?
//create socket and listen for packets &
FD_SET(listener, &read_fds);
The socket send buffer exists from the moment the socket is created, so the data will go into the buffer, so when select() runs it will see that and trigger a read event.
Assume that recv() is done once select is triggered.
What will happen if a packet arrives before the select() call is made.?
The socket send buffer exists from the moment the socket is created, so the data will go into the buffer, so when select() runs it will see that and trigger a read event.
does FD_ISSET still detects the packet which is already in socket buffer
Yes.
or it will be detected only if new packet arrives and select gets triggered?
It will always be detected.
If data is waiting to be read, select will return immediately, and FD_ISSET will return true for the file descriptor that the data arrived on. It doesn't matter if data arrived before or after select was called.
select() completes immediately if one or more of the watched conditions is already active; otherwise it blocks until one or more of the watched conditions becomes active (or the timeout, if specified, expires).

Check if socket listen backlog queue is empty

Is there any way to check if there are no pending connection requests to the server?
In my case, I have this code, from C:
listen(mysocket, 3);
//while(no pending connections) do this
int consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);
And what I need, is that while there are no pending connections, do something else, instead of getting stuck waiting, by calling accept().
You can set a socket in non-blocking mode with fcntl.
fcntl(sockfd, F_SETFD, O_NONBLOCK);
After that, a call to accept(sockfd) will either immediately return a newly accepted connection or immediately fail with EWOULDBLOCK which you can use to decide to do “something else”.
Another option would be to use select, maybe with a finite timeout, to gain finer grained control over blocking.
You can spawn a new thread and do your alternate work in that thread while the main thread waits for clients. Once a client is connected, accept() gets unblocked and then you can cancel the newly spawned thread.
The code should be on the lines of:
while(1) {
pthread_create(&thread, NULL, do_something_else, NULL);
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);
pthread_cancel(thread);
}

Resources