我试图使libwebsockets在OS X上的多线程环境中运行。我无法触发从与主服务线程不同的线程发送数据。在libwebsocket docs上暗示这应该可行(demo code,mailinglist)。因此,我深入研究了代码,并在poll()函数中发现了问题。
似乎poll()对于作为参数给出的struct pollfd
的行为有所不同。 libwebsockets依赖于在poll()处于 Activity 状态时更改fds.event字段的可能性。这在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;
}
使用
clang++ -std=c++11 -stdlib=libc++ -o poll poll.cpp
在OS X上编译并在Linux上使用
g++ -std=c++11 -pthread -o poll poll.cpp
程序开始侦听端口3490。如果您连接到端口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在期望在轮询处于 Activity 状态时更改fds.events是合法的时,这样做安全吗?我在联机帮助页(OS X,Linux)中找不到有关它的任何详细信息。
最佳答案
您似乎一开始似乎发现找到了一些文档,声称这些文档受支持并定义了行为。我很想知道您在哪里读的书,因为在Linux手册页poll(2)和POSIX man page for poll()中都找不到任何文档,该文档记录了另一个线程实际上可以更改事件数组参数中的值,而另一个传递给poll()的线程,并且具有不同线程的更改实际上在原始线程的poll()调用中生效,而不管与内存屏障等相关的任何问题。
在我看来,这两个手册页似乎都完全保持沉默。它们不指示这是预期的,受支持的还是已定义的行为;或这是否不是受支持或已定义的行为。
在另一个线程已经进入syscall之后,另一个线程可以修改另一个线程发出的系统调用的参数的建议对我来说似乎是反直觉的。如果这是受支持的行为,则希望可以对其进行明确记录,并且在Linux或POSIX手册页中找不到对此的任何引用。
话虽如此:即使我将软件范围限制为Linux,即使我不需要关心其他平台也是如此;考虑到没有任何相关文档,即使我的测试显示Linux内核以这种方式实现poll(2),我也无法保证任何将来的内核版本将继续以这种方式运行。我将不能依靠这种行为,除非依靠我对其进行测试的特定内核构建。
因此,回答您的问题:唯一有关此主题的权威文档是相关手册页。他们没有明确将其记录为合法行为;尽管他们也没有明确指出这是非法行为,但出于上述原因,我认为这是不受支持的,未定义的行为。