util: Add lock-free queue utility 66/270466/2
authorDongwoo Lee <dwoo08.lee@samsung.com>
Thu, 3 Feb 2022 06:30:59 +0000 (15:30 +0900)
committerDongwoo Lee <dwoo08.lee@samsung.com>
Thu, 3 Feb 2022 06:30:59 +0000 (15:30 +0900)
Now lock-free queue for supporting multi-thread without lock
contention will be provided.

Change-Id: I6459e397954c95831ca3b31359cb9136eb59705e
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
CMakeLists.txt
include/util/queue.h [new file with mode: 0644]
src/util/queue.c [new file with mode: 0644]

index 24425eb..686a478 100644 (file)
@@ -31,6 +31,7 @@ SET(SRCS
        src/util/gdbus-util.c
        src/util/timer.c
        src/util/thread.c
+       src/util/queue.c
        src/main.c
        #Generated by a custom command 'gdbus-codegen' below
        src/pass/pass-dbus-stub.c
diff --git a/include/util/queue.h b/include/util/queue.h
new file mode 100644 (file)
index 0000000..23b8331
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * PASS
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __QUEUE_H__
+#define __QUEUE_H__
+
+#define smp_mb()       dmb()
+#define smp_rmb()      dmb()
+#define smp_wmb()      dmb()
+
+#ifdef __aarch64__
+#define dmb()          __asm__ __volatile__ ("dmb sy" : : : "memory")
+#elif __arm__
+#define dmb()          __asm__ __volatile__ ("dmb" : : : "memory")
+#else
+#define dmb()          __asm__ __volatile__ ("" : : : "memory")
+#endif
+
+#define ATOMIC_SUB     __sync_sub_and_fetch
+#define ATOMIC_ADD     __sync_add_and_fetch
+#define CAS            __sync_bool_compare_and_swap
+#define XCHG           __sync_lock_test_and_set
+
+struct queue_node {
+       void *data;
+       struct queue_node *next;
+};
+
+struct queue {
+       struct queue_node *head;
+       struct queue_node *tail;
+};
+
+static inline void cpu_relax(void)
+{
+       asm volatile("yield" ::: "memory");
+}
+
+int enqueue(struct queue *queue, void *data);
+int dequeue(struct queue *queue, void **data);
+int create_queue(struct queue **queue);
+void destroy_queue(struct queue *queue);
+
+#endif
diff --git a/src/util/queue.c b/src/util/queue.c
new file mode 100644 (file)
index 0000000..ec3819d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * PASS
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#include <util/queue.h>
+
+int enqueue(struct queue *queue, void *data)
+{
+       struct queue_node *node;
+       struct queue_node *new_node;
+
+       new_node = (struct queue_node *)malloc(sizeof(struct queue_node));
+       if (!new_node)
+               return -ENOMEM;
+
+       new_node->data = data;
+       new_node->next = NULL;
+
+       for (;;) {
+               node = queue->tail;
+               if (CAS(&(node->next), NULL, new_node))
+                       break;
+
+               CAS(&(queue->tail), node, node->next);
+       }
+       CAS(&(queue->tail), node, new_node);
+
+       return 0;
+}
+
+int dequeue(struct queue *queue, void **data)
+{
+       struct queue_node *node;
+
+       for (;;) {
+               node = queue->head;
+               if (!node->next)
+                       return -ENOENT;
+
+               if (CAS(&(queue->head), node, node->next))
+                       break;
+       }
+       if (data)
+               *data = (void *) node->next->data;
+       free(node);
+
+       return 0;
+}
+
+int create_queue(struct queue **queue)
+{
+       struct queue *new_queue;
+       struct queue_node *sentinel;
+
+       new_queue = (struct queue *)malloc(sizeof(struct queue));
+       if (!new_queue)
+               return -ENOMEM;
+
+       sentinel = (struct queue_node *)malloc(sizeof(struct queue_node));
+       if (!sentinel) {
+               free(new_queue);
+               return -ENOMEM;
+       }
+
+       new_queue->head = new_queue->tail = sentinel;
+       new_queue->head->data = NULL;
+       new_queue->head->next = NULL;
+
+       *queue = new_queue;
+
+       return 0;
+}
+
+void destroy_queue(struct queue *queue)
+{
+       do { } while (!dequeue(queue, NULL));
+
+       /* freeing remaining sentinel */
+       free(queue->head);
+
+       queue->head = queue->tail = NULL;
+       free(queue);
+}