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