* Releases the own slot acquired by the constructor (TryAcquireSlot()) if not already done. * * Precisely, increments the number of free slots (semaphore) by one. * Also wakes up all waiting constructors (slow path) if necessary. */
| 98 | * Also wakes up all waiting constructors (slow path) if necessary. |
| 99 | */ |
| 100 | void CpuBoundWork::Done() |
| 101 | { |
| 102 | if (!m_Done) { |
| 103 | m_Done = true; |
| 104 | |
| 105 | auto& ie (IoEngine::Get()); |
| 106 | |
| 107 | // The constructor takes the slow path only if the semaphore is full, |
| 108 | // so we only have to wake up constructors if the semaphore was full. |
| 109 | // This works because after fetch_add(), TryAcquireSlot() (fast path) will succeed. |
| 110 | if (ie.m_CpuBoundSemaphore.fetch_add(1) == 0u) { |
| 111 | // So now there are only slow path subscribers from just before the fetch_add() to be woken up. |
| 112 | // Precisely, only subscribers from just before the fetch_add() which turned 0 to 1. |
| 113 | |
| 114 | decltype(ie.m_CpuBoundWaiting) subscribers; |
| 115 | |
| 116 | { |
| 117 | // Locking after fetch_add() is safe because a delayed wake-up is fine. |
| 118 | // Wake-up of constructors which subscribed after the fetch_add() is also not a problem. |
| 119 | // In worst case, they will just re-subscribe to the slow path. |
| 120 | // Lost wake-ups are mitigated by the constructor, see its implementation comments. |
| 121 | std::unique_lock lock (ie.m_CpuBoundWaitingMutex); |
| 122 | std::swap(subscribers, ie.m_CpuBoundWaiting); |
| 123 | } |
| 124 | |
| 125 | // Again, a delayed wake-up is fine, hence unlocked. |
| 126 | for (auto& subscriber : subscribers) { |
| 127 | boost::asio::post(subscriber.first, [cv = std::move(subscriber.second)] { cv->NotifyOne(); }); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | LazyInit<std::unique_ptr<IoEngine>> IoEngine::m_Instance ([]() { return std::unique_ptr<IoEngine>(new IoEngine()); }); |
| 134 |
no test coverage detected