1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ |
---|
2 | /* |
---|
3 | |
---|
4 | Copyright (C) 2001 Eazel, Inc |
---|
5 | Copyright (C) 2001 Maciej Stachowiak |
---|
6 | |
---|
7 | The Gnome Library is free software; you can redistribute it and/or |
---|
8 | modify it under the terms of the GNU Library General Public License as |
---|
9 | published by the Free Software Foundation; either version 2 of the |
---|
10 | License, or (at your option) any later version. |
---|
11 | |
---|
12 | The Gnome Library is distributed in the hope that it will be useful, |
---|
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
15 | Library General Public License for more details. |
---|
16 | |
---|
17 | You should have received a copy of the GNU Library General Public |
---|
18 | License along with the Gnome Library; see the file COPYING.LIB. If not, |
---|
19 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
20 | Boston, MA 02111-1307, USA. |
---|
21 | |
---|
22 | Author: Michael Fleming <mfleming@eazel.com> |
---|
23 | Maciej Stachowiak <mjs@enoisehavoc.org> |
---|
24 | */ |
---|
25 | |
---|
26 | |
---|
27 | #include <config.h> |
---|
28 | #include "gnome-vfs-module-callback.h" |
---|
29 | |
---|
30 | #include "gnome-vfs-module-callback-module-api.h" |
---|
31 | #include "gnome-vfs-module-callback-private.h" |
---|
32 | #include "gnome-vfs-backend-private.h" |
---|
33 | |
---|
34 | #include <pthread.h> |
---|
35 | |
---|
36 | |
---|
37 | /* -- Private data structure declarations -- */ |
---|
38 | |
---|
39 | typedef struct CallbackInfo { |
---|
40 | GnomeVFSModuleCallback callback; |
---|
41 | gpointer callback_data; |
---|
42 | GDestroyNotify destroy_notify; |
---|
43 | int ref_count; |
---|
44 | } CallbackInfo; |
---|
45 | |
---|
46 | typedef struct AsyncCallbackInfo { |
---|
47 | GnomeVFSAsyncModuleCallback callback; |
---|
48 | gpointer callback_data; |
---|
49 | GDestroyNotify destroy_notify; |
---|
50 | } AsyncCallbackInfo; |
---|
51 | |
---|
52 | typedef struct CallbackResponseData { |
---|
53 | gboolean done; |
---|
54 | } CallbackResponseData; |
---|
55 | |
---|
56 | struct GnomeVFSModuleCallbackStackInfo { |
---|
57 | GHashTable *current_callbacks; |
---|
58 | GHashTable *current_async_callbacks; |
---|
59 | }; |
---|
60 | |
---|
61 | |
---|
62 | /* -- Global variables -- */ |
---|
63 | |
---|
64 | static pthread_mutex_t callback_table_lock = PTHREAD_MUTEX_INITIALIZER; |
---|
65 | static GHashTable *default_callbacks = NULL; |
---|
66 | static GHashTable *default_async_callbacks = NULL; |
---|
67 | static GHashTable *stack_tables_to_free = NULL; |
---|
68 | |
---|
69 | static pthread_once_t stack_keys_once = PTHREAD_ONCE_INIT; |
---|
70 | static pthread_key_t callback_stacks_key; |
---|
71 | static pthread_key_t async_callback_stacks_key; |
---|
72 | static pthread_key_t in_async_thread_key; |
---|
73 | |
---|
74 | static pthread_mutex_t async_callback_mutex = PTHREAD_MUTEX_INITIALIZER; |
---|
75 | static pthread_cond_t async_callback_cond = PTHREAD_COND_INITIALIZER; |
---|
76 | |
---|
77 | /* -- Helper functions -- */ |
---|
78 | |
---|
79 | /* managing callback structs */ |
---|
80 | |
---|
81 | static CallbackInfo * |
---|
82 | callback_info_new (GnomeVFSModuleCallback callback_func, |
---|
83 | gpointer callback_data, |
---|
84 | GDestroyNotify notify) |
---|
85 | { |
---|
86 | CallbackInfo *callback; |
---|
87 | |
---|
88 | callback = g_new (CallbackInfo, 1); |
---|
89 | |
---|
90 | callback->callback = callback_func; |
---|
91 | callback->callback_data = callback_data; |
---|
92 | callback->destroy_notify = notify; |
---|
93 | callback->ref_count = 1; |
---|
94 | |
---|
95 | return callback; |
---|
96 | } |
---|
97 | |
---|
98 | #include <stdio.h> |
---|
99 | |
---|
100 | static void |
---|
101 | callback_info_ref (CallbackInfo *callback) |
---|
102 | { |
---|
103 | callback->ref_count++; |
---|
104 | } |
---|
105 | |
---|
106 | static void |
---|
107 | callback_info_unref (CallbackInfo *callback) |
---|
108 | { |
---|
109 | callback->ref_count--; |
---|
110 | |
---|
111 | if (callback->ref_count == 0) { |
---|
112 | if (callback->destroy_notify != NULL) { |
---|
113 | callback->destroy_notify (callback->callback_data); |
---|
114 | } |
---|
115 | g_free (callback); |
---|
116 | } |
---|
117 | } |
---|
118 | |
---|
119 | /* code for handling async callbacks */ |
---|
120 | |
---|
121 | static void |
---|
122 | async_callback_response (gpointer data) |
---|
123 | { |
---|
124 | CallbackResponseData *response_data; |
---|
125 | |
---|
126 | pthread_mutex_lock (&async_callback_mutex); |
---|
127 | response_data = data; |
---|
128 | response_data->done = TRUE; |
---|
129 | pthread_mutex_unlock (&async_callback_mutex); |
---|
130 | |
---|
131 | pthread_cond_broadcast (&async_callback_cond); |
---|
132 | } |
---|
133 | |
---|
134 | static void |
---|
135 | async_callback_invoke (gconstpointer in, |
---|
136 | gsize in_size, |
---|
137 | gpointer out, |
---|
138 | gsize out_size, |
---|
139 | gpointer callback_data) |
---|
140 | { |
---|
141 | AsyncCallbackInfo *async_callback; |
---|
142 | CallbackResponseData response_data; |
---|
143 | |
---|
144 | response_data.done = FALSE; |
---|
145 | async_callback = callback_data; |
---|
146 | |
---|
147 | /* Using a single mutex and condition variable could mean bad |
---|
148 | * performance if many async callbacks are active at once but |
---|
149 | * this is unlikeley, so we avoid the overhead of creating |
---|
150 | * new mutexes and condition variables all the time. |
---|
151 | */ |
---|
152 | |
---|
153 | pthread_mutex_lock (&async_callback_mutex); |
---|
154 | gnome_vfs_backend_dispatch_module_callback (async_callback->callback, |
---|
155 | in, in_size, |
---|
156 | out, out_size, |
---|
157 | async_callback->callback_data, |
---|
158 | async_callback_response, |
---|
159 | &response_data); |
---|
160 | |
---|
161 | while (!response_data.done) { |
---|
162 | pthread_cond_wait (&async_callback_cond, &async_callback_mutex); |
---|
163 | } |
---|
164 | pthread_mutex_unlock (&async_callback_mutex); |
---|
165 | } |
---|
166 | |
---|
167 | static void |
---|
168 | async_callback_destroy (gpointer callback_data) |
---|
169 | { |
---|
170 | AsyncCallbackInfo *async_callback; |
---|
171 | |
---|
172 | async_callback = callback_data; |
---|
173 | |
---|
174 | if (async_callback->destroy_notify != NULL) { |
---|
175 | async_callback->destroy_notify (async_callback->callback_data); |
---|
176 | } |
---|
177 | |
---|
178 | g_free (async_callback); |
---|
179 | |
---|
180 | } |
---|
181 | |
---|
182 | static CallbackInfo * |
---|
183 | async_callback_info_new (GnomeVFSAsyncModuleCallback callback_func, |
---|
184 | gpointer callback_data, |
---|
185 | GDestroyNotify notify) |
---|
186 | { |
---|
187 | AsyncCallbackInfo *async_callback; |
---|
188 | |
---|
189 | async_callback = g_new (AsyncCallbackInfo, 1); |
---|
190 | |
---|
191 | async_callback->callback = callback_func; |
---|
192 | async_callback->callback_data = callback_data; |
---|
193 | async_callback->destroy_notify = notify; |
---|
194 | |
---|
195 | return callback_info_new (async_callback_invoke, async_callback, async_callback_destroy); |
---|
196 | } |
---|
197 | |
---|
198 | |
---|
199 | |
---|
200 | /* Adding items to hash tables or stack tables */ |
---|
201 | static void |
---|
202 | insert_callback_into_table (GHashTable *table, |
---|
203 | const char *callback_name, |
---|
204 | CallbackInfo *callback) |
---|
205 | { |
---|
206 | gpointer orig_key; |
---|
207 | gpointer old_value; |
---|
208 | |
---|
209 | callback_info_ref (callback); |
---|
210 | |
---|
211 | if (g_hash_table_lookup_extended (table, |
---|
212 | callback_name, |
---|
213 | &orig_key, |
---|
214 | &old_value)) { |
---|
215 | g_hash_table_remove (table, orig_key); |
---|
216 | g_free (orig_key); |
---|
217 | callback_info_unref ((CallbackInfo *) old_value); |
---|
218 | } |
---|
219 | |
---|
220 | g_hash_table_insert (table, |
---|
221 | g_strdup (callback_name), |
---|
222 | callback); |
---|
223 | } |
---|
224 | |
---|
225 | static void |
---|
226 | push_callback_into_stack_table (GHashTable *table, |
---|
227 | const char *callback_name, |
---|
228 | CallbackInfo *callback) |
---|
229 | { |
---|
230 | gpointer orig_key; |
---|
231 | gpointer old_value; |
---|
232 | GSList *stack; |
---|
233 | |
---|
234 | callback_info_ref (callback); |
---|
235 | |
---|
236 | if (g_hash_table_lookup_extended (table, |
---|
237 | callback_name, |
---|
238 | &orig_key, |
---|
239 | &old_value)) { |
---|
240 | g_hash_table_remove (table, orig_key); |
---|
241 | g_free (orig_key); |
---|
242 | stack = old_value; |
---|
243 | } else { |
---|
244 | stack = NULL; |
---|
245 | } |
---|
246 | |
---|
247 | stack = g_slist_prepend (stack, callback); |
---|
248 | |
---|
249 | g_hash_table_insert (table, |
---|
250 | g_strdup (callback_name), |
---|
251 | stack); |
---|
252 | } |
---|
253 | |
---|
254 | static void |
---|
255 | pop_stack_table (GHashTable *table, |
---|
256 | const char *callback_name) |
---|
257 | { |
---|
258 | GSList *stack; |
---|
259 | GSList *first_link; |
---|
260 | gpointer orig_key; |
---|
261 | gpointer old_value; |
---|
262 | |
---|
263 | if (g_hash_table_lookup_extended (table, |
---|
264 | callback_name, |
---|
265 | &orig_key, |
---|
266 | &old_value)) { |
---|
267 | g_hash_table_remove (table, orig_key); |
---|
268 | g_free (orig_key); |
---|
269 | stack = old_value; |
---|
270 | } else { |
---|
271 | return; |
---|
272 | } |
---|
273 | |
---|
274 | /* Would not be in the hash table if it were NULL */ |
---|
275 | g_assert (stack != NULL); |
---|
276 | |
---|
277 | callback_info_unref ((CallbackInfo *) stack->data); |
---|
278 | |
---|
279 | first_link = stack; |
---|
280 | stack = stack->next; |
---|
281 | g_slist_free_1 (first_link); |
---|
282 | |
---|
283 | if (stack != NULL) { |
---|
284 | g_hash_table_insert (table, |
---|
285 | g_strdup (callback_name), |
---|
286 | stack); |
---|
287 | } |
---|
288 | } |
---|
289 | |
---|
290 | |
---|
291 | /* Functions to copy, duplicate and clear callback tables and callback |
---|
292 | * stack tables, and helpers for these functions. |
---|
293 | */ |
---|
294 | |
---|
295 | static void |
---|
296 | copy_one_callback (gpointer key, |
---|
297 | gpointer value, |
---|
298 | gpointer callback_data) |
---|
299 | { |
---|
300 | const char *callback_name; |
---|
301 | CallbackInfo *callback; |
---|
302 | GHashTable *table; |
---|
303 | |
---|
304 | callback_name = key; |
---|
305 | callback = value; |
---|
306 | table = callback_data; |
---|
307 | |
---|
308 | insert_callback_into_table (table, callback_name, callback); |
---|
309 | } |
---|
310 | |
---|
311 | static void |
---|
312 | copy_one_stack_top (gpointer key, |
---|
313 | gpointer value, |
---|
314 | gpointer callback_data) |
---|
315 | { |
---|
316 | GSList *stack; |
---|
317 | const char *callback_name; |
---|
318 | CallbackInfo *callback; |
---|
319 | GHashTable *table; |
---|
320 | |
---|
321 | callback_name = key; |
---|
322 | stack = value; |
---|
323 | callback = stack->data; |
---|
324 | table = callback_data; |
---|
325 | |
---|
326 | insert_callback_into_table (table, callback_name, callback); |
---|
327 | } |
---|
328 | |
---|
329 | static void |
---|
330 | copy_one_callback_to_stack (gpointer key, |
---|
331 | gpointer value, |
---|
332 | gpointer callback_data) |
---|
333 | { |
---|
334 | const char *callback_name; |
---|
335 | CallbackInfo *callback; |
---|
336 | GHashTable *table; |
---|
337 | |
---|
338 | callback_name = key; |
---|
339 | callback = value; |
---|
340 | table = callback_data; |
---|
341 | |
---|
342 | push_callback_into_stack_table (table, callback_name, callback); |
---|
343 | } |
---|
344 | |
---|
345 | static GHashTable * |
---|
346 | duplicate_callback_table (GHashTable *table) |
---|
347 | { |
---|
348 | GHashTable *copy; |
---|
349 | |
---|
350 | copy = g_hash_table_new (g_str_hash, g_str_equal); |
---|
351 | g_hash_table_foreach (table, copy_one_callback, copy); |
---|
352 | |
---|
353 | return copy; |
---|
354 | } |
---|
355 | |
---|
356 | static void |
---|
357 | copy_callback_stack_tops (GHashTable *source, |
---|
358 | GHashTable *target) |
---|
359 | { |
---|
360 | g_hash_table_foreach (source, |
---|
361 | copy_one_stack_top, |
---|
362 | target); |
---|
363 | } |
---|
364 | |
---|
365 | static void |
---|
366 | copy_callback_table_to_stack_table (GHashTable *source, |
---|
367 | GHashTable *target) |
---|
368 | { |
---|
369 | g_hash_table_foreach (source, |
---|
370 | copy_one_callback_to_stack, |
---|
371 | target); |
---|
372 | } |
---|
373 | |
---|
374 | static void |
---|
375 | callback_info_unref_func (gpointer data, |
---|
376 | gpointer callback_data) |
---|
377 | { |
---|
378 | callback_info_unref ((CallbackInfo *) data); |
---|
379 | } |
---|
380 | |
---|
381 | static gboolean |
---|
382 | remove_one_stack (gpointer key, |
---|
383 | gpointer value, |
---|
384 | gpointer callback_data) |
---|
385 | { |
---|
386 | char *callback_name; |
---|
387 | GSList *stack; |
---|
388 | |
---|
389 | callback_name = key; |
---|
390 | stack = value; |
---|
391 | |
---|
392 | g_free (callback_name); |
---|
393 | g_slist_foreach (stack, callback_info_unref_func, NULL); |
---|
394 | g_slist_free (stack); |
---|
395 | |
---|
396 | return TRUE; |
---|
397 | } |
---|
398 | |
---|
399 | static gboolean |
---|
400 | remove_one_callback (gpointer key, |
---|
401 | gpointer value, |
---|
402 | gpointer callback_data) |
---|
403 | { |
---|
404 | char *callback_name; |
---|
405 | CallbackInfo *callback; |
---|
406 | |
---|
407 | callback_name = key; |
---|
408 | callback = value; |
---|
409 | |
---|
410 | g_free (callback_name); |
---|
411 | callback_info_unref (callback); |
---|
412 | |
---|
413 | return TRUE; |
---|
414 | } |
---|
415 | |
---|
416 | static void |
---|
417 | clear_stack_table (GHashTable *stack_table) |
---|
418 | { |
---|
419 | g_hash_table_foreach_remove (stack_table, |
---|
420 | remove_one_stack, |
---|
421 | NULL); |
---|
422 | } |
---|
423 | |
---|
424 | static void |
---|
425 | clear_callback_table (GHashTable *stack_table) |
---|
426 | { |
---|
427 | g_hash_table_foreach_remove (stack_table, |
---|
428 | remove_one_callback, |
---|
429 | NULL); |
---|
430 | } |
---|
431 | |
---|
432 | |
---|
433 | /* Functions to inialize global and per-thread data on demand and |
---|
434 | * associated cleanup functions. |
---|
435 | */ |
---|
436 | static void |
---|
437 | stack_table_destroy (gpointer specific) |
---|
438 | { |
---|
439 | GHashTable *stack_table; |
---|
440 | |
---|
441 | stack_table = specific; |
---|
442 | |
---|
443 | pthread_mutex_lock (&callback_table_lock); |
---|
444 | g_hash_table_remove (stack_tables_to_free, stack_table); |
---|
445 | pthread_mutex_unlock (&callback_table_lock); |
---|
446 | |
---|
447 | clear_stack_table (stack_table); |
---|
448 | g_hash_table_destroy (stack_table); |
---|
449 | } |
---|
450 | |
---|
451 | static gboolean |
---|
452 | stack_table_free_hr_func (gpointer key, |
---|
453 | gpointer value, |
---|
454 | gpointer callback_data) |
---|
455 | { |
---|
456 | GHashTable *table; |
---|
457 | |
---|
458 | table = key; |
---|
459 | |
---|
460 | clear_stack_table (table); |
---|
461 | g_hash_table_destroy (table); |
---|
462 | |
---|
463 | return TRUE; |
---|
464 | } |
---|
465 | |
---|
466 | |
---|
467 | static void |
---|
468 | free_stack_tables_to_free (void) |
---|
469 | { |
---|
470 | pthread_mutex_lock (&callback_table_lock); |
---|
471 | g_hash_table_foreach_remove (stack_tables_to_free, stack_table_free_hr_func , NULL); |
---|
472 | g_hash_table_destroy (stack_tables_to_free); |
---|
473 | pthread_mutex_unlock (&callback_table_lock); |
---|
474 | } |
---|
475 | |
---|
476 | static void |
---|
477 | stack_keys_alloc (void) |
---|
478 | { |
---|
479 | pthread_key_create (&callback_stacks_key, stack_table_destroy); |
---|
480 | pthread_key_create (&async_callback_stacks_key, stack_table_destroy); |
---|
481 | pthread_key_create (&in_async_thread_key, NULL); |
---|
482 | |
---|
483 | pthread_mutex_lock (&callback_table_lock); |
---|
484 | stack_tables_to_free = g_hash_table_new (g_direct_hash, g_direct_equal); |
---|
485 | pthread_mutex_unlock (&callback_table_lock); |
---|
486 | |
---|
487 | g_atexit (free_stack_tables_to_free); |
---|
488 | } |
---|
489 | |
---|
490 | static void |
---|
491 | free_default_callbacks (void) |
---|
492 | { |
---|
493 | pthread_mutex_lock (&callback_table_lock); |
---|
494 | |
---|
495 | clear_callback_table (default_callbacks); |
---|
496 | g_hash_table_destroy (default_callbacks); |
---|
497 | |
---|
498 | clear_callback_table (default_async_callbacks); |
---|
499 | g_hash_table_destroy (default_async_callbacks); |
---|
500 | |
---|
501 | pthread_mutex_unlock (&callback_table_lock); |
---|
502 | } |
---|
503 | |
---|
504 | /* This function should only be called with the mutex held. */ |
---|
505 | static void |
---|
506 | initialize_global_if_needed (void) |
---|
507 | { |
---|
508 | if (default_callbacks == NULL) { |
---|
509 | default_callbacks = g_hash_table_new (g_str_hash, g_str_equal); |
---|
510 | default_async_callbacks = g_hash_table_new (g_str_hash, g_str_equal); |
---|
511 | |
---|
512 | g_atexit (free_default_callbacks); |
---|
513 | } |
---|
514 | } |
---|
515 | |
---|
516 | /* No need for a mutex, since it's all per-thread data. */ |
---|
517 | static void |
---|
518 | initialize_per_thread_if_needed (void) |
---|
519 | { |
---|
520 | /* Initialize keys for thread-specific data, once per program. */ |
---|
521 | pthread_once (&stack_keys_once, stack_keys_alloc); |
---|
522 | |
---|
523 | /* Initialize per-thread data, if needed. */ |
---|
524 | if (pthread_getspecific (callback_stacks_key) == NULL) { |
---|
525 | pthread_mutex_lock (&callback_table_lock); |
---|
526 | pthread_setspecific (callback_stacks_key, |
---|
527 | g_hash_table_new (g_str_hash, g_str_equal)); |
---|
528 | g_hash_table_insert (stack_tables_to_free, |
---|
529 | pthread_getspecific (callback_stacks_key), |
---|
530 | GINT_TO_POINTER (1)); |
---|
531 | pthread_mutex_unlock (&callback_table_lock); |
---|
532 | } |
---|
533 | |
---|
534 | if (pthread_getspecific (async_callback_stacks_key) == NULL) { |
---|
535 | pthread_mutex_lock (&callback_table_lock); |
---|
536 | pthread_setspecific (async_callback_stacks_key, |
---|
537 | g_hash_table_new (g_str_hash, g_str_equal)); |
---|
538 | g_hash_table_insert (stack_tables_to_free, |
---|
539 | pthread_getspecific (async_callback_stacks_key), |
---|
540 | GINT_TO_POINTER (1)); |
---|
541 | pthread_mutex_unlock (&callback_table_lock); |
---|
542 | } |
---|
543 | } |
---|
544 | |
---|
545 | /* -- Public entry points -- */ |
---|
546 | |
---|
547 | /** |
---|
548 | * GnomeVFSModuleCallback |
---|
549 | * @in: The in argument for this callback; the exact type depends on the specific callback |
---|
550 | * @in_size: Size of the in argument; useful for sanity-checking |
---|
551 | * @out: The out argument for this callback; the exact type depends on the specific callback |
---|
552 | * @out_size: Size of the out argument; useful for sanity-checking |
---|
553 | * @callback_data: The @callback_data specified when this callback was set |
---|
554 | * |
---|
555 | * This is the type of a callback function that gets set for a module |
---|
556 | * callback. |
---|
557 | * |
---|
558 | * When the callback is invoked, the user function is called with an |
---|
559 | * @in argument, the exact type of which depends on the specific |
---|
560 | * callback. It is generally a pointer to a struct with several fields |
---|
561 | * that provide information to the callback. |
---|
562 | * |
---|
563 | * The @out argument is used to return a values from the |
---|
564 | * callback. Once again the exact type depends on the specific |
---|
565 | * callback. It is generally a pointer to a pre-allocated struct with |
---|
566 | * several fields that the callback function should fill in before |
---|
567 | * returning. |
---|
568 | * |
---|
569 | */ |
---|
570 | |
---|
571 | |
---|
572 | /** |
---|
573 | * GnomeVFSModuleCallbackResponse |
---|
574 | * @response_data: Pass the @response_data argument originally passed to the async callback |
---|
575 | * |
---|
576 | * This is the type of the response function passed to a |
---|
577 | * GnomeVFSAsyncModuleCallback(). It should be called when the async |
---|
578 | * callback has completed. |
---|
579 | */ |
---|
580 | |
---|
581 | |
---|
582 | /** |
---|
583 | * GnomeVFSAsyncModuleCallback |
---|
584 | * @in: The in argument for this callback; the exact type depends on the specific callback |
---|
585 | * @in_size: Size of the in argument; useful for sanity-checking |
---|
586 | * @out: The out argument for this callback; the exact type depends on the specific callback |
---|
587 | * @out_size: Size of the out argument; useful for sanity-checking |
---|
588 | * @callback_data: The @callback_data specified when this callback was set |
---|
589 | * @response: Response function to call when the callback is completed |
---|
590 | * @response_data: Argument to pass to @response |
---|
591 | * |
---|
592 | * This is the type of a callback function that gets set for an async |
---|
593 | * module callback. |
---|
594 | * |
---|
595 | * Such callbacks are useful when you are using the API and want |
---|
596 | * callbacks to be handled from the main thread, for instance if they |
---|
597 | * need to put up a dialog. |
---|
598 | * |
---|
599 | * Like a GnomeVFSModuleCallback(), an async callback has @in and @out |
---|
600 | * arguments for passing data into and out of the callback. However, |
---|
601 | * an async callback does not need to fill in the @out argument before |
---|
602 | * returning. Instead, it can arrange to have the work done from a |
---|
603 | * callback on the main loop, from another thread, etc. The @response |
---|
604 | * function should be called by whatever code finishes the work of the |
---|
605 | * callback with @response_data as an argument once the @out argument |
---|
606 | * is filled in and the callback is done. |
---|
607 | * |
---|
608 | * The @in and @out arguments are guaranteed to remain valid until the |
---|
609 | * @response function is called. |
---|
610 | * |
---|
611 | */ |
---|
612 | |
---|
613 | |
---|
614 | |
---|
615 | |
---|
616 | |
---|
617 | /** |
---|
618 | * gnome_vfs_module_callback_set_default |
---|
619 | * @callback_name: The name of the module callback to set |
---|
620 | * @callback: The function to call when the callback is invoked |
---|
621 | * @callback_data: Pointer to pass as the @callback_data argument to @callback |
---|
622 | * @destroy_notify: Function to call when @callback_data is to be freed. |
---|
623 | * |
---|
624 | * Set the default callback for @callback_name to |
---|
625 | * @callback. @callback will be called with @callback_data on the |
---|
626 | * same thread as the gnome-vfs operation that invokes it. The default |
---|
627 | * value is shared for all threads, but setting it is thread-safe. |
---|
628 | * |
---|
629 | * Use this function if you want to set a handler to be used by your |
---|
630 | * whole application. You can use gnome_vfs_module_callback_push() to |
---|
631 | * set a callback function that will temporarily override the default |
---|
632 | * on the current thread instead. Or you can also use |
---|
633 | * gnome_vfs_async_module_callback_set_default() to set an async |
---|
634 | * callback function. |
---|
635 | * |
---|
636 | * Note: @destroy_notify may be called on any thread - it is not |
---|
637 | * guaranteed to be called on the main thread. |
---|
638 | * |
---|
639 | **/ |
---|
640 | void |
---|
641 | gnome_vfs_module_callback_set_default (const char *callback_name, |
---|
642 | GnomeVFSModuleCallback callback, |
---|
643 | gpointer callback_data, |
---|
644 | GDestroyNotify destroy_notify) |
---|
645 | { |
---|
646 | CallbackInfo *callback_info; |
---|
647 | |
---|
648 | callback_info = callback_info_new (callback, callback_data, destroy_notify); |
---|
649 | |
---|
650 | pthread_mutex_lock (&callback_table_lock); |
---|
651 | |
---|
652 | initialize_global_if_needed (); |
---|
653 | insert_callback_into_table (default_callbacks, callback_name, callback_info); |
---|
654 | |
---|
655 | pthread_mutex_unlock (&callback_table_lock); |
---|
656 | |
---|
657 | callback_info_unref (callback_info); |
---|
658 | } |
---|
659 | |
---|
660 | /** |
---|
661 | * gnome_vfs_module_callback_push |
---|
662 | * @callback_name: The name of the module callback to set temporarily |
---|
663 | * @callback: The function to call when the callback is invoked |
---|
664 | * @callback_data: Pointer to pass as the @callback_data argument to @callback |
---|
665 | * @destroy_notify: Function to call when @callback_data is to be freed. |
---|
666 | * |
---|
667 | * Set @callback as a temprary handler for @callback_name. @callback |
---|
668 | * will be called with @callback_data on the same thread as the |
---|
669 | * gnome-vfs operation that invokes it. The temporary handler is set |
---|
670 | * per-thread. |
---|
671 | * |
---|
672 | * gnome_vfs_module_callback_pop() removes the most recently set |
---|
673 | * temporary handler. The temporary handlers are treated as a first-in |
---|
674 | * first-out stack. |
---|
675 | * |
---|
676 | * Use this function to set a temporary callback handler for a single |
---|
677 | * call or a few calls. You can use |
---|
678 | * gnome_vfs_module_callback_set_default() to set a callback function |
---|
679 | * that will establish a permanent global setting for all threads |
---|
680 | * instead. |
---|
681 | * |
---|
682 | * Note: @destroy_notify may be called on any thread - it is not |
---|
683 | * guaranteed to be called on the main thread. |
---|
684 | * |
---|
685 | **/ |
---|
686 | void |
---|
687 | gnome_vfs_module_callback_push (const char *callback_name, |
---|
688 | GnomeVFSModuleCallback callback, |
---|
689 | gpointer callback_data, |
---|
690 | GDestroyNotify notify) |
---|
691 | { |
---|
692 | CallbackInfo *callback_info; |
---|
693 | |
---|
694 | initialize_per_thread_if_needed (); |
---|
695 | |
---|
696 | callback_info = callback_info_new (callback, callback_data, notify); |
---|
697 | push_callback_into_stack_table (pthread_getspecific (callback_stacks_key), |
---|
698 | callback_name, |
---|
699 | callback_info); |
---|
700 | callback_info_unref (callback_info); |
---|
701 | } |
---|
702 | |
---|
703 | /** |
---|
704 | * gnome_vfs_module_callback_pop |
---|
705 | * @callback_name: The name of the module callback to remove a temporary handler for |
---|
706 | * |
---|
707 | * Remove the temporary handler for @callback_name most recently set |
---|
708 | * with gnome_vfs_module_callback_push(). If another temporary |
---|
709 | * handler was previously set on the same thread, it becomes the |
---|
710 | * current handler. Otherwise, the default handler, if any, becomes |
---|
711 | * current. |
---|
712 | * |
---|
713 | * The temporary handlers are treated as a first-in first-out |
---|
714 | * stack. |
---|
715 | * |
---|
716 | **/ |
---|
717 | void |
---|
718 | gnome_vfs_module_callback_pop (const char *callback_name) |
---|
719 | { |
---|
720 | initialize_per_thread_if_needed (); |
---|
721 | pop_stack_table (pthread_getspecific (callback_stacks_key), |
---|
722 | callback_name); |
---|
723 | } |
---|
724 | |
---|
725 | |
---|
726 | /** |
---|
727 | * gnome_vfs_async_module_callback_set_default |
---|
728 | * @callback_name: The name of the async module callback to set |
---|
729 | * @callback: The function to call when the callback is invoked |
---|
730 | * @callback_data: Pointer to pass as the @callback_data argument to @callback |
---|
731 | * @destroy_notify: Function to call when @callback_data is to be freed. |
---|
732 | * |
---|
733 | * Set the default async callback for @callback_name to |
---|
734 | * @callback. @callback will be called with @callback_data |
---|
735 | * from a callback on the main thread. It will be passed a response |
---|
736 | * function which should be called to signal completion of the callback. |
---|
737 | * The callback function itself may return in the meantime. |
---|
738 | * |
---|
739 | * The default value is shared for all threads, but setting it is |
---|
740 | * thread-safe. |
---|
741 | * |
---|
742 | * Use this function if you want to globally set a callback handler |
---|
743 | * for use with async operations. |
---|
744 | * |
---|
745 | * You can use gnome_vfs_async_module_callback_push() to set an async |
---|
746 | * callback function that will temporarily override the default on the |
---|
747 | * current thread instead. Or you can also use |
---|
748 | * gnome_vfs_module_callback_set_default() to set a regular callback |
---|
749 | * function. |
---|
750 | * |
---|
751 | * Note: @destroy_notify may be called on any thread - it is not |
---|
752 | * guaranteed to be called on the main thread. |
---|
753 | * |
---|
754 | **/ |
---|
755 | void |
---|
756 | gnome_vfs_async_module_callback_set_default (const char *callback_name, |
---|
757 | GnomeVFSAsyncModuleCallback callback, |
---|
758 | gpointer callback_data, |
---|
759 | GDestroyNotify notify) |
---|
760 | { |
---|
761 | CallbackInfo *callback_info; |
---|
762 | |
---|
763 | callback_info = async_callback_info_new (callback, callback_data, notify); |
---|
764 | |
---|
765 | pthread_mutex_lock (&callback_table_lock); |
---|
766 | |
---|
767 | initialize_global_if_needed (); |
---|
768 | insert_callback_into_table (default_async_callbacks, callback_name, callback_info); |
---|
769 | |
---|
770 | pthread_mutex_unlock (&callback_table_lock); |
---|
771 | |
---|
772 | callback_info_unref (callback_info); |
---|
773 | } |
---|
774 | |
---|
775 | /** |
---|
776 | * gnome_vfs_async_module_callback_push |
---|
777 | * @callback_name: The name of the module callback to set temporarily |
---|
778 | * @callback: The function to call when the callback is invoked |
---|
779 | * @callback_data: Pointer to pass as the @callback_data argument to @callback |
---|
780 | * @destroy_notify: Function to call when @callback_data is to be freed. |
---|
781 | * |
---|
782 | * Set @callback_func as a temprary async handler for |
---|
783 | * @callback_name. @callback will be called with @callback_data |
---|
784 | * from a callback on the main thread. It will be passed a response |
---|
785 | * function which should be called to signal completion of the |
---|
786 | * callback. The callback function itself may return in the meantime. |
---|
787 | * |
---|
788 | * The temporary async handler is set per-thread. |
---|
789 | * |
---|
790 | * gnome_vfs_async_module_callback_pop() removes the most recently set |
---|
791 | * temporary temporary handler. The temporary async handlers are |
---|
792 | * treated as a first-in first-out stack. |
---|
793 | * |
---|
794 | * Use this function to set a temporary async callback handler for a |
---|
795 | * single call or a few calls. You can use |
---|
796 | * gnome_vfs_async_module_callback_set_default() to set an async |
---|
797 | * callback function that will establish a permanent global setting |
---|
798 | * for all threads instead. |
---|
799 | * |
---|
800 | * Note: @destroy_notify may be called on any thread - it is not |
---|
801 | * guaranteed to be called on the main thread. |
---|
802 | * |
---|
803 | **/ |
---|
804 | void |
---|
805 | gnome_vfs_async_module_callback_push (const char *callback_name, |
---|
806 | GnomeVFSAsyncModuleCallback callback, |
---|
807 | gpointer callback_data, |
---|
808 | GDestroyNotify notify) |
---|
809 | { |
---|
810 | CallbackInfo *callback_info; |
---|
811 | |
---|
812 | initialize_per_thread_if_needed (); |
---|
813 | |
---|
814 | callback_info = async_callback_info_new (callback, callback_data, notify); |
---|
815 | |
---|
816 | push_callback_into_stack_table (pthread_getspecific (async_callback_stacks_key), |
---|
817 | callback_name, |
---|
818 | callback_info); |
---|
819 | |
---|
820 | callback_info_unref (callback_info); |
---|
821 | } |
---|
822 | |
---|
823 | /** |
---|
824 | * gnome_vfs_async_module_callback_pop |
---|
825 | * @callback_name: The name of the module callback to remove a temporary handler for |
---|
826 | * |
---|
827 | * Remove the temporary async handler for @callback_name most recently |
---|
828 | * set with gnome_vfs_async_module_callback_push(). If another |
---|
829 | * temporary async handler was previously set on the same thread, it |
---|
830 | * becomes the current handler. Otherwise, the default async handler, |
---|
831 | * if any, becomes current. |
---|
832 | * |
---|
833 | * The temporary async handlers are treated as a first-in first-out |
---|
834 | * stack. |
---|
835 | * |
---|
836 | **/ |
---|
837 | void |
---|
838 | gnome_vfs_async_module_callback_pop (const char *callback_name) |
---|
839 | { |
---|
840 | initialize_per_thread_if_needed (); |
---|
841 | pop_stack_table (pthread_getspecific (async_callback_stacks_key), |
---|
842 | callback_name); |
---|
843 | } |
---|
844 | |
---|
845 | |
---|
846 | /* -- Module-only entry points -- */ |
---|
847 | |
---|
848 | /** |
---|
849 | * gnome_vfs_module_callback_invoke |
---|
850 | * @callback_name: The name of the module callback to set |
---|
851 | * @in: In argument - type dependent on the specific callback |
---|
852 | * @in_size: Size of the in argument |
---|
853 | * @out: Out argument - type dependent on the specific callback |
---|
854 | * @out_size: Size of the out argument |
---|
855 | * |
---|
856 | * Invoke a default callback for @callback_name, with in arguments |
---|
857 | * specified by @in and @in_size, and out arguments specified by @out |
---|
858 | * and @out_size. |
---|
859 | * |
---|
860 | * This function should only be called by gnome-vfs modules. |
---|
861 | * |
---|
862 | * If this function is called from an async job thread, it will invoke |
---|
863 | * the current async handler for @callback_name, if any. If no async |
---|
864 | * handler is set, or the function is not called from an async job |
---|
865 | * thread, the regular handler, if any, will be invoked instead. If no |
---|
866 | * handler at all is found for @callback_name, the function returns |
---|
867 | * FALSE. |
---|
868 | * |
---|
869 | * Return value: TRUE if a callback was invoked, FALSE if none was set. |
---|
870 | **/ |
---|
871 | gboolean |
---|
872 | gnome_vfs_module_callback_invoke (const char *callback_name, |
---|
873 | gconstpointer in, |
---|
874 | gsize in_size, |
---|
875 | gpointer out, |
---|
876 | gsize out_size) |
---|
877 | { |
---|
878 | CallbackInfo *callback; |
---|
879 | gboolean invoked; |
---|
880 | GSList *stack; |
---|
881 | |
---|
882 | callback = NULL; |
---|
883 | |
---|
884 | initialize_per_thread_if_needed (); |
---|
885 | |
---|
886 | if (pthread_getspecific (in_async_thread_key) != NULL) { |
---|
887 | stack = g_hash_table_lookup (pthread_getspecific (async_callback_stacks_key), |
---|
888 | callback_name); |
---|
889 | |
---|
890 | if (stack != NULL) { |
---|
891 | callback = stack->data; |
---|
892 | g_assert (callback != NULL); |
---|
893 | callback_info_ref (callback); |
---|
894 | } else { |
---|
895 | pthread_mutex_lock (&callback_table_lock); |
---|
896 | initialize_global_if_needed (); |
---|
897 | callback = g_hash_table_lookup (default_async_callbacks, callback_name); |
---|
898 | if (callback != NULL) { |
---|
899 | callback_info_ref (callback); |
---|
900 | } |
---|
901 | pthread_mutex_unlock (&callback_table_lock); |
---|
902 | } |
---|
903 | } |
---|
904 | |
---|
905 | if (callback == NULL) { |
---|
906 | stack = g_hash_table_lookup (pthread_getspecific (callback_stacks_key), |
---|
907 | callback_name); |
---|
908 | |
---|
909 | if (stack != NULL) { |
---|
910 | callback = stack->data; |
---|
911 | g_assert (callback != NULL); |
---|
912 | callback_info_ref (callback); |
---|
913 | } else { |
---|
914 | pthread_mutex_lock (&callback_table_lock); |
---|
915 | initialize_global_if_needed (); |
---|
916 | callback = g_hash_table_lookup (default_callbacks, callback_name); |
---|
917 | if (callback != NULL) { |
---|
918 | callback_info_ref (callback); |
---|
919 | } |
---|
920 | pthread_mutex_unlock (&callback_table_lock); |
---|
921 | } |
---|
922 | } |
---|
923 | |
---|
924 | if (callback == NULL) { |
---|
925 | invoked = FALSE; |
---|
926 | } else { |
---|
927 | callback->callback (in, in_size, out, out_size, callback->callback_data); |
---|
928 | invoked = TRUE; |
---|
929 | callback_info_unref (callback); |
---|
930 | } |
---|
931 | |
---|
932 | return invoked; |
---|
933 | } |
---|
934 | |
---|
935 | |
---|
936 | /* -- Private entry points -- */ |
---|
937 | |
---|
938 | /* (used by job mechanism to implement callback |
---|
939 | * state copying semantics for async jobs. |
---|
940 | */ |
---|
941 | |
---|
942 | GnomeVFSModuleCallbackStackInfo * |
---|
943 | gnome_vfs_module_callback_get_stack_info (void) |
---|
944 | { |
---|
945 | GnomeVFSModuleCallbackStackInfo *stack_info; |
---|
946 | |
---|
947 | stack_info = g_new (GnomeVFSModuleCallbackStackInfo, 1); |
---|
948 | |
---|
949 | pthread_mutex_lock (&callback_table_lock); |
---|
950 | initialize_global_if_needed (); |
---|
951 | stack_info->current_callbacks = duplicate_callback_table (default_callbacks); |
---|
952 | stack_info->current_async_callbacks = duplicate_callback_table (default_async_callbacks); |
---|
953 | pthread_mutex_unlock (&callback_table_lock); |
---|
954 | |
---|
955 | initialize_per_thread_if_needed (); |
---|
956 | copy_callback_stack_tops (pthread_getspecific (callback_stacks_key), |
---|
957 | stack_info->current_callbacks); |
---|
958 | copy_callback_stack_tops (pthread_getspecific (async_callback_stacks_key), |
---|
959 | stack_info->current_async_callbacks); |
---|
960 | |
---|
961 | return stack_info; |
---|
962 | } |
---|
963 | |
---|
964 | void |
---|
965 | gnome_vfs_module_callback_free_stack_info (GnomeVFSModuleCallbackStackInfo *stack_info) |
---|
966 | { |
---|
967 | clear_callback_table (stack_info->current_callbacks); |
---|
968 | g_hash_table_destroy (stack_info->current_callbacks); |
---|
969 | clear_callback_table (stack_info->current_async_callbacks); |
---|
970 | g_hash_table_destroy (stack_info->current_async_callbacks); |
---|
971 | |
---|
972 | g_free (stack_info); |
---|
973 | } |
---|
974 | |
---|
975 | void |
---|
976 | gnome_vfs_module_callback_use_stack_info (GnomeVFSModuleCallbackStackInfo *stack_info) |
---|
977 | { |
---|
978 | initialize_per_thread_if_needed (); |
---|
979 | copy_callback_table_to_stack_table (stack_info->current_callbacks, |
---|
980 | pthread_getspecific (callback_stacks_key)); |
---|
981 | copy_callback_table_to_stack_table (stack_info->current_async_callbacks, |
---|
982 | pthread_getspecific (async_callback_stacks_key)); |
---|
983 | } |
---|
984 | |
---|
985 | void |
---|
986 | gnome_vfs_module_callback_clear_stacks (void) |
---|
987 | { |
---|
988 | initialize_per_thread_if_needed (); |
---|
989 | clear_stack_table (pthread_getspecific (callback_stacks_key)); |
---|
990 | clear_stack_table (pthread_getspecific (async_callback_stacks_key)); |
---|
991 | } |
---|
992 | |
---|
993 | void |
---|
994 | gnome_vfs_module_callback_set_in_async_thread (gboolean in_async_thread) |
---|
995 | { |
---|
996 | initialize_per_thread_if_needed (); |
---|
997 | pthread_setspecific (in_async_thread_key, GINT_TO_POINTER (in_async_thread)); |
---|
998 | } |
---|