100.00% Lines (70/70) 100.00% Functions (7/7)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #include <boost/capy/ex/execution_context.hpp> 10   #include <boost/capy/ex/execution_context.hpp>
11   #include <boost/capy/ex/recycling_memory_resource.hpp> 11   #include <boost/capy/ex/recycling_memory_resource.hpp>
12   #include <boost/capy/detail/except.hpp> 12   #include <boost/capy/detail/except.hpp>
13   13  
14   namespace boost { 14   namespace boost {
15   namespace capy { 15   namespace capy {
16   16  
HITCBC 17   1562 execution_context:: 17   3629 execution_context::
HITCBC 18   1562 execution_context() 18   3629 execution_context()
HITCBC 19   1562 : frame_alloc_(get_recycling_memory_resource()) 19   3629 : frame_alloc_(get_recycling_memory_resource())
20   { 20   {
HITCBC 21   1562 } 21   3629 }
22   22  
HITCBC 23   1562 execution_context:: 23   3629 execution_context::
24   ~execution_context() 24   ~execution_context()
25   { 25   {
HITCBC 26   1562 shutdown(); 26   3629 shutdown();
HITCBC 27   1562 destroy(); 27   3629 destroy();
HITCBC 28   1562 } 28   3629 }
29   29  
30   void 30   void
HITCBC 31   1962 execution_context:: 31   7198 execution_context::
32   shutdown() noexcept 32   shutdown() noexcept
33   { 33   {
HITCBC 34   1962 if(shutdown_) 34   7198 if(shutdown_)
HITCBC 35   400 return; 35   3569 return;
HITCBC 36   1562 shutdown_ = true; 36   3629 shutdown_ = true;
37   37  
HITCBC 38   1562 service* p = head_; 38   3629 service* p = head_;
HITCBC 39   1613 while(p) 39   4709 while(p)
40   { 40   {
HITCBC 41   51 p->shutdown(); 41   1080 p->shutdown();
HITCBC 42   51 p = p->next_; 42   1080 p = p->next_;
43   } 43   }
44   } 44   }
45   45  
46   void 46   void
HITCBC 47   1962 execution_context:: 47   6196 execution_context::
48   destroy() noexcept 48   destroy() noexcept
49   { 49   {
ECB 50 - 1962 service* p = head_; 50 + // One at a time, so a destructor can still find the services not
ECB 51 - 1962 head_ = nullptr; 51 + // yet destroyed, and one it creates is destroyed in turn.
HITCBC 52 - 2013 while(p) 52 + 7282 while(service* p = head_)
53   { 53   {
HITCBC 54 - 51 service* next = p->next_; 54 + 1086 head_ = p->next_;
DCB 55 - 51 p = next;  
HITCBC 56   51 delete p; 55   1086 delete p;
HITGIC 57   } 56   1086 }
HITCBC 58   1962 } 57   6196 }
59   58  
60   execution_context::service* 59   execution_context::service*
HITCBC 61   11562 execution_context:: 60   13637 execution_context::
62   find_impl(detail::type_index ti) const noexcept 61   find_impl(detail::type_index ti) const noexcept
63   { 62   {
HITCBC 64   11562 auto p = head_; 63   13637 auto p = head_;
HITCBC 65   11571 while(p) 64   13651 while(p)
66   { 65   {
HITCBC 67   11455 if(p->t0_ == ti || p->t1_ == ti) 66   11465 if(p->t0_ == ti || p->t1_ == ti)
HITCBC 68   11446 break; 67   11451 break;
HITCBC 69   9 p = p->next_; 68   14 p = p->next_;
70   } 69   }
HITCBC 71   11562 return p; 70   13637 return p;
72   } 71   }
73   72  
74   execution_context::service& 73   execution_context::service&
HITCBC 75   11465 execution_context:: 74   12490 execution_context::
76   use_service_impl(factory& f) 75   use_service_impl(factory& f)
77   { 76   {
HITCBC 78   11465 std::unique_lock<std::mutex> lock(mutex_); 77   12490 std::unique_lock<std::mutex> lock(mutex_);
79   78  
HITCBC 80   11465 if(auto* p = find_impl(f.t0)) 79   12490 if(auto* p = find_impl(f.t0))
HITCBC 81   11421 return *p; 80   11425 return *p;
82   81  
HITCBC 83   44 lock.unlock(); 82   1065 lock.unlock();
84   83  
85   // Create the service outside lock, enabling nested calls 84   // Create the service outside lock, enabling nested calls
HITCBC 86   44 service* sp = f.create(*this); 85   1065 service* sp = f.create(*this);
HITCBC 87   44 sp->t0_ = f.t0; 86   1065 sp->t0_ = f.t0;
HITCBC 88   44 sp->t1_ = f.t1; 87   1065 sp->t1_ = f.t1;
89   88  
HITCBC 90   44 lock.lock(); 89   1065 lock.lock();
91   90  
HITCBC 92   44 if(auto* p = find_impl(f.t0)) 91   1065 if(auto* p = find_impl(f.t0))
93   { 92   {
94   delete sp; // LCOV_EXCL_LINE concurrent same-type registration race 93   delete sp; // LCOV_EXCL_LINE concurrent same-type registration race
95   return *p; // LCOV_EXCL_LINE concurrent same-type registration race 94   return *p; // LCOV_EXCL_LINE concurrent same-type registration race
96   } 95   }
97   96  
HITCBC 98   43 sp->next_ = head_; 97   1064 sp->next_ = head_;
HITCBC 99   43 head_ = sp; 98   1064 head_ = sp;
100   99  
HITGNC   100 + 1064 if(shutdown_)
  101 + {
  102 + // Created after the context shut down: it starts shut down,
  103 + // like the services that already were.
HITGNC   104 + 3 lock.unlock();
HITGNC   105 + 3 sp->shutdown();
  106 + }
HITCBC 101   43 return *sp; 107   1064 return *sp;
HITCBC 102   11465 } 108   12490 }
103   109  
104   execution_context::service& 110   execution_context::service&
HITCBC 105   12 execution_context:: 111   26 execution_context::
106   make_service_impl(factory& f) 112   make_service_impl(factory& f)
107   { 113   {
108   { 114   {
HITCBC 109   12 std::lock_guard<std::mutex> lock(mutex_); 115   26 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 110   12 if(find_impl(f.t0)) 116   26 if(find_impl(f.t0))
HITCBC 111   2 detail::throw_invalid_argument(); 117   2 detail::throw_invalid_argument();
HITCBC 112   10 if(f.t0 != f.t1 && find_impl(f.t1)) 118   24 if(f.t0 != f.t1 && find_impl(f.t1))
HITCBC 113   1 detail::throw_invalid_argument(); 119   1 detail::throw_invalid_argument();
HITCBC 114   12 } 120   26 }
115   121  
116   // Unlocked to allow nested service creation from constructor 122   // Unlocked to allow nested service creation from constructor
HITCBC 117   9 service* p = f.create(*this); 123   23 service* p = f.create(*this);
118   124  
HITCBC 119 - 9 std::lock_guard<std::mutex> lock(mutex_); 125 + 23 std::unique_lock<std::mutex> lock(mutex_);
HITCBC 120   9 if(find_impl(f.t0)) 126   23 if(find_impl(f.t0))
121   { 127   {
122   delete p; // LCOV_EXCL_LINE concurrent same-type registration race 128   delete p; // LCOV_EXCL_LINE concurrent same-type registration race
123   detail::throw_invalid_argument(); // LCOV_EXCL_LINE concurrent same-type registration race 129   detail::throw_invalid_argument(); // LCOV_EXCL_LINE concurrent same-type registration race
124   } 130   }
125   131  
HITCBC 126   9 p->t0_ = f.t0; 132   23 p->t0_ = f.t0;
HITCBC 127   9 if(f.t0 != f.t1) 133   23 if(f.t0 != f.t1)
128   { 134   {
HITCBC 129   3 if(find_impl(f.t1)) 135   3 if(find_impl(f.t1))
130   { 136   {
HITCBC 131   1 delete p; 137   1 delete p;
HITCBC 132   1 detail::throw_invalid_argument(); 138   1 detail::throw_invalid_argument();
133   } 139   }
HITCBC 134   2 p->t1_ = f.t1; 140   2 p->t1_ = f.t1;
135   } 141   }
136   else 142   else
137   { 143   {
HITCBC 138   6 p->t1_ = f.t0; 144   20 p->t1_ = f.t0;
139   } 145   }
140   146  
HITCBC 141   8 p->next_ = head_; 147   22 p->next_ = head_;
HITCBC 142   8 head_ = p; 148   22 head_ = p;
143   149  
HITGNC   150 + 22 if(shutdown_)
  151 + {
  152 + // Created after the context shut down: it starts shut down,
  153 + // like the services that already were.
HITGNC   154 + 3 lock.unlock();
HITGNC   155 + 3 p->shutdown();
  156 + }
HITCBC 144   8 return *p; 157   22 return *p;
HITCBC 145   9 } 158   23 }
146   159  
147   } // namespace capy 160   } // namespace capy
148   } // namespace boost 161   } // namespace boost