- 主题:boost asio用stream_descriptor访问fifo时,读写不会完成
环境是UOS。
代码原来是针对父子进程间的匿名管道的,跑着没啥问题。
现在改为命名管道(fifo)。原来用async_pipe操作匿名管道,改为用stream_descriptor来操作fifo,其他没变。
但是发现async_write()、deadline_timer的async_wait()的完成函数没被调用,所以卡死。
io_context的run()是在一个单独的线程中调用的,那个线程只执行run()这一句。线程个数是1,所以不会并发执行run。
搜了一下,发现有人也遇到过这个问题,他在linux ok,但是macos上有问题:
https://stackoverflow.com/questions/60446097/asio-posix-stream-descriptor-async-read-not-completing-on-mac
bool CPipeIPC::SendMessageInternal(const std::string &payload, size_t timeout) {
// 我们用于写入的fifo是按需打开的,因为只有当对端打开用于读取后,我们才能成功打开。
if (m_pipe_fd1 == -1) {
if (!OpenPipe(PIPE_NAME1, m_pipe_fd1, m_pipe_stream1, O_WRONLY | O_NONBLOCK)) {
LOG(ERROR, "can't open pipe for writing");
return false;
} else {
LOG(INFO, "open pipe success for writing");
}
}
m_status = STATUS::NONE;
ResetEvent(m_event);
volatile bool timer_done = false;
volatile bool write_done = false;
boost::asio::deadline_timer timer{*m_io};
timer.expires_from_now(boost::posix_time::millisec(timeout));
timer.async_wait([&](const boost::system::error_code &ec) {
timer_done = true;
if (ec == boost::asio::error::operation_aborted) {
LOG(INFO, "writer timer cancelled");
} else if (ec) {
LOG(INFO, "writer timer error: " << ec);
} else {
LOG(INFO, "writer timer timeout");
if (m_status == STATUS::NONE) {
m_status = STATUS::SEND_TIMEOUT;
m_pipe_stream1->cancel();
}
}
if (timer_done && write_done) SetEvent(m_event);
});
boost::asio::async_write(*m_pipe_stream1, boost::asio::buffer(payload),
[&](const boost::system::error_code &ec, std::size_t n) {
write_done = true;
if (ec == boost::asio::error::operation_aborted) {
LOG(INFO, "writer oper aborted by timer");
} else if (ec) {
LOG(INFO, "writer oper error: " << ec);
m_status = STATUS::SEND_FAILED;
timer.cancel();
} else {
LOG(INFO, "writer oper success");
m_status = STATUS::SEND_SUCCESS;
timer.cancel();
}
if (timer_done && write_done) SetEvent(m_event);
});
WaitForEvent(m_event, WAIT_INFINITE);
return m_status == STATUS::SEND_SUCCESS;
}
--
修改:z16166 FROM 222.131.206.*
FROM 222.131.206.*
目前把io_context的run()放到WaitForEvent的前面可以(这样改的话,WaitForEvent没用了)
为了避免手写select()循环才用的asio,谁知会有这个问题
--
修改:z16166 FROM 222.131.206.*
FROM 222.131.206.*
B
【 在 z16166 的大作中提到: 】
: 环境是UOS。
: 代码原来是针对父子进程间的匿名管道的,跑着没啥问题。
: 现在改为命名管道(fifo)。原来用async_pipe操作匿名管道,改为用stream_descriptor来操作fifo,其他没变。
: ...................
去掉Event相关的代码,两处if (timer_done && write_done) 后
io_context.stop()
return前增加io_context.run()
其实timer_done和write_done也是多余的
--
FROM 110.191.219.*
我需要尽可能复用io_context,毕竟每次分配新的io_context对象有开销
之前我试过,只要执行了io_context.stop()后,如果再执行io_context.restart()和io_context.run(),会不正常,卡住,此时就不能复用io_context对象。
所以在一定范围内改为只要执行了stop(),就释放掉io_context,重新分配一个新的io_context。
io_context.run()在执行完所有活之后(本例子里只有两个活儿async_wait、async_write),会自动退出run循环,不需要stop()。
run()和async_wait/async_write合并在同一个线程里的话,event相关的那些全都可以删了。
问题是为什么run和async_wait/async_write不能放在不同的线程,这个有空dig一下
【 在 perduamour 的大作中提到: 】
: B
: 去掉Event相关的代码,两处if (timer_done && write_done) 后
: io_context.stop()
: ...................
--
修改:z16166 FROM 222.131.206.*
FROM 222.131.206.*
说句实话,你自己用epoll 加上自己实现一个简单的async
framework都比用boost asio来得快
【 在 z16166 的大作中提到: 】
: 我需要尽可能复用io_context,毕竟每次分配新的io_context对象有开销
: 之前我试过,只要执行了io_context.stop()后,如果再执行io_context.restart()和io_context.run(),会不正常,卡住,此时就不能复用io_context对象。
: 所以在一定范围内改为只要执行了stop(),就释放掉io_context,重新分配一个新的io_context。
: ...................
--
FROM 24.0.210.*
哈哈,那是你可以
我没这个自信,时间紧,还要稳定,自己搞轮子适合空闲时间比较多的情况
【 在 mvtec 的大作中提到: 】
: 说句实话,你自己用epoll 加上自己实现一个简单的async
: framework都比用boost asio来得快
--
FROM 222.131.206.*
boost asio 已经是神器般的存在,跨平台接口简单
让普通码农自己撸epoll 有点压力
【 在 mvtec 的大作中提到: 】
: 说句实话,你自己用epoll 加上自己实现一个简单的async
: framework都比用boost asio来得快
--
FROM 114.92.54.*
那个接口太捉急了。抽象得难以理解。
真的,你不一定要从 epoll() 开始撸,用 libuv/libev 怎么都比 asio 简单些。
【 在 looseleaf 的大作中提到: 】
: boost asio 已经是神器般的存在,跨平台接口简单
: 让普通码农自己撸epoll 有点压力
--
FROM 183.253.146.*
所有的async 都有个id 对应着你提供的lambda
我猜如果放在不同的thread
也许每个thread有一个自己的map来维护产生的async
你的问题可能就在于没有找到对应的lambda
【 在 z16166 的大作中提到: 】
: 哈哈,那是你可以
: 我没这个自信,时间紧,还要稳定,自己搞轮子适合空闲时间比较多的情况
:
--
FROM 107.77.224.*
1. 你的代码里没有看见io_context.run()在哪儿,你说把io_context.run放到WaitEvent之前,是指你手动改的代码?那你原来代码里io_context.run()是在哪儿运行的?
2. 如果你至始至终只有一个线程运行io_context.run(),那显然你不能阻塞这个线程。因为io_context本身不是thread_pool,是不带worker的,哪个线程运行run,哪个线程是worker,io_context本身只是一个事件队列而已。
【 在 z16166 的大作中提到: 】
: 我需要尽可能复用io_context,毕竟每次分配新的io_context对象有开销
: 之前我试过,只要执行了io_context.stop()后,如果再执行io_context.restart()和io_context.run(),会不正常,卡住,此时就不能复用io_context对象。
: 所以在一定范围内改为只要执行了stop(),就释放掉io_context,重新分配一个新的io_context。
: ...................
--
FROM 125.119.102.*