[gnome-keyring/dbus-api] Add support for thread timers.



commit f2131bc5ab08b15e2095482eaf6c0adb37823de2
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]