1

This problem only hapens when socket is set timeout with SO_RCVTIMEO.

recv should block for 3sec. But it returns because of EINTR as soon as another thread starts.

If I run thread t2, recv in thread t1 will return -1 without blocking and sets errno to EINTR.

But recv in thread t1 functions normally when thread t2 is not started, it just blocks for 3 seconds.

If thread t2 runs before thread t1, recv also works properly.

I found it fails every time when I was debuging with SlickEdit or gdb. But works properly when it runs in terminal.

Here is code:

test.cpp: link -pthread to use <thread> or thread throws exception

#include<unistd.h>
#include<netdb.h>
#include<string.h>
#include<thread>

int socket_fd;
sockaddr_in server_addr;

void recvThread()
{
    char pData[4096];
    int len = recv(socket_fd,pData,4096,0);
    if(len<=0)
    {
        printf("len:%d\n",len);
        printf("errno:%d\n",errno);
    }
}

void otherThread()
{
    while(1)
    {
         sleep(1);
    }
}

int main()
{
    hostent *host;
    if((host=gethostbyname("127.0.0.1"))==NULL)
    {
         return 1;
    }
    memset(&server_addr, 0, sizeof(sockaddr_in));
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(8887);
    server_addr.sin_addr=*((in_addr*)host->h_addr);
    bzero(&(server_addr.sin_zero),8);

    socket_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    timeval timeout = {3,0};
    setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval));
    if(connect(socket_fd, (sockaddr*)&server_addr, sizeof(server_addr))<0)
    {
         return 1;
    }

    std::thread t1(recvThread);
    std::thread t2(otherThread);
    t1.join();
    t2.join();
}
zero15
  • 13
  • 4
  • 5
    Do you know what `EINTR` *means*? Please start by reading [the `recv` manual page](http://man7.org/linux/man-pages/man2/recv.2.html). – Some programmer dude Aug 03 '18 at 06:12
  • I don't know why it is interrupted by a signal? std::thread t2(otherThread) call will send a signal? – zero15 Aug 03 '18 at 06:52
  • If your second thread does nothing like shown in the question, that won't cause a signal to be sent to your process (but it will lead to wasting a lot of CPU cycles). It can be *any* signal, and there's simply no way for us to know anything more without a proper [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve). – Some programmer dude Aug 03 '18 at 06:58
  • I updated all code there. Since I can't copy from inner machine, so there might be some typing mistakes. This code can present what i asked. – zero15 Aug 03 '18 at 10:12
  • The weird part is: by exchanging the calling order of std:thread t1 t2, recv can be works well. – zero15 Aug 03 '18 at 10:18
  • @Someprogrammerdude – zero15 Aug 03 '18 at 10:29
  • Signals are used all over within threading internals. Don't be surprised if you get peripheral evidence of a signal without actually seeing it. – o11c Aug 04 '18 at 03:55
  • @o11c How should I deal with it? I just want recv() block for 3sec. – zero15 Aug 04 '18 at 04:28
  • You check how much time passed, and wait for the remaining amount. Assuming, of course, that the signal wasn't one that *deliberately* said "go back to the event loop and do something else" – o11c Aug 04 '18 at 21:20

2 Answers2

2

See comment on EINTR from "Some programmer dude"

The debugger (gdb), unless can set/modify breakpoints asynchronously, needs to stop the target (your task), set the breakpoints, then resume it. To stop it it could send SIGINT, which would thus cause EINTR on your system blocked calls.

If you using GNU C library you could use TEMP_FAILURE_RETRY macro, see this post: TEMP_FAILURE_RETRY and __USE_GNU

v01d
  • 189
  • 1
  • 12
1

Here is a potential bug

bzero(&(server_addr.sin_zero),8);

should be

bzero(&(server_addr.sin_zero), sizeof(server_addr.sin_zero));

Add signal handlers to find out what the error is, for linux there is a method to handle all, example.

Surt
  • 15,501
  • 3
  • 23
  • 39