blob: ba1eba3131eec3a76a2036c1d3988b7fcbc1bc93 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "wpi/SafeThread.h"
6
James Kuszmaulcf324122023-01-14 14:07:17 -08007#include <atomic>
8
Brian Silverman8fce7482020-01-05 13:18:21 -08009using namespace wpi;
10
James Kuszmaulcf324122023-01-14 14:07:17 -080011// thread start/stop notifications for bindings that need to set up
12// per-thread state
13
14static void* DefaultOnThreadStart() {
15 return nullptr;
16}
17static void DefaultOnThreadEnd(void*) {}
18
19using OnThreadStartFn = void* (*)();
20using OnThreadEndFn = void (*)(void*);
21static std::atomic<int> gSafeThreadRefcount;
22static std::atomic<OnThreadStartFn> gOnSafeThreadStart{DefaultOnThreadStart};
23static std::atomic<OnThreadEndFn> gOnSafeThreadEnd{DefaultOnThreadEnd};
24
25namespace wpi::impl {
26void SetSafeThreadNotifiers(OnThreadStartFn OnStart, OnThreadEndFn OnEnd) {
27 if (gSafeThreadRefcount != 0) {
28 throw std::runtime_error(
29 "cannot set notifier while safe threads are running");
30 }
31 // Note: there's a race here, but if you're not calling this function on
32 // the main thread before you start anything else, you're using this function
33 // incorrectly
34 gOnSafeThreadStart = OnStart ? OnStart : DefaultOnThreadStart;
35 gOnSafeThreadEnd = OnEnd ? OnEnd : DefaultOnThreadEnd;
36}
37} // namespace wpi::impl
38
39void SafeThread::Stop() {
40 m_active = false;
41 m_cond.notify_all();
42}
43
44void SafeThreadEvent::Stop() {
45 m_active = false;
46 m_stopEvent.Set();
47}
48
Brian Silverman8fce7482020-01-05 13:18:21 -080049detail::SafeThreadProxyBase::SafeThreadProxyBase(
James Kuszmaulcf324122023-01-14 14:07:17 -080050 std::shared_ptr<SafeThreadBase> thr)
Brian Silverman8fce7482020-01-05 13:18:21 -080051 : m_thread(std::move(thr)) {
Austin Schuh812d0d12021-11-04 20:16:48 -070052 if (!m_thread) {
53 return;
54 }
Brian Silverman8fce7482020-01-05 13:18:21 -080055 m_lock = std::unique_lock<wpi::mutex>(m_thread->m_mutex);
56 if (!m_thread->m_active) {
57 m_lock.unlock();
58 m_thread = nullptr;
59 return;
60 }
61}
62
63detail::SafeThreadOwnerBase::~SafeThreadOwnerBase() {
Austin Schuh812d0d12021-11-04 20:16:48 -070064 if (m_joinAtExit) {
Brian Silverman8fce7482020-01-05 13:18:21 -080065 Join();
Austin Schuh812d0d12021-11-04 20:16:48 -070066 } else {
Brian Silverman8fce7482020-01-05 13:18:21 -080067 Stop();
Austin Schuh812d0d12021-11-04 20:16:48 -070068 }
Brian Silverman8fce7482020-01-05 13:18:21 -080069}
70
James Kuszmaulcf324122023-01-14 14:07:17 -080071void detail::SafeThreadOwnerBase::Start(std::shared_ptr<SafeThreadBase> thr) {
Brian Silverman8fce7482020-01-05 13:18:21 -080072 std::scoped_lock lock(m_mutex);
Austin Schuh812d0d12021-11-04 20:16:48 -070073 if (auto thr = m_thread.lock()) {
74 return;
75 }
James Kuszmaulcf324122023-01-14 14:07:17 -080076 m_stdThread = std::thread([=] {
77 gSafeThreadRefcount++;
78 void* opaque = (gOnSafeThreadStart.load())();
79 thr->Main();
80 (gOnSafeThreadEnd.load())(opaque);
81 gSafeThreadRefcount--;
82 });
Brian Silverman8fce7482020-01-05 13:18:21 -080083 thr->m_threadId = m_stdThread.get_id();
84 m_thread = thr;
85}
86
87void detail::SafeThreadOwnerBase::Stop() {
88 std::scoped_lock lock(m_mutex);
89 if (auto thr = m_thread.lock()) {
James Kuszmaulcf324122023-01-14 14:07:17 -080090 thr->Stop();
Brian Silverman8fce7482020-01-05 13:18:21 -080091 m_thread.reset();
92 }
Austin Schuh812d0d12021-11-04 20:16:48 -070093 if (m_stdThread.joinable()) {
94 m_stdThread.detach();
95 }
Brian Silverman8fce7482020-01-05 13:18:21 -080096}
97
98void detail::SafeThreadOwnerBase::Join() {
99 std::unique_lock lock(m_mutex);
100 if (auto thr = m_thread.lock()) {
101 auto stdThread = std::move(m_stdThread);
102 m_thread.reset();
103 lock.unlock();
James Kuszmaulcf324122023-01-14 14:07:17 -0800104 thr->Stop();
Brian Silverman8fce7482020-01-05 13:18:21 -0800105 stdThread.join();
106 } else if (m_stdThread.joinable()) {
107 m_stdThread.detach();
108 }
109}
110
111void detail::swap(SafeThreadOwnerBase& lhs, SafeThreadOwnerBase& rhs) noexcept {
112 using std::swap;
Austin Schuh812d0d12021-11-04 20:16:48 -0700113 if (&lhs == &rhs) {
114 return;
115 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800116 std::scoped_lock lock(lhs.m_mutex, rhs.m_mutex);
117 std::swap(lhs.m_stdThread, rhs.m_stdThread);
118 std::swap(lhs.m_thread, rhs.m_thread);
119}
120
121detail::SafeThreadOwnerBase::operator bool() const {
122 std::scoped_lock lock(m_mutex);
123 return !m_thread.expired();
124}
125
126std::thread::native_handle_type
127detail::SafeThreadOwnerBase::GetNativeThreadHandle() {
128 std::scoped_lock lock(m_mutex);
129 return m_stdThread.native_handle();
130}
131
James Kuszmaulcf324122023-01-14 14:07:17 -0800132std::shared_ptr<SafeThreadBase>
133detail::SafeThreadOwnerBase::GetThreadSharedPtr() const {
Brian Silverman8fce7482020-01-05 13:18:21 -0800134 std::scoped_lock lock(m_mutex);
135 return m_thread.lock();
136}