1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
19 #ifndef REALM_UTIL_THREAD_HPP
20 #define REALM_UTIL_THREAD_HPP
26 #include <condition_variable> // for windows non-interprocess condvars we use std::condition_variable
28 #include <process.h> // _getpid()
33 // Use below line to enable a thread bug detection tool. Note: Will make program execution slower.
34 // #include <../test/pthread_test.hpp>
40 #include <realm/util/features.h>
41 #include <realm/util/assert.hpp>
42 #include <realm/util/terminate.hpp>
51 /// A separate thread of execution.
53 /// This class is a C++03 compatible reproduction of a subset of std::thread
54 /// from C++11 (when discounting Thread::start(), Thread::set_name(), and
55 /// Thread::get_name()).
62 explicit Thread(F func);
64 // Disable copying. It is an error to copy this Thread class.
65 Thread(const Thread&) = delete;
66 Thread& operator=(const Thread&) = delete;
70 /// This method is an extension of the API provided by
71 /// std::thread. This method exists because proper move semantics
72 /// is unavailable in C++03. If move semantics had been available,
73 /// calling `start(func)` would have been equivalent to `*this =
74 /// Thread(func)`. Please see std::thread::operator=() for
79 bool joinable() noexcept;
83 // If supported by the platform, set the name of the calling thread (mainly
84 // for debugging purposes). The name will be silently clamped to whatever
85 // limit the platform places on these names. Linux places a limit of 15
86 // characters for these names.
87 static void set_name(const std::string&);
89 // If supported by the platform, this function assigns the name of the
90 // calling thread to \a name, and returns true, otherwise it does nothing
92 static bool get_name(std::string& name);
97 std::thread m_std_thread;
102 typedef void* (*entry_func_type)(void*);
104 void start(entry_func_type, void* arg);
107 static void* entry_point(void*) noexcept;
109 REALM_NORETURN static void create_failed(int);
110 REALM_NORETURN static void join_failed(int);
114 /// Low-level mutual exclusion device.
120 struct process_shared_tag {
122 /// Initialize this mutex for use across multiple processes. When
123 /// constructed this way, the instance may be placed in memory
124 /// shared by multiple processes, as well as in a memory mapped
125 /// file. Such a mutex remains valid even after the constructing
126 /// process terminates. Deleting the instance (freeing the memory
127 /// or deleting the file) without first calling the destructor is
128 /// legal and will not cause any system resources to be leaked.
129 Mutex(process_shared_tag);
132 Mutex(const Mutex&) = delete;
133 Mutex& operator=(const Mutex&) = delete;
135 friend class LockGuard;
136 friend class UniqueLock;
137 friend class InterprocessCondVar;
139 void lock() noexcept;
140 bool try_lock() noexcept;
141 void unlock() noexcept;
145 // Used for non-process-shared mutex. We only know at runtime whether or not to use it, depending on if we call
146 // Mutex::Mutex(process_shared_tag)
147 CRITICAL_SECTION m_critical_section;
149 pthread_mutex_t m_impl = PTHREAD_MUTEX_INITIALIZER;
158 void init_as_regular();
159 void init_as_process_shared(bool robust_if_available);
161 REALM_NORETURN static void init_failed(int);
162 REALM_NORETURN static void attr_init_failed(int);
163 REALM_NORETURN static void destroy_failed(int) noexcept;
164 REALM_NORETURN static void lock_failed(int) noexcept;
167 friend class CondVar;
168 friend class RobustMutex;
172 /// A simple mutex ownership wrapper.
175 LockGuard(Mutex&) noexcept;
176 ~LockGuard() noexcept;
180 friend class CondVar;
185 struct defer_lock_tag {
188 /// A general-purpose mutex ownership wrapper supporting deferred
189 /// locking as well as repeated unlocking and relocking.
192 UniqueLock(Mutex&) noexcept;
193 UniqueLock(Mutex&, defer_lock_tag) noexcept;
194 ~UniqueLock() noexcept;
196 void lock() noexcept;
197 void unlock() noexcept;
198 bool holds_lock() noexcept;
206 /// A robust version of a process-shared mutex.
208 /// A robust mutex is one that detects whether a thread (or process)
209 /// has died while holding a lock on the mutex.
211 /// When the present platform does not offer support for robust
212 /// mutexes, this mutex class behaves as a regular process-shared
213 /// mutex, which means that if a thread dies while holding a lock, any
214 /// future attempt at locking will block indefinitely.
215 class RobustMutex : private Mutex {
218 ~RobustMutex() noexcept;
220 static bool is_robust_on_this_platform() noexcept;
222 class NotRecoverable;
224 /// \param recover_func If the present platform does not support
225 /// robust mutexes, this function is never called. Otherwise it is
226 /// called if, and only if a thread has died while holding a
227 /// lock. The purpose of the function is to reestablish a
228 /// consistent shared state. If it fails to do this by throwing an
229 /// exception, the mutex enters the 'unrecoverable' state where
230 /// any future attempt at locking it will fail and cause
231 /// NotRecoverable to be thrown. This function is advised to throw
232 /// NotRecoverable when it fails, but it may throw any exception.
234 /// \throw NotRecoverable If thrown by the specified recover
235 /// function, or if the mutex has entered the 'unrecoverable'
236 /// state due to a different thread throwing from its recover
238 template <class Func>
239 void lock(Func recover_func);
241 template <class Func>
242 bool try_lock(Func recover_func);
244 void unlock() noexcept;
246 /// Low-level locking of robust mutex.
248 /// If the present platform does not support robust mutexes, this
249 /// function always returns true. Otherwise it returns false if,
250 /// and only if a thread has died while holding a lock.
252 /// \note Most application should never call this function
253 /// directly. It is called automatically when using the ordinary
256 /// \throw NotRecoverable If this mutex has entered the "not
257 /// recoverable" state. It enters this state if
258 /// mark_as_consistent() is not called between a call to
259 /// robust_lock() that returns false and the corresponding call to
261 bool low_level_lock();
263 /// Low-level try-lock of robust mutex
265 /// If the present platform does not support robust mutexes, this
266 /// function always returns 0 or 1. Otherwise it returns -1 if,
267 /// and only if a thread has died while holding a lock.
269 /// Returns 1 if the lock is succesfully obtained.
270 /// Returns 0 if the lock is held by somebody else (not obtained)
271 /// Returns -1 if a thread has died while holding a lock.
273 /// \note Most application should never call this function
274 /// directly. It is called automatically when using the ordinary
277 /// \throw NotRecoverable If this mutex has entered the "not
278 /// recoverable" state. It enters this state if
279 /// mark_as_consistent() is not called between a call to
280 /// robust_lock() that returns false and the corresponding call to
282 int try_low_level_lock();
284 /// Pull this mutex out of the 'inconsistent' state.
286 /// Must be called only after low_level_lock() has returned false.
288 /// \note Most application should never call this function
289 /// directly. It is called automatically when using the ordinary
291 void mark_as_consistent() noexcept;
293 /// Attempt to check if this mutex is a valid object.
295 /// This attempts to trylock() the mutex, and if that fails returns false if
296 /// the return value indicates that the low-level mutex is invalid (which is
297 /// distinct from 'inconsistent'). Although pthread_mutex_trylock() may
298 /// return EINVAL if the argument is not an initialized mutex object, merely
299 /// attempting to check if an arbitrary blob of memory is a mutex object may
300 /// involve undefined behavior, so it is only safe to assume that this
301 /// function will run correctly when it is known that the mutex object is
303 bool is_valid() noexcept;
305 friend class CondVar;
308 class RobustMutex::NotRecoverable : public std::exception {
310 const char* what() const noexcept override
312 return "Failed to recover consistent state of shared memory";
317 /// A simple robust mutex ownership wrapper.
318 class RobustLockGuard {
320 /// \param m the mutex to guard
321 /// \param func See RobustMutex::lock().
322 template <class TFunc>
323 RobustLockGuard(RobustMutex& m, TFunc func);
324 ~RobustLockGuard() noexcept;
327 RobustMutex& m_mutex;
328 friend class CondVar;
332 /// Condition variable for use in synchronization monitors.
338 struct process_shared_tag {
341 /// Initialize this condition variable for use across multiple
342 /// processes. When constructed this way, the instance may be
343 /// placed in memory shared by multimple processes, as well as in
344 /// a memory mapped file. Such a condition variable remains valid
345 /// even after the constructing process terminates. Deleting the
346 /// instance (freeing the memory or deleting the file) without
347 /// first calling the destructor is legal and will not cause any
348 /// system resources to be leaked.
349 CondVar(process_shared_tag);
351 /// Wait for another thread to call notify() or notify_all().
352 void wait(LockGuard& l) noexcept;
353 template <class Func>
354 void wait(RobustMutex& m, Func recover_func, const struct timespec* tp = nullptr);
356 /// If any threads are wating for this condition, wake up at least
358 void notify() noexcept;
360 /// Wake up every thread that is currently wating on this
362 void notify_all() noexcept;
366 CONDITION_VARIABLE m_condvar = CONDITION_VARIABLE_INIT;
368 pthread_cond_t m_impl;
371 REALM_NORETURN static void init_failed(int);
372 REALM_NORETURN static void attr_init_failed(int);
373 REALM_NORETURN static void destroy_failed(int) noexcept;
374 void handle_wait_error(int error);
380 inline Thread::Thread()
386 inline Thread::Thread(F func)
389 std::unique_ptr<F> func2(new F(func)); // Throws
390 start(&Thread::entry_point<F>, func2.get()); // Throws
394 inline Thread::Thread(Thread&& thread)
398 m_joinable = thread.m_joinable;
399 thread.m_joinable = false;
404 inline void Thread::start(F func)
408 std::unique_ptr<F> func2(new F(func)); // Throws
409 start(&Thread::entry_point<F>, func2.get()); // Throws
414 inline Thread::~Thread() noexcept
417 REALM_TERMINATE("Destruction of joinable thread");
420 inline bool Thread::joinable() noexcept
425 inline void Thread::start(entry_func_type entry_func, void* arg)
428 m_std_thread = std::thread(entry_func, arg);
430 const pthread_attr_t* attr = nullptr; // Use default thread attributes
431 int r = pthread_create(&m_id, attr, entry_func, arg);
432 if (REALM_UNLIKELY(r != 0))
433 create_failed(r); // Throws
438 inline void* Thread::entry_point(void* cookie) noexcept
440 std::unique_ptr<F> func(static_cast<F*>(cookie));
451 inline Mutex::Mutex()
456 inline Mutex::Mutex(process_shared_tag)
458 bool robust_if_available = false;
459 init_as_process_shared(robust_if_available);
462 inline Mutex::~Mutex() noexcept
465 int r = pthread_mutex_destroy(&m_impl);
466 if (REALM_UNLIKELY(r != 0))
469 DeleteCriticalSection(&m_critical_section);
473 inline void Mutex::init_as_regular()
476 int r = pthread_mutex_init(&m_impl, 0);
477 if (REALM_UNLIKELY(r != 0))
480 InitializeCriticalSection(&m_critical_section);
484 inline void Mutex::lock() noexcept
487 EnterCriticalSection(&m_critical_section);
489 int r = pthread_mutex_lock(&m_impl);
490 if (REALM_LIKELY(r == 0))
496 inline bool Mutex::try_lock() noexcept
499 return TryEnterCriticalSection(&m_critical_section);
501 int r = pthread_mutex_trylock(&m_impl);
512 inline void Mutex::unlock() noexcept
515 LeaveCriticalSection(&m_critical_section);
517 int r = pthread_mutex_unlock(&m_impl);
518 REALM_ASSERT(r == 0);
523 inline LockGuard::LockGuard(Mutex& m) noexcept
529 inline LockGuard::~LockGuard() noexcept
535 inline UniqueLock::UniqueLock(Mutex& m) noexcept
542 inline UniqueLock::UniqueLock(Mutex& m, defer_lock_tag) noexcept
548 inline UniqueLock::~UniqueLock() noexcept
554 inline bool UniqueLock::holds_lock() noexcept
559 inline void UniqueLock::lock() noexcept
565 inline void UniqueLock::unlock() noexcept
571 template <typename TFunc>
572 inline RobustLockGuard::RobustLockGuard(RobustMutex& m, TFunc func)
578 inline RobustLockGuard::~RobustLockGuard() noexcept
584 inline RobustMutex::RobustMutex()
585 : Mutex(no_init_tag())
587 bool robust_if_available = true;
588 init_as_process_shared(robust_if_available);
591 inline RobustMutex::~RobustMutex() noexcept
595 template <class Func>
596 inline void RobustMutex::lock(Func recover_func)
598 bool no_thread_has_died = low_level_lock(); // Throws
599 if (REALM_LIKELY(no_thread_has_died))
602 recover_func(); // Throws
603 mark_as_consistent();
604 // If we get this far, the protected memory has been
605 // brought back into a consistent state, and the mutex has
606 // been notified about this. This means that we can safely
607 // enter the applications critical section.
610 // Unlocking without first calling mark_as_consistent()
611 // means that the mutex enters the "not recoverable"
612 // state, which will cause all future attempts at locking
619 template <class Func>
620 inline bool RobustMutex::try_lock(Func recover_func)
622 int lock_result = try_low_level_lock(); // Throws
623 if (lock_result == 0) return false;
624 bool no_thread_has_died = lock_result == 1;
625 if (REALM_LIKELY(no_thread_has_died))
628 recover_func(); // Throws
629 mark_as_consistent();
630 // If we get this far, the protected memory has been
631 // brought back into a consistent state, and the mutex has
632 // been notified aboit this. This means that we can safely
633 // enter the applications critical section.
636 // Unlocking without first calling mark_as_consistent()
637 // means that the mutex enters the "not recoverable"
638 // state, which will cause all future attempts at locking
646 inline void RobustMutex::unlock() noexcept
652 inline CondVar::CondVar()
655 int r = pthread_cond_init(&m_impl, 0);
656 if (REALM_UNLIKELY(r != 0))
661 inline CondVar::~CondVar() noexcept
664 int r = pthread_cond_destroy(&m_impl);
665 if (REALM_UNLIKELY(r != 0))
670 inline void CondVar::wait(LockGuard& l) noexcept
673 SleepConditionVariableCS(&m_condvar, &l.m_mutex.m_critical_section, INFINITE);
675 int r = pthread_cond_wait(&m_impl, &l.m_mutex.m_impl);
676 if (REALM_UNLIKELY(r != 0))
677 REALM_TERMINATE("pthread_cond_wait() failed");
681 template <class Func>
682 inline void CondVar::wait(RobustMutex& m, Func recover_func, const struct timespec* tp)
688 if (!SleepConditionVariableCS(&m_condvar, &m.m_critical_section, INFINITE))
693 r = pthread_cond_wait(&m_impl, &m.m_impl);
698 if (!SleepConditionVariableCS(&m_condvar, &m.m_critical_section, tp->tv_sec / 1000)) {
700 if (r == ERROR_TIMEOUT)
706 r = pthread_cond_timedwait(&m_impl, &m.m_impl, tp);
712 if (REALM_LIKELY(r == 0))
715 handle_wait_error(r);
718 recover_func(); // Throws
719 m.mark_as_consistent();
720 // If we get this far, the protected memory has been
721 // brought back into a consistent state, and the mutex has
722 // been notified aboit this. This means that we can safely
723 // enter the applications critical section.
726 // Unlocking without first calling mark_as_consistent()
727 // means that the mutex enters the "not recoverable"
728 // state, which will cause all future attempts at locking
735 inline void CondVar::notify() noexcept
738 WakeConditionVariable(&m_condvar);
740 int r = pthread_cond_signal(&m_impl);
741 REALM_ASSERT(r == 0);
745 inline void CondVar::notify_all() noexcept
748 WakeAllConditionVariable(&m_condvar);
750 int r = pthread_cond_broadcast(&m_impl);
751 REALM_ASSERT(r == 0);
759 #endif // REALM_UTIL_THREAD_HPP