[gcr] gck: Chaining of enumerators



commit 468414442f0c4a785fcf784752cae8a1e44323c9
Author: Stef Walter <stefw collabora co uk>
Date:   Wed Nov 2 14:10:43 2011 +0100

    gck: Chaining of enumerators
    
     * Add functions gck_enumerator_get_chained() and
       gck_enumerator_set_chained()
     * Chaining enumerator returns results from the chained enumerator
       after all results have been retrieved from the former.

 docs/reference/gck/gck-sections.txt |    2 +
 gck/gck-enumerator.c                |  198 ++++++++++++++++++++++++++++-------
 gck/gck.h                           |    5 +
 gck/gck.symbols                     |    2 +
 gck/tests/test-gck-enumerator.c     |   40 +++++++
 5 files changed, 210 insertions(+), 37 deletions(-)
---
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt
index 5a36a28..da8cab5 100644
--- a/docs/reference/gck/gck-sections.txt
+++ b/docs/reference/gck/gck-sections.txt
@@ -358,6 +358,8 @@ gck_enumerator_get_interaction
 gck_enumerator_set_interaction
 gck_enumerator_get_object_type
 gck_enumerator_set_object_type
+gck_enumerator_get_chained
+gck_enumerator_set_chained
 <SUBSECTION Standard>
 GckEnumeratorClass
 gck_enumerator_get_type
diff --git a/gck/gck-enumerator.c b/gck/gck-enumerator.c
index 060b355..933c344 100644
--- a/gck/gck-enumerator.c
+++ b/gck/gck-enumerator.c
@@ -46,7 +46,8 @@
 enum {
 	PROP_0,
 	PROP_INTERACTION,
-	PROP_OBJECT_TYPE
+	PROP_OBJECT_TYPE,
+	PROP_CHAINED
 };
 
 /**
@@ -67,6 +68,9 @@ typedef gpointer (*GckEnumeratorFunc)     (GckEnumeratorState *args,
                                            gboolean forward);
 
 struct _GckEnumeratorState {
+	gpointer enumerator;
+	GckEnumeratorState *chained;
+
 	/* For the current call */
 	gint want_objects;
 
@@ -105,6 +109,7 @@ struct _GckEnumeratorPrivate {
 	GTlsInteraction *interaction;
 	GType object_type;
 	GckObjectClass *object_class;
+	GckEnumerator *chained;
 };
 
 G_DEFINE_TYPE (GckEnumerator, gck_enumerator, G_TYPE_OBJECT);
