I'm trying to get libwebsockets running in a multithreaded environment on OS X. I couldn't trigger sending Data from a different thread than the main service thread. On libwebsocket docs it was implied this should be possible (demo code, mailinglist). So I dug into the code and found the problem in the poll() function.
It seems that poll() is behaving differently concerning the struct pollfd
that is given as parameter. libwebsockets is relying on the possibility to change the fds.event fields while poll() is active. This is working fine on Linux but is not working on OS X.
I wrote a small test program to demonstrate the behaviour:
#include <unistd.h>
#include <netdb.h>
#include <poll.h>
#include <iostream>
#include <thread>
#define PORT "3490"
struct pollfd fds[1];
bool connected = false;
void main_loop() {
int sockfd, new_fd;
struct addrinfo hints, *servinfo, *p;
socklen_t sin_size;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return;
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo);
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, 10) == -1) {
perror("listen");
exit(1);
}
printf("server: waiting for connections...\n");
new_fd = accept(sockfd, NULL, &sin_size);
if (new_fd == -1) {
perror("accept");
return;
}
fds[0].fd = new_fd;
fds[0].events = POLLIN;
connected = true;
printf("event is %i\n", fds[0].events);
int ret = poll(fds, 1, 5000);
printf("event is %i\n", fds[0].events); //expecting 1 on Mac and 5 on Linux
if (send(new_fd, "Hello, world!\n", 14, 0) == -1)
perror("send");
close(new_fd);
close(sockfd);
}
void second_thread()
{
while(connected == false){}
sleep(1);
fds[0].events = POLLIN|POLLOUT;
printf("set event to %i\n", fds[0].events);
}
int main() {
std::thread t1(main_loop);
std::thread t2(second_thread);
t1.join();
t2.join();
return 0;
}
Compile on OS X using clang++ -std=c++11 -stdlib=libc++ -o poll poll.cpp
and on Linux using g++ -std=c++11 -pthread -o poll poll.cpp
The program starts listening on port 3490. If you connect to it (e.g. using netcat localhost 3490
) it will poll for input on the main thread and try to change the event flags in the second thread. It will exit after 5 seconds.
The output on OS X:
server: waiting for connections...
event is 1
set event to 5
event is 1
The output on Linux:
server: waiting for connections...
event is 1
set event to 5
event is 5
So my question is: is there any documentation available that explains this behavior? Is it safe what libwebsockets is doing in expecting that it is legal to change fds.events while poll is active? I couldn't find any details about it in the manpages (OS X, Linux).