Я пытаюсь запустить libwebsockets в многопоточной среде на OS X. Я не мог инициировать отправку данных из другого потока, отличного от основного потока службы. В документации по libwebsocket подразумевалось, что это должно быть возможно (демонстрационный код, список рассылки) . Поэтому я покопался в коде и нашел проблему в функции poll().
Кажется, что poll() ведет себя по-разному в отношении struct pollfd
, заданного в качестве параметра. libwebsockets полагается на возможность изменять поля fds.event, когда poll() активен. Это нормально работает в Linux, но не работает в OS X.
Я написал небольшую тестовую программу, чтобы продемонстрировать поведение:
#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;
}
Скомпилируйте в OS X, используя clang++ -std=c++11 -stdlib=libc++ -o poll poll.cpp
, и в Linux, используя g++ -std=c++11 -pthread -o poll poll.cpp
Программа начинает прослушивать порт 3490. Если вы подключитесь к ней (например, используя netcat localhost 3490
), она будет опрашивать входные данные в основном потоке и пытаться изменить флаги событий во втором потоке. Он выйдет через 5 секунд.
Вывод на OS X:
server: waiting for connections...
event is 1
set event to 5
event is 1
Вывод в Linux:
server: waiting for connections...
event is 1
set event to 5
event is 5
Итак, мой вопрос: есть ли какая-либо доступная документация, объясняющая такое поведение? Безопасно ли то, что делает libwebsockets, ожидая, что законно изменить fds.events, пока опрос активен? Я не смог найти никаких подробностей об этом на справочных страницах (OS X, Linux).