jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
locks.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com> and Svenson Han Gothel
3 * Copyright (c) 2022-2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#ifndef JAU_LOCKS_HPP_
25#define JAU_LOCKS_HPP_
26
27#include <cstdint>
28#include <ios>
29#include <mutex>
30#include <ratio>
31#include <string>
32#include <thread>
33
34#include <jau/basic_types.hpp>
35#include <jau/int_types.hpp>
37#include <jau/string_util.hpp>
38
39namespace jau {
40
41 /** \addtogroup Concurrency
42 *
43 * @{
44 */
45
47 public:
48 typedef void (*callback_func)() noexcept;
49
50 private:
51 std::recursive_timed_mutex m_mtx_lock;
52 relaxed_atomic_nsize_t m_lock_count;
53 std::thread::id m_owner_id;
54
55 public:
56 RecursiveLock() noexcept
57 : m_lock_count(0), m_owner_id() { }
58
59 RecursiveLock(const RecursiveLock &) = delete;
60 void operator=(const RecursiveLock &) = delete;
61
62 bool isOwner(std::thread::id id) const noexcept { return id == m_owner_id; }
63 bool isOwner() const noexcept { return std::this_thread::get_id() == m_owner_id; }
64
65 /**
66 * Return the number of locks issued to this lock by the same thread.
67 * <ul>
68 * <li>A hold count of 0 identifies this lock as unlocked.</li>
69 * <li>A hold count of 1 identifies this lock as locked.</li>
70 * <li>A hold count of > 1 identifies this lock as recursively lock.</li>
71 * </ul>
72 */
73 nsize_t holdCount() noexcept { return m_lock_count; }
74
75 /**
76 * Acquire this lock indefinitely (no timeout)
77 */
78 void lock() {
79 m_mtx_lock.lock();
80 if( 1 == ++m_lock_count ) {
81 m_owner_id = std::this_thread::get_id();
82 }
83 }
84
85 /**
86 * Try to acquire this lock within given timeout in seconds
87 *
88 * @param timeout maximum duration in fractions of seconds to wait, where fractions_i64::zero waits infinitely
89 * @return true if successful, otherwise false in case timeout occurred or otherwise.
90 */
91 bool tryLock(const fraction_i64 &timeout) {
92 // const fraction_timespec timeout_time = getMonotonicTime() + fraction_timespec(timeout);
93 bool overflow = false;
94
95 std::chrono::duration<int64_t, std::nano> d = timeout.to_duration(std::chrono::nanoseconds::zero(), &overflow);
96 if( overflow ) {
97 return false;
98 }
99 auto timeout_time = std::chrono::steady_clock::now() + d;
100
101 if( m_mtx_lock.try_lock_until(timeout_time) ) {
102 if( 1 == ++m_lock_count ) {
103 m_owner_id = std::this_thread::get_id();
104 }
105 return true;
106 } else {
107 return false;
108 }
109 }
110
111 void validateLocked() const {
112 std::thread::id id = std::this_thread::get_id();
113 if( !isOwner(id) ) {
114 throw RuntimeException(threadName(id) + ": Not locked: " + toString(), E_FILE_LINE);
115 }
116 }
117
118 /**
119 * Unlock ...
120 *
121 * @param taskBeforeUnlock optional callback_func to be execiting before final unlock.
122 *
123 * @see lock()
124 * @see tryLock()
125 */
126 void unlock(callback_func taskBeforeUnlock=nullptr) {
128
129 if( 0 < --m_lock_count ) {
130 m_mtx_lock.unlock();
131 return;
132 }
133 if( nullptr != taskBeforeUnlock ) {
134 taskBeforeUnlock();
135 }
136 m_owner_id = std::thread::id();
137 m_mtx_lock.unlock();
138 }
139
140 std::string toString() const {
141 return "RL[count " + std::to_string(m_lock_count.load()) + ", owner " + threadName(m_owner_id) + "]";
142 }
143 };
144
145 /**@}*/
146
147} // namespace jau
148
149#endif /* JAU_LOCKS_HPP_ */
#define E_FILE_LINE
void lock()
Acquire this lock indefinitely (no timeout)
Definition locks.hpp:78
void operator=(const RecursiveLock &)=delete
RecursiveLock() noexcept
Definition locks.hpp:56
RecursiveLock(const RecursiveLock &)=delete
bool tryLock(const fraction_i64 &timeout)
Try to acquire this lock within given timeout in seconds.
Definition locks.hpp:91
std::string toString() const
Definition locks.hpp:140
nsize_t holdCount() noexcept
Return the number of locks issued to this lock by the same thread.
Definition locks.hpp:73
bool isOwner() const noexcept
Definition locks.hpp:63
void unlock(callback_func taskBeforeUnlock=nullptr)
Unlock ...
Definition locks.hpp:126
void validateLocked() const
Definition locks.hpp:111
bool isOwner(std::thread::id id) const noexcept
Definition locks.hpp:62
void(* callback_func)() noexcept
Definition locks.hpp:48
ordered_atomic< jau::nsize_t, std::memory_order_relaxed > relaxed_atomic_nsize_t
Relaxed non-SC atomic integral scalar jau::nsize_t.
fraction< int64_t > fraction_i64
fraction using int64_t as integral type
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition int_types.hpp:55
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
std::string threadName(const std::thread::id id) noexcept