LCOV - code coverage report
Current view: top level - /jenkins/workspace/boost-root/libs/capy/src/test - run_blocking.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 97.7 % 87 85 2
Test Date: 2026-10-17 14:10:44 Functions: 94.4 % 18 17 1

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/capy
       8                 : //
       9                 : 
      10                 : #include <boost/capy/test/run_blocking.hpp>
      11                 : 
      12                 : #include <boost/capy/ex/abandon.hpp>
      13                 : #include <boost/capy/ex/frame_allocator.hpp>
      14                 : #include <condition_variable>
      15                 : #include <mutex>
      16                 : #include <queue>
      17                 : #include <system_error>
      18                 : #include <utility>
      19                 : 
      20                 : namespace boost {
      21                 : namespace capy {
      22                 : namespace test {
      23                 : 
      24                 : namespace {
      25                 : 
      26                 : // The context whose run() is on this thread's stack. Only there is it
      27                 : // safe to resume dispatched work inline.
      28                 : thread_local blocking_context const* running_ = nullptr;
      29                 : 
      30                 : } // namespace
      31                 : 
      32                 : struct blocking_context::impl
      33                 : {
      34                 :     std::mutex mtx;
      35                 :     std::condition_variable cv;
      36                 :     std::queue<continuation> queue;
      37                 :     std::exception_ptr ep;
      38                 :     bool done = false;
      39                 :     bool abandoned = false;
      40                 : };
      41                 : 
      42 HIT        1125 : blocking_context::blocking_context()
      43            1125 :     : impl_(new impl)
      44                 : {
      45            1125 : }
      46                 : 
      47            1125 : blocking_context::~blocking_context()
      48                 : {
      49                 :     // Nothing will run these now; abandon them as their holder. The
      50                 :     // services may post while they shut down or are destroyed, so the
      51                 :     // queue outlives both. Each is taken under the lock, as other
      52                 :     // threads may still post, and abandoned outside it.
      53            2254 :     auto drain = [this]
      54                 :     {
      55            2254 :         bool any = false;
      56                 :         for(;;)
      57                 :         {
      58            4261 :             continuation c;
      59                 :             {
      60            4261 :                 std::lock_guard<std::mutex> lock(impl_->mtx);
      61            4261 :                 if(impl_->queue.empty())
      62            4508 :                     return any;
      63            2007 :                 c = impl_->queue.front();
      64            2007 :                 impl_->queue.pop();
      65            4261 :             }
      66            2007 :             any = true;
      67            2007 :             capy::abandon(c);
      68            2007 :         }
      69            1125 :     };
      70            1125 :     shutdown();
      71            1125 :     drain();
      72                 :     // A frame the drain unwinds can create a service, whose destructor
      73                 :     // can post again: repeat until nothing is left of either.
      74                 :     do
      75                 :     {
      76            1129 :         destroy();
      77                 :     }
      78            1129 :     while(drain());
      79            1125 :     delete impl_;
      80            1125 : }
      81                 : 
      82                 : blocking_executor
      83            1126 : blocking_context::get_executor() noexcept
      84                 : {
      85            1126 :     return blocking_executor{this};
      86                 : }
      87                 : 
      88                 : void
      89             778 : blocking_context::signal_done() noexcept
      90                 : {
      91             778 :     std::lock_guard<std::mutex> lock(impl_->mtx);
      92             778 :     impl_->done = true;
      93             778 :     impl_->cv.notify_one();
      94             778 : }
      95                 : 
      96                 : void
      97             336 : blocking_context::signal_done(
      98                 :     std::exception_ptr ep) noexcept
      99                 : {
     100             336 :     std::lock_guard<std::mutex> lock(impl_->mtx);
     101             336 :     impl_->ep = ep;
     102             336 :     impl_->done = true;
     103             336 :     impl_->cv.notify_one();
     104             336 : }
     105                 : 
     106                 : void
     107               1 : blocking_context::signal_abandoned() noexcept
     108                 : {
     109               1 :     std::lock_guard<std::mutex> lock(impl_->mtx);
     110               1 :     impl_->abandoned = true;
     111               1 :     impl_->done = true;
     112               1 :     impl_->cv.notify_one();
     113               1 : }
     114                 : 
     115                 : void
     116            1115 : blocking_context::run()
     117                 : {
     118                 :     struct scoped_running
     119                 :     {
     120                 :         blocking_context const* prev_;
     121                 : 
     122            1115 :         explicit scoped_running(blocking_context const* ctx) noexcept
     123            1115 :             : prev_(std::exchange(running_, ctx))
     124                 :         {
     125            1115 :         }
     126                 : 
     127            1115 :         ~scoped_running()
     128                 :         {
     129            1115 :             running_ = prev_;
     130            1115 :         }
     131            1115 :     } guard(this);
     132                 : 
     133                 :     for(;;)
     134                 :     {
     135            2352 :         continuation c;
     136                 :         {
     137            2352 :             std::unique_lock<std::mutex> lock(impl_->mtx);
     138            2352 :             impl_->cv.wait(lock, [&] {
     139            2353 :                 return impl_->done || !impl_->queue.empty();
     140                 :             });
     141            2352 :             if(impl_->done && impl_->queue.empty())
     142            1115 :                 break;
     143            1237 :             c = impl_->queue.front();
     144            1237 :             impl_->queue.pop();
     145            2352 :         }
     146            1237 :         safe_resume(c.h);
     147            1237 :     }
     148            1115 :     if(impl_->ep)
     149             672 :         std::rethrow_exception(impl_->ep);
     150             779 :     if(impl_->abandoned)
     151               1 :         throw std::system_error(error::canceled, "task abandoned");
     152            1115 : }
     153                 : 
     154                 : void
     155            3244 : blocking_context::enqueue(
     156                 :     continuation const& c)
     157                 : {
     158            3244 :     std::lock_guard<std::mutex> lock(impl_->mtx);
     159            3244 :     impl_->queue.push(c);
     160            3244 :     impl_->cv.notify_one();
     161            3244 : }
     162                 : 
     163                 : //----------------------------------------------------------
     164                 : 
     165                 : bool
     166 MIS           0 : blocking_executor::operator==(
     167                 :     blocking_executor const& other) const noexcept
     168                 : {
     169               0 :     return ctx_ == other.ctx_;
     170                 : }
     171                 : 
     172                 : blocking_context&
     173 HIT        1117 : blocking_executor::context() const noexcept
     174                 : {
     175            1117 :     return *ctx_;
     176                 : }
     177                 : 
     178                 : void
     179            1120 : blocking_executor::on_work_started() const noexcept
     180                 : {
     181            1120 : }
     182                 : 
     183                 : void
     184            1120 : blocking_executor::on_work_finished() const noexcept
     185                 : {
     186            1120 : }
     187                 : 
     188                 : std::coroutine_handle<>
     189            1163 : blocking_executor::dispatch(
     190                 :     continuation& c) const
     191                 : {
     192            1163 :     if(running_ == ctx_)
     193              47 :         return c.h;
     194            1116 :     ctx_->enqueue(c);
     195            1116 :     return std::noop_coroutine();
     196                 : }
     197                 : 
     198                 : void
     199            2128 : blocking_executor::post(
     200                 :     continuation& c) const
     201                 : {
     202            2128 :     ctx_->enqueue(c);
     203            2128 : }
     204                 : 
     205                 : } // namespace test
     206                 : } // namespace capy
     207                 : } // namespace boost
        

Generated by: LCOV version 2.3