Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#pragma once
28
29#include <AK/Assertions.h>
30#include <AK/Types.h>
31#include <AK/Atomic.h>
32#include <unistd.h>
33
34namespace LibThread {
35
36class Lock {
37public:
38 Lock() {}
39 ~Lock() {}
40
41 void lock();
42 void unlock();
43
44private:
45 AK::Atomic<bool> m_lock { false };
46 u32 m_level { 0 };
47 int m_holder { -1 };
48};
49
50#ifdef __serenity__
51
52class Locker {
53public:
54 [[gnu::always_inline]] inline explicit Locker(Lock& l)
55 : m_lock(l)
56 {
57 lock();
58 }
59 [[gnu::always_inline]] inline ~Locker() { unlock(); }
60 [[gnu::always_inline]] inline void unlock() { m_lock.unlock(); }
61 [[gnu::always_inline]] inline void lock() { m_lock.lock(); }
62
63private:
64 Lock& m_lock;
65};
66
67[[gnu::always_inline]] inline void Lock::lock()
68{
69 int tid = gettid();
70 for (;;) {
71 bool expected = false;
72 if (m_lock.compare_exchange_strong(expected, true, AK::memory_order_acq_rel)) {
73 if (m_holder == -1 || m_holder == tid) {
74 m_holder = tid;
75 ++m_level;
76 m_lock.store(false, AK::memory_order_release);
77 return;
78 }
79 m_lock.store(false, AK::memory_order_release);
80 }
81 donate(m_holder);
82 }
83}
84
85inline void Lock::unlock()
86{
87 for (;;) {
88 bool expected = false;
89 if (m_lock.compare_exchange_strong(expected, true, AK::memory_order_acq_rel)) {
90 ASSERT(m_holder == gettid());
91 ASSERT(m_level);
92 --m_level;
93 if (m_level) {
94 m_lock.store(false, AK::memory_order_release);
95 return;
96 }
97 m_holder = -1;
98 m_lock.store(false, AK::memory_order_release);
99 return;
100 }
101 donate(m_holder);
102 }
103}
104
105#define LOCKER(lock) LibThread::Locker locker(lock)
106
107#else
108
109#define LOCKER(x)
110
111#endif
112
113template<typename T>
114class Lockable {
115public:
116 Lockable() {}
117 Lockable(T&& resource)
118 : m_resource(move(resource))
119 {
120 }
121 Lock& lock() { return m_lock; }
122 T& resource() { return m_resource; }
123
124 T lock_and_copy()
125 {
126 LOCKER(m_lock);
127 return m_resource;
128 }
129
130private:
131 T m_resource;
132 Lock m_lock;
133};
134
135}