Update comments
This commit is contained in:
1 parent
f73d25c92d
commit
7527bfec85
1 file changed
+44
-23
+44
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* M*LIB - GENINT module
|
||||
* M*LIB - Integer Generator (GENINT) module
|
||||
*
|
||||
* Copyright (c) 2017-2020, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
@@ -28,41 +28,43 @@
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
|
||||
/* GENINT is a container of integers with the following properties:
|
||||
|
||||
/* GENINT is a container providing unique integers.
|
||||
It has the following properties:
|
||||
- it stores integer from [0..N) (N is fixed).
|
||||
- an integer can have only one occurrence in the container.
|
||||
- you can atomically push in / pop out integer from this container
|
||||
provided that they were not already in the container.
|
||||
provided that it is not already in the container.
|
||||
- there are no order (like FIFO or stack)
|
||||
|
||||
This can be used to map integers to index of resources in a table.
|
||||
At most we can support N = GENINT_LIMBSIZE * GENINT_LIMBSIZE >= 4096
|
||||
due to the master trick. For the typical usage of this container
|
||||
At most we can support N = 2048 with the master limb usage.
|
||||
For the typical usage of this container
|
||||
(mapping hardware or software limited resources), this should be
|
||||
enough.
|
||||
*/
|
||||
|
||||
// TO DO: We may want a specialization with constant N value.
|
||||
|
||||
// Define the limb size
|
||||
// Define the limb size used by genint
|
||||
typedef unsigned long long genint_limb_t;
|
||||
|
||||
/* Define a generator of unique integer (Lock Free) */
|
||||
typedef struct genint_s {
|
||||
unsigned int n; // size of the container
|
||||
unsigned int max; // number of allocated limb - 1
|
||||
genint_limb_t mask0; // mask of the last limb
|
||||
genint_limb_t mask_master; // mask of master
|
||||
genint_limb_t mask0; // mask of the last limb (constant)
|
||||
genint_limb_t mask_master; // mask of the master limb that controls others (constant)
|
||||
atomic_ullong master; // master bitfield (which informs if a limb is full or not)
|
||||
atomic_ullong *data; // the bitfield which informs if an integer
|
||||
atomic_ullong *data; // the bitfield which informs if an integer is used or not
|
||||
} genint_t[1];
|
||||
|
||||
// Define the max supported value
|
||||
// Define the max absolute supported value
|
||||
#define GENINT_MAX_ALLOC (GENINT_LIMBSIZE * (GENINT_LIMBSIZE - GENINT_ABA_CPT))
|
||||
|
||||
// Define the size of a limb in bits.
|
||||
#define GENINT_LIMBSIZE (sizeof(genint_limb_t) * CHAR_BIT)
|
||||
|
||||
// Define the contract of a genint
|
||||
#define GENINT_CONTRACT(s) do { \
|
||||
assert (s != NULL); \
|
||||
assert (s->n > 0 && s->n <= GENINT_MAX_ALLOC); \
|
||||
@@ -70,26 +72,37 @@ typedef struct genint_s {
|
||||
assert (s->data != NULL); \
|
||||
} while (0)
|
||||
|
||||
// Define the limb one
|
||||
#define GENINT_ONE ((genint_limb_t)1)
|
||||
|
||||
// Value returned in case of error.
|
||||
// Value returned in case of error (not integer available).
|
||||
#define GENINT_ERROR (-1U)
|
||||
|
||||
// 32 bits of master are kept for handling the ABA problems.
|
||||
/* 32 bits of the master mask are kept for handling the ABA problem.
|
||||
* NOTE: May be too much. 16 bits should be more than enough. TBC
|
||||
*/
|
||||
#define GENINT_ABA_CPT 32
|
||||
#define GENINT_ABA_CPT_T uint32_t
|
||||
|
||||
// Set the bit i of master
|
||||
// Set the bit 'i' of the master limb, and increase ABA counter.
|
||||
#define GENINT_MASTER_SET(master, i) \
|
||||
((((master)& (~((GENINT_ONE<< GENINT_ABA_CPT)-1))) | (GENINT_ONE << (GENINT_LIMBSIZE - 1 - i))) \
|
||||
|((GENINT_ABA_CPT_T)((master) + 1)))
|
||||
|
||||
// Reset the bit i of master
|
||||
// Reset the bit i of the master limb, and increase ABA counter.
|
||||
#define GENINT_MASTER_RESET(master, i) \
|
||||
(((master) & (~((GENINT_ONE<< GENINT_ABA_CPT)-1)) & ~(GENINT_ONE << (GENINT_LIMBSIZE - 1 - i))) \
|
||||
(((master) & (~((GENINT_ONE<< GENINT_ABA_CPT)-1)) & ~(GENINT_ONE << (GENINT_LIMBSIZE - 1 - i))) \
|
||||
|((GENINT_ABA_CPT_T)((master) + 1)))
|
||||
|
||||
static inline void genint_init(genint_t s, unsigned int n)
|
||||
/* Initialize an integer generator (CONSTRUCTOR).
|
||||
* Initialy, the container is full of all the integers up to 'n-1'
|
||||
* The typical sequence is to initialize the container, and pop
|
||||
* the integer from it. Each pop integer is **unique** for all threads,
|
||||
* meaning it can be used to index global unique resources shared
|
||||
* for all threads.
|
||||
*/
|
||||
static inline void
|
||||
genint_init(genint_t s, unsigned int n)
|
||||
{
|
||||
assert (s != NULL && n > 0 && n <= GENINT_MAX_ALLOC);
|
||||
const size_t alloc = (n + GENINT_LIMBSIZE - 1) / GENINT_LIMBSIZE;
|
||||
@@ -110,21 +123,27 @@ static inline void genint_init(genint_t s, unsigned int n)
|
||||
GENINT_CONTRACT(s);
|
||||
}
|
||||
|
||||
static inline void genint_clear(genint_t s)
|
||||
/* Clear an integer generator (Destructor) */
|
||||
static inline void
|
||||
genint_clear(genint_t s)
|
||||
{
|
||||
GENINT_CONTRACT(s);
|
||||
M_MEMORY_FREE(s->data);
|
||||
s->data = NULL;
|
||||
}
|
||||
|
||||
static inline size_t genint_size(genint_t s)
|
||||
/* Return the maximum integer that the generator will provide */
|
||||
static inline size_t
|
||||
genint_size(genint_t s)
|
||||
{
|
||||
GENINT_CONTRACT(s);
|
||||
return s->n;
|
||||
}
|
||||
|
||||
// For a typical case, the amortized cost is one CAS per pop.
|
||||
static inline unsigned int genint_pop(genint_t s)
|
||||
/* Get an unique integer from the integer generator.
|
||||
* NOTE: For a typical case, the amortized cost is one CAS per pop. */
|
||||
static inline unsigned int
|
||||
genint_pop(genint_t s)
|
||||
{
|
||||
GENINT_CONTRACT(s);
|
||||
// First read master to see which limb is not full.
|
||||
@@ -181,8 +200,10 @@ static inline unsigned int genint_pop(genint_t s)
|
||||
return GENINT_ERROR; // No more resource available
|
||||
}
|
||||
|
||||
// For a typical case, the amortized cost is one CAS per pop.
|
||||
static inline void genint_push(genint_t s, unsigned int n)
|
||||
/* Restore a used integer in the integer generator.
|
||||
* NOTE: For a typical case, the amortized cost is one CAS per pop */
|
||||
static inline void
|
||||
genint_push(genint_t s, unsigned int n)
|
||||
{
|
||||
GENINT_CONTRACT(s);
|
||||
assert (n < s->n);
|
||||
|
||||
Reference in new issue
Block a user