Dies ist ein Fehler. Ich konnte es kopieren, indem ich eine Verzögerung in den unkritischen Abschnitt von task_io_service::do_poll_one
hinzufüge. Hier ist ein Ausschnitt der modifizierten task_io_service::do_poll_one()
in booost/asio/detail/impl/task_io_service.ipp
. Die einzige hinzugefügte Zeile ist der Schlaf.
std::size_t task_io_service::do_poll_one(mutex::scoped_lock& lock,
task_io_service::thread_info& this_thread,
const boost::system::error_code& ec)
{
if (stopped_)
return 0;
operation* o = op_queue_.front();
if (o == &task_operation_)
{
op_queue_.pop();
lock.unlock();
{
task_cleanup c = { this, &lock, &this_thread };
(void)c;
// Run the task. May throw an exception. Only block if the operation
// queue is empty and we're not polling, otherwise we want to return
// as soon as possible.
task_->run(false, this_thread.private_op_queue);
boost::this_thread::sleep_for(boost::chrono::seconds(3));
}
o = op_queue_.front();
if (o == &task_operation_)
return 0;
}
...
Mein Testfahrer ist recht einfach: ""
- Eine asynchrone Arbeit Schleife über einen Timer, der druckt alle 3 Sekunden.
- Spawn aus einem einzelnen Thread, der die
io_service
abfragen wird.
- Verzögerung, um die neue Thread-Zeit zu ermöglichen,
io_service
abzufragen und Hauptanruf io_service::run()
zu haben, während der Abfrage-Thread in task_io_service::do_poll_one()
schläft.
Prüfregeln:
#include <iostream>
#include <boost/asio/io_service.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/chrono.hpp>
#include <boost/thread.hpp>
boost::asio::io_service io_service;
boost::asio::steady_timer timer(io_service);
void arm_timer()
{
std::cout << ".";
std::cout.flush();
timer.expires_from_now(boost::chrono::seconds(3));
timer.async_wait(boost::bind(&arm_timer));
}
int main()
{
// Add asynchronous work loop.
arm_timer();
// Spawn poll thread.
boost::thread poll_thread(
boost::bind(&boost::asio::io_service::poll, boost::ref(io_service)));
// Give time for poll thread service reactor.
boost::this_thread::sleep_for(boost::chrono::seconds(1));
io_service.run();
}
Und die debug:
[[email protected] bug]$ gdb a.out
...
(gdb) r
Starting program: /home/twsansbury/dev/bug/a.out
[Thread debugging using libthread_db enabled]
.[New Thread 0xb7feeb90 (LWP 31892)]
[Thread 0xb7feeb90 (LWP 31892) exited]
An diesem Punkt der arm_timer()
gedruckt hat "" einmal (wenn es anfänglich bewaffnet war). Der Poll-Thread bediente den Reaktor in einer nicht-blockierenden Weise und schlief 3 Sekunden lang, während op_queue_
leer war (task_operation_
wird wieder zu der op_queue_
hinzugefügt, wenn task_cleanup c
den Geltungsbereich verlässt). Während der op_queue_
leer war, ruft der Hauptthread io_service::run()
auf, sieht die op_queue_
ist leer, und macht sich die first_idle_thread_
, wo es auf seine wakeup_event
wartet. Der Abfragthread beendet den Ruhezustand und gibt 0
zurück, wobei der Hauptthread auf wakeup_event
wartet.
Nach einer Wartezeit 10 ~ Sekunden, viel Zeit für die arm_timer()
bereit sein, unterbreche ich den Debugger:
Program received signal SIGINT, Interrupt.
0x00919402 in __kernel_vsyscall()
(gdb) bt
#0 0x00919402 in __kernel_vsyscall()
#1 0x0081bbc5 in [email protected]@GLIBC_2.3.2() from /lib/libpthread.so.0
#2 0x00763b3d in [email protected]@GLIBC_2.3.2() from /lib/libc.so.6
#3 0x08059dc2 in void boost::asio::detail::posix_event::wait >(boost::asio::detail::scoped_lock&)()
#4 0x0805a009 in boost::asio::detail::task_io_service::do_run_one(boost::asio::detail::scoped_lock&, boost::asio::detail::task_io_service_thread_info&, boost::system::error_code const&)()
#5 0x0805a11c in boost::asio::detail::task_io_service::run(boost::system::error_code&)()
#6 0x0805a1e2 in boost::asio::io_service::run()()
#7 0x0804db78 in main()
Die Side-by-Side-Timeline ist wie folgt:
poll thread | main thread
---------------------------------------+---------------------------------------
lock() |
do_poll_one() |
|-- pop task_operation_ from |
| queue_op_ |
|-- unlock() | lock()
|-- create task_cleanup | do_run_one()
|-- service reactor (non-block) | `-- queue_op_ is empty
|-- ~task_cleanup() | |-- set thread as idle
| |-- lock() | `-- unlock()
| `-- queue_op_.push( |
| task_operation_) |
`-- task_operation_ is |
queue_op_.front() |
`-- return 0 | // still waiting on wakeup_event
unlock() |
So gut ich sagen kann, gibt es keine Nebenwirkungen durch Patchen:
if (o == &task_operation_)
return 0;
zu:
if (o == &task_operation_)
{
if (!one_thread_)
wake_one_thread_and_unlock(lock);
return 0;
}
Egal, ich habe einen bug and fix vorgelegt. Überlegen Sie, ob Sie das Ticket für eine offizielle Antwort im Auge behalten.
Wie lautet der Rückgabecode von run_one()? –
Wenn 1 als normal zurückgegeben wird, muss der io_service nur zurückgesetzt werden, wenn 0 zurückgegeben wird. Es hört sich nicht so an, als ob du irgendetwas falsch machst. Kannst du einen sscce veröffentlichen? –
@DavidSchwartz Sind Sie sicher, dass der Anruf pthread_cond_timedwait von asio stammt? Ich habe Probleme, das im Code zu sehen. – janm