[evolution-data-server] Introduce cancellable locks



commit c155d7e1fb57bde5e90a056306b4e257b72e1264
Author: Milan Crha <mcrha redhat com>
Date:   Fri Dec 14 17:42:41 2012 +0100

    Introduce cancellable locks
    
    There are currently two cancellable lock types, one extends GMutex,
    the other extends GRecMutex, in a way that the waiting for a lock
    can be cancelled.

 libedataserver/Makefile.am           |    2 +
 libedataserver/e-cancellable-locks.c |  297 ++++++++++++++++++++++++++++++++++
 libedataserver/e-cancellable-locks.h |   69 ++++++++
 libedataserver/libedataserver.h      |    1 +
 4 files changed, 369 insertions(+), 0 deletions(-)
---
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index 3e684a0..1ceede0 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -46,6 +46,7 @@ libedataserver_1_2_la_CPPFLAGS = \
 
 libedataserver_1_2_la_SOURCES =		\
 	$(BUILT_SOURCES)		\
+	e-cancellable-locks.c		\
 	e-categories.c			\
 	e-client.c			\
 	e-client-private.h		\
@@ -116,6 +117,7 @@ libedataserverincludedir = $(privincludedir)/libedataserver
 
 libedataserverinclude_HEADERS =		\
 	libedataserver.h		\
+	e-cancellable-locks.h		\
 	e-categories.h			\
 	e-client.h			\
 	e-credentials.h			\
