1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18 
19 #include "evolution-data-server-config.h"
20 
21 #include "e-cancellable-locks.h"
22 
23 /**
24  * SECTION:e-cancellable-locks
25  * @title: Cancellable Locks
26  * @short_description: locks, which can listen for a #GCancellable during lock call
27  *
28  * An #ECancellableMutex and an #ECancellableRecMutex are similar to
29  * GLib's #GMutex and #GRecMutex, with one exception, their <emphasis>lock</emphasis>
30  * function takes also a @GCancellable instance, thus the waiting for a lock
31  * can be cancelled any time.
32  **/
33 
34 static void
cancellable_locks_cancelled_cb(GCancellable * cancellable,struct _ECancellableLocksBase * base)35 cancellable_locks_cancelled_cb (GCancellable *cancellable,
36                                 struct _ECancellableLocksBase *base)
37 {
38 	g_return_if_fail (base != NULL);
39 
40 	/* wake-up any waiting threads */
41 	g_mutex_lock (&base->cond_mutex);
42 	g_cond_broadcast (&base->cond);
43 	g_mutex_unlock (&base->cond_mutex);
44 }
45 
46 /**
47  * e_cancellable_mutex_init:
48  * @mutex: an #ECancellableMutex instance
49  *
50  * Initializes @mutex structure.
51  *
52  * Since: 3.8
53  *
54  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
55  **/
56 void
e_cancellable_mutex_init(ECancellableMutex * mutex)57 e_cancellable_mutex_init (ECancellableMutex *mutex)
58 {
59 	g_return_if_fail (mutex != NULL);
60 
61 	g_mutex_init (&mutex->mutex);
62 	g_mutex_init (&mutex->base.cond_mutex);
63 	g_cond_init (&mutex->base.cond);
64 }
65 
66 /**
67  * e_cancellable_mutex_clear:
68  * @mutex: an #ECancellableMutex instance
69  *
70  * Frees memory allocated by e_cancellable_mutex_init().
71  *
72  * Since: 3.8
73  *
74  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
75  **/
76 void
e_cancellable_mutex_clear(ECancellableMutex * mutex)77 e_cancellable_mutex_clear (ECancellableMutex *mutex)
78 {
79 	g_return_if_fail (mutex != NULL);
80 
81 	g_mutex_clear (&mutex->mutex);
82 	g_mutex_clear (&mutex->base.cond_mutex);
83 	g_cond_clear (&mutex->base.cond);
84 }
85 
86 /**
87  * e_cancellable_mutex_lock:
88  * @mutex: an #ECancellableMutex instance
89  * @cancellable: a #GCancellable, or %NULL
90  *
91  * Acquires lock on @mutex. The returned value indicates whether
92  * the lock was acquired, while %FALSE is returned only either or
93  * invalid arguments or the passed in @cancellable had been cancelled.
94  * In case of %NULL @cancellable the function blocks like g_mutex_lock().
95  *
96  * Returns: %TRUE, if lock had been acquired, %FALSE otherwise
97  *
98  * Since: 3.8
99  *
100  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
101  **/
102 gboolean
e_cancellable_mutex_lock(ECancellableMutex * mutex,GCancellable * cancellable)103 e_cancellable_mutex_lock (ECancellableMutex *mutex,
104                           GCancellable *cancellable)
105 {
106 	gulong handler_id;
107 	gboolean res = TRUE;
108 
109 	g_return_val_if_fail (mutex != NULL, FALSE);
110 
111 	g_mutex_lock (&mutex->base.cond_mutex);
112 	if (!cancellable) {
113 		g_mutex_unlock (&mutex->base.cond_mutex);
114 		g_mutex_lock (&mutex->mutex);
115 		return TRUE;
116 	}
117 
118 	if (g_cancellable_is_cancelled (cancellable)) {
119 		g_mutex_unlock (&mutex->base.cond_mutex);
120 		return FALSE;
121 	}
122 
123 	handler_id = g_signal_connect (
124 		cancellable, "cancelled",
125 		G_CALLBACK (cancellable_locks_cancelled_cb), &mutex->base);
126 
127 	while (!g_mutex_trylock (&mutex->mutex)) {
128 		/* recheck once per 10 seconds, just in case */
129 		g_cond_wait_until (
130 			&mutex->base.cond, &mutex->base.cond_mutex,
131 			g_get_monotonic_time () + (10 * G_TIME_SPAN_SECOND));
132 
133 		if (g_cancellable_is_cancelled (cancellable)) {
134 			res = FALSE;
135 			break;
136 		}
137 	}
138 
139 	g_signal_handler_disconnect (cancellable, handler_id);
140 
141 	g_mutex_unlock (&mutex->base.cond_mutex);
142 
143 	return res;
144 }
145 
146 /**
147  * e_cancellable_mutex_unlock:
148  * @mutex: an #ECancellableMutex instance
149  *
150  * Releases lock previously acquired by e_cancellable_mutex_lock().
151  * Behaviour is undefined if this is called on a @mutex which returned
152  * %FALSE in e_cancellable_mutex_lock().
153  *
154  * Since: 3.8
155  *
156  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
157  **/
158 void
e_cancellable_mutex_unlock(ECancellableMutex * mutex)159 e_cancellable_mutex_unlock (ECancellableMutex *mutex)
160 {
161 	g_return_if_fail (mutex != NULL);
162 
163 	g_mutex_unlock (&mutex->mutex);
164 
165 	g_mutex_lock (&mutex->base.cond_mutex);
166 	/* also wake-up any waiting threads */
167 	g_cond_broadcast (&mutex->base.cond);
168 	g_mutex_unlock (&mutex->base.cond_mutex);
169 }
170 
171 /**
172  * e_cancellable_mutex_get_internal_mutex:
173  * @mutex: an #ECancellableMutex instance
174  *
175  * To get internal #GMutex. This is meant for cases when a lock is already
176  * acquired, and the caller needs to wait for a #GCond, in which case
177  * the returned #GMutex can be used to g_cond_wait() or g_cond_wait_until().
178  *
179  * Returns: Internal #GMutex, used in @mutex
180  *
181  * Since: 3.8
182  *
183  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
184  **/
185 GMutex *
e_cancellable_mutex_get_internal_mutex(ECancellableMutex * mutex)186 e_cancellable_mutex_get_internal_mutex (ECancellableMutex *mutex)
187 {
188 	g_return_val_if_fail (mutex != NULL, NULL);
189 
190 	return &mutex->mutex;
191 }
192 
193 /**
194  * e_cancellable_rec_mutex_init:
195  * @rec_mutex: an #ECancellableRecMutex instance
196  *
197  * Initializes @rec_mutex structure.
198  *
199  * Since: 3.8
200  *
201  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
202  **/
203 void
e_cancellable_rec_mutex_init(ECancellableRecMutex * rec_mutex)204 e_cancellable_rec_mutex_init (ECancellableRecMutex *rec_mutex)
205 {
206 	g_return_if_fail (rec_mutex != NULL);
207 
208 	g_rec_mutex_init (&rec_mutex->rec_mutex);
209 	g_mutex_init (&rec_mutex->base.cond_mutex);
210 	g_cond_init (&rec_mutex->base.cond);
211 }
212 
213 /**
214  * e_cancellable_rec_mutex_clear:
215  * @rec_mutex: an #ECancellableRecMutex instance
216  *
217  * Frees memory allocated by e_cancellable_rec_mutex_init().
218  *
219  * Since: 3.8
220  *
221  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
222  **/
223 void
e_cancellable_rec_mutex_clear(ECancellableRecMutex * rec_mutex)224 e_cancellable_rec_mutex_clear (ECancellableRecMutex *rec_mutex)
225 {
226 	g_return_if_fail (rec_mutex != NULL);
227 
228 	g_rec_mutex_clear (&rec_mutex->rec_mutex);
229 	g_mutex_clear (&rec_mutex->base.cond_mutex);
230 	g_cond_clear (&rec_mutex->base.cond);
231 }
232 
233 /**
234  * e_cancellable_rec_mutex_lock:
235  * @rec_mutex: an #ECancellableRecMutex instance
236  * @cancellable: a #GCancellable, or %NULL
237  *
238  * Acquires lock on @rec_mutex. The returned value indicates whether
239  * the lock was acquired, while %FALSE is returned only either or
240  * invalid arguments or the passed in @cancellable had been cancelled.
241  * In case of %NULL @cancellable the function blocks like g_rec_mutex_lock().
242  *
243  * Returns: %TRUE, if lock had been acquired, %FALSE otherwise
244  *
245  * Since: 3.8
246  *
247  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
248  **/
249 gboolean
e_cancellable_rec_mutex_lock(ECancellableRecMutex * rec_mutex,GCancellable * cancellable)250 e_cancellable_rec_mutex_lock (ECancellableRecMutex *rec_mutex,
251                               GCancellable *cancellable)
252 {
253 	gulong handler_id;
254 	gboolean res = TRUE;
255 
256 	g_return_val_if_fail (rec_mutex != NULL, FALSE);
257 
258 	g_mutex_lock (&rec_mutex->base.cond_mutex);
259 	if (!cancellable) {
260 		g_mutex_unlock (&rec_mutex->base.cond_mutex);
261 		g_rec_mutex_lock (&rec_mutex->rec_mutex);
262 		return TRUE;
263 	}
264 
265 	if (g_cancellable_is_cancelled (cancellable)) {
266 		g_mutex_unlock (&rec_mutex->base.cond_mutex);
267 		return FALSE;
268 	}
269 
270 	handler_id = g_signal_connect (
271 		cancellable, "cancelled",
272 		G_CALLBACK (cancellable_locks_cancelled_cb), &rec_mutex->base);
273 
274 	while (!g_rec_mutex_trylock (&rec_mutex->rec_mutex)) {
275 		/* recheck once per 10 seconds, just in case */
276 		g_cond_wait_until (
277 			&rec_mutex->base.cond, &rec_mutex->base.cond_mutex,
278 			g_get_monotonic_time () + (10 * G_TIME_SPAN_SECOND));
279 
280 		if (g_cancellable_is_cancelled (cancellable)) {
281 			res = FALSE;
282 			break;
283 		}
284 	}
285 
286 	g_signal_handler_disconnect (cancellable, handler_id);
287 
288 	g_mutex_unlock (&rec_mutex->base.cond_mutex);
289 
290 	return res;
291 }
292 
293 /**
294  * e_cancellable_rec_mutex_unlock:
295  * @rec_mutex: an #ECancellableRecMutex instance
296  *
297  * Releases lock previously acquired by e_cancellable_rec_mutex_lock().
298  * Behaviour is undefined if this is called on a @rec_mutex which returned
299  * %FALSE in e_cancellable_rec_mutex_lock().
300  *
301  * Since: 3.8
302  *
303  * Deprecated: 3.12: If you think you need this, you're using mutexes wrong.
304  **/
305 void
e_cancellable_rec_mutex_unlock(ECancellableRecMutex * rec_mutex)306 e_cancellable_rec_mutex_unlock (ECancellableRecMutex *rec_mutex)
307 {
308 	g_return_if_fail (rec_mutex != NULL);
309 
310 	g_rec_mutex_unlock (&rec_mutex->rec_mutex);
311 
312 	g_mutex_lock (&rec_mutex->base.cond_mutex);
313 	/* also wake-up any waiting threads */
314 	g_cond_broadcast (&rec_mutex->base.cond);
315 	g_mutex_unlock (&rec_mutex->base.cond_mutex);
316 }
317