source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-module-callback.c @ 17128

Revision 17128, 27.8 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17127, which included commits to RCS files with non-trunk default branches.
Line 
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
39typedef struct CallbackInfo {
40        GnomeVFSModuleCallback callback;
41        gpointer callback_data;
42        GDestroyNotify destroy_notify;
43        int ref_count;
44} CallbackInfo;
45
46typedef struct AsyncCallbackInfo {
47        GnomeVFSAsyncModuleCallback callback;
48        gpointer callback_data;
49        GDestroyNotify destroy_notify;
50} AsyncCallbackInfo;
51
52typedef struct CallbackResponseData {
53        gboolean done;
54} CallbackResponseData;
55
56struct GnomeVFSModuleCallbackStackInfo {
57        GHashTable *current_callbacks;
58        GHashTable *current_async_callbacks;
59};
60
61
62/* -- Global variables -- */
63
64static pthread_mutex_t callback_table_lock = PTHREAD_MUTEX_INITIALIZER;
65static GHashTable *default_callbacks = NULL;
66static GHashTable *default_async_callbacks = NULL;
67static GHashTable *stack_tables_to_free = NULL;
68
69static pthread_once_t stack_keys_once = PTHREAD_ONCE_INIT;
70static pthread_key_t callback_stacks_key;
71static pthread_key_t async_callback_stacks_key;
72static pthread_key_t in_async_thread_key;
73
74static pthread_mutex_t async_callback_mutex = PTHREAD_MUTEX_INITIALIZER;
75static pthread_cond_t async_callback_cond = PTHREAD_COND_INITIALIZER;
76
77/* -- Helper functions -- */
78
79/* managing callback structs */
80
81static CallbackInfo *
82callback_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
100static void
101callback_info_ref (CallbackInfo *callback)
102{
103        callback->ref_count++;
104}
105
106static void
107callback_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
121static void
122async_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
134static void
135async_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
167static void
168async_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
182static CallbackInfo *
183async_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 */
201static void
202insert_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
225static void
226push_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
254static void
255pop_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
295static void
296copy_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
311static void
312copy_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
329static void
330copy_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
345static GHashTable *
346duplicate_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
356static void
357copy_callback_stack_tops (GHashTable *source,
358                          GHashTable *target)
359{
360        g_hash_table_foreach (source,
361                              copy_one_stack_top,
362                              target);
363}
364
365static void
366copy_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
374static void
375callback_info_unref_func (gpointer data,
376                          gpointer callback_data)
377{
378        callback_info_unref ((CallbackInfo *) data);
379}
380
381static gboolean
382remove_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
399static gboolean
400remove_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
416static void
417clear_stack_table (GHashTable *stack_table)
418{
419        g_hash_table_foreach_remove (stack_table,
420                                     remove_one_stack,
421                                     NULL);
422}
423
424static void
425clear_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 */
436static void
437stack_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
451static gboolean
452stack_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
467static void
468free_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
476static void
477stack_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
490static void
491free_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. */
505static void
506initialize_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. */
517static void
518initialize_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 **/
640void
641gnome_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 **/
686void
687gnome_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 **/
717void
718gnome_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 **/
755void
756gnome_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 **/
804void
805gnome_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 **/
837void
838gnome_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 **/
871gboolean
872gnome_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
942GnomeVFSModuleCallbackStackInfo *
943gnome_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
964void
965gnome_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
975void
976gnome_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
985void
986gnome_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
993void
994gnome_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}
Note: See TracBrowser for help on using the repository browser.