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