[gnome-keyring: 1/6] Add support for thread timers.
- From: Stefan Walter <stefw src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-keyring: 1/6] Add support for thread timers.
- Date: Sat, 18 Jul 2009 18:35:50 +0000 (UTC)
commit 30d87d669ce3c17a3de5de1e065ddc09357d2170
Author: Stef Walter <stef memberwebs com>
Date: Sat Jul 18 18:25:48 2009 +0000
Add support for thread timers.
We can't use mainloop timers from PKCS#11 modules, since no
mainloop runs in those modules. Thread timers all share a
single thread and callback into the module at the given time.
pkcs11/gck/Makefile.am | 1 +
pkcs11/gck/gck-module-ep.h | 4 +-
pkcs11/gck/gck-module.c | 32 +++++-
pkcs11/gck/gck-module.h | 6 +-
pkcs11/gck/gck-timer.c | 225 ++++++++++++++++++++++++++++++++++++
pkcs11/gck/gck-timer.h | 43 +++++++
pkcs11/gck/gck-types.h | 1 +
pkcs11/gck/tests/Makefile.am | 2 +
pkcs11/gck/tests/test-module.c | 70 +++++++++++
pkcs11/gck/tests/test-module.h | 41 +++++++
pkcs11/gck/tests/unit-test-timer.c | 159 +++++++++++++++++++++++++
11 files changed, 578 insertions(+), 6 deletions(-)
---
diff --git a/pkcs11/gck/Makefile.am b/pkcs11/gck/Makefile.am
index f264a53..730f416 100644
--- a/pkcs11/gck/Makefile.am
+++ b/pkcs11/gck/Makefile.am
@@ -40,6 +40,7 @@ libgck_la_SOURCES = \
gck-session.c gck-session.h \
gck-sexp.c gck-sexp.h \
gck-store.c gck-store.h \
+ gck-timer.c gck-timer.h \
gck-transaction.c gck-transaction.h \
gck-types.h \
gck-util.c gck-util.h \
diff --git a/pkcs11/gck/gck-module-ep.h b/pkcs11/gck/gck-module-ep.h
index 09b5564..7ef2216 100644
--- a/pkcs11/gck/gck-module-ep.h
+++ b/pkcs11/gck/gck-module-ep.h
@@ -28,7 +28,7 @@
#include <unistd.h>
/* Forward declaration, this must be defined manually or using GCK_DEFINE_MODULE */
-static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args);
+static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args, GMutex *mutex);
static GckModule *pkcs11_module = NULL;
static pid_t pkcs11_module_pid = 0;
@@ -73,7 +73,7 @@ gck_C_Initialize (CK_VOID_PTR init_args)
else
pkcs11_module_pid = pid;
} else {
- pkcs11_module = gck_module_instantiate (args);
+ pkcs11_module = gck_module_instantiate (args, g_static_mutex_get_mutex (&pkcs11_module_mutex));
if (!pkcs11_module) {
g_warning ("module could not be instantiated");
rv = CKR_GENERAL_ERROR;
diff --git a/pkcs11/gck/gck-module.c b/pkcs11/gck/gck-module.c
index b6a06cb..32cdcf3 100644
--- a/pkcs11/gck/gck-module.c
+++ b/pkcs11/gck/gck-module.c
@@ -33,6 +33,7 @@
#include "gck-private-key.h"
#include "gck-public-key.h"
#include "gck-session.h"
+#include "gck-timer.h"
#include "gck-transaction.h"
#include "gck-util.h"
@@ -40,7 +41,8 @@ enum {
PROP_0,
PROP_MANAGER,
PROP_WRITE_PROTECTED,
- PROP_INITIALIZE_ARGS
+ PROP_INITIALIZE_ARGS,
+ PROP_MUTEX
};
struct _GckModulePrivate {
@@ -50,6 +52,7 @@ struct _GckModulePrivate {
gint handle_counter; /* Constantly incrementing counter for handles and the like */
GArray *factories; /* Various registered object factories */
gboolean factories_sorted; /* Whether we need to sort the object factories */
+ GMutex *mutex; /* The mutex controlling entry to this module */
};
typedef struct _VirtualSlot {
@@ -128,6 +131,9 @@ static const MechanismAndInfo mechanism_list[] = {
{ CKM_DSA, { 512, 1024, CKF_SIGN | CKF_VERIFY } }
};
+/* Hidden function that you should not use */
+GMutex* _gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *self);
+
/* -----------------------------------------------------------------------------
* INTERNAL
*/
@@ -435,6 +441,8 @@ gck_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
static void
gck_module_init (GckModule *self)
{
+ gck_timer_initialize ();
+
self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate);
self->pv->token_manager = g_object_new (GCK_TYPE_MANAGER, "for-token", TRUE, NULL);
self->pv->sessions_by_handle = g_hash_table_new_full (gck_util_ulong_hash, gck_util_ulong_equal,
@@ -486,6 +494,8 @@ gck_module_finalize (GObject *obj)
g_array_free (self->pv->factories, TRUE);
self->pv->factories = NULL;
+ gck_timer_shutdown ();
+
G_OBJECT_CLASS (gck_module_parent_class)->finalize (obj);
}
@@ -502,6 +512,10 @@ gck_module_set_property (GObject *obj, guint prop_id, const GValue *value,
if (args != NULL && args->pReserved != NULL)
parse_arguments (self, args->pReserved);
break;
+ case PROP_MUTEX:
+ self->pv->mutex = g_value_get_pointer (value);
+ g_return_if_fail (self->pv->mutex);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
@@ -564,6 +578,10 @@ gck_module_class_init (GckModuleClass *klass)
g_object_class_install_property (gobject_class, PROP_INITIALIZE_ARGS,
g_param_spec_pointer ("initialize-args", "Initialize Args", "Arguments passed to C_Initialize",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class, PROP_MUTEX,
+ g_param_spec_pointer ("mutex", "Mutex", "Module mutex",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}
/* -----------------------------------------------------------------------------
@@ -731,6 +749,18 @@ gck_module_find_factory (GckModule *self, CK_ATTRIBUTE_PTR attrs, CK_ULONG n_att
return NULL;
}
+/*
+ * Hidden method to get the mutex for a module. This is for timers to be
+ * able to reenter the module. Don't use this method.
+ */
+
+GMutex*
+_gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *self)
+{
+ g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
+ return self->pv->mutex;
+}
+
/* -----------------------------------------------------------------------------
* PKCS#11
*/
diff --git a/pkcs11/gck/gck-module.h b/pkcs11/gck/gck-module.h
index cf877d5..c374330 100644
--- a/pkcs11/gck/gck-module.h
+++ b/pkcs11/gck/gck-module.h
@@ -94,10 +94,10 @@ struct _GckModuleClass {
extern const CK_FUNCTION_LIST_PTR prefix ## _function_list
#define GCK_DEFINE_MODULE(prefix, type) \
- static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args) \
- { return g_object_new ((type), "initialize-args", args, NULL); } \
+ static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args, GMutex* mutex) \
+ { return g_object_new ((type), "initialize-args", args, "mutex", mutex, NULL); } \
const CK_FUNCTION_LIST_PTR prefix ## _function_list = &gck_module_function_list;
-
+
GType gck_module_get_type (void);
GckManager* gck_module_get_manager (GckModule *self);
diff --git a/pkcs11/gck/gck-timer.c b/pkcs11/gck/gck-timer.c
new file mode 100644
index 0000000..4776c3b
--- /dev/null
+++ b/pkcs11/gck/gck-timer.c
@@ -0,0 +1,225 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-timer.h"
+
+#include <glib.h>
+
+struct _GckTimer {
+ glong when;
+ GMutex *mutex;
+ gpointer identifier;
+ GckTimerFunc callback;
+ gpointer user_data;
+};
+
+static GStaticMutex timer_mutex = G_STATIC_MUTEX_INIT;
+static GQueue *timer_queue = NULL;
+static GThread *timer_thread = NULL;
+static GCond *timer_cond = NULL;
+static gboolean timer_run = FALSE;
+static gint timer_refs = 0;
+
+static gint
+compare_timers (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ const GckTimer *ta = a;
+ const GckTimer *tb = b;
+ if (ta->when < tb->when)
+ return -1;
+ return ta->when > tb->when;
+}
+
+static gpointer
+timer_thread_func (gpointer unused)
+{
+ GMutex *mutex = g_static_mutex_get_mutex (&timer_mutex);
+ GckTimer *timer;
+ GTimeVal tv;
+
+ g_mutex_lock (mutex);
+
+ while (timer_run) {
+ timer = g_queue_peek_head (timer_queue);
+
+ /* Nothing in the queue, wait until we have action */
+ if (!timer) {
+ g_cond_wait (timer_cond, mutex);
+ continue;
+ }
+
+ g_get_current_time (&tv);
+
+ /* We have to wait until the next timer? */
+ if (tv.tv_sec < timer->when) {
+ tv.tv_sec = timer->when;
+ tv.tv_usec = 0;
+ g_cond_timed_wait (timer_cond, mutex, &tv);
+ continue;
+ }
+
+ /* Leave our thread mutex, and enter the module */
+ g_mutex_unlock (mutex);
+ g_mutex_lock (timer->mutex);
+
+ if (timer->callback)
+ (timer->callback) (timer, timer->user_data);
+
+ /* Leave the module, and go back into our thread mutex */
+ g_mutex_unlock (timer->mutex);
+ g_mutex_lock (mutex);
+
+ /* There's a chance that the timer may no longer be at head of queue */
+ g_queue_remove (timer_queue, timer);
+ g_slice_free (GckTimer, timer);
+ }
+
+ g_mutex_unlock (mutex);
+ return NULL;
+}
+
+void
+gck_timer_initialize (void)
+{
+ GError *error = NULL;
+ g_static_mutex_lock (&timer_mutex);
+
+ g_atomic_int_inc (&timer_refs);
+ if (!timer_thread) {
+ timer_run = TRUE;
+ timer_thread = g_thread_create (timer_thread_func, NULL, TRUE, &error);
+ if (timer_thread) {
+ g_assert (timer_queue == NULL);
+ timer_queue = g_queue_new ();
+
+ g_assert (timer_cond == NULL);
+ timer_cond = g_cond_new ();
+ } else {
+ g_warning ("could not create timer thread: %s",
+ error && error->message ? error->message : "");
+ }
+ }
+
+ g_static_mutex_unlock (&timer_mutex);
+}
+
+void
+gck_timer_shutdown (void)
+{
+ GckTimer *timer;
+
+ if (g_atomic_int_dec_and_test (&timer_refs)) {
+ timer_run = FALSE;
+ g_assert (timer_cond);
+ g_cond_broadcast (timer_cond);
+
+ g_assert (timer_thread);
+ g_thread_join (timer_thread);
+ timer_thread = NULL;
+
+ g_assert (timer_queue);
+
+ /* Cleanup any outstanding timers */
+ while (!g_queue_is_empty (timer_queue)) {
+ timer = g_queue_pop_head (timer_queue);
+ g_slice_free (GckTimer, timer);
+ }
+
+ g_queue_free (timer_queue);
+ timer_queue = NULL;
+
+ g_cond_free (timer_cond);
+ timer_cond = NULL;
+ }
+}
+
+/* We're the only caller of this function */
+GMutex* _gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *self);
+
+GckTimer*
+gck_timer_start (GckModule *module, glong when, GckTimerFunc callback, gpointer user_data)
+{
+ GckTimer *timer;
+
+ g_return_val_if_fail (callback, NULL);
+ g_return_val_if_fail (timer_queue, NULL);
+
+ timer = g_slice_new (GckTimer);
+ timer->when = when;
+ timer->callback = callback;
+ timer->user_data = user_data;
+
+ timer->mutex = _gck_module_get_scary_mutex_that_you_should_not_touch (module);
+ g_return_val_if_fail (timer->mutex, NULL);
+
+ g_static_mutex_lock (&timer_mutex);
+
+ g_assert (timer_queue);
+ g_queue_insert_sorted (timer_queue, timer, compare_timers, NULL);
+ g_assert (timer_cond);
+ g_cond_broadcast (timer_cond);
+
+ g_static_mutex_unlock (&timer_mutex);
+
+ /*
+ * Note that the timer thread could not already completed this timer.
+ * This is because we're in the module, and in order to complete a timer
+ * the timer thread must enter the module mutex.
+ */
+
+ return timer;
+}
+
+void
+gck_timer_cancel (GckTimer *timer)
+{
+ GList *link;
+
+ g_return_if_fail (timer_queue);
+
+ g_static_mutex_lock (&timer_mutex);
+
+ g_assert (timer_queue);
+
+ link = g_queue_find (timer_queue, timer);
+ if (link) {
+
+ /*
+ * For thread safety the timer struct must be freed
+ * from the timer thread. So to cancel, what we do
+ * is move the timer to the front of the queue,
+ * and reset the callback and when.
+ */
+
+ timer->when = 0;
+ timer->callback = NULL;
+
+ g_queue_delete_link (timer_queue, link);
+ g_queue_push_head (timer_queue, timer);
+
+ g_assert (timer_cond);
+ g_cond_broadcast (timer_cond);
+ }
+
+ g_static_mutex_unlock (&timer_mutex);
+}
diff --git a/pkcs11/gck/gck-timer.h b/pkcs11/gck/gck-timer.h
new file mode 100644
index 0000000..14fdf93
--- /dev/null
+++ b/pkcs11/gck/gck-timer.h
@@ -0,0 +1,43 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef GCKTIMER_H_
+#define GCKTIMER_H_
+
+#include <glib.h>
+
+#include "gck-types.h"
+
+typedef void (*GckTimerFunc) (GckTimer *timer,
+ gpointer user_data);
+
+GckTimer* gck_timer_start (GckModule *module,
+ glong when,
+ GckTimerFunc func,
+ gpointer user_data);
+
+void gck_timer_cancel (GckTimer *timer);
+
+void gck_timer_initialize (void);
+
+void gck_timer_shutdown (void);
+
+#endif /* GCKTIMER_H_ */
diff --git a/pkcs11/gck/gck-types.h b/pkcs11/gck/gck-types.h
index eebcce4..67ae9de 100644
--- a/pkcs11/gck/gck-types.h
+++ b/pkcs11/gck/gck-types.h
@@ -38,6 +38,7 @@ typedef struct _GckSessionPrivateKey GckSessionPrivateKey;
typedef struct _GckSessionPublicKey GckSessionPublicKey;
typedef struct _GckSexp GckSexp;
typedef struct _GckStore GckStore;
+typedef struct _GckTimer GckTimer;
typedef struct _GckTransaction GckTransaction;
#endif /* __GCK_TYPES_H__ */
diff --git a/pkcs11/gck/tests/Makefile.am b/pkcs11/gck/tests/Makefile.am
index 9bfbe88..7596afa 100644
--- a/pkcs11/gck/tests/Makefile.am
+++ b/pkcs11/gck/tests/Makefile.am
@@ -10,12 +10,14 @@ UNIT_AUTO = \
unit-test-crypto.c \
unit-test-data-asn1.c \
unit-test-data-der.c \
+ unit-test-timer.c \
unit-test-transaction.c \
unit-test-store.c \
unit-test-memory-store.c \
unit-test-login.c \
unit-test-data-file.c \
unit-test-file-tracker.c \
+ test-module.c test-module.h \
$(BUILT_SOURCES)
UNIT_PROMPT =
diff --git a/pkcs11/gck/tests/test-module.c b/pkcs11/gck/tests/test-module.c
new file mode 100644
index 0000000..cb2f395
--- /dev/null
+++ b/pkcs11/gck/tests/test-module.c
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* test-module.c: A test PKCS#11 module implementation
+
+ Copyright (C) 2009 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+#include "test-module.h"
+
+/* Include all the module entry points */
+#include "gck/gck-module-ep.h"
+GCK_DEFINE_MODULE (test_module, GCK_TYPE_MODULE);
+
+CK_FUNCTION_LIST_PTR
+test_module_get_functions (void)
+{
+ gck_crypto_initialize ();
+ return test_module_function_list;
+}
+
+GckModule*
+test_module_initialize (void)
+{
+ CK_RV rv;
+
+ gck_crypto_initialize ();
+ rv = test_module_function_list->C_Initialize (NULL);
+ g_return_val_if_fail (rv == CKR_OK, NULL);
+
+ g_return_val_if_fail (pkcs11_module, NULL);
+ return pkcs11_module;
+}
+
+void
+test_module_finalize (void)
+{
+ CK_RV rv;
+
+ rv = test_module_function_list->C_Finalize (NULL);
+ g_return_if_fail (rv == CKR_OK);
+}
+
+void
+test_module_leave (void)
+{
+ g_static_mutex_unlock (&pkcs11_module_mutex);
+}
+
+void
+test_module_enter (void)
+{
+ g_static_mutex_lock (&pkcs11_module_mutex);
+}
diff --git a/pkcs11/gck/tests/test-module.h b/pkcs11/gck/tests/test-module.h
new file mode 100644
index 0000000..8a8ba3f
--- /dev/null
+++ b/pkcs11/gck/tests/test-module.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* test-module.h: A test PKCS#11 module implementation
+
+ Copyright (C) 2009 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#ifndef TESTMODULE_H_
+#define TESTMODULE_H_
+
+#include "gck-types.h"
+
+#include "pkcs11.h"
+
+CK_FUNCTION_LIST_PTR test_module_get_functions (void);
+
+void test_module_leave (void);
+
+void test_module_enter (void);
+
+GckModule* test_module_initialize (void);
+
+void test_module_finalize (void);
+
+#endif /* TESTMODULE_H_ */
diff --git a/pkcs11/gck/tests/unit-test-timer.c b/pkcs11/gck/tests/unit-test-timer.c
new file mode 100644
index 0000000..e826799
--- /dev/null
+++ b/pkcs11/gck/tests/unit-test-timer.c
@@ -0,0 +1,159 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-timer.c: Test thread timer functionality
+
+ Copyright (C) 2009 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "run-auto-test.h"
+#include "test-module.h"
+
+#include "gck/gck-timer.h"
+
+GckModule *module = NULL;
+
+DEFINE_SETUP(timer_setup)
+{
+ module = test_module_initialize ();
+ test_module_enter ();
+}
+
+DEFINE_TEARDOWN(timer_teardown)
+{
+ test_module_leave ();
+ test_module_finalize ();
+}
+
+DEFINE_TEST(timer_extra_initialize)
+{
+ gck_timer_initialize ();
+ gck_timer_shutdown ();
+}
+
+static void
+timer_callback (GckTimer *timer, gpointer user_data)
+{
+ GckTimer **value = user_data;
+ g_assert (timer);
+ g_assert (timer == *value);
+ *value = NULL;
+}
+
+DEFINE_TEST(timer_simple)
+{
+ GTimeVal tv;
+ GckTimer *timer;
+
+ g_get_current_time (&tv);
+ timer = gck_timer_start (module, tv.tv_sec + 2, timer_callback, &timer);
+
+ test_module_leave ();
+ test_mainloop_run (2200);
+ test_module_enter ();
+
+ g_assert (timer == NULL);
+}
+
+DEFINE_TEST(timer_cancel)
+{
+ GTimeVal tv;
+ GckTimer *timer;
+
+ g_get_current_time (&tv);
+ timer = gck_timer_start (module, tv.tv_sec + 2, timer_callback, &timer);
+
+ test_module_leave ();
+ test_mainloop_run (500);
+ test_module_enter ();
+
+ gck_timer_cancel (timer);
+
+ test_module_leave ();
+ test_mainloop_run (2000);
+ test_module_enter ();
+
+ /* The callback should not have been called */
+ g_assert (timer != NULL);
+}
+
+DEFINE_TEST(timer_immediate)
+{
+ GTimeVal tv;
+ GckTimer *timer;
+
+ /* Setup timer in the past, should execute as soon as possible */
+ g_get_current_time (&tv);
+ timer = gck_timer_start (module, tv.tv_sec - 5, timer_callback, &timer);
+
+ /* Should not be called immediately */
+ g_assert (timer != NULL);
+
+ test_module_leave ();
+ test_mainloop_run (50);
+ test_module_enter ();
+
+ /* Should have been called now */
+ g_assert (timer == NULL);
+}
+
+static GckTimer *timer_last = NULL;
+static gint timer_check = 0;
+
+static void
+multiple_callback (GckTimer *timer, gpointer user_data)
+{
+ gint value = GPOINTER_TO_INT (user_data);
+ g_assert (timer);
+ g_assert (timer != timer_last);
+ g_assert (value == timer_check);
+ timer_last = timer;
+ timer_check += 1;
+}
+
+DEFINE_TEST(timer_multiple)
+{
+ GTimeVal tv;
+
+ timer_check = 0;
+ g_get_current_time (&tv);
+
+ /* Multiple timers, add out of order, should be called in order */
+ gck_timer_start (module, tv.tv_sec + 1, multiple_callback, GINT_TO_POINTER (1));
+ gck_timer_start (module, tv.tv_sec + 3, multiple_callback, GINT_TO_POINTER (3));
+ gck_timer_start (module, tv.tv_sec + 2, multiple_callback, GINT_TO_POINTER (2));
+ gck_timer_start (module, tv.tv_sec + 0, multiple_callback, GINT_TO_POINTER (0));
+
+ test_module_leave ();
+ test_mainloop_run (3500);
+ test_module_enter ();
+
+ g_assert (timer_check == 4);
+}
+
+DEFINE_TEST(timer_outstanding)
+{
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+
+ /* A timer that can't be called */
+ gck_timer_start (module, tv.tv_sec + 5, timer_callback, NULL);
+ gck_timer_start (module, tv.tv_sec + 10, timer_callback, NULL);
+ gck_timer_start (module, tv.tv_sec + 1, timer_callback, NULL);
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]