diff --git a/libedataserver/e-cancellable-locks.c b/libedataserver/e-cancellable-locks.c
new file mode 100644
index 0000000..dc8b11a
--- /dev/null
+++ b/libedataserver/e-cancellable-locks.c
@@ -0,0 +1,297 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-cancellable-locks.h"
+
+/**
+ * SECTION:cancellable_locks
+ * @title: Cancellable Locks
+ * @short_description: locks, which can listen for a #GCancellable during lock call
+ *
+ * An #ECancellableMutex and an #ECancellableRecMutex are similar to
+ * GLib's #GMutex and #GRecMutex, with one exception, their <i>lock</i>
+ * function takes also a @GCancellable instance, thus the waiting for a lock
+ * can be cancelled any time.
+ **/
+
+static void
+cancellable_locks_cancelled_cb (GCancellable *cancellable,
+				struct _ECancellableLocksBase *base)
+{
+	g_return_if_fail (base != NULL);
+
+	/* wake-up any waiting threads */
+	g_mutex_lock (&base->cond_mutex);
+	g_cond_broadcast (&base->cond);
+	g_mutex_unlock (&base->cond_mutex);
+}
+
+/**
+ * e_cancellable_mutex_init:
+ * @mutex: an #ECancellableMutex instance
+ *
+ * Initializes @mutex structure.
+ *
+ * Since: 3.8
+ **/
+void
+e_cancellable_mutex_init (ECancellableMutex *mutex)
+{
+	g_return_if_fail (mutex != NULL);
+
+	g_mutex_init (&mutex->mutex);
+	g_mutex_init (&mutex->base.cond_mutex);
+	g_cond_init (&mutex->base.cond);
+}
+
+/**
+ * e_cancellable_mutex_clear:
+ * @mutex: an #ECancellableMutex instance
+ *
+ * Frees memory allocated by e_cancellable_mutex_init().
+ *
+ * Since: 3.8
+ **/
+void
+e_cancellable_mutex_clear (ECancellableMutex *mutex)
+{
+	g_return_if_fail (mutex != NULL);
+
+	g_mutex_clear (&mutex->mutex);
+	g_mutex_clear (&mutex->base.cond_mutex);
+	g_cond_clear (&mutex->base.cond);
+}
+
+/**
+ * e_cancellable_mutex_lock:
+ * @mutex: an #ECancellableMutex instance
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ *
+ * Acquires lock on @mutex. The returned value indicates whether
+ * the lock was acquired, while %FALSE is returned only either or
+ * invalid arguments or the passed in @cancellable had been cancelled.
+ * In case of %NULL @cancellable the function blocks like g_mutex_lock().
+ *
+ * Returns: %TRUE, if lock had been acquired, %FALSE otherwise
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_cancellable_mutex_lock (ECancellableMutex *mutex,
+			  GCancellable *cancellable)
+{
+	gulong handler_id;
+	gboolean res = TRUE;
+
+	g_return_val_if_fail (mutex != NULL, FALSE);
+
+	g_mutex_lock (&mutex->base.cond_mutex);
+	if (!cancellable) {
+		g_mutex_unlock (&mutex->base.cond_mutex);
+		g_mutex_lock (&mutex->mutex);
+		return TRUE;
+	}
+
+	if (g_cancellable_is_cancelled (cancellable)) {
+		g_mutex_unlock (&mutex->base.cond_mutex);
+		return FALSE;
+	}
+
+	handler_id = g_signal_connect (cancellable, "cancelled",
+		G_CALLBACK (cancellable_locks_cancelled_cb), &mutex->base);
+
+	while (!g_mutex_trylock (&mutex->mutex)) {
+		/* recheck once per 10 seconds, just in case */
+		g_cond_wait_until (&mutex->base.cond, &mutex->base.cond_mutex,
+			g_get_monotonic_time () + (10 * G_TIME_SPAN_SECOND));
+
+		if (g_cancellable_is_cancelled (cancellable)) {
+			res = FALSE;
+			break;
+		}
+	}
+
+	g_signal_handler_disconnect (cancellable, handler_id);
+
+	g_mutex_unlock (&mutex->base.cond_mutex);
+
+	return res;
+}
+
+/**
+ * e_cancellable_mutex_unlock:
+ * @mutex: an #ECancellableMutex instance
+ *
+ * Releases lock previously acquired by e_cancellable_mutex_lock().
+ * Behaviour is undefined if this is called on a @mutex which returned
+ * %FALSE in e_cancellable_mutex_lock().
+ *
+ * Since: 3.8
+ **/
+void
+e_cancellable_mutex_unlock (ECancellableMutex *mutex)
+{
+	g_return_if_fail (mutex != NULL);
+
+	g_mutex_unlock (&mutex->mutex);
+
+	g_mutex_lock (&mutex->base.cond_mutex);
+	/* also wake-up any waiting threads */
+	g_cond_broadcast (&mutex->base.cond);
+	g_mutex_unlock (&mutex->base.cond_mutex);
+}
+
+/**
+ * e_cancellable_mutex_get_internal_mutex:
+ * @mutex: an #ECancellableMutex instance
+ *
+ * To get internal #GMutex. This is meant for cases when a lock is already
+ * acquired, and the caller needs to wait for a #GCond, in which case
+ * the returned #GMutex can be used to g_cond_wait() or g_cond_wait_until().
+ *
+ * Returns: Internal #GMutex, used in @mutex
+ *
+ * Since: 3.8
+ **/
+GMutex *
+e_cancellable_mutex_get_internal_mutex (ECancellableMutex *mutex)
+{
+	g_return_val_if_fail (mutex != NULL, NULL);
+
+	return &mutex->mutex;
+}
+
+/**
+ * e_cancellable_rec_mutex_init:
+ * @rec_mutex: an #ECancellableRecMutex instance
+ *
+ * Initializes @rec_mutex structure.
+ *
+ * Since: 3.8
+ **/
+void
+e_cancellable_rec_mutex_init (ECancellableRecMutex *rec_mutex)
+{
+	g_return_if_fail (rec_mutex != NULL);
+
+	g_rec_mutex_init (&rec_mutex->rec_mutex);
+	g_mutex_init (&rec_mutex->base.cond_mutex);
+	g_cond_init (&rec_mutex->base.cond);
+}
+
+/**
+ * e_cancellable_rec_mutex_clear:
+ * @rec_mutex: an #ECancellableRecMutex instance
+ *
+ * Frees memory allocated by e_cancellable_rec_mutex_init().
+ *
+ * Since: 3.8
+ **/
+void
+e_cancellable_rec_mutex_clear (ECancellableRecMutex *rec_mutex)
+{
+	g_return_if_fail (rec_mutex != NULL);
+
+	g_rec_mutex_clear (&rec_mutex->rec_mutex);
+	g_mutex_clear (&rec_mutex->base.cond_mutex);
+	g_cond_clear (&rec_mutex->base.cond);
+}
+
+/**
+ * e_cancellable_rec_mutex_lock:
+ * @rec_mutex: an #ECancellableRecMutex instance
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ *
+ * Acquires lock on @rec_mutex. The returned value indicates whether
+ * the lock was acquired, while %FALSE is returned only either or
+ * invalid arguments or the passed in @cancellable had been cancelled.
+ * In case of %NULL @cancellable the function blocks like g_rec_mutex_lock().
+ *
+ * Returns: %TRUE, if lock had been acquired, %FALSE otherwise
+ *
+ * Since: 3.8
+ **/
+gboolean
+e_cancellable_rec_mutex_lock (ECancellableRecMutex *rec_mutex,
+			      GCancellable *cancellable)
+{
+	gulong handler_id;
+	gboolean res = TRUE;
+
+	g_return_val_if_fail (rec_mutex != NULL, FALSE);
+
+	g_mutex_lock (&rec_mutex->base.cond_mutex);
+	if (!cancellable) {
+		g_mutex_unlock (&rec_mutex->base.cond_mutex);
+		g_rec_mutex_lock (&rec_mutex->rec_mutex);
+		return TRUE;
+	}
+
+	if (g_cancellable_is_cancelled (cancellable)) {
+		g_mutex_unlock (&rec_mutex->base.cond_mutex);
+		return FALSE;
+	}
+
+	handler_id = g_signal_connect (cancellable, "cancelled",
+		G_CALLBACK (cancellable_locks_cancelled_cb), &rec_mutex->base);
+
+	while (!g_rec_mutex_trylock (&rec_mutex->rec_mutex)) {
+		/* recheck once per 10 seconds, just in case */
+		g_cond_wait_until (&rec_mutex->base.cond, &rec_mutex->base.cond_mutex,
+			g_get_monotonic_time () + (10 * G_TIME_SPAN_SECOND));
+
+		if (g_cancellable_is_cancelled (cancellable)) {
+			res = FALSE;
+			break;
+		}
+	}
+
+	g_signal_handler_disconnect (cancellable, handler_id);
+
+	g_mutex_unlock (&rec_mutex->base.cond_mutex);
+
+	return res;
+}
+
+/**
+ * e_cancellable_rec_mutex_unlock:
+ * @rec_mutex: an #ECancellableRecMutex instance
+ *
+ * Releases lock previously acquired by e_cancellable_rec_mutex_lock().
+ * Behaviour is undefined if this is called on a @rec_mutex which returned
+ * %FALSE in e_cancellable_rec_mutex_lock().
+ *
+ * Since: 3.8
+ **/
+void
+e_cancellable_rec_mutex_unlock (ECancellableRecMutex *rec_mutex)
+{
+	g_return_if_fail (rec_mutex != NULL);
+
+	g_rec_mutex_unlock (&rec_mutex->rec_mutex);
+
+	g_mutex_lock (&rec_mutex->base.cond_mutex);
+	/* also wake-up any waiting threads */
+	g_cond_broadcast (&rec_mutex->base.cond);
+	g_mutex_unlock (&rec_mutex->base.cond_mutex);
+}
diff --git a/libedataserver/e-cancellable-locks.h b/libedataserver/e-cancellable-locks.h
new file mode 100644
index 0000000..0ccb23c
--- /dev/null
+++ b/libedataserver/e-cancellable-locks.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_CANCELLABLE_LOCKS_H
+#define E_CANCELLABLE_LOCKS_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ECancellableMutex ECancellableMutex;
+typedef struct _ECancellableRecMutex ECancellableRecMutex;
+
+void		e_cancellable_mutex_init	(ECancellableMutex *mutex);
+void		e_cancellable_mutex_clear	(ECancellableMutex *mutex);
+gboolean	e_cancellable_mutex_lock	(ECancellableMutex *mutex,
+						 GCancellable *cancellable);
+void		e_cancellable_mutex_unlock	(ECancellableMutex *mutex);
+GMutex *	e_cancellable_mutex_get_internal_mutex
+						(ECancellableMutex *mutex);
+
+void		e_cancellable_rec_mutex_init	(ECancellableRecMutex *rec_mutex);
+void		e_cancellable_rec_mutex_clear	(ECancellableRecMutex *rec_mutex);
+gboolean	e_cancellable_rec_mutex_lock	(ECancellableRecMutex *rec_mutex,
+						 GCancellable *cancellable);
+void		e_cancellable_rec_mutex_unlock	(ECancellableRecMutex *rec_mutex);
+
+/* private structures, members should not be accessed
+   otherwise than with above functions */
+
+struct _ECancellableLocksBase {
+	GMutex cond_mutex;
+	GCond cond;
+};
+
+struct _ECancellableMutex {
+	struct _ECancellableLocksBase base;
+	GMutex mutex;
+};
+
+struct _ECancellableRecMutex {
+	struct _ECancellableLocksBase base;
+	GRecMutex rec_mutex;
+};
+
+G_END_DECLS
+
+#endif /* E_CANCELLABLE_LOCKS_H */
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index ca98003..1bf689c 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -21,6 +21,7 @@
 
 #define __LIBEDATASERVER_H_INSIDE__
 
+#include <libedataserver/e-cancellable-locks.h>
 #include <libedataserver/e-categories.h>
 #include <libedataserver/e-client.h>
 #include <libedataserver/e-credentials.h>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]