Skip to content
This repository was archived by the owner on Jan 15, 2021. It is now read-only.

Commit 1b7e54d

Browse files
committed
Add a lock-free queue to core-util
This queue is written in C and can serve as the basis for C++ typesafe queues.
1 parent a1ae6d8 commit 1b7e54d

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

core-util/atomic-queue.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright (c) 2015 ARM Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#ifndef CORE_UTIL_ATOMIC_QUEUE_H
19+
#define CORE_UTIL_ATOMIC_QUEUE_H
20+
21+
#ifdef __cplusplus
22+
extern "C" {
23+
#endif
24+
25+
struct lockfree_queue_element {
26+
struct lockfree_queue_element * volatile next;
27+
void * data;
28+
};
29+
30+
struct lockfree_queue {
31+
struct lockfree_queue_element * volatile tail;
32+
};
33+
34+
/**
35+
* \brief Add an element to the tail of the queue
36+
*
37+
* Since the queue only maintains a tail pointer, this simply inserts the new element before the tail pointer
38+
*
39+
* @param[in,out] q the queue structure to operate on
40+
* @param[in] e The element to add to the queue
41+
*/
42+
void lfq_push_tail(struct lockfree_queue * q, struct lockfree_queue_element * e);
43+
/**
44+
* \brief Get an element from the head of the queue
45+
*
46+
* This function iterates over the queue and removes an element from the head when it finds the head. This is slower
47+
* than maintaining a head pointer, but it is necessary to ensure that a pop is completely atomic.
48+
*
49+
* @param[in,out] q The queue to pop from
50+
* @return The popped element or NULL if the queue was empty
51+
*/
52+
struct lockfree_queue_element * lfq_pop_head(struct lockfree_queue * q);
53+
/**
54+
* Check if there are any elements in the queue
55+
*
56+
* Note that there is no guarantee that a queue which is not empty when this API is called will not be become empty
57+
* before lfq_pop_head is called
58+
*
59+
* @retval non-zero when the queue is empty
60+
* @retval 0 when the queue is not empty
61+
*/
62+
int lfq_empty(struct lockfree_queue * q);
63+
/**
64+
* Iterates over the queue and counts the elements in the queue
65+
*
66+
* The value returned by this function may be invalid by the time it returns. Do not depend on this value except in
67+
* a critical section.
68+
*
69+
* @return the number of elements in the queue
70+
*/
71+
unsigned lfq_count(struct lockfree_queue * q);
72+
#ifdef __cplusplus
73+
}
74+
#endif
75+
76+
#endif // CORE_UTIL_ATOMIC_QUEUE_H

source/atomic-queue.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright (c) 2015 ARM Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "core-util/assert.h"
19+
#include "core-util/atomic-queue.h"
20+
#include <stddef.h>
21+
#include "cmsis.h"
22+
23+
void lfq_push_tail(struct lockfree_queue * q, struct lockfree_queue_element * e)
24+
{
25+
CORE_UTIL_ASSERT_MSG(q != NULL, "null queue used");
26+
if (q == NULL) {
27+
return;
28+
}
29+
do {
30+
e->next = q->tail;
31+
} while (!__sync_bool_compare_and_swap(&q->tail, e->next, e));
32+
}
33+
34+
volatile int trip = 0;
35+
36+
int tripped() {
37+
return trip;
38+
}
39+
40+
struct lockfree_queue_element * lfq_pop_head(struct lockfree_queue * q)
41+
{
42+
struct lockfree_queue_element * rx;
43+
CORE_UTIL_ASSERT_MSG(q != NULL, "null queue used");
44+
if (q == NULL) {
45+
return NULL;
46+
}
47+
while (1) {
48+
// Set the element reference pointer to the tail pointer
49+
struct lockfree_queue_element * volatile * px = &q->tail;
50+
if (*px == NULL) {
51+
return NULL;
52+
}
53+
while (1) {
54+
rx = *px;
55+
if (rx == NULL) {
56+
break;
57+
}
58+
if (rx->next == NULL) {
59+
break;
60+
}
61+
px = &rx->next;
62+
}
63+
if (rx == NULL) {
64+
continue;
65+
}
66+
if(__sync_bool_compare_and_swap(px, rx, NULL)){
67+
break;
68+
}
69+
}
70+
return rx;
71+
}
72+
73+
74+
int lfq_empty(struct lockfree_queue * q)
75+
{
76+
return q->tail == NULL;
77+
}
78+
79+
unsigned lfq_count(struct lockfree_queue *q)
80+
{
81+
unsigned x;
82+
struct lockfree_queue_element * volatile e;
83+
if (lfq_empty(q)) {
84+
return 0;
85+
}
86+
e = q->tail;
87+
for (x = 1; e->next != NULL; x++, e = e->next);
88+
return x;
89+
}

0 commit comments

Comments
 (0)