mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
added new lock-less stack data structure
This commit is contained in:
parent
62d46390fb
commit
6392073223
2 changed files with 216 additions and 0 deletions
82
include/lstack.h
Normal file
82
include/lstack.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/** Lock-less LIFO stack
|
||||
*
|
||||
* Based on a single-linked list and double word compare exchange (DWCAS)
|
||||
* to solve the ABA problem.
|
||||
*
|
||||
* Based on https://github.com/skeeto/lstack
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of S2SS. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
*********************************************************************************/
|
||||
|
||||
#ifndef _LSTACK_H_
|
||||
#define _LSTACK_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
struct lstack_node {
|
||||
void *value;
|
||||
struct lstack_node *next;
|
||||
};
|
||||
|
||||
/** DWCAS cmpexch16 */
|
||||
struct lstack_head {
|
||||
uintptr_t aba;
|
||||
struct lstack_node *node;
|
||||
};
|
||||
|
||||
struct lstack {
|
||||
struct lstack_node *node_buffer;
|
||||
_Atomic struct lstack_head head, free;
|
||||
_Atomic size_t size, avail;
|
||||
};
|
||||
|
||||
/** Initialize a lock-less stack which can hold up to maxsz values.
|
||||
*
|
||||
* Note: this function is not thread-safe.
|
||||
*/
|
||||
int lstack_init(struct lstack *lstack, size_t maxsz);
|
||||
|
||||
/** Pop cnt values from the stack an place them in the array values */
|
||||
ssize_t lstack_pop_many(struct lstack *lstack, void *values[], size_t cnt);
|
||||
|
||||
/** Push cnt values which are giving by the array values to the stack. */
|
||||
ssize_t lstack_push_many(struct lstack *lstack, void *values[], size_t cnt);
|
||||
|
||||
/** Push a single value to the stack. */
|
||||
static inline __attribute__((unused)) int lstack_push(struct lstack *lstack, void *value)
|
||||
{
|
||||
return lstack_push_many(lstack, &value, 1) == 1 ? 0 : -1;
|
||||
}
|
||||
|
||||
/** Pop a single element from the stack and return it. */
|
||||
static inline __attribute__((unused)) void * lstack_pop(struct lstack *lstack)
|
||||
{
|
||||
void *value;
|
||||
|
||||
lstack_pop_many(lstack, &value, 1);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Return the approximate size of the stack. */
|
||||
static inline __attribute__((unused)) size_t lstack_size(struct lstack *lstack)
|
||||
{
|
||||
return atomic_load(&lstack->size);
|
||||
}
|
||||
|
||||
/** Release memory used by the stack.
|
||||
*
|
||||
* Note: this function is not thread-safe.
|
||||
*/
|
||||
static inline __attribute__((unused)) void lstack_destroy(struct lstack *lstack)
|
||||
{
|
||||
free(lstack->node_buffer);
|
||||
}
|
||||
|
||||
#endif /* _LSTACK_H_ */
|
134
lib/lstack.c
Normal file
134
lib/lstack.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/** Lock-less LIFO stack
|
||||
*
|
||||
* Based on a single-linked list and double word compare exchange (DWCAS)
|
||||
* to solve the ABA problem.
|
||||
*
|
||||
* Based on https://github.com/skeeto/lstack
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of S2SS. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lstack.h"
|
||||
#include "utils.h"
|
||||
|
||||
static struct lstack_node * pop_many(_Atomic struct lstack_head *head, size_t cnt)
|
||||
{
|
||||
size_t i;
|
||||
struct lstack_head next, orig = atomic_load(head);
|
||||
|
||||
do {
|
||||
next.aba = orig.aba + 1;
|
||||
|
||||
for (i = 0, next.node = orig.node;
|
||||
i < cnt && next.node;
|
||||
i++, next.node = next.node->next) {
|
||||
// debug(3, "pop_many next.node %p next.node->next %p", next.node, next.node->next);
|
||||
}
|
||||
|
||||
if (i != cnt)
|
||||
return NULL;
|
||||
} while (!atomic_compare_exchange_weak(head, &orig, next));
|
||||
|
||||
return orig.node;
|
||||
}
|
||||
|
||||
static int push_many(_Atomic struct lstack_head *head, struct lstack_node *node, size_t cnt)
|
||||
{
|
||||
size_t i;
|
||||
struct lstack_head next, orig = atomic_load(head);
|
||||
struct lstack_node *last = node;
|
||||
|
||||
/* Find last node which will be pushed */
|
||||
for (i = 1; i < cnt; i++) {
|
||||
// debug(3, "push_many node %p node->next %p", last, last->next);
|
||||
last = last->next;
|
||||
}
|
||||
|
||||
do {
|
||||
next.aba = orig.aba + 1;
|
||||
next.node = node;
|
||||
|
||||
last->next = orig.node;
|
||||
} while (!atomic_compare_exchange_weak(head, &orig, next));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lstack_init(struct lstack *lstack, size_t maxsz)
|
||||
{
|
||||
/* Pre-allocate all nodes. */
|
||||
lstack->node_buffer = alloc(maxsz * sizeof(struct lstack_node));
|
||||
|
||||
for (size_t i = 1; i < maxsz; i++)
|
||||
lstack->node_buffer[i-1].next = &lstack->node_buffer[i];
|
||||
lstack->node_buffer[maxsz - 1].next = NULL;
|
||||
|
||||
/* List of unused elements */
|
||||
lstack->free.aba = ATOMIC_VAR_INIT(0);
|
||||
lstack->free.node = ATOMIC_VAR_INIT(lstack->node_buffer);
|
||||
|
||||
/* List of stack elements */
|
||||
lstack->head.aba = ATOMIC_VAR_INIT(0);
|
||||
lstack->head.node = ATOMIC_VAR_INIT(NULL);
|
||||
|
||||
lstack->size = ATOMIC_VAR_INIT(0);
|
||||
lstack->avail = ATOMIC_VAR_INIT(maxsz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t lstack_push_many(struct lstack *lstack, void *values[], size_t cnt)
|
||||
{
|
||||
size_t i;
|
||||
struct lstack_node *nodes, *node;
|
||||
|
||||
if (cnt == 0)
|
||||
return 0;
|
||||
|
||||
nodes = pop_many(&lstack->free, cnt);
|
||||
if (!nodes)
|
||||
return 0;
|
||||
|
||||
atomic_fetch_sub(&lstack->avail, cnt);
|
||||
|
||||
for (i = 0, node = nodes;
|
||||
i < cnt && node;
|
||||
i++, node = node->next)
|
||||
node->value = values[i];
|
||||
|
||||
push_many(&lstack->head, nodes, cnt);
|
||||
atomic_fetch_add(&lstack->size, cnt);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
ssize_t lstack_pop_many(struct lstack *lstack, void *values[], size_t cnt)
|
||||
{
|
||||
size_t i;
|
||||
struct lstack_node *nodes, *node;
|
||||
|
||||
if (cnt == 0)
|
||||
return 0;
|
||||
|
||||
nodes = pop_many(&lstack->head, cnt);
|
||||
if (!nodes)
|
||||
return 0;
|
||||
|
||||
atomic_fetch_sub(&lstack->size, cnt);
|
||||
|
||||
for (i = 0, node = nodes;
|
||||
i < cnt && node;
|
||||
i++, node = node->next)
|
||||
values[i] = node->value;
|
||||
|
||||
push_many(&lstack->free, nodes, cnt);
|
||||
atomic_fetch_add(&lstack->avail, cnt);
|
||||
|
||||
return i;
|
||||
}
|
Loading…
Add table
Reference in a new issue