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