From f47aa584a8b447d8225fc5b09afb2d1fe6764c1d Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sun, 3 Apr 2022 10:48:14 +0100 Subject: 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 --- firmware/buflib.c | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) (limited to 'firmware/buflib.c') diff --git a/firmware/buflib.c b/firmware/buflib.c index 2a4b4b4f14..7263f1b95d 100644 --- a/firmware/buflib.c +++ b/firmware/buflib.c @@ -102,10 +102,12 @@ #define PARANOIA_CHECK_HANDLE (1 << 1) #define PARANOIA_CHECK_BLOCK_HANDLE (1 << 2) #define PARANOIA_CHECK_CRC (1 << 3) +#define PARANOIA_CHECK_PINNING (1 << 4) /* Bitmask of enabled paranoia checks */ #define BUFLIB_PARANOIA \ (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 # define BUFLIB_HAS_CRC @@ -122,6 +124,7 @@ enum { /* Backward indices, used to index a block end pointer as block[-bidx_XXX] */ enum { bidx_USER, /* dummy to get below fields to be 1-based */ + bidx_PIN, /* pin count */ #ifdef BUFLIB_HAS_CRC bidx_CRC, /* CRC, protects all metadata behind it */ #endif @@ -132,9 +135,9 @@ enum { * accounted for using the BSIZE field. Note that bidx_USER is not an * actual field so it is not included in the count. */ #ifdef BUFLIB_HAS_CRC -# define BUFLIB_NUM_FIELDS 5 +# define BUFLIB_NUM_FIELDS 6 #else -# define BUFLIB_NUM_FIELDS 4 +# define BUFLIB_NUM_FIELDS 5 #endif 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); check_block_crc(ctx, block, block_end); - if (!IS_MOVABLE(block)) + if (!IS_MOVABLE(block) || block_end[-bidx_PIN].pincount > 0) return false; 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); union buflib_data *block_end = block + bsize; + block_end[-bidx_PIN].pincount = 0; block_end[-bidx_BSIZE].val = bsize; 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; } +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) { union buflib_data *data = handle_to_block_end(ctx, handle); -- cgit v1.2.3