@@ -492,8 +497,6 @@ state_attributes (GckEnumeratorState *args,
 	session = gck_session_get_handle (args->session);
 	g_return_val_if_fail (session, NULL);
 
-	count = 0;
-
 	/* Get the attributes for want_objects */
 	for (count = 0, l = args->results->head;
 	     l != NULL && count < args->want_objects;
@@ -545,19 +548,22 @@ state_results (GckEnumeratorState *args,
                gboolean forward)
 {
 	g_assert (args->results != NULL);
+	gint length;
 
 	/* No cleanup, just unwind */
 	if (!forward)
 		return state_authenticated;
 
-	while (args->want_objects > g_queue_get_length (args->results)) {
+	length = g_queue_get_length (args->results);
+
+	while (args->want_objects > length) {
 		_gck_debug ("wanted %d objects, have %d, looking for more",
-		            args->want_objects, g_queue_get_length (args->results));
+		            args->want_objects, length);
 		return rewind_state (args, state_slots);
 	}
 
 	_gck_debug ("wanted %d objects, returned %d objects",
-	            args->want_objects, g_queue_get_length (args->results));
+	            args->want_objects, length);
 
 	/* We got all the results we wanted */
 	return NULL;
@@ -589,6 +595,9 @@ gck_enumerator_get_property (GObject *obj,
 	case PROP_OBJECT_TYPE:
 		g_value_set_gtype (value, gck_enumerator_get_object_type (self));
 		break;
+	case PROP_CHAINED:
+		g_value_set_object (value, gck_enumerator_get_chained (self));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -610,6 +619,9 @@ gck_enumerator_set_property (GObject *obj,
 	case PROP_OBJECT_TYPE:
 		gck_enumerator_set_object_type (self, g_value_get_gtype (value));
 		break;
+	case PROP_CHAINED:
+		gck_enumerator_set_chained (self, g_value_get_object (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -622,6 +634,7 @@ gck_enumerator_dispose (GObject *obj)
 	GckEnumerator *self = GCK_ENUMERATOR (obj);
 
 	gck_enumerator_set_interaction (self, NULL);
+	gck_enumerator_set_chained (self, NULL);
 
 	G_OBJECT_CLASS (gck_enumerator_parent_class)->dispose (obj);
 }
@@ -675,6 +688,16 @@ gck_enumerator_class_init (GckEnumeratorClass *klass)
 	g_object_class_install_property (gobject_class, PROP_OBJECT_TYPE,
 		g_param_spec_gtype ("object-type", "Object Type", "Type of objects created",
 		                    GCK_TYPE_OBJECT, G_PARAM_READWRITE));
+
+	/**
+	 * GckEnumerator:chained:
+	 *
+	 * Chained enumerator, which will be enumerated when this enumerator
+	 * has enumerated all its objects.
+	 */
+	g_object_class_install_property (gobject_class, PROP_CHAINED,
+	            g_param_spec_object ("chained", "Chained", "Chained enumerator",
+	                                 GCK_TYPE_ENUMERATOR, G_PARAM_READWRITE));
 }
 
 static void
@@ -775,6 +798,7 @@ _gck_enumerator_new_for_session (GckSession *session,
 typedef struct _EnumerateNext {
 	GckArguments base;
 	GckEnumeratorState *state;
+	gint want_objects;
 } EnumerateNext;
 
 static CK_RV
@@ -782,17 +806,23 @@ perform_enumerate_next (EnumerateNext *args)
 {
 	GckEnumeratorFunc handler;
 	GckEnumeratorState *state;
+	gint count = 0;
 
 	g_assert (args->state);
-	state = args->state;
 
-	g_assert (state->handler);
+	for (state = args->state; state != NULL; state = state->chained) {
+		g_assert (state->handler);
+		state->want_objects = args->want_objects - count;
+		for (;;) {
+			handler = (state->handler) (state, TRUE);
+			if (!handler)
+				break;
+			state->handler = handler;
+		}
 
-	for (;;) {
-		handler = (state->handler) (state, TRUE);
-		if (!handler)
+		count += state->results ? g_queue_get_length (state->results) : 0;
+		if (count >= args->want_objects)
 			break;
-		state->handler = handler;
 	}
 
 	/* TODO: In some modes, errors */
@@ -872,6 +902,64 @@ gck_enumerator_set_object_type (GckEnumerator *self,
 }
 
 /**
+ * gck_enumerator_get_chained:
+ * @self: the enumerator
+ *
+ * Get the enumerator that will be run after all objects from this one
+ * are seen.
+ *
+ * Returns: (transfer full) (allow-none): the chained enumerator or %NULL
+ */
+GckEnumerator *
+gck_enumerator_get_chained (GckEnumerator *self)
+{
+	GckEnumerator *chained = NULL;
+
+	g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL);
+
+	g_mutex_lock (self->pv->mutex);
+
+		if (self->pv->chained)
+			chained = g_object_ref (self->pv->chained);
+
+	g_mutex_unlock (self->pv->mutex);
+
+	return chained;
+}
+
+/**
+ * gck_enumerator_set_interaction:
+ * @self: the enumerator
+ * @chained: (allow-none): the chained enumerator or %NULL
+ *
+ * Set a chained enumerator that will be run after all objects from this one
+ * are seen.
+ */
+void
+gck_enumerator_set_chained (GckEnumerator *self,
+                            GckEnumerator *chained)
+{
+	GckEnumerator *old_chained = NULL;
+
+	g_return_if_fail (GCK_IS_ENUMERATOR (self));
+	g_return_if_fail (chained == NULL || GCK_IS_ENUMERATOR (chained));
+
+	g_mutex_lock (self->pv->mutex);
+
+		old_chained = self->pv->chained;
+		if (chained)
+			g_object_ref (chained);
+		self->pv->chained = chained;
+
+	g_mutex_unlock (self->pv->mutex);
+
+	if (old_chained)
+		g_object_unref (old_chained);
+
+	g_object_notify (G_OBJECT (self), "chained");
+}
+
+/**
  * gck_enumerator_get_interaction:
  * @self: the enumerator
  *
@@ -931,6 +1019,16 @@ static GckEnumeratorState *
 check_out_enumerator_state (GckEnumerator *self)
 {
 	GckEnumeratorState *state = NULL;
+	GTlsInteraction *old_interaction = NULL;
+	gpointer old_object_class = NULL;
+	GckEnumeratorState *chained_state = NULL;
+	GckEnumerator *chained;
+
+	chained = gck_enumerator_get_chained (self);
+	if (chained) {
+		chained_state = check_out_enumerator_state (chained);
+		g_object_unref (chained);
+	}
 
 	g_mutex_lock (self->pv->mutex);
 
@@ -938,12 +1036,17 @@ check_out_enumerator_state (GckEnumerator *self)
 			state = self->pv->the_state;
 			self->pv->the_state = NULL;
 
-			g_clear_object (&state->interaction);
+			state->enumerator = g_object_ref (self);
+			g_assert (state->chained == NULL);
+			state->chained = chained_state;
+
+			old_interaction = state->interaction;
 			if (self->pv->interaction)
 				state->interaction = g_object_ref (self->pv->interaction);
+			else
+				state->interaction = NULL;
 
-			if (state->object_class)
-				g_type_class_unref (state->object_class);
+			old_object_class = state->object_class;
 
 			/* Must already be holding a reference, state also holds a ref */
 			state->object_type = self->pv->object_type;
@@ -959,36 +1062,57 @@ check_out_enumerator_state (GckEnumerator *self)
 	if (state == NULL)
 		g_warning ("this enumerator is already running a next operation");
 
+	/* Free these outside the lock */
+	if (old_interaction)
+		g_object_unref (old_interaction);
+	if (old_object_class)
+		g_type_class_unref (old_object_class);
+
 	return state;
 }
 
 static void
-check_in_enumerator_state (GckEnumerator *self,
-                           GckEnumeratorState *state)
+check_in_enumerator_state (GckEnumeratorState *state)
 {
+	GckEnumeratorState *chained = NULL;
+	GckEnumerator *self;
+
+	g_assert (GCK_IS_ENUMERATOR (state->enumerator));
+	self = state->enumerator;
+
 	g_mutex_lock (self->pv->mutex);
 
+		state->enumerator = NULL;
 		g_assert (self->pv->the_state == NULL);
 		self->pv->the_state = state;
+		chained = state->chained;
+		state->chained = NULL;
 
 	g_mutex_unlock (self->pv->mutex);
+
+	/* matches ref in check_in */
+	g_object_unref (self);
+
+	if (chained)
+		check_in_enumerator_state (chained);
 }
 
 static GckObject *
 extract_result (GckEnumeratorState *state)
 {
-	GckEnumeratorResult *result;
+	GckEnumeratorResult *result = NULL;
 	GckModule *module;
 	GckObject *object;
 
 	g_assert (state != NULL);
 
-	if (state->results == NULL)
-		return NULL;
-
-	result = g_queue_pop_head (state->results);
-	if (result == NULL)
+	if (state->results != NULL)
+		result = g_queue_pop_head (state->results);
+	if (result == NULL) {
+		if (state->chained)
+			return extract_result (state->chained);
 		return NULL;
+	}
 
 	module = gck_session_get_module (result->session);
 	object = g_object_new (state->object_type,
@@ -1044,7 +1168,7 @@ gck_enumerator_next (GckEnumerator *self,
                      GCancellable *cancellable,
                      GError **error)
 {
-	EnumerateNext args = { GCK_ARGUMENTS_INIT, NULL, };
+	EnumerateNext args = { GCK_ARGUMENTS_INIT, NULL, 0, };
 	GckObject *result = NULL;
 
 	g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL);
@@ -1056,17 +1180,17 @@ gck_enumerator_next (GckEnumerator *self,
 	/* A result from a previous run? */
 	result = extract_result (args.state);
 	if (result == NULL) {
-		args.state->want_objects = 1;
+		args.want_objects = 1;
 
 		/* Run the operation and steal away the results */
 		if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error))
 			result = extract_result (args.state);
 
-		args.state->want_objects = 0;
+		args.want_objects = 0;
 	}
 
 	/* Put the state back */
-	check_in_enumerator_state (self, args.state);
+	check_in_enumerator_state (args.state);
 
 	return result;
 }
@@ -1094,7 +1218,7 @@ gck_enumerator_next_n (GckEnumerator *self,
                        GCancellable *cancellable,
                        GError **error)
 {
-	EnumerateNext args = { GCK_ARGUMENTS_INIT, NULL, };
+	EnumerateNext args = { GCK_ARGUMENTS_INIT, NULL, 0, };
 	GList *results = NULL;
 	gint want_objects;
 
@@ -1111,21 +1235,21 @@ gck_enumerator_next_n (GckEnumerator *self,
 	/* A result from a previous run? */
 	results = extract_results (args.state, &want_objects);
 	if (want_objects > 0) {
-		args.state->want_objects = want_objects;
+		args.want_objects = want_objects;
 
 		/* Run the operation and steal away the results */
 		if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error))
 			results = g_list_concat (results, extract_results (args.state, &want_objects));
 
-		args.state->want_objects = 0;
+		args.want_objects = 0;
 	}
 
+	/* Put the state back */
+	check_in_enumerator_state (args.state);
+
 	if (results)
 		g_clear_error (error);
 
-	/* Put the state back */
-	check_in_enumerator_state (self, args.state);
-
 	return results;
 }
 
@@ -1158,9 +1282,9 @@ gck_enumerator_next_async (GckEnumerator *self, gint max_objects, GCancellable *
 	state = check_out_enumerator_state (self);
 	g_return_if_fail (state != NULL);
 
-	state->want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
 	args =  _gck_call_async_prep (NULL, self, perform_enumerate_next, NULL,
 	                               sizeof (*args), free_enumerate_next);
+	args->want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
 
 	args->state = state;
 	_gck_call_async_ready_go (args, cancellable, callback, user_data);
@@ -1194,14 +1318,14 @@ gck_enumerator_next_finish (GckEnumerator *self, GAsyncResult *result, GError **
 	args = _gck_call_arguments (result, EnumerateNext);
 	state = args->state;
 	args->state = NULL;
-	want_objects = state->want_objects;
-	state->want_objects = 0;
+	want_objects = args->want_objects;
+	args->want_objects = 0;
 
 	if (_gck_call_basic_finish (result, error))
 		results = extract_results (state, &want_objects);
 
 	/* Put the state back */
-	check_in_enumerator_state (self, state);
+	check_in_enumerator_state (state);
 
 	g_object_unref (self);
 
diff --git a/gck/gck.h b/gck/gck.h
index 7acfaa9..42b16e2 100644
--- a/gck/gck.h
+++ b/gck/gck.h
@@ -461,6 +461,11 @@ GType                 gck_enumerator_get_object_type          (GckEnumerator *se
 void                  gck_enumerator_set_object_type          (GckEnumerator *self,
                                                                GType object_type);
 
+GckEnumerator *       gck_enumerator_get_chained              (GckEnumerator *self);
+
+void                  gck_enumerator_set_chained              (GckEnumerator *self,
+                                                               GckEnumerator *chained);
+
 GckObject *           gck_enumerator_next                     (GckEnumerator *self,
                                                                GCancellable *cancellable,
                                                                GError **error);
diff --git a/gck/gck.symbols b/gck/gck.symbols
index 5070913..f336260 100644
--- a/gck/gck.symbols
+++ b/gck/gck.symbols
@@ -57,6 +57,7 @@ gck_attributes_set_date
 gck_attributes_set_string
 gck_attributes_set_ulong
 gck_attributes_unref
+gck_enumerator_get_chained
 gck_enumerator_get_interaction
 gck_enumerator_get_object_type
 gck_enumerator_get_type
@@ -64,6 +65,7 @@ gck_enumerator_next
 gck_enumerator_next_async
 gck_enumerator_next_finish
 gck_enumerator_next_n
+gck_enumerator_set_chained
 gck_enumerator_set_interaction
 gck_enumerator_set_object_type
 gck_error_get_quark
diff --git a/gck/tests/test-gck-enumerator.c b/gck/tests/test-gck-enumerator.c
index d46d437..b248b4e 100644
--- a/gck/tests/test-gck-enumerator.c
+++ b/gck/tests/test-gck-enumerator.c
@@ -499,6 +499,45 @@ test_attribute_get (Test *test,
 	g_object_unref (en);
 }
 
+static void
+test_chained (Test *test,
+              gconstpointer unused)
+{
+	GckEnumerator *one;
+	GckEnumerator *two;
+	GckEnumerator *three;
+	GckUriData *uri_data;
+	GError *error = NULL;
+	GList *objects;
+
+	uri_data = gck_uri_data_new ();
+	uri_data->attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (uri_data->attributes, CKA_CLASS, CKO_PUBLIC_KEY);
+	one = _gck_enumerator_new_for_modules (test->modules, 0, uri_data);
+
+	uri_data = gck_uri_data_new ();
+	uri_data->attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (uri_data->attributes, CKA_CLASS, CKO_PRIVATE_KEY);
+	two = _gck_enumerator_new_for_modules (test->modules, 0, uri_data);
+	gck_enumerator_set_chained (one, two);
+
+	uri_data = gck_uri_data_new ();
+	uri_data->attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (uri_data->attributes, CKA_CLASS, CKO_DATA);
+	three = _gck_enumerator_new_for_modules (test->modules, 0, uri_data);
+	gck_enumerator_set_chained (two, three);
+
+	g_object_unref (two);
+	g_object_unref (three);
+
+	objects = gck_enumerator_next_n (one, -1, NULL, &error);
+	g_assert_no_error (error);
+	g_assert_cmpint (g_list_length (objects), ==, 5);
+
+	gck_list_unref_free (objects);
+	g_object_unref (one);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -520,6 +559,7 @@ main (int argc, char **argv)
 	g_test_add ("/gck/enumerator/attribute_match", Test, NULL, setup, test_attribute_match, teardown);
 	g_test_add ("/gck/enumerator/token_match", Test, NULL, setup, test_token_match, teardown);
 	g_test_add ("/gck/enumerator/attribute_get", Test, NULL, setup, test_attribute_get, teardown);
+	g_test_add ("/gck/enumerator/chained", Test, NULL, setup, test_chained, teardown);
 
 	return egg_tests_run_in_thread_with_loop ();
 }



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