#include "gskhook.h"
#include "gsktree.h"
#include "gskmainloop.h"
#include "gskdebug.h"
#include "debug.h"
/* privately used flags */
enum
{
GSK_HOOK_HAS_POLL = (1 << 8),
GSK_HOOK_IS_NOTIFYING = (1 << 9),
GSK_HOOK_IS_NOTIFYING_SHUTDOWN = (1 << 10),
GSK_HOOK_BLOCKED_NOTIFY = (1 << 11),
GSK_HOOK_BLOCKED_SHUTDOWN_NOTIFY = (1 << 12),
GSK_HOOK_UNTRAPPED_DURING_NOTIFY = (1 << 13),
/* aliases */
GSK_HOOK_IDLE_NOTIFY = GSK_HOOK_private_IDLE_NOTIFY,
GSK_HOOK_JUST_NEVER_BLOCKS = GSK_HOOK_private_JUST_NEVER_BLOCKS,
GSK_HOOK_NEVER_BLOCKS = GSK_HOOK_private_NEVER_BLOCKS,
GSK_HOOK_CAN_DEFER_SHUTDOWN = GSK_HOOK_private_CAN_DEFER_SHUTDOWN,
GSK_HOOK_SHUTTING_DOWN = GSK_HOOK_private_SHUTTING_DOWN
};
static GQuark gsk_hook_main_loop_quark = 0;
#ifdef GSK_DEBUG
static void
gsk_hook_debug (GskHook *hook,
const char *format,
...) G_GNUC_PRINTF(2,3);
#define DEBUG(args) \
G_STMT_START{ \
if (GSK_IS_DEBUGGING (HOOK)) \
gsk_hook_debug args; \
}G_STMT_END
#else
#define DEBUG(args)
#endif
extern GskDebugFlags gsk_debug_flags;
/* --- implementation of non-blocking hooks --- */
typedef struct _PendingDestroyNotify PendingDestroyNotify;
struct _PendingDestroyNotify
{
/* WARNING: this structure must be no more than 3 pointers to work! */
gpointer data;
GDestroyNotify destroy;
PendingDestroyNotify *next;
};
/* abuse GList allocator */
#define pending_destroy_notify_alloc() (PendingDestroyNotify*)g_list_alloc()
#define pending_destroy_notify_free(notify) g_list_free_1((GList*)notify)
typedef struct
{
GskTree *hook_tree;
GskSource *source;
PendingDestroyNotify *first_destroy;
PendingDestroyNotify *last_destroy;
} NBThreadData;
static void
flush_pending_destroys (NBThreadData *nb_data)
{
while (nb_data->first_destroy)
{
PendingDestroyNotify *at = nb_data->first_destroy;
nb_data->first_destroy = at->next;
if (nb_data->first_destroy == NULL)
nb_data->last_destroy = NULL;
at->destroy (at->data);
pending_destroy_notify_free (at);
}
}
static gboolean
run_all_nonblocking_hooks (gpointer data)
{
NBThreadData *nb_data = data;
GskTree *tree = nb_data->hook_tree;
GskTreeNode *node;
flush_pending_destroys (nb_data);
node = gsk_tree_node_first (tree);
if (GSK_IS_DEBUGGING (HOOK))
g_message ("run_all_nonblocking_hooks: %d hooks, first=%p", gsk_tree_n_nodes (tree), node);
/* is the nonblocking thread totally inactive? if so, remove it. */
if (node == NULL)
{
nb_data->source = NULL;
return FALSE;
}
do
{
GskHook *hook = gsk_tree_node_peek_key (node);
DEBUG((hook, "about to run notify, since this hook is nonblocking"));
gsk_hook_notify (hook);
node = gsk_tree_node_next (tree, node);
}
while (node != NULL);
flush_pending_destroys (nb_data);
if (GSK_IS_DEBUGGING (HOOK))
g_message ("done -- run_all_nonblocking_hooks");
return TRUE;
}
static void
destroy_nonblocking_thread_data (gpointer data)
{
NBThreadData *nb_data = data;
/* ignore source -- it's going away with the main-loop */
gsk_tree_unref (nb_data->hook_tree);
g_free (nb_data);
}
static gint
pointer_compare (gconstpointer a, gconstpointer b)
{
const char *pa = a;
const char *pb = b;
return (pa < pb) ? -1 : (pa > pb) ? +1 : 0;
}
static inline NBThreadData *
main_loop_force_nonblocking_data (GskMainLoop *loop)
{
NBThreadData *data = g_object_get_qdata (G_OBJECT (loop), gsk_hook_main_loop_quark);
if (data == NULL)
{
data = g_new (NBThreadData, 1);
data->hook_tree = gsk_tree_new (pointer_compare);
data->source = NULL;
data->first_destroy = data->last_destroy = NULL;
g_object_set_qdata_full (G_OBJECT (loop), gsk_hook_main_loop_quark, data,
destroy_nonblocking_thread_data);
}
return data;
}
static inline void
verify_nonblocking_data_has_source (NBThreadData *nb_data,
GskMainLoop *main_loop)
{
if (nb_data->source == NULL)
nb_data->source = gsk_main_loop_add_idle (main_loop,
run_all_nonblocking_hooks,
nb_data, NULL);
}
static inline void
gsk_hook_add_to_nonblocking_list (GskHook *hook)
{
GskMainLoop *loop = gsk_main_loop_default ();
NBThreadData *data = main_loop_force_nonblocking_data (loop);
gsk_tree_insert (data->hook_tree, hook, hook);
verify_nonblocking_data_has_source (data, loop);
}
static inline void
gsk_hook_remove_from_nonblocking_list (GskHook *hook)
{
GskMainLoop *loop = gsk_main_loop_default ();
NBThreadData *data = main_loop_force_nonblocking_data (loop);
gsk_tree_remove (data->hook_tree, hook);
/* note: to avoid deleting and re-adding the idle function repeatedly
* in certain cases, we always do the removal by returning FALSE
* from the run_all_nonblocking_hooks
*/
}
static inline void
gsk_hook_maybe_defer_destroy (GDestroyNotify destroy, gpointer destroy_data)
{
if (destroy)
{
GskMainLoop *loop = gsk_main_loop_default ();
NBThreadData *data = main_loop_force_nonblocking_data (loop);
PendingDestroyNotify *notify = pending_destroy_notify_alloc ();
verify_nonblocking_data_has_source (data, loop);
notify->destroy = destroy;
notify->data = destroy_data;
notify->next = NULL;
if (data->last_destroy)
data->last_destroy->next = notify;
else
data->first_destroy = notify;
data->last_destroy = notify;
}
}
/* --- public interface --- */
/**
* gsk_hook_init:
* @hook: The hook to initialize.
* @flags: Special details about the hook.
* @inset: Offset in bytes of this hook inside the containing object.
* @class_set_poll_offset: Offset in bytes of the set_poll
* method in the containing object's class.
* @class_shutdown_offset: Offset in bytes of the shutdown
* method in the containing object's class, or 0 if there is no shutdown
* method.
*
* Prepare a GskHook to be used. This should almost always
* be done in the instance-init function of the class which contains the hook.
*/
void
gsk_hook_init (GskHook *hook,
GskHookFlags flags,
guint inset,
guint class_set_poll_offset,
guint class_shutdown_offset)
{
/* currently these are stored in guint16's... */
g_return_if_fail (inset < 65536
&& class_shutdown_offset < 65536
&& class_set_poll_offset < 65536);
hook->flags = flags & ~_GSK_HOOK_FLAGS_RESERVED;
hook->block_count = 0;
hook->inset = inset;
hook->class_set_poll_offset = class_set_poll_offset;
hook->class_shutdown_offset = class_shutdown_offset;
hook->func = NULL;
hook->shutdown_func = NULL;
hook->data = NULL;
hook->destroy = NULL;
}
static inline void
gsk_hook_call_set_poll_func (GskHook *hook,
gboolean value)
{
GObject *object = GSK_HOOK_GET_OBJECT (hook);
GObjectClass *class = G_OBJECT_GET_CLASS (object);
GskHookSetPollFunc set_poll_func =
G_STRUCT_MEMBER (GskHookSetPollFunc, class, hook->class_set_poll_offset);
if (set_poll_func)
(*set_poll_func) (object, value);
}
static inline gboolean
gsk_hook_call_shutdown_func (GskHook *hook,
GError **error)
{
GObject *object = GSK_HOOK_GET_OBJECT (hook);
GObjectClass *class = G_OBJECT_GET_CLASS (object);
guint offset = hook->class_shutdown_offset;
GHookFunc shutdown_func =
(GHookFunc) G_STRUCT_MEMBER (GHookFunc, class, offset);
if (!shutdown_func)
return TRUE;
if (GSK_HOOK_TEST_FLAG (hook, CAN_HAVE_SHUTDOWN_ERROR))
return (* (GskHookShutdownErrorFunc) shutdown_func) (object, error);
else
{
(* (GskHookShutdownFunc) shutdown_func) (object);
return TRUE;
}
}
static inline void
gsk_hook_set_poll (GskHook *hook)
{
DEBUG ((hook, "gsk_hook_set_poll: idle-notify=%d",
GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY)));
GSK_HOOK_SET_FLAG (hook, HAS_POLL);
if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
gsk_hook_add_to_nonblocking_list (hook);
gsk_hook_call_set_poll_func (hook, TRUE);
}
static inline void
gsk_hook_clear_poll (GskHook *hook)
{
DEBUG ((hook, "gsk_hook_clear_poll: idle-notify=%d",
GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY)));
GSK_HOOK_CLEAR_FLAG (hook, HAS_POLL);
if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
gsk_hook_remove_from_nonblocking_list (hook);
gsk_hook_call_set_poll_func (hook, FALSE);
}
/**
* gsk_hook_trap:
* @hook: the hook to trap
* @func: function to call with the hook is triggered.
* @shutdown: function to be called if the hook is shutting down and will
* never trigger again.
* @data: user-data to pass to func and shutdown.
* @destroy: function to call on user-data when the hook is untrapped.
*
* Traps hook triggers and shutdowns. The caller's functions will
* be run in response to #gsk_hook_notify.
*/
void
gsk_hook_trap (GskHook *hook,
GskHookFunc func,
GskHookFunc shutdown,
gpointer data,
GDestroyNotify destroy)
{
g_return_if_fail (hook->func == NULL);
g_return_if_fail (GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE));
DEBUG ((hook, "gsk_hook_trap"));
hook->func = func;
hook->shutdown_func = shutdown;
hook->data = data;
hook->destroy = destroy;
if (hook->block_count == 0
&& !GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
gsk_hook_set_poll (hook);
}
/**
* gsk_hook_untrap:
* @hook: the hook to untrap
*
* Stops trapping the hook. The user's function will
* no longer be run, and invoke the user's destroy method,
* if it was supplied to #gsk_hook_trap.
*
* If you untrap the hook while it is executing,
* the destroy handler will be deferred
* until after the hook is done notifying and/or shutdown-notifying.
*/
void
gsk_hook_untrap (GskHook *hook)
{
GDestroyNotify old_destroy = hook->destroy;
gpointer old_data = hook->data;
DEBUG ((hook, "gsk_hook_untrap"));
hook->func = NULL;
hook->shutdown_func = NULL;
hook->data = NULL;
hook->destroy = NULL;
if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
gsk_hook_clear_poll (hook);
/* If a hook is untrapped while one of the user's callbacks is running,
gsk_hook_notify and gsk_hook_notify_shutdown need to ignore the
callback's return value. */
if (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING)
|| GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN))
{
GSK_HOOK_SET_FLAG (hook, UNTRAPPED_DURING_NOTIFY);
gsk_hook_maybe_defer_destroy (old_destroy, old_data);
}
else if (old_destroy)
old_destroy (old_data);
}
/**
* gsk_hook_block:
* @hook: the hook to block.
*
* Temporarily block the hook from triggerring the callback.
* This may cause the set_poll method to be run.
*
* This increases a block_count, so you may call
* gsk_hook_block as many times as you like,
* but you must call #gsk_hook_unblock an equal number of times.
*/
void
gsk_hook_block (GskHook *hook)
{
hook->block_count++;
DEBUG ((hook, "gsk_hook_block: new-count=%d", hook->block_count));
if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
gsk_hook_clear_poll (hook);
}
/**
* gsk_hook_unblock:
* @hook: the hook to unblock.
*
* Undoes a gsk_hook_block(). The block count is decreased,
* and if it gets to 0, the hook may be eligible to start
* triggering again.
*/
void
gsk_hook_unblock (GskHook *hook)
{
g_return_if_fail (hook->block_count > 0);
hook->block_count--;
DEBUG ((hook, "gsk_hook_unblock: new-count=%d", hook->block_count));
if (hook->block_count == 0
&& GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE)
&& hook->func != NULL)
gsk_hook_set_poll (hook);
}
/**
* gsk_hook_shutdown:
* @hook: the hook to shut down.
* @error: optional error return.
*
* Shutdown a hook. You may call gsk_hook_shutdown on a hook more than
* once, but it will be ignored after the first time.
*
* Note that when gsk_hook_shutdown succeeds, the hook is not necessarily
* completely shut down. Some hooks (for example, for a socket waiting
* for a reply from a remote server) may remain in the SHUTTING_DOWN state
* for some time before the shutdown completes and the user's shutdown
* callback is invoked. You may test whether the shutdown has completed
* using GSK_HOOK_TEST_SHUTTING_DOWN (hook): if true, the shutdown is still
* in progress.
*
* Regardless of whether the shutdown succeeds, or whether the shutdown
* completes immediately, the hook will be marked unavailable after
* calling gsk_hook_shutdown. (That is, GSK_HOOK_TEST_IS_AVAILABLE (hook)
* will be false.)
*
* returns: true if successful, or false if an error occurred.
*/
gboolean
gsk_hook_shutdown (GskHook *hook,
GError **error)
{
GObject *object = GSK_HOOK_GET_OBJECT (hook);
GError *dummy = NULL;
GError **err = error ? error : &dummy;
gboolean success, do_notify;
DEBUG ((hook, "gsk_hook_shutdown: is-available-initially=%d",
GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE)));
if (!GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE))
return TRUE;
if (GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN))
return TRUE;
g_object_ref (object);
GSK_HOOK_SET_FLAG (hook, SHUTTING_DOWN);
if (GSK_HOOK_TEST_FLAG (hook, CAN_DEFER_SHUTDOWN))
{
/* If the hook CAN_DEFER_SHUTDOWN, false indicates that the
shutdown was deferred, whether or not an error occurred. */
if (gsk_hook_call_shutdown_func (hook, err))
do_notify = TRUE;
else
{
do_notify = FALSE;
/* The class method should not have cleared this, since it
wanted to defer shutdown. */
g_return_val_if_fail (GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN),
FALSE);
}
success = (*err == NULL);
}
else
{
/* If the hook can't defer shutdown, false indicates that
an error occurred. */
if (gsk_hook_call_shutdown_func (hook, err))
success = (*err == NULL); /* TODO: ??? */
else
success = FALSE;
do_notify = TRUE;
}
if (do_notify)
{
gsk_hook_notify_shutdown (hook);
/* We should be completely shut down, unless we were already
notifying and shutdown notification was blocked. */
g_return_val_if_fail
(!GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN)
|| (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING)
&& GSK_HOOK_TEST_FLAG (hook, BLOCKED_SHUTDOWN_NOTIFY)),
FALSE);
}
/* We always clear this here, in case shutdown notification was blocked. */
GSK_HOOK_CLEAR_FLAG (hook, IS_AVAILABLE);
if (dummy)
g_error_free (dummy);
g_object_unref (object);
return success;
}
/* for use by classes which contain hooks */
/**
* gsk_hook_notify:
* @hook: the hook which is triggering.
*
* This is called by the implementation of a class
* which contains a hook to trigger it, i.e. call the user's
* callback.
*
* Generally, you should only call this if the user is ready
* for data (so that set_poll has been called with TRUE);
* however, if you call it, it will be remembered,
* and immediately triggered once it is allowed to:
* for example, when the block count gets to 0.
*
* Some gory details come up now and then:
*
* - there are reentrance guards which prevent recursive calls
* to do anything. Also, notify within shutdown is not allowed.
*/
void
gsk_hook_notify (GskHook *hook)
{
GObject *object;
gboolean handler_rv;
g_return_if_fail (GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE));
DEBUG ((hook, "gsk_hook_notify: block-cnt=%d; notifying=%d, notifying-shutdown=%d, shutting-down=%d",
hook->block_count,
GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING),
GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN),
GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN)));
if (hook->block_count > 0
|| GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING)
|| GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN))
{
GSK_HOOK_SET_FLAG (hook, BLOCKED_NOTIFY);
return;
}
GSK_HOOK_CLEAR_FLAG (hook, BLOCKED_NOTIFY);
object = GSK_HOOK_GET_OBJECT (hook);
g_object_ref (object);
begin_notify:
if (hook->func == NULL)
{
goto done_notify;
}
GSK_HOOK_SET_FLAG (hook, IS_NOTIFYING);
handler_rv = hook->func (object, hook->data);
GSK_HOOK_CLEAR_FLAG (hook, IS_NOTIFYING);
if (!handler_rv
&& !GSK_HOOK_TEST_FLAG (hook, UNTRAPPED_DURING_NOTIFY))
{
gsk_hook_untrap (hook);
}
GSK_HOOK_CLEAR_FLAG (hook, UNTRAPPED_DURING_NOTIFY);
if (GSK_HOOK_TEST_FLAG (hook, BLOCKED_SHUTDOWN_NOTIFY))
goto got_shutdown_notify;
if (GSK_HOOK_TEST_FLAG (hook, BLOCKED_NOTIFY))
{
/* restart */
GSK_HOOK_CLEAR_FLAG (hook, BLOCKED_NOTIFY);
goto begin_notify;
}
done_notify:
g_object_unref (object);
return;
got_shutdown_notify:
gsk_hook_notify_shutdown (hook);
g_object_unref (object);
return;
}
static inline void
_gsk_hook_clear_idle_notify (GskHook *hook)
{
GSK_HOOK_CLEAR_FLAG (hook, IDLE_NOTIFY);
if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
gsk_hook_remove_from_nonblocking_list (hook);
}
/**
* gsk_hook_notify_shutdown:
* @hook: the hook which has shut down.
*
* Notify the user that a shutdown event has occurred.
* This may happen because you called gsk_hook_shutdown()
* or because the hook was shut down by the object:
* for example, the remote side of a connection may have shutdown.
*
* This does nothing if the hook is not presently available or in the
* middle of shutting down (i.e., IS_AVAILABLE || SHUTTING_DOWN); after
* this call the hook will be marked both unavailable and completely
* shut down (i.e., !IS_AVAILABLE && !SHUTTING_DOWN).
*
* A shutdown hook will no longer be idle-notified.
*/
void
gsk_hook_notify_shutdown (GskHook *hook)
{
DEBUG ((hook, "gsk_hook_notify_shutdown: available=%d, shutting-down=%d, notifying=%d, notifying-shutdown=%d",
GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE),
GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN),
GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING),
GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN)));
/* Notify-shutdown within notify-shutdown is not allowed. */
if (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN))
return;
/* If the hook is neither available nor shutting down, either the user has
already been notified, or the hook was never available to begin with. */
if (!GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE)
&& !GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN))
return;
/* Notify-shutdown within notify will be deferred until the notification
is complete. */
if (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING))
{
GSK_HOOK_SET_FLAG (hook, BLOCKED_SHUTDOWN_NOTIFY);
return;
}
/* The hook is now completely shut down. */
if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
_gsk_hook_clear_idle_notify (hook);
GSK_HOOK_CLEAR_FLAG (hook, IS_AVAILABLE);
GSK_HOOK_CLEAR_FLAG (hook, SHUTTING_DOWN);
/* Notify the user that the hook is completely shut down. */
if (hook->shutdown_func == NULL)
{
if (gsk_hook_is_trapped (hook))
gsk_hook_untrap (hook);
}
else
{
GObject *object = GSK_HOOK_GET_OBJECT (hook);
gboolean handler_rv;
g_object_ref (object);
GSK_HOOK_SET_FLAG (hook, IS_NOTIFYING_SHUTDOWN);
handler_rv = (*hook->shutdown_func) (object, hook->data);
GSK_HOOK_CLEAR_FLAG (hook, IS_NOTIFYING_SHUTDOWN);
if (!handler_rv
&& !GSK_HOOK_TEST_FLAG (hook, UNTRAPPED_DURING_NOTIFY))
{
gsk_hook_untrap (hook);
}
GSK_HOOK_CLEAR_FLAG (hook, UNTRAPPED_DURING_NOTIFY);
g_object_unref (object);
}
}
/* an alternative to implementing set_poll */
/**
* gsk_hook_set_idle_notify:
* @hook: the hook to run constantly.
* @should_idle_notify: whether to run the user's callback
* continually.
*
* When idle_notify is set, the hook will run every cycle of the
* main-loop. Nothing will happen unless the user
* has trapped the event, and it's not blocked.
*
* Opposite of gsk_hook_clear_idle_notify().
*/
void
gsk_hook_set_idle_notify (GskHook *hook,
gboolean should_idle_notify)
{
/* TODO: optimize */
if (should_idle_notify)
gsk_hook_mark_idle_notify (hook);
else
gsk_hook_clear_idle_notify (hook);
}
/**
* gsk_hook_mark_idle_notify:
* @hook: the hook to run constantly.
*
* When idle_notify is set, the hook will run every cycle of the
* main-loop. Of course, nothing will happen unless the user
* has trapped the event, and it's not blocked.
*
* Opposite of gsk_hook_clear_idle_notify().
*/
void
gsk_hook_mark_idle_notify (GskHook *hook)
{
g_return_if_fail (!GSK_HOOK_TEST_FLAG (hook, JUST_NEVER_BLOCKS));
if (!GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE))
return;
if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
return;
DEBUG ((hook, "gsk_hook_mark_idle_notify"));
GSK_HOOK_SET_FLAG (hook, IDLE_NOTIFY);
if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
gsk_hook_add_to_nonblocking_list (hook);
}
/**
* gsk_hook_clear_idle_notify:
* @hook: the hook to stop running constantly.
*
* Stop running the hook at every cycle of the main-loop.
*
* Opposite of gsk_hook_set_idle_notify().
*/
void
gsk_hook_clear_idle_notify (GskHook *hook)
{
g_return_if_fail (!GSK_HOOK_TEST_FLAG (hook, JUST_NEVER_BLOCKS));
if (!GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
return;
DEBUG ((hook, "gsk_hook_clear_idle_notify"));
_gsk_hook_clear_idle_notify (hook);
}
/**
* gsk_hook_mark_never_blocks:
* @hook: the hook which will never block.
*
* Indicate that you will never block, ever.
*/
void
gsk_hook_mark_never_blocks (GskHook *hook)
{
gsk_hook_mark_idle_notify (hook);
GSK_HOOK_SET_FLAG (hook, JUST_NEVER_BLOCKS);
}
/**
* gsk_hook_mark_can_defer_shutdown:
* @hook: the hook which can defer shutdown.
*
* Indicate that the hook may remain in the SHUTTING_DOWN state after the
* class's shutdown method is called, deferring shutdown notification until
* a later time.
*
* If you set this flag, your class's shutdown method must return FALSE
* if shutdown notification is deferred, or TRUE if shutdown is complete
* and the user should be notified immediately; failure is indicated only
* by setting the GError parameter to the shutdown method. (Therefore,
* setting this flag has no effect unless you also set SHUTDOWN_HAS_ERROR.)
*/
void
gsk_hook_mark_can_defer_shutdown (GskHook *hook)
{
GSK_HOOK_SET_FLAG (hook, CAN_DEFER_SHUTDOWN);
}
/**
* gsk_hook_destruct:
* @hook: the hook to destroy.
*
* This should be called only by the class which contains the hook,
* from the instance's finalize method.
*/
void
gsk_hook_destruct (GskHook *hook)
{
if (hook->flags & GSK_HOOK_HAS_POLL)
gsk_hook_clear_poll (hook);
if (hook->destroy)
hook->destroy (hook->data);
}
/* per-class initialization */
typedef struct {
GType type;
const char *name;
} PerHookTypeInfo;
typedef struct {
guint num_info;
PerHookTypeInfo type_infos[1]; /* flexible array */
} PerOffsetInfo;
static GPtrArray *per_offset = NULL;
/**
* gsk_hook_class_init:
* @object_class: the class of the object which contains this hook.
* @name: an identifying name for this type of hook.
* @hook_offset: the offset of the hook in the structure.
*
* This is used to register a hook. This is mostly used
* to make debugging printouts easier to read.
*/
void
gsk_hook_class_init (GObjectClass *object_class,
const char *name,
guint hook_offset)
{
guint index;
PerOffsetInfo *oi;
g_assert (hook_offset % 4 == 0);
g_assert (hook_offset >= sizeof (GObject));
index = (hook_offset - sizeof (GObject)) / 4;
if (per_offset->len <= index)
g_ptr_array_set_size (per_offset, index + 1);
oi = per_offset->pdata[index];
if (!oi)
{
oi = g_new (PerOffsetInfo, 1);
oi->num_info = 0;
}
else
{
guint size = sizeof (PerOffsetInfo)
+ sizeof (PerHookTypeInfo) * (oi->num_info);
oi = g_realloc (oi, size);
}
per_offset->pdata[index] = oi;
oi->type_infos[oi->num_info].type = G_OBJECT_CLASS_TYPE (object_class);
oi->type_infos[oi->num_info].name = name;
++(oi->num_info);
}
/**
* gsk_hook_get_last_poll_state:
* @hook: test whether the last invocation of the set-poll function
* was called with TRUE or FALSE.
*
* Get whether this hook is supposed to be polling or not.
*
* returns: whether the hook is being polled.
*/
gboolean
gsk_hook_get_last_poll_state(GskHook *hook)
{
return GSK_HOOK_TEST_FLAG (hook, HAS_POLL);
}
#ifdef GSK_DEBUG
static const char *
get_hook_name_and_type (GskHook *hook,
GType *type_out)
{
guint index = hook->inset;
PerOffsetInfo *oi;
GObject *object;
GType object_type;
guint i;
g_assert (index % 4 == 0 && index >= sizeof (GObject));
index -= sizeof (GObject);
index /= 4;
if (per_offset->len <= index)
goto fail;
oi = per_offset->pdata[index];
object = GSK_HOOK_GET_OBJECT (hook);
object_type = G_OBJECT_TYPE (object);
for (i = 0; i < oi->num_info; i++)
if (g_type_is_a (object_type, oi->type_infos[i].type))
{
*type_out = oi->type_infos[i].type;
return oi->type_infos[i].name;
}
fail:
*type_out = 0;
return NULL;
}
#include <stdio.h>
static void
gsk_hook_debug (GskHook *hook,
const char *format,
...)
{
GType hook_type;
const char *name = get_hook_name_and_type (hook, &hook_type);
GObject *instance = GSK_HOOK_GET_OBJECT (hook);
GType instance_type = G_OBJECT_TYPE (instance);
va_list args;
fprintf (stderr, "debug: hook: %s:%s [%s,%p]: ",
g_type_name (hook_type), name,
g_type_name (instance_type), instance);
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
fprintf (stderr, ".\n");
}
#endif
/**
* _gsk_hook_init:
*
* Initialize the hook system.
*/
void _gsk_hook_init ()
{
per_offset = g_ptr_array_new ();
gsk_hook_main_loop_quark = g_quark_from_static_string ("GskHook--main-loop-nonblocking");
}
syntax highlighted by Code2HTML, v. 0.9.1