While working on refactoring how shmem is allocated, I made a mistake
where the main LWLock array did not reserve space for the LWLocks
allocated with RequestNamedLWLockTranche(), and the test still
passed. Matthias van de Meent spotted that before it got committed,
but in order to catch such mistakes in the future, add checks in
test_lwlock_tranches that the locks allocated with
RequestNamedLWLockTranche() can be acquired and released.
Another change is to stop requesting multiple tranches with the same
name with RequestNamedLWLockTranche(). As soon as I started to test
using the locks I realized that's bogus, and the next commit will
forbid it. Keep test coverage for duplicates requested with
LWLockNewTrancheId() for now, but make it more clear that that's what
the test does.
Reviewed-by: Sami Imseih <samimseih@gmail.com>
Discussion: https://www.postgresql.org/message-id/
463a28db-0c0b-4af6-bac6-
3891828bbbfe@iki.fi
Discussion: https://www.postgresql.org/message-id/CAEze2WjgCROMMXY0+j8FFdm3iFcr7By-+6Mwiz=PgGSEydiW3A@mail.gmail.com
CREATE EXTENSION test_lwlock_tranches;
-SELECT test_lwlock_tranches();
- test_lwlock_tranches
+-- Test lock tranches created with RequestNamedLWLockTranche()
+SELECT test_startup_lwlocks();
+ test_startup_lwlocks
----------------------
(1 row)
-SELECT test_lwlock_tranche_creation(NULL);
+-- Test creating new lock tranches with LWLockNewTrancheId()
+CREATE TEMP TABLE test_tranches(tranche_name text, tranche_id int);
+INSERT INTO test_tranches VALUES
+ ('test_tranche_a', test_lwlock_tranche_create('test_tranche_a')),
+ ('test_tranche_b', test_lwlock_tranche_create('test_tranche_b'));
+-- You can create multiple tranches with the same name. It's confusing, so
+-- not recommended, but test it.
+INSERT INTO test_tranches VALUES
+ ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate')),
+ ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate'));
+-- Check that all tranches were assigned different tranche IDs, including
+-- the ones with duplicate names
+select count(distinct tranche_id) from test_tranches;
+ count
+-------
+ 4
+(1 row)
+
+-- Test GetLWLockIdentifier() on tranches created with LWLockNewTrancheId()
+select tranche_name, test_lwlock_get_lwlock_identifier(tranche_id) as lookup_result
+from test_tranches;
+ tranche_name | lookup_result
+------------------------+------------------------
+ test_tranche_a | test_tranche_a
+ test_tranche_b | test_tranche_b
+ test_tranche_duplicate | test_tranche_duplicate
+ test_tranche_duplicate | test_tranche_duplicate
+(4 rows)
+
+-- negative tests
+SELECT test_lwlock_tranche_create(NULL);
ERROR: tranche name cannot be NULL
-SELECT test_lwlock_tranche_creation(repeat('a', 64));
+SELECT test_lwlock_tranche_create(repeat('a', 64));
ERROR: tranche name too long
DETAIL: LWLock tranche names must be no longer than 63 bytes.
-SELECT test_lwlock_tranche_creation('test');
-ERROR: maximum number of tranches already registered
-DETAIL: No more than 256 tranches may be registered.
-SELECT test_lwlock_tranche_lookup('test_lwlock_tranches_startup');
- test_lwlock_tranche_lookup
-----------------------------
-
-(1 row)
-
SELECT test_lwlock_tranche_lookup('bogus');
ERROR: requested tranche is not registered
+SELECT test_lwlock_tranche_lookup('test_tranche_a');
+ERROR: requested tranche was not registered with RequestNamedLWLockTranche()
SELECT test_lwlock_initialize(65535);
ERROR: tranche 65535 is not registered
+-- Test what happens when you use up all the slots.
+--
+-- MAX_USER_DEFINED_TRANCHES is 256. Two locks were created with
+-- RequestNamedLWLockTranche() when the library was loaded, and we created
+-- four more.
+insert into test_tranches
+ select 'test_tranche_consume_all', test_lwlock_tranche_create('test_tranche_consume_all')
+ from generate_series(1, 256 - 2 - 4);
+select test_lwlock_tranche_create('out-of-tranches');
+ERROR: maximum number of tranches already registered
+DETAIL: No more than 256 tranches may be registered.
CREATE EXTENSION test_lwlock_tranches;
-SELECT test_lwlock_tranches();
-SELECT test_lwlock_tranche_creation(NULL);
-SELECT test_lwlock_tranche_creation(repeat('a', 64));
-SELECT test_lwlock_tranche_creation('test');
-SELECT test_lwlock_tranche_lookup('test_lwlock_tranches_startup');
+
+-- Test lock tranches created with RequestNamedLWLockTranche()
+SELECT test_startup_lwlocks();
+
+-- Test creating new lock tranches with LWLockNewTrancheId()
+CREATE TEMP TABLE test_tranches(tranche_name text, tranche_id int);
+INSERT INTO test_tranches VALUES
+ ('test_tranche_a', test_lwlock_tranche_create('test_tranche_a')),
+ ('test_tranche_b', test_lwlock_tranche_create('test_tranche_b'));
+
+-- You can create multiple tranches with the same name. It's confusing, so
+-- not recommended, but test it.
+INSERT INTO test_tranches VALUES
+ ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate')),
+ ('test_tranche_duplicate', test_lwlock_tranche_create('test_tranche_duplicate'));
+
+-- Check that all tranches were assigned different tranche IDs, including
+-- the ones with duplicate names
+select count(distinct tranche_id) from test_tranches;
+
+-- Test GetLWLockIdentifier() on tranches created with LWLockNewTrancheId()
+select tranche_name, test_lwlock_get_lwlock_identifier(tranche_id) as lookup_result
+from test_tranches;
+
+-- negative tests
+SELECT test_lwlock_tranche_create(NULL);
+SELECT test_lwlock_tranche_create(repeat('a', 64));
SELECT test_lwlock_tranche_lookup('bogus');
+SELECT test_lwlock_tranche_lookup('test_tranche_a');
SELECT test_lwlock_initialize(65535);
+
+-- Test what happens when you use up all the slots.
+--
+-- MAX_USER_DEFINED_TRANCHES is 256. Two locks were created with
+-- RequestNamedLWLockTranche() when the library was loaded, and we created
+-- four more.
+insert into test_tranches
+ select 'test_tranche_consume_all', test_lwlock_tranche_create('test_tranche_consume_all')
+ from generate_series(1, 256 - 2 - 4);
+
+select test_lwlock_tranche_create('out-of-tranches');
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION test_lwlock_tranches" to load this file. \quit
-CREATE FUNCTION test_lwlock_tranches() RETURNS VOID
+CREATE FUNCTION test_startup_lwlocks() RETURNS VOID
AS 'MODULE_PATHNAME' LANGUAGE C;
-CREATE FUNCTION test_lwlock_tranche_creation(tranche_name TEXT) RETURNS VOID
+CREATE FUNCTION test_lwlock_tranche_create(tranche_name TEXT) RETURNS INT
AS 'MODULE_PATHNAME' LANGUAGE C;
CREATE FUNCTION test_lwlock_tranche_lookup(tranche_name TEXT) RETURNS VOID
AS 'MODULE_PATHNAME' LANGUAGE C;
+CREATE FUNCTION test_lwlock_get_lwlock_identifier(event_id INT) RETURNS TEXT
+ AS 'MODULE_PATHNAME' LANGUAGE C;
+
CREATE FUNCTION test_lwlock_initialize(tranche_id INT) RETURNS VOID
AS 'MODULE_PATHNAME' LANGUAGE C;
PG_MODULE_MAGIC;
-#define STARTUP_TRANCHE_NAME "test_lwlock_tranches_startup"
-#define DYNAMIC_TRANCHE_NAME "test_lwlock_tranches_dynamic"
-
-#define NUM_STARTUP_TRANCHES (32)
-#define NUM_DYNAMIC_TRANCHES (256 - NUM_STARTUP_TRANCHES)
-
-#define GET_TRANCHE_NAME(a) GetLWLockIdentifier(PG_WAIT_LWLOCK, (a))
-
static shmem_request_hook_type prev_shmem_request_hook;
static void test_lwlock_tranches_shmem_request(void);
if (prev_shmem_request_hook)
prev_shmem_request_hook();
- for (int i = 0; i < NUM_STARTUP_TRANCHES; i++)
- RequestNamedLWLockTranche(STARTUP_TRANCHE_NAME, 1);
+ /*
+ * Request some tranches with RequestNamedLWLockTranche() for
+ * test_startup_lwlocks()
+ */
+ RequestNamedLWLockTranche("test_lwlock_tranches_startup", 1);
+ RequestNamedLWLockTranche("test_lwlock_tranches_startup10", 10);
}
/*
- * Checks that GetLWLockIdentifier() returns the expected value for tranches
- * registered via RequestNamedLWLockTranche() and LWLockNewTrancheId().
+ * Test locks requested with RequestNamedLWLockTranche
*/
-PG_FUNCTION_INFO_V1(test_lwlock_tranches);
+PG_FUNCTION_INFO_V1(test_startup_lwlocks);
Datum
-test_lwlock_tranches(PG_FUNCTION_ARGS)
+test_startup_lwlocks(PG_FUNCTION_ARGS)
{
- int dynamic_tranches[NUM_DYNAMIC_TRANCHES];
-
- for (int i = 0; i < NUM_DYNAMIC_TRANCHES; i++)
- dynamic_tranches[i] = LWLockNewTrancheId(DYNAMIC_TRANCHE_NAME);
-
- for (int i = 0; i < NUM_STARTUP_TRANCHES; i++)
- {
- if (strcmp(GET_TRANCHE_NAME(LWTRANCHE_FIRST_USER_DEFINED + i),
- STARTUP_TRANCHE_NAME) != 0)
- elog(ERROR, "incorrect startup lock tranche name");
- }
-
- for (int i = 0; i < NUM_DYNAMIC_TRANCHES; i++)
- {
- if (strcmp(GET_TRANCHE_NAME(dynamic_tranches[i]),
- DYNAMIC_TRANCHE_NAME) != 0)
- elog(ERROR, "incorrect dynamic lock tranche name");
- }
+ LWLockPadded *lwlock_startup;
+ LWLockPadded *lwlock_startup10;
+
+ /* Check that the locks can be used */
+ lwlock_startup = GetNamedLWLockTranche("test_lwlock_tranches_startup");
+ lwlock_startup10 = GetNamedLWLockTranche("test_lwlock_tranches_startup10");
+
+ LWLockAcquire(&lwlock_startup->lock, LW_EXCLUSIVE);
+ for (int i = 0; i < 10; i++)
+ LWLockAcquire(&lwlock_startup10[i].lock, LW_EXCLUSIVE);
+
+ LWLockRelease(&lwlock_startup->lock);
+ for (int i = 0; i < 10; i++)
+ LWLockRelease(&lwlock_startup10[i].lock);
+
+ /*
+ * Check that GetLWLockIdentifier() returns the expected value for
+ * tranches
+ */
+ if (strcmp("test_lwlock_tranches_startup",
+ GetLWLockIdentifier(PG_WAIT_LWLOCK, LWTRANCHE_FIRST_USER_DEFINED)) != 0)
+ elog(ERROR, "incorrect startup lock tranche name");
+ if (strcmp("test_lwlock_tranches_startup10",
+ GetLWLockIdentifier(PG_WAIT_LWLOCK, LWTRANCHE_FIRST_USER_DEFINED + 1)) != 0)
+ elog(ERROR, "incorrect startup lock tranche name");
PG_RETURN_VOID();
}
/*
* Wrapper for LWLockNewTrancheId().
*/
-PG_FUNCTION_INFO_V1(test_lwlock_tranche_creation);
+PG_FUNCTION_INFO_V1(test_lwlock_tranche_create);
Datum
-test_lwlock_tranche_creation(PG_FUNCTION_ARGS)
+test_lwlock_tranche_create(PG_FUNCTION_ARGS)
{
char *tranche_name = PG_ARGISNULL(0) ? NULL : TextDatumGetCString(PG_GETARG_DATUM(0));
+ int tranche_id;
- (void) LWLockNewTrancheId(tranche_name);
+ tranche_id = LWLockNewTrancheId(tranche_name);
- PG_RETURN_VOID();
+ PG_RETURN_INT32(tranche_id);
}
/*
PG_RETURN_VOID();
}
+PG_FUNCTION_INFO_V1(test_lwlock_get_lwlock_identifier);
+Datum
+test_lwlock_get_lwlock_identifier(PG_FUNCTION_ARGS)
+{
+ int eventId = PG_GETARG_INT32(0);
+ const char *tranche_name;
+
+ tranche_name = GetLWLockIdentifier(PG_WAIT_LWLOCK, eventId);
+
+ if (tranche_name)
+ PG_RETURN_TEXT_P(cstring_to_text(tranche_name));
+ else
+ PG_RETURN_NULL();
+}
+
/*
* Wrapper for LWLockInitialize().
*/