如何优雅地取消libgpiod中的gpiod_line_event_wait调用?
问题:libgpiod GPIO监听线程的优雅退出
我需要创建一个监听GPIO芯片线路事件的工作线程,并且能正常停止它。当前的实现方式如下:
std::jthread([this]() { gpiod_line_event event; while (is_running) { gpiod_line_event_wait(line, NULL); gpiod_line_event_read(line, &event); if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE) { std::cout << "GPIOD_LINE_EVENT_RISING_EDGE\n"; } else if (event.event_type == GPIOD_LINE_EVENT_FALLING_EDGE) { std::cout << "GPIOD_LINE_EVENT_FALLING_EDGE\n"; } } });
但线程会卡在gpiod_line_event_wait调用中无法退出。我试过关闭芯片、释放线路、设置线路值,都没有效果。注意到常量GPIOD_CTXLESS_EVENT_POLL_RET_STOP,但不清楚它的用途和用法。
另外,我知道可以给gpiod_line_event_wait加超时参数定期检查退出条件,但希望有其他更优雅的解决方案。
解决方案:通过文件描述符+ppoll实现可中断监听
libgpiod提供了gpiod_line_request_get_fd函数,能获取线路请求对应的文件描述符。我们可以结合一个用于触发停止信号的通道(比如pipe),通过ppoll同时监听这两个文件描述符,当需要停止线程时,向通道写入数据即可唤醒ppoll,进而退出线程。
示例实现代码:
int wait_until(int fd, int channel, int64_t timeout_ns) { struct pollfd pfds[2]; int ret; memset(&pfds, 0, sizeof(pfds)); pfds[0].fd = fd; pfds[0].events = POLLIN | POLLPRI; pfds[1].fd = channel; pfds[1].events = POLLIN | POLLERR; ret = poll_fds(pfds, 2, timeout_ns); if (ret >= 1 && pfds[1].revents != 0) return 2; return ret; } static int poll_fds(struct pollfd *pfds, uint8_t len, int64_t timeout_ns) { struct timespec ts; int ret; if (timeout_ns >= 0) { ts.tv_sec = timeout_ns / 1000000000ULL; ts.tv_nsec = timeout_ns % 1000000000ULL; } ret = ppoll(pfds, len, timeout_ns < 0 ? NULL : &ts, NULL); if (ret < 0) return -1; else if (ret == 0) return 0; return 1; }
使用说明
- 先创建一个pipe作为停止信号通道,线程中监听pipe的读端
- 调用
gpiod_line_request_get_fd获取GPIO线路的文件描述符 - 在线程循环中调用
wait_until替代gpiod_line_event_wait - 当需要停止线程时,向pipe的写端写入任意数据,
wait_until会返回2,此时即可退出循环
内容的提问来源于stack exchange,提问作者alajnabi
相关产品推荐
相关产品推荐

