Make GWBITMASK fixed size

Originally GWBITMASK was capable of extending itself dynamically,
a functionality that in practice was never used, and that, in fact,
was broken.

As GWBITMASK is used for a single purpose inside MaxScale, its size
is now fixed at 256 bits, which is sufficient for 256 threads. Its
fixed size removes a few potential error situations that now then
do not need to be dealt with. And in principle is also more
performant.

In a separate change, bitmask_free will be renamed to bitmask_finish,
as contrary to free-functions in general, the function does not free
the argument, but only its data. The implementation will be empty,
but the function left in place for symmetry reasons (alloc/free,
init/finish).
This commit is contained in:
Johan Wikman
2016-08-15 22:59:39 +03:00
parent 1f10b6337e
commit ed96e2c54a
3 changed files with 62 additions and 132 deletions

View File

@ -19,24 +19,7 @@
/** /**
* @file gwbitmask.c Implementation of bitmask operations for the gateway * @file gwbitmask.c Implementation of bitmask operations for the gateway
* *
* We provide basic bitmask manipulation routines, the size of * GWBITMASK is a fixed size bitmask with space for 256 bits.
* the bitmask will grow dynamically based on the highest bit
* number that is set or cleared within the bitmask.
*
* Bitmask growth happens in increments rather than via a single bit as
* a time.
*
* Please note limitations to these mechanisms:
*
* 1. The initial size and increment size MUST be exact multiples of 8
* 2. Only suitable for a compact set of bit numbers i.e. the numbering
* needs to start near to 0 and grow without sizeable gaps
* 3. It is assumed that a bit number bigger than the current size can
* be accommodated by adding a single extra block of bits
* 4. During copy, if memory cannot be allocated, a zero length bitmap is
* created as the destination. This will test true for all bits clear, which
* may be a serious error. However, the memory requirement is very small and
* is only likely to fail in circumstances where a lot else is going wrong.
* *
* @verbatim * @verbatim
* Revision History * Revision History
@ -67,18 +50,12 @@ static const unsigned char bitmapset[8] =
* Initialise a bitmask * Initialise a bitmask
* *
* @param bitmask Pointer the bitmask * @param bitmask Pointer the bitmask
* @return The value of *variable before the add occurred
*/ */
void void
bitmask_init(GWBITMASK *bitmask) bitmask_init(GWBITMASK *bitmask)
{ {
bitmask->length = BIT_LENGTH_INITIAL;
bitmask->size = bitmask->length / 8;
if ((bitmask->bits = MXS_CALLOC(bitmask->size, 1)) == NULL)
{
bitmask->length = bitmask->size = 0;
}
spinlock_init(&bitmask->lock); spinlock_init(&bitmask->lock);
memset(bitmask->bits, 0, MXS_BITMASK_SIZE);
} }
/** /**
@ -89,52 +66,40 @@ bitmask_init(GWBITMASK *bitmask)
void void
bitmask_free(GWBITMASK *bitmask) bitmask_free(GWBITMASK *bitmask)
{ {
if (bitmask->length)
{
MXS_FREE(bitmask->bits);
bitmask->length = bitmask->size = 0;
}
} }
/** /**
* Set the bit at the specified bit position in the bitmask. * Set the bit at the specified bit position in the bitmask.
* The bitmask will automatically be extended if the bit is
* beyond the current bitmask length. Note that growth is only
* by a single increment - the bit numbers used need to be a
* fairly dense set.
* *
* @param bitmask Pointer the bitmask * @param bitmask Pointer the bitmask
* @param bit Bit to set * @param bit Bit to set
* @return 1 if the bit could be set, 0 otherwise.
* Setting a bit may fail only if the bit exceeds
* the maximum length of the bitmask.
*/ */
void int
bitmask_set(GWBITMASK *bitmask, int bit) bitmask_set(GWBITMASK *bitmask, int bit)
{ {
unsigned char *ptr; ss_dassert(bit >= 0);
spinlock_acquire(&bitmask->lock); spinlock_acquire(&bitmask->lock);
if (bit >= 8)
if (bit < MXS_BITMASK_LENGTH)
{ {
while (bit >= bitmask->length) unsigned char *ptr = bitmask->bits;
if (bit >= 8)
{ {
bitmask->bits = MXS_REALLOC(bitmask->bits, (bitmask->size + (BIT_LENGTH_INC / 8))); ptr += (bit / 8);
MXS_ABORT_IF_NULL(bitmask->bits); bit = bit % 8;
memset(bitmask->bits + (bitmask->size), 0,
BIT_LENGTH_INC / 8);
bitmask->length += BIT_LENGTH_INC;
bitmask->size += (BIT_LENGTH_INC / 8);
} }
ptr = bitmask->bits; *ptr |= bitmapset[bit];
ptr += (bit / 8);
bit = bit % 8;
}
else
{
ptr = bitmask->bits;
} }
*ptr |= bitmapset[bit];
spinlock_release(&bitmask->lock); spinlock_release(&bitmask->lock);
return bit < MXS_BITMASK_LENGTH;
} }
/** /**
@ -153,10 +118,11 @@ bitmask_set(GWBITMASK *bitmask, int bit)
int int
bitmask_clear_without_spinlock(GWBITMASK *bitmask, int bit) bitmask_clear_without_spinlock(GWBITMASK *bitmask, int bit)
{ {
unsigned char *ptr = bitmask->bits; ss_dassert(bit >= 0);
int i;
if (bit < bitmask->length) unsigned char *ptr = bitmask->bits;
if (bit < MXS_BITMASK_LENGTH)
{ {
if (bit >= 8) if (bit >= 8)
{ {
@ -166,7 +132,7 @@ bitmask_clear_without_spinlock(GWBITMASK *bitmask, int bit)
*ptr &= bitmapclear[bit]; *ptr &= bitmapclear[bit];
} }
ptr = bitmask->bits; ptr = bitmask->bits;
for (i = 0; i < bitmask->size; i++) for (int i = 0; i < MXS_BITMASK_SIZE; i++)
{ {
if (*(ptr + i) != 0) if (*(ptr + i) != 0)
{ {
@ -228,12 +194,15 @@ bitmask_isset(GWBITMASK *bitmask, int bit)
static int static int
bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit) bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit)
{ {
unsigned char *ptr = bitmask->bits; ss_dassert(bit >= 0);
if (bit >= bitmask->length) if (bit >= MXS_BITMASK_LENGTH)
{ {
return 0; return 0;
} }
unsigned char *ptr = bitmask->bits;
if (bit >= 8) if (bit >= 8)
{ {
ptr += (bit / 8); ptr += (bit / 8);
@ -255,11 +224,10 @@ int
bitmask_isallclear(GWBITMASK *bitmask) bitmask_isallclear(GWBITMASK *bitmask)
{ {
unsigned char *ptr = bitmask->bits; unsigned char *ptr = bitmask->bits;
int i;
int result = 1; int result = 1;
spinlock_acquire(&bitmask->lock); spinlock_acquire(&bitmask->lock);
for (i = 0; i < bitmask->size; i++) for (int i = 0; i < MXS_BITMASK_SIZE; i++)
{ {
if (*(ptr + i) != 0) if (*(ptr + i) != 0)
{ {
@ -275,10 +243,6 @@ bitmask_isallclear(GWBITMASK *bitmask)
/** /**
* Copy the contents of one bitmap to another. * Copy the contents of one bitmap to another.
* *
* On memory failure, a zero length bitmask is created in the destination,
* which could seriously undermine the logic. Given the small size of the
* bitmask, this is unlikely to happen.
*
* @param dest Bitmap tp update * @param dest Bitmap tp update
* @param src Bitmap to copy * @param src Bitmap to copy
*/ */
@ -287,28 +251,14 @@ bitmask_copy(GWBITMASK *dest, GWBITMASK *src)
{ {
spinlock_acquire(&src->lock); spinlock_acquire(&src->lock);
spinlock_acquire(&dest->lock); spinlock_acquire(&dest->lock);
if (dest->length) memcpy(dest->bits, src->bits, MXS_BITMASK_SIZE);
{
MXS_FREE(dest->bits);
}
if ((dest->bits = MXS_MALLOC(src->size)) == NULL)
{
dest->length = 0;
}
else
{
dest->length = src->length;
dest->size = src->size;
memcpy(dest->bits, src->bits, src->size);
}
spinlock_release(&dest->lock); spinlock_release(&dest->lock);
spinlock_release(&src->lock); spinlock_release(&src->lock);
} }
/** /**
* Return a comma separated list of the numbers of the bits that are set in * Return a comma separated list of the numbers of the bits that are set in
* a bitmask, numbering starting at zero. Constrained to reject requests that * a bitmask, numbering starting at zero. The returned string must be
* could require more than three digit numbers. The returned string must be
* freed by the caller (unless it is null on account of memory allocation * freed by the caller (unless it is null on account of memory allocation
* failure). * failure).
* *
@ -318,48 +268,35 @@ bitmask_copy(GWBITMASK *dest, GWBITMASK *src)
char * char *
bitmask_render_readable(GWBITMASK *bitmask) bitmask_render_readable(GWBITMASK *bitmask)
{ {
static const char toobig[] = "Bitmask is too large to render readable";
static const char empty[] = "No bits are set"; static const char empty[] = "No bits are set";
char onebit[5];
char *result; char *result;
int count_set = 0;
spinlock_acquire(&bitmask->lock); spinlock_acquire(&bitmask->lock);
if (999 < bitmask->length) int count_set = bitmask_count_bits_set(bitmask);
if (count_set)
{ {
result = MXS_MALLOC(sizeof(toobig)); result = MXS_MALLOC(1 + (4 * count_set));
if (result) if (result)
{ {
strcpy(result, toobig); result[0] = 0;
for (int i = 0; i < MXS_BITMASK_LENGTH; i++)
{
if (bitmask_isset_without_spinlock(bitmask, i))
{
char onebit[5];
sprintf(onebit, "%d,", i);
strcat(result, onebit);
}
}
result[strlen(result) - 1] = 0;
} }
} }
else else
{ {
count_set = bitmask_count_bits_set(bitmask); result = MXS_MALLOC(sizeof(empty));
if (count_set) if (result)
{ {
result = MXS_MALLOC(1 + (4 * count_set)); strcpy(result, empty);
if (result)
{
result[0] = 0;
for (int i = 0; i < bitmask->length; i++)
{
if (bitmask_isset_without_spinlock(bitmask, i))
{
sprintf(onebit, "%d,", i);
strcat(result, onebit);
}
}
result[strlen(result) - 1] = 0;
}
}
else
{
result = MXS_MALLOC(sizeof(empty));
if (result)
{
strcpy(result, empty);
}
} }
} }
spinlock_release(&bitmask->lock); spinlock_release(&bitmask->lock);
@ -376,13 +313,13 @@ bitmask_render_readable(GWBITMASK *bitmask)
static int static int
bitmask_count_bits_set(GWBITMASK *bitmask) bitmask_count_bits_set(GWBITMASK *bitmask)
{ {
const unsigned char oneBits[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; static const unsigned char oneBits[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
unsigned char partresults; unsigned char partresults;
int result = 0; int result = 0;
unsigned char *ptr, *eptr; unsigned char *ptr, *eptr;
ptr = bitmask->bits; ptr = bitmask->bits;
eptr = ptr + (bitmask->length / 8); eptr = ptr + MXS_BITMASK_SIZE;
while (ptr < eptr) while (ptr < eptr)
{ {
partresults = oneBits[*ptr & 0x0f]; partresults = oneBits[*ptr & 0x0f];

View File

@ -38,10 +38,9 @@
#include <skygw_debug.h> #include <skygw_debug.h>
/** /**
* test1 Allocate table of users and mess around with it * test1 Create a bitmap and mess around with it
* *
*/ */
static int static int
test1() test1()
{ {
@ -52,8 +51,7 @@ test1()
ss_dfprintf(stderr, ss_dfprintf(stderr,
"testgwbitmask : Initialise a bitmask"); "testgwbitmask : Initialise a bitmask");
bitmask_init(&bitmask); bitmask_init(&bitmask);
ss_info_dassert(BIT_LENGTH_INITIAL == bitmask.length, "Length should be initial length."); for (i = 0; i < MXS_BITMASK_LENGTH; i++)
for (i = 0; i < BIT_LENGTH_INITIAL; i++)
{ {
ss_info_dassert(0 == bitmask_isset(&bitmask, i), "All bits should initially be zero"); ss_info_dassert(0 == bitmask_isset(&bitmask, i), "All bits should initially be zero");
} }
@ -64,14 +62,11 @@ test1()
ss_info_dassert(0 != bitmask_isset(&another, 17), "Test bit should be set"); ss_info_dassert(0 != bitmask_isset(&another, 17), "Test bit should be set");
ss_dfprintf(stderr, "\t..done\nClear the arbitrary bit."); ss_dfprintf(stderr, "\t..done\nClear the arbitrary bit.");
bitmask_clear(&bitmask, 17); bitmask_clear(&bitmask, 17);
ss_info_dassert(0 == bitmask_isset(&bitmask, 17), "Test bit should be clear");
ss_info_dassert(0 != bitmask_isallclear(&bitmask), "Should be all clear"); ss_info_dassert(0 != bitmask_isallclear(&bitmask), "Should be all clear");
// Testing the allocation mechanism, for use with valgrind. ss_info_dassert(0 == bitmask_isset(&bitmask, 17), "Test bit should be clear");
bitmask_set(&bitmask, BIT_LENGTH_INC + 1);
bitmask_set(&bitmask, 2 * BIT_LENGTH_INC + 1);
ss_dfprintf(stderr, "\t..done\nFree the bitmask."); ss_dfprintf(stderr, "\t..done\nFree the bitmask.");
bitmask_free(&bitmask); bitmask_free(&bitmask);
ss_info_dassert(0 == bitmask.length, "Length should be zero after bit mask freed.");
ss_dfprintf(stderr, "\t..done\n"); ss_dfprintf(stderr, "\t..done\n");
return 0; return 0;

View File

@ -27,27 +27,25 @@
* @endverbatim * @endverbatim
*/ */
/* Both these numbers MUST be exact multiples of 8 */ /* This number MUST an be exact multiple of 8 */
#define BIT_LENGTH_INITIAL 256 /**< Initial number of bits in the bitmask */ #define MXS_BITMASK_LENGTH 256 /**< Number of bits in the bitmask */
#define BIT_LENGTH_INC 256 /**< Number of bits to add on each increment */
#define MXS_BITMASK_SIZE (MXS_BITMASK_LENGTH / 8) /**< Number of bytes in the bitmask */
/** /**
* The bitmask structure used to store an arbitrary large bitmask * The bitmask structure used to store a fixed size bitmask
*/ */
typedef struct typedef struct
{ {
SPINLOCK lock; /**< Lock to protect the bitmask */ SPINLOCK lock; /**< Lock to protect the bitmask */
unsigned char *bits; /**< Pointer to the bits themselves */ unsigned char bits[MXS_BITMASK_SIZE]; /**< The bits themselves */
int length; /**< The number of bits in the bitmask */
int size; /**< The number of bytes in the bitmask */
} GWBITMASK; } GWBITMASK;
#define GWBITMASK_INIT {SPINLOCK_INIT} #define GWBITMASK_INIT {SPINLOCK_INIT}
extern void bitmask_init(GWBITMASK *); extern void bitmask_init(GWBITMASK *);
extern void bitmask_free(GWBITMASK *); extern void bitmask_free(GWBITMASK *);
extern void bitmask_set(GWBITMASK *, int); extern int bitmask_set(GWBITMASK *, int);
extern int bitmask_clear(GWBITMASK *, int); extern int bitmask_clear(GWBITMASK *, int);
extern int bitmask_clear_without_spinlock(GWBITMASK *, int); extern int bitmask_clear_without_spinlock(GWBITMASK *, int);
extern int bitmask_isset(GWBITMASK *, int); extern int bitmask_isset(GWBITMASK *, int);