168 lines · cpp
1//===-- PThreadEvent.cpp ----------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Created by Greg Clayton on 6/16/07.10//11//===----------------------------------------------------------------------===//12 13#include "PThreadEvent.h"14#include "DNBLog.h"15#include <cerrno>16 17PThreadEvent::PThreadEvent(uint32_t bits, uint32_t validBits)18 : m_mutex(), m_set_condition(), m_bits(bits), m_validBits(validBits),19 m_reset_ack_mask(0) {20 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, 0x%8.8x)",21 // this, __FUNCTION__, bits, validBits);22}23 24PThreadEvent::~PThreadEvent() {25 // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);26}27 28uint32_t PThreadEvent::NewEventBit() {29 // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);30 std::lock_guard<std::mutex> guard(m_mutex);31 uint32_t mask = 1;32 while (mask & m_validBits)33 mask <<= 1;34 m_validBits |= mask;35 return mask;36}37 38void PThreadEvent::FreeEventBits(const uint32_t mask) {39 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,40 // __FUNCTION__, mask);41 if (mask) {42 std::lock_guard<std::mutex> guard(m_mutex);43 m_bits &= ~mask;44 m_validBits &= ~mask;45 }46}47 48uint32_t PThreadEvent::GetEventBits() const {49 // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);50 std::lock_guard<std::mutex> guard(m_mutex);51 uint32_t bits = m_bits;52 return bits;53}54 55// Replace the event bits with a new bitmask value56void PThreadEvent::ReplaceEventBits(const uint32_t bits) {57 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,58 // __FUNCTION__, bits);59 std::lock_guard<std::mutex> guard(m_mutex);60 // Make sure we have some bits and that they aren't already set...61 if (m_bits != bits) {62 // Figure out which bits are changing63 uint32_t changed_bits = m_bits ^ bits;64 // Set the new bit values65 m_bits = bits;66 // If any new bits are set, then broadcast67 if (changed_bits & m_bits)68 m_set_condition.notify_all();69 }70}71 72// Set one or more event bits and broadcast if any new event bits get set73// that weren't already set.74 75void PThreadEvent::SetEvents(const uint32_t mask) {76 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,77 // __FUNCTION__, mask);78 // Make sure we have some bits to set79 if (mask) {80 std::lock_guard<std::mutex> guard(m_mutex);81 // Save the old event bit state so we can tell if things change82 uint32_t old = m_bits;83 // Set the all event bits that are set in 'mask'84 m_bits |= mask;85 // Broadcast only if any extra bits got set.86 if (old != m_bits)87 m_set_condition.notify_all();88 }89}90 91// Reset one or more event bits92void PThreadEvent::ResetEvents(const uint32_t mask) {93 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,94 // __FUNCTION__, mask);95 if (mask) {96 std::lock_guard<std::mutex> guard(m_mutex);97 // Clear the all event bits that are set in 'mask'98 m_bits &= ~mask;99 }100}101 102static std::chrono::nanoseconds ToDuration(timespec ts) {103 auto duration =104 std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec};105 return std::chrono::duration_cast<std::chrono::nanoseconds>(duration);106}107 108static std::chrono::time_point<std::chrono::system_clock,109 std::chrono::nanoseconds>110ToTimePoint(timespec ts) {111 return std::chrono::time_point<std::chrono::system_clock,112 std::chrono::nanoseconds>{113 std::chrono::duration_cast<std::chrono::system_clock::duration>(114 ToDuration(ts))};115}116 117// Wait until 'timeout_abstime' for any events that are set in118// 'mask'. If 'timeout_abstime' is NULL, then wait forever.119uint32_t120PThreadEvent::WaitForEventsImpl(const uint32_t mask,121 const struct timespec *timeout_abstime,122 std::function<bool()> predicate) const {123 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,124 // __FUNCTION__, mask, timeout_abstime);125 std::unique_lock<std::mutex> lock(m_mutex);126 127 if (timeout_abstime) {128 // Wait for condition to get broadcast, or for a timeout. If we get129 // a timeout we will drop out of the loop on the next iteration and we130 // will recompute the mask in case of a race between the condition and the131 // timeout.132 m_set_condition.wait_until(lock, ToTimePoint(*timeout_abstime), predicate);133 } else {134 // Wait for condition to get broadcast.135 m_set_condition.wait(lock, predicate);136 }137 138 // Either the predicate passed, we hit the specified timeout (ETIMEDOUT) or we139 // encountered an unrecoverable error (EINVAL, EPERM). Regardless of how we140 // got here, recompute and return the mask indicating which bits (if any) are141 // set.142 return GetBitsMasked(mask);143}144 145uint32_t146PThreadEvent::WaitForSetEvents(const uint32_t mask,147 const struct timespec *timeout_abstime) const {148 auto predicate = [&]() -> uint32_t { return GetBitsMasked(mask) != 0; };149 return WaitForEventsImpl(mask, timeout_abstime, predicate);150}151 152uint32_t PThreadEvent::WaitForEventsToReset(153 const uint32_t mask, const struct timespec *timeout_abstime) const {154 auto predicate = [&]() -> uint32_t { return GetBitsMasked(mask) == 0; };155 return WaitForEventsImpl(mask, timeout_abstime, predicate);156}157 158uint32_t159PThreadEvent::WaitForResetAck(const uint32_t mask,160 const struct timespec *timeout_abstime) const {161 if (mask & m_reset_ack_mask) {162 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,163 // __FUNCTION__, mask, timeout_abstime);164 return WaitForEventsToReset(mask & m_reset_ack_mask, timeout_abstime);165 }166 return 0;167}168