buflib: add pin/unpin operation
An allocation is pinned by calling buflib_pin() to up its pin count. The pin count is like a reference count: when above 0, buflib won't move the allocation and won't call its move callbacks. This makes it safe to hold the pointer returned by buflib_get_data() across yields or allocations. Note that pinned allocations can still shrink because there are some use cases where this would be valid, if buffer users coordinate with the shrink callback. Change-Id: I0d0c2a8ac7d891d3ad6b3d0eb80c5b5a1b4b9a9d
This commit is contained in:
parent
ecfec3e9bf
commit
f47aa584a8
4 changed files with 79 additions and 4 deletions
|
@ -102,10 +102,12 @@
|
||||||
#define PARANOIA_CHECK_HANDLE (1 << 1)
|
#define PARANOIA_CHECK_HANDLE (1 << 1)
|
||||||
#define PARANOIA_CHECK_BLOCK_HANDLE (1 << 2)
|
#define PARANOIA_CHECK_BLOCK_HANDLE (1 << 2)
|
||||||
#define PARANOIA_CHECK_CRC (1 << 3)
|
#define PARANOIA_CHECK_CRC (1 << 3)
|
||||||
|
#define PARANOIA_CHECK_PINNING (1 << 4)
|
||||||
/* Bitmask of enabled paranoia checks */
|
/* Bitmask of enabled paranoia checks */
|
||||||
#define BUFLIB_PARANOIA \
|
#define BUFLIB_PARANOIA \
|
||||||
(PARANOIA_CHECK_LENGTH | PARANOIA_CHECK_HANDLE | \
|
(PARANOIA_CHECK_LENGTH | PARANOIA_CHECK_HANDLE | \
|
||||||
PARANOIA_CHECK_BLOCK_HANDLE | PARANOIA_CHECK_CRC)
|
PARANOIA_CHECK_BLOCK_HANDLE | PARANOIA_CHECK_CRC | \
|
||||||
|
PARANOIA_CHECK_PINNING)
|
||||||
|
|
||||||
#if BUFLIB_PARANOIA & PARANOIA_CHECK_CRC
|
#if BUFLIB_PARANOIA & PARANOIA_CHECK_CRC
|
||||||
# define BUFLIB_HAS_CRC
|
# define BUFLIB_HAS_CRC
|
||||||
|
@ -122,6 +124,7 @@ enum {
|
||||||
/* Backward indices, used to index a block end pointer as block[-bidx_XXX] */
|
/* Backward indices, used to index a block end pointer as block[-bidx_XXX] */
|
||||||
enum {
|
enum {
|
||||||
bidx_USER, /* dummy to get below fields to be 1-based */
|
bidx_USER, /* dummy to get below fields to be 1-based */
|
||||||
|
bidx_PIN, /* pin count */
|
||||||
#ifdef BUFLIB_HAS_CRC
|
#ifdef BUFLIB_HAS_CRC
|
||||||
bidx_CRC, /* CRC, protects all metadata behind it */
|
bidx_CRC, /* CRC, protects all metadata behind it */
|
||||||
#endif
|
#endif
|
||||||
|
@ -132,9 +135,9 @@ enum {
|
||||||
* accounted for using the BSIZE field. Note that bidx_USER is not an
|
* accounted for using the BSIZE field. Note that bidx_USER is not an
|
||||||
* actual field so it is not included in the count. */
|
* actual field so it is not included in the count. */
|
||||||
#ifdef BUFLIB_HAS_CRC
|
#ifdef BUFLIB_HAS_CRC
|
||||||
# define BUFLIB_NUM_FIELDS 5
|
# define BUFLIB_NUM_FIELDS 6
|
||||||
#else
|
#else
|
||||||
# define BUFLIB_NUM_FIELDS 4
|
# define BUFLIB_NUM_FIELDS 5
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct buflib_callbacks buflib_ops_locked = {
|
struct buflib_callbacks buflib_ops_locked = {
|
||||||
|
@ -394,7 +397,7 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
|
||||||
union buflib_data *block_end = h_entry_to_block_end(ctx, h_entry);
|
union buflib_data *block_end = h_entry_to_block_end(ctx, h_entry);
|
||||||
check_block_crc(ctx, block, block_end);
|
check_block_crc(ctx, block, block_end);
|
||||||
|
|
||||||
if (!IS_MOVABLE(block))
|
if (!IS_MOVABLE(block) || block_end[-bidx_PIN].pincount > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int handle = ctx->handle_table - h_entry;
|
int handle = ctx->handle_table - h_entry;
|
||||||
|
@ -751,6 +754,7 @@ buffer_alloc:
|
||||||
|
|
||||||
size_t bsize = BUFLIB_NUM_FIELDS + name_len/sizeof(union buflib_data);
|
size_t bsize = BUFLIB_NUM_FIELDS + name_len/sizeof(union buflib_data);
|
||||||
union buflib_data *block_end = block + bsize;
|
union buflib_data *block_end = block + bsize;
|
||||||
|
block_end[-bidx_PIN].pincount = 0;
|
||||||
block_end[-bidx_BSIZE].val = bsize;
|
block_end[-bidx_BSIZE].val = bsize;
|
||||||
update_block_crc(ctx, block, block_end);
|
update_block_crc(ctx, block, block_end);
|
||||||
|
|
||||||
|
@ -1050,6 +1054,39 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void buflib_pin(struct buflib_context *ctx, int handle)
|
||||||
|
{
|
||||||
|
if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
|
||||||
|
buflib_panic(ctx, "invalid handle pin: %d", handle);
|
||||||
|
|
||||||
|
union buflib_data *data = handle_to_block_end(ctx, handle);
|
||||||
|
data[-bidx_PIN].pincount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void buflib_unpin(struct buflib_context *ctx, int handle)
|
||||||
|
{
|
||||||
|
if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
|
||||||
|
buflib_panic(ctx, "invalid handle unpin: %d", handle);
|
||||||
|
|
||||||
|
union buflib_data *data = handle_to_block_end(ctx, handle);
|
||||||
|
if (BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING)
|
||||||
|
{
|
||||||
|
if (data[-bidx_PIN].pincount == 0)
|
||||||
|
buflib_panic(ctx, "handle pin underflow: %d", handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[-bidx_PIN].pincount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
|
||||||
|
{
|
||||||
|
if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
|
||||||
|
buflib_panic(ctx, "invalid handle: %d", handle);
|
||||||
|
|
||||||
|
union buflib_data *data = handle_to_block_end(ctx, handle);
|
||||||
|
return data[-bidx_PIN].pincount;
|
||||||
|
}
|
||||||
|
|
||||||
const char* buflib_get_name(struct buflib_context *ctx, int handle)
|
const char* buflib_get_name(struct buflib_context *ctx, int handle)
|
||||||
{
|
{
|
||||||
union buflib_data *data = handle_to_block_end(ctx, handle);
|
union buflib_data *data = handle_to_block_end(ctx, handle);
|
||||||
|
|
|
@ -104,6 +104,21 @@ bool core_shrink(int handle, void* new_start, size_t new_size)
|
||||||
return buflib_shrink(&core_ctx, handle, new_start, new_size);
|
return buflib_shrink(&core_ctx, handle, new_start, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void core_pin(int handle)
|
||||||
|
{
|
||||||
|
buflib_pin(&core_ctx, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void core_unpin(int handle)
|
||||||
|
{
|
||||||
|
buflib_unpin(&core_ctx, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned core_pin_count(int handle)
|
||||||
|
{
|
||||||
|
return buflib_pin_count(&core_ctx, handle);
|
||||||
|
}
|
||||||
|
|
||||||
const char* core_get_name(int handle)
|
const char* core_get_name(int handle)
|
||||||
{
|
{
|
||||||
const char *name = buflib_get_name(&core_ctx, handle);
|
const char *name = buflib_get_name(&core_ctx, handle);
|
||||||
|
|
|
@ -38,6 +38,7 @@ union buflib_data
|
||||||
intptr_t val; /* length of the block in n*sizeof(union buflib_data).
|
intptr_t val; /* length of the block in n*sizeof(union buflib_data).
|
||||||
Includes buflib metadata overhead. A negative value
|
Includes buflib metadata overhead. A negative value
|
||||||
indicates block is unallocated */
|
indicates block is unallocated */
|
||||||
|
volatile unsigned pincount; /* number of pins */
|
||||||
struct buflib_callbacks* ops; /* callback functions for move and shrink. Can be NULL */
|
struct buflib_callbacks* ops; /* callback functions for move and shrink. Can be NULL */
|
||||||
char* alloc; /* start of allocated memory area */
|
char* alloc; /* start of allocated memory area */
|
||||||
union buflib_data *handle; /* pointer to entry in the handle table.
|
union buflib_data *handle; /* pointer to entry in the handle table.
|
||||||
|
@ -291,6 +292,25 @@ static inline void* buflib_get_data(struct buflib_context *ctx, int handle)
|
||||||
*/
|
*/
|
||||||
bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size);
|
bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the pin count for a handle. When pinned the handle will not
|
||||||
|
* be moved and move callbacks will not be triggered, allowing a pointer
|
||||||
|
* to the buffer to be kept across yields or used for I/O.
|
||||||
|
*
|
||||||
|
* Note that shrink callbacks can still be invoked for pinned handles.
|
||||||
|
*/
|
||||||
|
void buflib_pin(struct buflib_context *ctx, int handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement the pin count for a handle.
|
||||||
|
*/
|
||||||
|
void buflib_unpin(struct buflib_context *ctx, int handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current pin count of a handle. Zero means the handle is not pinned.
|
||||||
|
*/
|
||||||
|
unsigned buflib_pin_count(struct buflib_context *ctx, int handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees memory associated with the given handle
|
* Frees memory associated with the given handle
|
||||||
*
|
*
|
||||||
|
|
|
@ -14,6 +14,9 @@ int core_alloc(const char* name, size_t size);
|
||||||
int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops);
|
int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops);
|
||||||
int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops);
|
int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops);
|
||||||
bool core_shrink(int handle, void* new_start, size_t new_size);
|
bool core_shrink(int handle, void* new_start, size_t new_size);
|
||||||
|
void core_pin(int handle);
|
||||||
|
void core_unpin(int handle);
|
||||||
|
unsigned core_pin_count(int handle);
|
||||||
int core_free(int handle);
|
int core_free(int handle);
|
||||||
size_t core_available(void);
|
size_t core_available(void);
|
||||||
size_t core_allocatable(void);
|
size_t core_allocatable(void);
|
||||||
|
|
Loading…
Reference in a new issue