Permalink
Browse files

[threadpool] Added dynamic concurrent queue implementation

	Replace the big lock around the list+array used to queue work items
	with a dynamic concurrent queue to reduce contention.
  • Loading branch information...
1 parent b8f9fee commit 2670761ee7f20ecfb4b49610394aca0f44aa0738 @gonzalop gonzalop committed Feb 5, 2011
@@ -55,7 +55,7 @@ public static class Environment {
* of icalls, do not require an increment.
*/
#pragma warning disable 169
- private const int mono_corlib_version = 94;
+ private const int mono_corlib_version = 95;
#pragma warning restore 169
[ComVisible (true)]
@@ -0,0 +1,40 @@
+//
+// System.MonoCQItem.cs
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo@novell.com)
+//
+// Copyright (C) 2011 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace System {
+#pragma warning disable 169
+ internal sealed class MonoCQItem {
+ object [] array;
+ byte [] array_state;
+ int head;
+ int tail;
+
+ }
+#pragma warning disable 169
+}
+
@@ -197,6 +197,7 @@ System/MissingFieldException.cs
System/MissingMemberException.cs
System/MissingMethodException.cs
System/MonoAsyncCall.cs
+System/MonoCQItem.cs
System/MonoCustomAttrs.cs
System/MonoListItem.cs
System/MonoType.cs
@@ -139,6 +139,8 @@ libmonoruntime_la_SOURCES = \
mono-basic-block.c \
mono-basic-block.h \
mono-config.c \
+ mono-cq.c \
+ mono-cq.h \
mono-debug.h \
mono-debug.c \
mono-debug-debugger.h \
@@ -73,7 +73,7 @@
* Changes which are already detected at runtime, like the addition
* of icalls, do not require an increment.
*/
-#define MONO_CORLIB_VERSION 94
+#define MONO_CORLIB_VERSION 95
typedef struct
{
View
@@ -0,0 +1,229 @@
+/*
+ * mono-cq.c: concurrent queue
+ *
+ * Authors:
+ * Gonzalo Paniagua Javier (gonzalo@novell.com)
+ *
+ * Copyright (c) 2011 Novell, Inc (http://www.novell.com)
+ */
+
+#include <mono/metadata/object.h>
+#include <mono/metadata/mono-cq.h>
+#include <mono/metadata/mono-mlist.h>
+
+#define CQ_DEBUG(...)
+//#define CQ_DEBUG(...) g_message(__VA_ARGS__)
+
+struct _MonoCQ {
+ MonoMList *head;
+ MonoMList *tail;
+ volatile gint32 count;
+};
+
+/* matches the System.MonoListItem object */
+struct _MonoMList {
+ MonoObject object;
+ MonoMList *next;
+ MonoObject *data;
+};
+
+/* matches the System.MonoCQItem object */
+struct _MonoCQItem {
+ MonoObject object;
+ MonoArray *array; // MonoObjects
+ MonoArray *array_state; // byte array
+ volatile gint32 first;
+ volatile gint32 last;
+};
+
+typedef struct _MonoCQItem MonoCQItem;
+#define CQ_ARRAY_SIZE 64
+
+static MonoVTable *monocq_item_vtable = NULL;
+
+static MonoCQItem *
+mono_cqitem_alloc (void)
+{
+ MonoCQItem *queue;
+ MonoDomain *domain = mono_get_root_domain ();
+
+ if (!monocq_item_vtable) {
+ MonoClass *klass = mono_class_from_name (mono_defaults.corlib, "System", "MonoCQItem");
+ monocq_item_vtable = mono_class_vtable (domain, klass);
+ g_assert (monocq_item_vtable);
+ }
+ queue = (MonoCQItem *) mono_object_new_fast (monocq_item_vtable);
+ queue->array = mono_array_new (domain, mono_defaults.object_class, CQ_ARRAY_SIZE);
+ queue->array_state = mono_array_new (domain, mono_defaults.byte_class, CQ_ARRAY_SIZE);
+ return queue;
+}
+
+MonoCQ *
+mono_cq_create ()
+{
+ MonoCQ *cq;
+
+ cq = g_new0 (MonoCQ, 1);
+ MONO_GC_REGISTER_ROOT (cq->head);
+ MONO_GC_REGISTER_ROOT (cq->tail);
+ cq->head = mono_mlist_alloc ((MonoObject *) mono_cqitem_alloc ());
+ cq->tail = cq->head;
+ CQ_DEBUG ("Created %p", cq);
+ return cq;
+}
+
+void
+mono_cq_destroy (MonoCQ *cq)
+{
+ CQ_DEBUG ("Destroy %p", cq);
+ if (!cq)
+ return;
+
+ memset (cq, 0, sizeof (MonoCQ));
+ MONO_GC_UNREGISTER_ROOT (cq->tail);
+ MONO_GC_UNREGISTER_ROOT (cq->head);
+ g_free (cq);
+}
+
+gint32
+mono_cq_count (MonoCQ *cq)
+{
+ if (!cq)
+ return 0;
+
+ CQ_DEBUG ("Count %d", cq->count);
+ return cq->count;
+}
+
+static void
+mono_cq_add_node (MonoCQ *cq)
+{
+ MonoMList *n;
+ MonoMList *prev_tail;
+
+ CQ_DEBUG ("Adding node");
+ n = mono_mlist_alloc ((MonoObject *) mono_cqitem_alloc ());
+ prev_tail = cq->tail;
+ prev_tail->next = n;
+ cq->tail = n;
+}
+
+static gboolean
+mono_cqitem_try_enqueue (MonoCQ *cq, MonoObject *obj)
+{
+ MonoCQItem *queue;
+ MonoMList *tail;
+ gint32 pos;
+
+ tail = cq->tail;
+ queue = (MonoCQItem *) tail->data;
+ do {
+ pos = queue->last;
+ if (pos >= CQ_ARRAY_SIZE) {
+ CQ_DEBUG ("enqueue(): pos >= CQ_ARRAY_SIZE, %d >= %d", pos, CQ_ARRAY_SIZE);
+ return FALSE;
+ }
+
+ if (InterlockedCompareExchange (&queue->last, pos + 1, pos) == pos) {
+ mono_array_setref (queue->array, pos, obj);
+ mono_array_set (queue->array_state, char, pos, TRUE);
+ if ((pos + 1) == CQ_ARRAY_SIZE) {
+ CQ_DEBUG ("enqueue(): pos + 1 == CQ_ARRAY_SIZE, %d. Adding node.", CQ_ARRAY_SIZE);
+ mono_cq_add_node (cq);
+ }
+ return TRUE;
+ }
+ } while (TRUE);
+ g_assert_not_reached ();
+}
+
+void
+mono_cq_enqueue (MonoCQ *cq, MonoObject *obj)
+{
+ if (cq == NULL || obj == NULL)
+ return;
+
+ do {
+ if (mono_cqitem_try_enqueue (cq, obj)) {
+ CQ_DEBUG ("Queued one");
+ InterlockedIncrement (&cq->count);
+ break;
+ }
+ SleepEx (0, FALSE);
+ } while (TRUE);
+}
+
+static void
+mono_cq_remove_node (MonoCQ *cq)
+{
+ MonoMList *old_head;
+
+ CQ_DEBUG ("Removing node");
+ old_head = cq->head;
+ /* Not needed now that array_state is GC memory
+ MonoCQItem *queue;
+ int i;
+ gboolean retry;
+ queue = (MonoCQItem *) old_head->data;
+ do {
+ retry = FALSE;
+ for (i = 0; i < CQ_ARRAY_SIZE; i++) {
+ if (mono_array_get (queue->array_state, char, i) == TRUE) {
+ retry = TRUE;
+ break;
+ }
+ }
+ if (retry)
+ SleepEx (0, FALSE);
+ } while (retry);
+ */
+ while (old_head->next == NULL)
+ SleepEx (0, FALSE);
+ cq->head = old_head->next;
+ old_head = NULL;
+}
+
+static gboolean
+mono_cqitem_try_dequeue (MonoCQ *cq, MonoObject **obj)
+{
+ MonoCQItem *queue;
+ MonoMList *head;
+ gint32 pos;
+
+ head = cq->head;
+ queue = (MonoCQItem *) head->data;
+ do {
+ pos = queue->first;
+ if (pos >= queue->last || pos >= CQ_ARRAY_SIZE)
+ return FALSE;
+
+ if (InterlockedCompareExchange (&queue->first, pos + 1, pos) == pos) {
+ while (mono_array_get (queue->array_state, char, pos) == FALSE) {
+ SleepEx (0, FALSE);
+ }
+ *obj = mono_array_get (queue->array, MonoObject *, pos);
+ mono_array_set (queue->array, MonoObject *, pos, NULL);
+ mono_array_set (queue->array_state, char, pos, FALSE);
+ if ((pos + 1) == CQ_ARRAY_SIZE) {
+ mono_cq_remove_node (cq);
+ }
+ return TRUE;
+ }
+ } while (TRUE);
+ g_assert_not_reached ();
+}
+
+gboolean
+mono_cq_dequeue (MonoCQ *cq, MonoObject **result)
+{
+ while (cq->count > 0) {
+ if (mono_cqitem_try_dequeue (cq, result)) {
+ CQ_DEBUG ("Dequeued one");
+ InterlockedDecrement (&cq->count);
+ return TRUE;
+ }
+ SleepEx (0, FALSE);
+ }
+ return FALSE;
+}
+
View
@@ -0,0 +1,22 @@
+#ifndef _MONO_CQ_H
+#define _MONO_CQ_H
+
+#include <config.h>
+#include <glib.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/gc-internal.h>
+
+G_BEGIN_DECLS
+
+typedef struct _MonoCQ MonoCQ;
+
+MonoCQ *mono_cq_create (void) MONO_INTERNAL;
+void mono_cq_destroy (MonoCQ *cq) MONO_INTERNAL;
+gint mono_cq_count (MonoCQ *cq) MONO_INTERNAL;
+void mono_cq_enqueue (MonoCQ *cq, MonoObject *obj) MONO_INTERNAL;
+gboolean mono_cq_dequeue (MonoCQ *cq, MonoObject **result) MONO_INTERNAL;
+
+G_END_DECLS
+
+#endif
+
Oops, something went wrong.

0 comments on commit 2670761

Please sign in to comment.