1

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?

Lasse Meyer
  • 1,429
  • 18
  • 38
  • You *do* tell the thread to exit? You *do* check for `read` to returning `0` or `-1`? If `read` returns `0` or `-1`, do you break the loop or otherwise end the thread? And if possible please try to create a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve) and show us. – Some programmer dude May 30 '16 at 10:48
  • Before I call shutdown, a flag is set so poll_end() returns 1. If the result is <= 0, I skip the rest of the loop. So after read returns, it should break the loop on it's own. – Lasse Meyer May 30 '16 at 10:53
  • What makes you think calling `shudown()` on a file-descriptor in use by another thread is even allowed? – EOF May 30 '16 at 10:54
  • Is it not? How else do I end the thread then? Is there another way get a thread out of a blocking system function call? – Lasse Meyer May 30 '16 at 10:56
  • 1
    You could send a signal (`pthread_kill()`) or cancel the thread `pthread_cancel()`. The *sane* way would be to `[e]poll()` or `[p]select()` on a non-blocking socket. – EOF May 30 '16 at 10:57
  • See e.g. [this old question](http://stackoverflow.com/q/6389970/440558), the highest voted answer says you have to use `SHUT_RDWR`. – Some programmer dude May 30 '16 at 11:00
  • I already tried SHUT_RDWR, nothing changes. – Lasse Meyer May 30 '16 at 11:02
  • 1
    `shutdown()` only does anything on TCP (SOCK_STREAM) sockets. This is a SOCK_RAW socket. @EOF This is a perfectly valid technique in TCP sockets, but here it's not a TCP socket. – user207421 May 30 '16 at 11:04
  • @JoachimPileborg SHUT_RD is sufficient, in TCP. – user207421 May 30 '16 at 11:08
  • @EJP: Can you point me at some documentation that supports this as valid? ' Cause http://pubs.opengroup.org/onlinepubs/9699919799/ only specifies the effect on *subsequent* I/O-operations, not *concurrent* I/O. – EOF May 30 '16 at 11:10
  • @EOF 30+ years of experience. It doesn't make sense for it not to unblock a concurrent `recv()`: what would it be waiting for? – user207421 May 30 '16 at 11:12
  • @OP You don't appear to be testing the result of `recv()` for zero. So how can you tell it never returns? How do you know it isn't just looping forever in your code? – user207421 May 30 '16 at 11:13
  • @EJP: That's not a sane argument. Lots of people relied on interger overflow wrapping like 2's complement for a long time, doesn't make it defined that way (even if you think it "makes sense"). – EOF May 30 '16 at 11:13
  • @EOF *That* is not a sane argument. Integer overflow doesn't have anything to do with I/O. You haven't answered the question in my comment: what exactly would it be waiting for, after the input has been shutdown? – user207421 May 30 '16 at 11:15
  • @EJP: "What else should integer overflow do but wrap?" I/O is not magically more defined than everything else. If calling `shutdown()` on a socket that is in use by another thread is undefined, then *anything* could happen. – EOF May 30 '16 at 11:16
  • @EOF You can argue, insanely or otherwise, all you want, but the fact remains that it is a recognized technique, and it does work. In TCP. And you haven't answered my question. Integer overflow can do lots of other things besides wrapping, as I learnt in 1971. The point remains irrelevant. – user207421 May 30 '16 at 11:19
  • I have never seen, on either Windows or Linux, a recv() or read() call that does not return 'early' if the fd it is waiting on is closed from another thread. It is very difficult for me to imagine any other outcome when the socket resources need to be released while thread/s are waiting on them. The only sane action that the comms stack could perform would be to make all the waiting threads ready with an error and then cleanup/release/TIME_WAIT the socket, as required by the protocol in use. – Martin James May 30 '16 at 12:58
  • @EJP I am testing the result. See 2nd comment. – Lasse Meyer May 30 '16 at 13:02
  • Solved the problem with pthread_cancel, as EOF suggested. – Lasse Meyer May 30 '16 at 14:07
  • Would still be interesting how to solve this problem on a raw socket without pthread_cancel. poll/select don't seem to work either, I tried. Then again, I never worked with these functions before, so maybe I did something wrong. – Lasse Meyer May 30 '16 at 14:22
  • Using the `pthread_cancel()`-hammer is the worth approach to solve this. If not polling/selecting then send the *thread* a signal using `pthread_kill()`, this would make the blocking system call (`read()` here) return with `errno` set to `EINTR` (if not `SA_RESTART` had been set on the socket). Install a handler for the signal you send. The handle would do nothing, but just be around. – alk May 30 '16 at 18:27

1 Answers1

2

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.

rodolk
  • 5,606
  • 3
  • 28
  • 34