1 /*
2 * Copyright (c) 2005, Eric Crahen
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is furnished
9 * to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 */
22
23 #include "Monitor.h"
24 #include "../Debug.h"
25 #include "../TimeStrategy.h"
26
27 #include <errno.h>
28 #include <assert.h>
29 #include <signal.h>
30
31 namespace ZThread {
32
Monitor()33 Monitor::Monitor() : _owner(0), _waiting(false) {
34
35 pthread_cond_init(&_waitCond, 0);
36 pthread_mutex_init(&_waitLock, 0);
37
38 }
39
~Monitor()40 Monitor::~Monitor() {
41
42 assert(!_waiting);
43
44 pthread_cond_destroy(&_waitCond);
45 pthread_mutex_destroy(&_waitLock);
46
47 }
48
wait(unsigned long ms)49 Monitor::STATE Monitor::wait(unsigned long ms) {
50
51 // Update the owner on first use. The owner will not change, each
52 // thread waits only on a single Monitor and a Monitor is never
53 // shared
54 if(_owner == 0)
55 _owner = pthread_self();
56
57 STATE state(INVALID);
58
59 // Serialize access to the state of the Monitor
60 // and test the state to determine if a wait is needed.
61
62 pthread_mutex_lock(&_waitLock);
63
64 if(pending(ANYTHING)) {
65
66 // Return without waiting when possible
67 state = next();
68
69 pthread_mutex_unlock(&_waitLock);
70 return state;
71
72 }
73
74 // Unlock the external lock if a wait() is probably needed.
75 // Access to the state is still serial.
76 _lock.release();
77
78 // Wait for a transition in the state that is of interest, this
79 // allows waits to exclude certain flags (e.g. INTERRUPTED)
80 // for a single wait() w/o actually discarding those flags -
81 // they will remain set until a wait interested in those flags
82 // occurs.
83 // if(!currentState(interest)) {
84
85 // Wait, ignoring signals
86 _waiting = true;
87 int status = 0;
88
89 if(ms == 0) { // Wait forever
90
91 do { // ignore signals unless the state is interesting
92 status = pthread_cond_wait(&_waitCond, &_waitLock);
93 } while(status == EINTR && !pending(ANYTHING));
94
95 // Akwaken only when a state is pending
96 assert(status == 0);
97
98 } else {
99
100 // Find the target time
101 TimeStrategy t;
102
103 ms += t.milliseconds();
104
105 unsigned long s = t.seconds() + (ms / 1000);
106 ms %= 1000;
107
108 // Convert to a timespec
109 struct ::timespec timeout;
110
111 timeout.tv_sec = s;
112 timeout.tv_nsec = ms*1000000;
113
114 // Wait ignoring signals until the state is interesting
115 do {
116
117 // When a timeout occurs, update the state to reflect that.
118 status = pthread_cond_timedwait(&_waitCond, &_waitLock, &timeout);
119
120 } while(status == EINTR && !pending(ANYTHING));
121
122 // Akwaken only when a state is pending or when the timeout expired
123 assert(status == 0 || status == ETIMEDOUT);
124
125 if(status == ETIMEDOUT)
126 push(TIMEDOUT);
127
128 }
129
130 // Get the next available STATE
131 state = next();
132 _waiting = false;
133
134 pthread_mutex_unlock(&_waitLock);
135
136 // Reaquire the external lock, keep from deadlocking threads calling
137 // notify(), interrupt(), etc.
138
139 _lock.acquire();
140
141 return state;
142
143 }
144
145
interrupt()146 bool Monitor::interrupt() {
147
148 // Serialize access to the state
149 pthread_mutex_lock(&_waitLock);
150
151 bool wasInterruptable = !pending(INTERRUPTED);
152 bool hadWaiter = _waiting;
153
154 if(wasInterruptable) {
155
156 // Update the state & wake the waiter if there is one
157 push(INTERRUPTED);
158
159 wasInterruptable = false;
160
161 if(hadWaiter && !masked(Monitor::INTERRUPTED))
162 pthread_cond_signal(&_waitCond);
163 else
164 wasInterruptable = !pthread_equal(_owner, pthread_self());
165
166 }
167
168 pthread_mutex_unlock(&_waitLock);
169
170 // Only returns true when an interrupted thread is not currently blocked
171 return wasInterruptable;
172
173 }
174
isInterrupted()175 bool Monitor::isInterrupted() {
176
177 // Serialize access to the state
178 pthread_mutex_lock(&_waitLock);
179
180 bool wasInterrupted = pending(INTERRUPTED);
181
182 clear(INTERRUPTED);
183
184 pthread_mutex_unlock(&_waitLock);
185
186 return wasInterrupted;
187
188 }
189
isCanceled()190 bool Monitor::isCanceled() {
191
192 // Serialize access to the state
193 pthread_mutex_lock(&_waitLock);
194
195 bool wasCanceled = examine(CANCELED);
196
197 if(pthread_equal(_owner, pthread_self()))
198 clear(INTERRUPTED);
199
200 pthread_mutex_unlock(&_waitLock);
201
202 return wasCanceled;
203
204 }
205
cancel()206 bool Monitor::cancel() {
207
208 // Serialize access to the state
209 pthread_mutex_lock(&_waitLock);
210
211 bool wasInterrupted = !pending(INTERRUPTED);
212 bool hadWaiter = _waiting;
213
214 push(CANCELED);
215
216 if(wasInterrupted) {
217
218 // Update the state & wake the waiter if there is one
219 push(INTERRUPTED);
220
221 if(hadWaiter && !masked(Monitor::INTERRUPTED))
222 pthread_cond_signal(&_waitCond);
223
224 }
225
226 pthread_mutex_unlock(&_waitLock);
227
228 return wasInterrupted;
229
230 }
231
notify()232 bool Monitor::notify() {
233
234 // Serialize access to the state
235 pthread_mutex_lock(&_waitLock);
236
237 bool wasNotifyable = !pending(INTERRUPTED);
238
239 if(wasNotifyable) {
240
241 // Set the flag and wake the waiter if there
242 // is one
243 push(SIGNALED);
244
245 if(_waiting)
246 pthread_cond_signal(&_waitCond);
247
248 }
249
250 pthread_mutex_unlock(&_waitLock);
251
252 return wasNotifyable;
253
254 }
255
256 } // namespace ZThread
257
258