Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This is a bug. I have been able to duplicate it by adding a delay into the non-critical section of <code>task_io_service::do_poll_one</code>. Here is a snippet of the modified <code>task_io_service::do_poll_one()</code> in <a href="http://svn.boost.org/svn/boost/trunk/boost/asio/detail/impl/task_io_service.ipp"><code>booost/asio/detail/impl/task_io_service.ipp</code></a>. The only line added is the sleep.</p> <pre class="lang-cpp prettyprint-override"><code>std::size_t task_io_service::do_poll_one(mutex::scoped_lock&amp; lock, task_io_service::thread_info&amp; this_thread, const boost::system::error_code&amp; ec) { if (stopped_) return 0; operation* o = op_queue_.front(); if (o == &amp;task_operation_) { op_queue_.pop(); lock.unlock(); { task_cleanup c = { this, &amp;lock, &amp;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_-&gt;run(false, this_thread.private_op_queue); boost::this_thread::sleep_for(boost::chrono::seconds(3)); } o = op_queue_.front(); if (o == &amp;task_operation_) return 0; } ... </code></pre> <p>My test driver is fairly basic:</p> <ul> <li>An asynchronous work loop via a timer that will print "." every 3 seconds.</li> <li>Spawn off a single thread that will poll the <code>io_service</code>.</li> <li>Delay to allow the new thread time to poll <code>io_service</code>, and have main call <code>io_service::run()</code> while the poll thread sleeps in <code>task_io_service::do_poll_one()</code>.</li> </ul> <p>Test code:</p> <pre class="lang-cpp prettyprint-override"><code>#include &lt;iostream&gt; #include &lt;boost/asio/io_service.hpp&gt; #include &lt;boost/asio/steady_timer.hpp&gt; #include &lt;boost/chrono.hpp&gt; #include &lt;boost/thread.hpp&gt; boost::asio::io_service io_service; boost::asio::steady_timer timer(io_service); void arm_timer() { std::cout &lt;&lt; "."; std::cout.flush(); timer.expires_from_now(boost::chrono::seconds(3)); timer.async_wait(boost::bind(&amp;arm_timer)); } int main() { // Add asynchronous work loop. arm_timer(); // Spawn poll thread. boost::thread poll_thread( boost::bind(&amp;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(); } </code></pre> <p>And the debug:</p> <pre>[twsansbury@localhost 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]</pre> <p>At this point, the <code>arm_timer()</code> has printed "." once (when it was intially armed). The poll thread serviced the reactor in a non-blocking manner, and slept for 3 seconds while <code>op_queue_</code> was empty (<code>task_operation_</code> will be added back to the <code>op_queue_</code> when <code>task_cleanup c</code> exits scope). While the <code>op_queue_</code> was empty, the main thread calls <code>io_service::run()</code>, sees the <code>op_queue_</code> is empty, and makes itself the <code>first_idle_thread_</code>, where it waits on its <code>wakeup_event</code>. The poll thread finishes sleeping, and returns <code>0</code>, leaving the main thread waiting on <code>wakeup_event</code>.</p> <p>After waiting 10~ seconds, plenty of time for the <code>arm_timer()</code> to be ready, I interrupt the debugger:</p> <pre>Program received signal SIGINT, Interrupt. 0x00919402 in __kernel_vsyscall () (gdb) bt #0 0x00919402 in __kernel_vsyscall () #1 0x0081bbc5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/libpthread.so.0 #2 0x00763b3d in pthread_cond_wait@@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 ()</pre> <hr> <p>The side-by-side timeline is as follows:</p> <pre> 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() |</pre> <hr> <p>As best as I could tell, there are no side effects by patching:</p> <pre class="lang-cpp prettyprint-override"><code>if (o == &amp;task_operation_) return 0; </code></pre> <p>to:</p> <pre class="lang-cpp prettyprint-override"><code>if (o == &amp;task_operation_) { if (!one_thread_) wake_one_thread_and_unlock(lock); return 0; } </code></pre> <p>Regardless, I have submitted a <a href="https://svn.boost.org/trac/boost/ticket/8354">bug and fix</a>. Consider keeping an eye on the ticket for an official response.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload