Search code examples
csocketsbluetoothnetwork-programminghci

Why is read() not returning after shutdown/close on raw socket?


I have thread which reads messages from a raw HCI socket in a loop like this:

void* loop_hci (void* args) {
    params_hci_t* params = (params_hci_t*) args;
    int result_hci = 0;
    uint8_t* buf_hci = calloc(1, HCI_EVENT_MAX_LENGTH);
    while (!poll_end()) {
        result_hci = read(params->hci_sock, buf_hci, HCI_EVENT_MAX_LENGTH);
        if (result_hci > 0) {
            // ... do stuff with the received data
        }
    }
    ancs_pdebug("HCI loop shutting down...");
    return NULL;    
}

The poll_end() function works fine and as intended. It returns 0 until a SIGINT signal is received, after that it returns 1.

In the main thread I create the socket like this:

hci_sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);

And also the thread:

ph->hci_sock = hci_sock;
pthread_create(&t_hci, NULL, &loop_hci, ph);

Then after a while call shutdown like this (in the main thread):

shutdown(hci_sock, SHUT_RD);

I'm assuming read() should return after I call shutdown(), I use the same method in a different thread for a L2CAP socket and it works fine. But it doesn't. My pthread_join(t_hci, NULL) call in the main thread never returns.

The socket works fine. I can read messages from it. I also tried to call close (which I do after the threads have ended) instead, but the results are the same.

What could be the problem, or are my assumptions wrong?


Solution

  • The problem you are having might be because of the way you are handling sockets and multi-threading. You shouldn't use shutdown with raw sockets. It is meant for connected sockets I really never tried it with raw sockets or packet sockets. But there is a suggestion to kill or cancel a thread that I would avoid because killing a thread is brute force and you have the risk of not disposing resources in an ordered way. Instead you should design your solution in a different way:

    1. One possibility is to use non-blocking sockets along with select, poll, or epoll. You will have a loop that waits in select/poll/epoll until a socket is ready or there is a timeout. When you want to close a socket you just set a variable, like endLoop for example to true indicating the polling thread should leave the loop. See man for select: http://man7.org/linux/man-pages/man2/select.2.html

    2. Other possibility, that is used in Thrift server for example, is make the application send a special message to itself with certain code. The listening thread unblocks and reads this special message that indicates it should check if it must finish listening (by reading a variable value for example).

    So while he listening thread is listening or reading, the main thread will set the variable endLoop variable to true indicating it to finish or it will do (2) to unblock the listening thread.

    The two options are elegant ways to deal with your problem. Either use non-blocking sockets (1) or unblock the blocked reading or listening thread by sending a message to itself.