Threading primitives, in prep for removing Win32 from kernel/ and others.
This commit is contained in:
parent
d89bad7380
commit
bd490d5833
|
@ -67,6 +67,123 @@ void Sleep(std::chrono::duration<Rep, Period> duration) {
|
||||||
Sleep(std::chrono::duration_cast<std::chrono::microseconds>(duration));
|
Sleep(std::chrono::duration_cast<std::chrono::microseconds>(duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WaitHandle {
|
||||||
|
public:
|
||||||
|
// bool Wait();
|
||||||
|
// static bool Wait();
|
||||||
|
// static bool SignalAndWait();
|
||||||
|
// static bool WaitMultiple();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WaitHandle() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Models a Win32-like event object.
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx
|
||||||
|
class Event : public WaitHandle {
|
||||||
|
public:
|
||||||
|
// Creates a manual-reset event object, which requires the use of the
|
||||||
|
// Reset() function to set the event state to nonsignaled.
|
||||||
|
// If initial_state is true the event will start in the signaled state.
|
||||||
|
static std::unique_ptr<Event> CreateManualResetEvent(bool initial_state);
|
||||||
|
|
||||||
|
// Creates an auto-reset event object, and system automatically resets the
|
||||||
|
// event state to nonsignaled after a single waiting thread has been released.
|
||||||
|
// If initial_state is true the event will start in the signaled state.
|
||||||
|
static std::unique_ptr<Event> CreateAutoResetEvent(bool initial_state);
|
||||||
|
|
||||||
|
// Sets the specified event object to the signaled state.
|
||||||
|
// If this is a manual reset event the event stays signaled until Reset() is
|
||||||
|
// called. If this is an auto reset event until exactly one wait is satisfied.
|
||||||
|
virtual void Set() = 0;
|
||||||
|
|
||||||
|
// Sets the specified event object to the nonsignaled state.
|
||||||
|
// Resetting an event that is already reset has no effect.
|
||||||
|
virtual void Reset() = 0;
|
||||||
|
|
||||||
|
// Sets the specified event object to the signaled state and then resets it to
|
||||||
|
// the nonsignaled state after releasing the appropriate number of waiting
|
||||||
|
// threads.
|
||||||
|
virtual void Pulse() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Models a Win32-like semaphore object.
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682438(v=vs.85).aspx
|
||||||
|
class Semaphore : public WaitHandle {
|
||||||
|
public:
|
||||||
|
// Creates a new semaphore object.
|
||||||
|
// The initial_count must be greater than or equal to zero and less than or
|
||||||
|
// equal to maximum_count. The state of a semaphore is signaled when its count
|
||||||
|
// is greater than zero and nonsignaled when it is zero. The count is
|
||||||
|
// decreased by one whenever a wait function releases a thread that was
|
||||||
|
// waiting for the semaphore. The count is increased by a specified amount by
|
||||||
|
// calling the Release() function.
|
||||||
|
std::unique_ptr<Semaphore> Create(int initial_count, int maximum_count);
|
||||||
|
|
||||||
|
// Increases the count of the specified semaphore object by a specified
|
||||||
|
// amount.
|
||||||
|
// release_count must be greater than zero.
|
||||||
|
// Returns false if adding release_count would set the semaphore over the
|
||||||
|
// initially specified maximum_count.
|
||||||
|
virtual bool Release(int release_count, int* out_previous_count) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Models a Win32-like mutant (mutex) object.
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682411(v=vs.85).aspx
|
||||||
|
class Mutant : public WaitHandle {
|
||||||
|
public:
|
||||||
|
// Creates a new mutant object, initially owned by the calling thread if
|
||||||
|
// specified.
|
||||||
|
std::unique_ptr<Mutant> Create(bool initial_owner);
|
||||||
|
|
||||||
|
// Releases ownership of the specified mutex object.
|
||||||
|
// Returns false if the calling thread does not own the mutant object.
|
||||||
|
virtual bool Release() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Timer : public WaitHandle {
|
||||||
|
public:
|
||||||
|
// Creates a timer whose state remains signaled until SetOnce() or
|
||||||
|
// SetRepeating() is called to establish a new due time.
|
||||||
|
std::unique_ptr<Timer> CreateManualResetTimer();
|
||||||
|
|
||||||
|
// Creates a timer whose state remains signaled until a thread completes a
|
||||||
|
// wait operation on the timer object.
|
||||||
|
std::unique_ptr<Timer> CreateSynchronizationTimer();
|
||||||
|
|
||||||
|
// Activates the specified waitable timer. When the due time arrives, the
|
||||||
|
// timer is signaled and the thread that set the timer calls the optional
|
||||||
|
// completion routine.
|
||||||
|
// Returns true on success.
|
||||||
|
virtual bool SetOnce(std::chrono::nanoseconds due_time,
|
||||||
|
std::function<void()> opt_callback = nullptr) = 0;
|
||||||
|
|
||||||
|
// Activates the specified waitable timer. When the due time arrives, the
|
||||||
|
// timer is signaled and the thread that set the timer calls the optional
|
||||||
|
// completion routine. A periodic timer automatically reactivates each time
|
||||||
|
// the period elapses, until the timer is canceled or reset.
|
||||||
|
// Returns true on success.
|
||||||
|
virtual bool SetRepeating(std::chrono::nanoseconds due_time,
|
||||||
|
std::chrono::milliseconds period,
|
||||||
|
std::function<void()> opt_callback = nullptr) = 0;
|
||||||
|
template <typename Rep, typename Period>
|
||||||
|
void SetRepeating(std::chrono::nanoseconds due_time,
|
||||||
|
std::chrono::duration<Rep, Period> period,
|
||||||
|
std::function<void()> opt_callback = nullptr) {
|
||||||
|
SetRepeating(due_time,
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(period),
|
||||||
|
std::move(opt_callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stops the timer before it can be set to the signaled state and cancels
|
||||||
|
// outstanding callbacks. Threads performing a wait operation on the timer
|
||||||
|
// remain waiting until they time out or the timer is reactivated and its
|
||||||
|
// state is set to signaled. If the timer is already in the signaled state, it
|
||||||
|
// remains in that state.
|
||||||
|
// Returns true on success.
|
||||||
|
virtual bool Cancel() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace threading
|
} // namespace threading
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
|
@ -67,5 +67,130 @@ void Sleep(std::chrono::microseconds duration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Win32Handle {
|
||||||
|
public:
|
||||||
|
Win32Handle(HANDLE handle) : handle_(handle) {}
|
||||||
|
virtual ~Win32Handle() {
|
||||||
|
CloseHandle(handle_);
|
||||||
|
handle_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HANDLE handle_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Win32Event : public Event, public Win32Handle {
|
||||||
|
public:
|
||||||
|
Win32Event(HANDLE handle) : Win32Handle(handle) {}
|
||||||
|
~Win32Event() = default;
|
||||||
|
void Set() override { SetEvent(handle_); }
|
||||||
|
void Reset() override { ResetEvent(handle_); }
|
||||||
|
void Pulse() override { PulseEvent(handle_); }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Event> Event::CreateManualResetEvent(bool initial_state) {
|
||||||
|
return std::make_unique<Win32Event>(
|
||||||
|
CreateEvent(nullptr, TRUE, initial_state ? TRUE : FALSE, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Event> Event::CreateAutoResetEvent(bool initial_state) {
|
||||||
|
return std::make_unique<Win32Event>(
|
||||||
|
CreateEvent(nullptr, FALSE, initial_state ? TRUE : FALSE, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Win32Semaphore : public Semaphore, public Win32Handle {
|
||||||
|
public:
|
||||||
|
Win32Semaphore(HANDLE handle) : Win32Handle(handle) {}
|
||||||
|
~Win32Semaphore() override = default;
|
||||||
|
bool Release(int release_count, int* out_previous_count) override {
|
||||||
|
return ReleaseSemaphore(handle_, release_count,
|
||||||
|
reinterpret_cast<LPLONG>(out_previous_count))
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Semaphore> Semaphore::Create(int initial_count,
|
||||||
|
int maximum_count) {
|
||||||
|
return std::make_unique<Win32Semaphore>(
|
||||||
|
CreateSemaphore(nullptr, initial_count, maximum_count, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Win32Mutant : public Mutant, public Win32Handle {
|
||||||
|
public:
|
||||||
|
Win32Mutant(HANDLE handle) : Win32Handle(handle) {}
|
||||||
|
~Win32Mutant() = default;
|
||||||
|
bool Release() override { return ReleaseMutex(handle_) ? true : false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Mutant> Mutant::Create(bool initial_owner) {
|
||||||
|
return std::make_unique<Win32Mutant>(
|
||||||
|
CreateMutex(nullptr, initial_owner ? TRUE : FALSE, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Win32Timer : public Timer, public Win32Handle {
|
||||||
|
public:
|
||||||
|
Win32Timer(HANDLE handle) : Win32Handle(handle) {}
|
||||||
|
~Win32Timer() = default;
|
||||||
|
bool SetOnce(std::chrono::nanoseconds due_time,
|
||||||
|
std::function<void()> opt_callback) override {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
callback_ = std::move(opt_callback);
|
||||||
|
LARGE_INTEGER due_time_li;
|
||||||
|
due_time_li.QuadPart = due_time.count() / 100;
|
||||||
|
auto completion_routine =
|
||||||
|
opt_callback ? reinterpret_cast<PTIMERAPCROUTINE>(CompletionRoutine)
|
||||||
|
: NULL;
|
||||||
|
return SetWaitableTimer(handle_, &due_time_li, 0, completion_routine, this,
|
||||||
|
FALSE)
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
bool SetRepeating(std::chrono::nanoseconds due_time,
|
||||||
|
std::chrono::milliseconds period,
|
||||||
|
std::function<void()> opt_callback) override {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
callback_ = std::move(opt_callback);
|
||||||
|
LARGE_INTEGER due_time_li;
|
||||||
|
due_time_li.QuadPart = due_time.count() / 100;
|
||||||
|
auto completion_routine =
|
||||||
|
opt_callback ? reinterpret_cast<PTIMERAPCROUTINE>(CompletionRoutine)
|
||||||
|
: NULL;
|
||||||
|
return SetWaitableTimer(handle_, &due_time_li, int32_t(period.count()),
|
||||||
|
completion_routine, this, FALSE)
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
bool Cancel() override {
|
||||||
|
// Reset the callback immediately so that any completions don't call it.
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
callback_ = nullptr;
|
||||||
|
return CancelWaitableTimer(handle_) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void CompletionRoutine(Win32Timer* timer, DWORD timer_low,
|
||||||
|
DWORD timer_high) {
|
||||||
|
// As the callback may reset the timer, store local.
|
||||||
|
std::function<void()> callback;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(timer->mutex_);
|
||||||
|
callback = timer->callback_;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::function<void()> callback_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Timer> Timer::CreateManualResetTimer() {
|
||||||
|
return std::make_unique<Win32Timer>(CreateWaitableTimer(NULL, TRUE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Timer> Timer::CreateSynchronizationTimer() {
|
||||||
|
return std::make_unique<Win32Timer>(CreateWaitableTimer(NULL, FALSE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace threading
|
} // namespace threading
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -46,6 +46,11 @@ void XTimer::Initialize(uint32_t timer_type) {
|
||||||
|
|
||||||
X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
|
X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
|
||||||
uint32_t routine, uint32_t routine_arg, bool resume) {
|
uint32_t routine, uint32_t routine_arg, bool resume) {
|
||||||
|
// Caller is checking for STATUS_TIMER_RESUME_IGNORED.
|
||||||
|
if (resume) {
|
||||||
|
return X_STATUS_TIMER_RESUME_IGNORED;
|
||||||
|
}
|
||||||
|
|
||||||
// Stash routine for callback.
|
// Stash routine for callback.
|
||||||
current_routine_ = routine;
|
current_routine_ = routine;
|
||||||
current_routine_arg_ = routine_arg;
|
current_routine_arg_ = routine_arg;
|
||||||
|
@ -60,12 +65,6 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
|
||||||
routine ? (PTIMERAPCROUTINE)CompletionRoutine : NULL,
|
routine ? (PTIMERAPCROUTINE)CompletionRoutine : NULL,
|
||||||
this, resume ? TRUE : FALSE);
|
this, resume ? TRUE : FALSE);
|
||||||
|
|
||||||
// Caller is checking for STATUS_TIMER_RESUME_IGNORED.
|
|
||||||
// This occurs if result == TRUE but error is set.
|
|
||||||
if (!result && GetLastError() == ERROR_NOT_SUPPORTED) {
|
|
||||||
return X_STATUS_TIMER_RESUME_IGNORED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL;
|
return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue