Line data Source code
1 :
2 : /* Copyright (c) 2012, 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 : namespace detail
30 : {
31 : class SpinLock
32 : {
33 : public:
34 25 : SpinLock() : _state( _unlocked ) {}
35 25 : ~SpinLock() { _state = _unlocked; }
36 :
37 4094 : inline void set()
38 : {
39 : while( true )
40 : {
41 5105 : if( trySet( ))
42 6168 : return;
43 1009 : lunchbox::Thread::yield();
44 : }
45 : }
46 :
47 3084 : inline void unset()
48 : {
49 3084 : LBASSERT( _state == _writelocked );
50 3084 : _state = _unlocked;
51 3084 : }
52 :
53 4094 : inline bool trySet()
54 : {
55 4094 : if( !_state.compareAndSwap( _unlocked, _writelocked ))
56 1009 : return false;
57 3084 : LBASSERTINFO( isSetWrite(), _state );
58 3084 : return true;
59 : }
60 :
61 0 : inline void setRead()
62 : {
63 : while( true )
64 : {
65 0 : if( trySetRead( ))
66 0 : return;
67 0 : lunchbox::Thread::yield();
68 : }
69 : }
70 :
71 0 : inline void unsetRead()
72 : {
73 : while( true )
74 : {
75 0 : LBASSERT( _state > _unlocked );
76 0 : memoryBarrier();
77 0 : const int32_t expected = _state;
78 0 : if( _state.compareAndSwap( expected, expected-1 ))
79 0 : return;
80 0 : }
81 : }
82 :
83 0 : inline bool trySetRead()
84 : {
85 0 : memoryBarrier();
86 0 : const int32_t state = _state;
87 : // Note: 0 used here since using _unlocked unexplicably gives
88 : // 'undefined reference to lunchbox::SpinLock::_unlocked'
89 0 : const int32_t expected = (state==_writelocked) ? 0 : state;
90 :
91 0 : if( !_state.compareAndSwap( expected, expected+1 ))
92 0 : return false;
93 :
94 0 : LBASSERTINFO( isSetRead(), _state << ", " << expected );
95 0 : return true;
96 : }
97 :
98 0 : inline bool isSet() { return ( _state != _unlocked ); }
99 3084 : inline bool isSetWrite() { return ( _state == _writelocked ); }
100 0 : inline bool isSetRead() { return ( _state > _unlocked ); }
101 :
102 : private:
103 : a_int32_t _state;
104 : };
105 : }
106 :
107 25 : SpinLock::SpinLock()
108 25 : : _impl( new detail::SpinLock ) {}
109 :
110 50 : SpinLock::~SpinLock()
111 : {
112 25 : delete _impl;
113 25 : }
114 :
115 3083 : void SpinLock::set()
116 : {
117 3083 : _impl->set();
118 3084 : }
119 :
120 3084 : void SpinLock::unset()
121 : {
122 3084 : _impl->unset();
123 3084 : }
124 :
125 0 : bool SpinLock::trySet()
126 : {
127 0 : return _impl->trySet();
128 : }
129 :
130 0 : void SpinLock::setRead()
131 : {
132 0 : _impl->setRead();
133 0 : }
134 :
135 0 : void SpinLock::unsetRead()
136 : {
137 0 : _impl->unsetRead();
138 0 : }
139 :
140 0 : bool SpinLock::trySetRead()
141 : {
142 0 : return _impl->trySetRead();
143 : }
144 :
145 0 : bool SpinLock::isSet()
146 : {
147 0 : return _impl->isSet();
148 : }
149 :
150 0 : bool SpinLock::isSetWrite()
151 : {
152 0 : return _impl->isSetWrite();
153 : }
154 :
155 0 : bool SpinLock::isSetRead()
156 : {
157 0 : return _impl->isSetRead();
158 : }
159 :
160 72 : }
|