Line data Source code
1 :
2 : /* Copyright (c) 2012-2017, Stefan Eilemann <eile@eyescale.ch>
3 : *
4 : * This library is free software; you can redistribute it and/or modify it under
5 : * the terms of the GNU Lesser General Public License version 2.1 as published
6 : * by the Free Software Foundation.
7 : *
8 : * This library is distributed in the hope that it will be useful, but WITHOUT
9 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 : * details.
12 : *
13 : * You should have received a copy of the GNU Lesser General Public License
14 : * along with this library; if not, write to the Free Software Foundation, Inc.,
15 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 : */
17 :
18 : #include "spinLock.h"
19 : #include <lunchbox/atomic.h>
20 : #include <lunchbox/thread.h>
21 :
22 : namespace lunchbox
23 : {
24 : namespace
25 : {
26 : static const long _writelocked = -1;
27 : static const long _unlocked = 0;
28 : }
29 :
30 : class SpinLock::Impl
31 : {
32 : public:
33 48 : Impl()
34 48 : : _state(_unlocked)
35 : {
36 48 : }
37 :
38 48 : ~Impl() { _state = _unlocked; }
39 :
40 8606 : inline void set()
41 : {
42 : while (true)
43 : {
44 9187 : if (trySet())
45 16120 : return;
46 584 : lunchbox::Thread::yield();
47 : }
48 : }
49 :
50 8044 : inline void unset()
51 : {
52 8044 : LBASSERT(_state == _writelocked);
53 8068 : _state = _unlocked;
54 8067 : }
55 :
56 8605 : inline bool trySet()
57 : {
58 8605 : if (!_state.compareAndSwap(_unlocked, _writelocked))
59 584 : return false;
60 8056 : LBASSERTINFO(isSetWrite(), _state);
61 8060 : return true;
62 : }
63 :
64 0 : inline void setRead()
65 : {
66 : while (true)
67 : {
68 0 : if (trySetRead())
69 0 : return;
70 0 : lunchbox::Thread::yield();
71 : }
72 : }
73 :
74 0 : inline void unsetRead()
75 : {
76 : while (true)
77 : {
78 0 : LBASSERT(_state > _unlocked);
79 0 : memoryBarrier();
80 0 : const int32_t expected = _state;
81 0 : if (_state.compareAndSwap(expected, expected - 1))
82 0 : return;
83 0 : }
84 : }
85 :
86 0 : inline bool trySetRead()
87 : {
88 0 : memoryBarrier();
89 0 : const int32_t state = _state;
90 : // Note: 0 used here since using _unlocked unexplicably gives
91 : // 'undefined reference to lunchbox::SpinLock::_unlocked'
92 0 : const int32_t expected = (state == _writelocked) ? 0 : state;
93 :
94 0 : if (!_state.compareAndSwap(expected, expected + 1))
95 0 : return false;
96 :
97 0 : LBASSERTINFO(isSetRead(), _state << ", " << expected);
98 0 : return true;
99 : }
100 :
101 0 : inline bool isSet() { return (_state != _unlocked); }
102 8056 : inline bool isSetWrite() { return (_state == _writelocked); }
103 0 : inline bool isSetRead() { return (_state > _unlocked); }
104 :
105 : private:
106 : a_int32_t _state;
107 : };
108 :
109 48 : SpinLock::SpinLock()
110 48 : : _impl(new SpinLock::Impl)
111 : {
112 48 : }
113 :
114 48 : SpinLock::~SpinLock()
115 : {
116 48 : }
117 :
118 8048 : void SpinLock::set()
119 : {
120 8048 : _impl->set();
121 8060 : }
122 :
123 8044 : void SpinLock::unset()
124 : {
125 8044 : _impl->unset();
126 8068 : }
127 :
128 0 : bool SpinLock::trySet()
129 : {
130 0 : return _impl->trySet();
131 : }
132 :
133 0 : void SpinLock::setRead()
134 : {
135 0 : _impl->setRead();
136 0 : }
137 :
138 0 : void SpinLock::unsetRead()
139 : {
140 0 : _impl->unsetRead();
141 0 : }
142 :
143 0 : bool SpinLock::trySetRead()
144 : {
145 0 : return _impl->trySetRead();
146 : }
147 :
148 0 : bool SpinLock::isSet()
149 : {
150 0 : return _impl->isSet();
151 : }
152 :
153 0 : bool SpinLock::isSetWrite()
154 : {
155 0 : return _impl->isSetWrite();
156 : }
157 :
158 0 : bool SpinLock::isSetRead()
159 : {
160 0 : return _impl->isSetRead();
161 : }
162 75 : }
|