1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 Copyright (C) 2001 Eazel, Inc
5 Copyright (C) 2001 Maciej Stachowiak
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.
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.
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.
22 Author: Michael Fleming <mfleming@eazel.com>
23 Maciej Stachowiak <mjs@enoisehavoc.org>
28 #include "gnome-vfs-module-callback.h"
30 #include "gnome-vfs-module-callback-module-api.h"
31 #include "gnome-vfs-module-callback-private.h"
32 #include "gnome-vfs-backend.h"
34 /* -- Private data structure declarations -- */
36 typedef struct CallbackInfo {
37 GnomeVFSModuleCallback callback;
38 gpointer callback_data;
39 GDestroyNotify destroy_notify;
43 typedef struct AsyncCallbackInfo {
44 GnomeVFSAsyncModuleCallback callback;
45 gpointer callback_data;
46 GDestroyNotify destroy_notify;
49 typedef struct CallbackResponseData {
51 } CallbackResponseData;
53 struct GnomeVFSModuleCallbackStackInfo {
54 GHashTable *current_callbacks;
55 GHashTable *current_async_callbacks;
59 /* -- Global variables -- */
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;
66 static GPrivate *callback_stacks_key;
67 static GPrivate *async_callback_stacks_key;
68 static GPrivate *in_async_thread_key;
70 static GCond *async_callback_cond;
71 static GStaticMutex async_callback_lock = G_STATIC_MUTEX_INIT;
73 /* -- Helper functions -- */
75 /* managing callback structs */
78 callback_info_new (GnomeVFSModuleCallback callback_func,
79 gpointer callback_data,
80 GDestroyNotify notify)
82 CallbackInfo *callback;
84 callback = g_new (CallbackInfo, 1);
86 callback->callback = callback_func;
87 callback->callback_data = callback_data;
88 callback->destroy_notify = notify;
89 callback->ref_count = 1;
97 callback_info_ref (CallbackInfo *callback)
99 callback->ref_count++;
103 callback_info_unref (CallbackInfo *callback)
105 callback->ref_count--;
107 if (callback->ref_count == 0) {
108 if (callback->destroy_notify != NULL) {
109 callback->destroy_notify (callback->callback_data);
115 /* code for handling async callbacks */
118 async_callback_response (gpointer data)
120 CallbackResponseData *response_data;
122 g_static_mutex_lock (&async_callback_lock);
123 response_data = data;
124 response_data->done = TRUE;
125 g_cond_broadcast (async_callback_cond);
127 g_static_mutex_unlock (&async_callback_lock);
131 async_callback_invoke (gconstpointer in,
135 gpointer callback_data)
137 AsyncCallbackInfo *async_callback;
138 CallbackResponseData response_data;
140 async_callback = callback_data;
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.
148 g_static_mutex_lock (&async_callback_lock);
149 response_data.done = FALSE;
150 _gnome_vfs_dispatch_module_callback (async_callback->callback,
153 async_callback->callback_data,
154 async_callback_response,
156 while (!response_data.done) {
157 g_cond_wait (async_callback_cond,
158 g_static_mutex_get_mutex (&async_callback_lock));
161 g_static_mutex_unlock (&async_callback_lock);
165 async_callback_destroy (gpointer callback_data)
167 AsyncCallbackInfo *async_callback;
169 async_callback = callback_data;
171 if (async_callback->destroy_notify != NULL) {
172 async_callback->destroy_notify (async_callback->callback_data);
175 g_free (async_callback);
179 static CallbackInfo *
180 async_callback_info_new (GnomeVFSAsyncModuleCallback callback_func,
181 gpointer callback_data,
182 GDestroyNotify notify)
184 AsyncCallbackInfo *async_callback;
186 async_callback = g_new (AsyncCallbackInfo, 1);
188 async_callback->callback = callback_func;
189 async_callback->callback_data = callback_data;
190 async_callback->destroy_notify = notify;
192 return callback_info_new (async_callback_invoke, async_callback, async_callback_destroy);
197 /* Adding items to hash tables or stack tables */
199 insert_callback_into_table (GHashTable *table,
200 const char *callback_name,
201 CallbackInfo *callback)
206 callback_info_ref (callback);
208 if (g_hash_table_lookup_extended (table,
212 g_hash_table_remove (table, orig_key);
214 callback_info_unref ((CallbackInfo *) old_value);
217 g_hash_table_insert (table,
218 g_strdup (callback_name),
223 push_callback_into_stack_table (GHashTable *table,
224 const char *callback_name,
225 CallbackInfo *callback)
231 callback_info_ref (callback);
233 if (g_hash_table_lookup_extended (table,
237 g_hash_table_remove (table, orig_key);
244 stack = g_slist_prepend (stack, callback);
246 g_hash_table_insert (table,
247 g_strdup (callback_name),
252 pop_stack_table (GHashTable *table,
253 const char *callback_name)
260 if (g_hash_table_lookup_extended (table,
264 g_hash_table_remove (table, orig_key);
271 /* Would not be in the hash table if it were NULL */
272 g_assert (stack != NULL);
274 callback_info_unref ((CallbackInfo *) stack->data);
278 g_slist_free_1 (first_link);
281 g_hash_table_insert (table,
282 g_strdup (callback_name),
287 /* Functions to copy, duplicate and clear callback tables and callback
288 * stack tables, and helpers for these functions.
292 copy_one_stack_top (gpointer key,
294 gpointer callback_data)
297 const char *callback_name;
298 CallbackInfo *callback;
303 callback = stack->data;
304 table = callback_data;
306 insert_callback_into_table (table, callback_name, callback);
310 copy_one_callback_to_stack (gpointer key,
312 gpointer callback_data)
314 const char *callback_name;
315 CallbackInfo *callback;
320 table = callback_data;
322 push_callback_into_stack_table (table, callback_name, callback);
326 copy_callback_stack_tops (GHashTable *source,
329 g_hash_table_foreach (source,
335 copy_callback_table_to_stack_table (GHashTable *source,
338 g_hash_table_foreach (source,
339 copy_one_callback_to_stack,
344 callback_info_unref_func (gpointer data,
345 gpointer callback_data)
347 callback_info_unref ((CallbackInfo *) data);
351 remove_one_stack (gpointer key,
353 gpointer callback_data)
361 g_free (callback_name);
362 g_slist_foreach (stack, callback_info_unref_func, NULL);
363 g_slist_free (stack);
369 remove_one_callback (gpointer key,
371 gpointer callback_data)
374 CallbackInfo *callback;
379 g_free (callback_name);
380 callback_info_unref (callback);
386 clear_stack_table (GHashTable *stack_table)
388 g_hash_table_foreach_remove (stack_table,
394 clear_callback_table (GHashTable *stack_table)
396 g_hash_table_foreach_remove (stack_table,
402 /* Functions to inialize global and per-thread data on demand and
403 * associated cleanup functions.
406 stack_table_destroy (gpointer specific)
408 GHashTable *stack_table;
410 stack_table = specific;
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);
416 clear_stack_table (stack_table);
417 g_hash_table_destroy (stack_table);
421 stack_table_free_hr_func (gpointer key,
423 gpointer callback_data)
429 clear_stack_table (table);
430 g_hash_table_destroy (table);
437 free_stack_tables_to_free (void)
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);
446 _gnome_vfs_module_callback_private_init (void)
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);
452 stack_tables_to_free = g_hash_table_new (g_direct_hash, g_direct_equal);
454 async_callback_cond = g_cond_new ();
456 g_atexit (free_stack_tables_to_free);
460 free_default_callbacks (void)
462 g_static_mutex_lock (&callback_table_lock);
464 clear_callback_table (default_callbacks);
465 g_hash_table_destroy (default_callbacks);
467 clear_callback_table (default_async_callbacks);
468 g_hash_table_destroy (default_async_callbacks);
470 g_static_mutex_unlock (&callback_table_lock);
473 /* This function should only be called with the mutex held. */
475 initialize_global_if_needed (void)
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);
481 g_atexit (free_default_callbacks);
485 /* No need for a mutex, since it's all per-thread data. */
487 initialize_per_thread_if_needed (void)
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);
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);
511 /* -- Public entry points -- */
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
521 * This is the type of a callback function that gets set for a module
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.
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
539 * GnomeVFSModuleCallbackResponse
540 * @response_data: Pass the @response_data argument originally passed to the async callback
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.
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
558 * This is the type of a callback function that gets set for an async
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.
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.
574 * The @in and @out arguments are guaranteed to remain valid until the
575 * @response function is called.
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.
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.
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
602 * Note: @destroy_notify may be called on any thread - it is not
603 * guaranteed to be called on the main thread.
607 gnome_vfs_module_callback_set_default (const char *callback_name,
608 GnomeVFSModuleCallback callback,
609 gpointer callback_data,
610 GDestroyNotify destroy_notify)
612 CallbackInfo *callback_info;
614 callback_info = callback_info_new (callback, callback_data, destroy_notify);
616 g_static_mutex_lock (&callback_table_lock);
618 initialize_global_if_needed ();
619 insert_callback_into_table (default_callbacks, callback_name, callback_info);
621 g_static_mutex_unlock (&callback_table_lock);
623 callback_info_unref (callback_info);
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.
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
638 * gnome_vfs_module_callback_pop() removes the most recently set
639 * temporary handler. The temporary handlers are treated as a first-in
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
648 * Note: @destroy_notify may be called on any thread - it is not
649 * guaranteed to be called on the main thread.
653 gnome_vfs_module_callback_push (const char *callback_name,
654 GnomeVFSModuleCallback callback,
655 gpointer callback_data,
656 GDestroyNotify notify)
658 CallbackInfo *callback_info;
660 initialize_per_thread_if_needed ();
662 callback_info = callback_info_new (callback, callback_data, notify);
663 push_callback_into_stack_table (g_private_get (callback_stacks_key),
666 callback_info_unref (callback_info);
670 * gnome_vfs_module_callback_pop
671 * @callback_name: The name of the module callback to remove a temporary handler for
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
679 * The temporary handlers are treated as a first-in first-out
684 gnome_vfs_module_callback_pop (const char *callback_name)
686 initialize_per_thread_if_needed ();
687 pop_stack_table (g_private_get (callback_stacks_key),
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.
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.
705 * The default value is shared for all threads, but setting it is
708 * Use this function if you want to globally set a callback handler
709 * for use with async operations.
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
717 * Note: @destroy_notify may be called on any thread - it is not
718 * guaranteed to be called on the main thread.
722 gnome_vfs_async_module_callback_set_default (const char *callback_name,
723 GnomeVFSAsyncModuleCallback callback,
724 gpointer callback_data,
725 GDestroyNotify notify)
727 CallbackInfo *callback_info;
729 callback_info = async_callback_info_new (callback, callback_data, notify);
731 g_static_mutex_lock (&callback_table_lock);
733 initialize_global_if_needed ();
734 insert_callback_into_table (default_async_callbacks, callback_name, callback_info);
736 g_static_mutex_unlock (&callback_table_lock);
738 callback_info_unref (callback_info);
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.
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.
754 * The temporary async handler is set per-thread.
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.
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.
766 * Note: @destroy_notify may be called on any thread - it is not
767 * guaranteed to be called on the main thread.
771 gnome_vfs_async_module_callback_push (const char *callback_name,
772 GnomeVFSAsyncModuleCallback callback,
773 gpointer callback_data,
774 GDestroyNotify notify)
776 CallbackInfo *callback_info;
778 initialize_per_thread_if_needed ();
780 callback_info = async_callback_info_new (callback, callback_data, notify);
782 push_callback_into_stack_table (g_private_get (async_callback_stacks_key),
786 callback_info_unref (callback_info);
790 * gnome_vfs_async_module_callback_pop
791 * @callback_name: The name of the module callback to remove a temporary handler for
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.
799 * The temporary async handlers are treated as a first-in first-out
804 gnome_vfs_async_module_callback_pop (const char *callback_name)
806 initialize_per_thread_if_needed ();
807 pop_stack_table (g_private_get (async_callback_stacks_key),
812 /* -- Module-only entry points -- */
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
822 * Invoke a default callback for @callback_name, with in arguments
823 * specified by @in and @in_size, and out arguments specified by @out
826 * This function should only be called by gnome-vfs modules.
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
835 * Returns: TRUE if a callback was invoked, FALSE if none was set.
839 gnome_vfs_module_callback_invoke (const char *callback_name,
845 CallbackInfo *callback;
851 initialize_per_thread_if_needed ();
853 if (g_private_get (in_async_thread_key) != NULL) {
854 stack = g_hash_table_lookup (g_private_get (async_callback_stacks_key),
858 callback = stack->data;
859 g_assert (callback != NULL);
860 callback_info_ref (callback);
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);
868 g_static_mutex_unlock (&callback_table_lock);
872 if (callback == NULL) {
873 stack = g_hash_table_lookup (g_private_get (callback_stacks_key),
877 callback = stack->data;
878 g_assert (callback != NULL);
879 callback_info_ref (callback);
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);
887 g_static_mutex_unlock (&callback_table_lock);
891 if (callback == NULL) {
894 callback->callback (in, in_size, out, out_size, callback->callback_data);
896 callback_info_unref (callback);
903 /* -- Private entry points -- */
905 /* (used by job mechanism to implement callback
906 * state copying semantics for async jobs.
909 GnomeVFSModuleCallbackStackInfo *
910 _gnome_vfs_module_callback_get_stack_info (void)
912 GnomeVFSModuleCallbackStackInfo *stack_info;
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);
918 g_static_mutex_lock (&callback_table_lock);
919 initialize_global_if_needed ();
920 g_static_mutex_unlock (&callback_table_lock);
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);
932 _gnome_vfs_module_callback_free_stack_info (GnomeVFSModuleCallbackStackInfo *stack_info)
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);
943 _gnome_vfs_module_callback_use_stack_info (GnomeVFSModuleCallbackStackInfo *stack_info)
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));
953 _gnome_vfs_module_callback_clear_stacks (void)
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));
961 _gnome_vfs_module_callback_set_in_async_thread (gboolean in_async_thread)
963 initialize_per_thread_if_needed ();
964 g_private_set (in_async_thread_key, GINT_TO_POINTER (in_async_thread));