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
*
* We provide basic bitmask manipulation routines, the size of
* 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.
* GWBITMASK is a fixed size bitmask with space for 256 bits.
*
* @verbatim
* Revision History
@ -67,18 +50,12 @@ static const unsigned char bitmapset[8] =
* Initialise a bitmask
*
* @param bitmask Pointer the bitmask
* @return The value of *variable before the add occurred
*/
void
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);
memset(bitmask->bits, 0, MXS_BITMASK_SIZE);
}
/**
@ -89,52 +66,40 @@ bitmask_init(GWBITMASK *bitmask)
void
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.
* 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 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)
{
unsigned char *ptr;
ss_dassert(bit >= 0);
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)));
MXS_ABORT_IF_NULL(bitmask->bits);
memset(bitmask->bits + (bitmask->size), 0,
BIT_LENGTH_INC / 8);
bitmask->length += BIT_LENGTH_INC;
bitmask->size += (BIT_LENGTH_INC / 8);
ptr += (bit / 8);
bit = bit % 8;
}
ptr = bitmask->bits;
ptr += (bit / 8);
bit = bit % 8;
}
else
{
ptr = bitmask->bits;
*ptr |= bitmapset[bit];
}
*ptr |= bitmapset[bit];
spinlock_release(&bitmask->lock);
return bit < MXS_BITMASK_LENGTH;
}
/**
@ -153,10 +118,11 @@ bitmask_set(GWBITMASK *bitmask, int bit)
int
bitmask_clear_without_spinlock(GWBITMASK *bitmask, int bit)
{
unsigned char *ptr = bitmask->bits;
int i;
ss_dassert(bit >= 0);
if (bit < bitmask->length)
unsigned char *ptr = bitmask->bits;
if (bit < MXS_BITMASK_LENGTH)
{
if (bit >= 8)
{
@ -166,7 +132,7 @@ bitmask_clear_without_spinlock(GWBITMASK *bitmask, int bit)
*ptr &= bitmapclear[bit];
}
ptr = bitmask->bits;
for (i = 0; i < bitmask->size; i++)
for (int i = 0; i < MXS_BITMASK_SIZE; i++)
{
if (*(ptr + i) != 0)
{
@ -228,12 +194,15 @@ bitmask_isset(GWBITMASK *bitmask, int bit)
static int
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;
}
unsigned char *ptr = bitmask->bits;
if (bit >= 8)
{
ptr += (bit / 8);
@ -255,11 +224,10 @@ int
bitmask_isallclear(GWBITMASK *bitmask)
{
unsigned char *ptr = bitmask->bits;
int i;
int result = 1;
spinlock_acquire(&bitmask->lock);
for (i = 0; i < bitmask->size; i++)
for (int i = 0; i < MXS_BITMASK_SIZE; i++)
{
if (*(ptr + i) != 0)
{
@ -275,10 +243,6 @@ bitmask_isallclear(GWBITMASK *bitmask)
/**
* 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 src Bitmap to copy
*/
@ -287,28 +251,14 @@ bitmask_copy(GWBITMASK *dest, GWBITMASK *src)
{
spinlock_acquire(&src->lock);
spinlock_acquire(&dest->lock);
if (dest->length)
{
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);
}
memcpy(dest->bits, src->bits, MXS_BITMASK_SIZE);
spinlock_release(&dest->lock);
spinlock_release(&src->lock);
}
/**
* 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
* could require more than three digit numbers. The returned string must be
* a bitmask, numbering starting at zero. The returned string must be
* freed by the caller (unless it is null on account of memory allocation
* failure).
*
@ -318,48 +268,35 @@ bitmask_copy(GWBITMASK *dest, GWBITMASK *src)
char *
bitmask_render_readable(GWBITMASK *bitmask)
{
static const char toobig[] = "Bitmask is too large to render readable";
static const char empty[] = "No bits are set";
char onebit[5];
char *result;
int count_set = 0;
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)
{
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
{
count_set = bitmask_count_bits_set(bitmask);
if (count_set)
result = MXS_MALLOC(sizeof(empty));
if (result)
{
result = MXS_MALLOC(1 + (4 * count_set));
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);
}
strcpy(result, empty);
}
}
spinlock_release(&bitmask->lock);
@ -376,13 +313,13 @@ bitmask_render_readable(GWBITMASK *bitmask)
static int
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;
int result = 0;
unsigned char *ptr, *eptr;
ptr = bitmask->bits;
eptr = ptr + (bitmask->length / 8);
eptr = ptr + MXS_BITMASK_SIZE;
while (ptr < eptr)
{
partresults = oneBits[*ptr & 0x0f];

View File

@ -38,10 +38,9 @@
#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
test1()
{
@ -52,8 +51,7 @@ test1()
ss_dfprintf(stderr,
"testgwbitmask : Initialise a bitmask");
bitmask_init(&bitmask);
ss_info_dassert(BIT_LENGTH_INITIAL == bitmask.length, "Length should be initial length.");
for (i = 0; i < BIT_LENGTH_INITIAL; i++)
for (i = 0; i < MXS_BITMASK_LENGTH; i++)
{
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_dfprintf(stderr, "\t..done\nClear the arbitrary bit.");
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");
// Testing the allocation mechanism, for use with valgrind.
bitmask_set(&bitmask, BIT_LENGTH_INC + 1);
bitmask_set(&bitmask, 2 * BIT_LENGTH_INC + 1);
ss_info_dassert(0 == bitmask_isset(&bitmask, 17), "Test bit should be clear");
ss_dfprintf(stderr, "\t..done\nFree the bitmask.");
bitmask_free(&bitmask);
ss_info_dassert(0 == bitmask.length, "Length should be zero after bit mask freed.");
ss_dfprintf(stderr, "\t..done\n");
return 0;