Skip to content

Commit f9615fb

Browse files
authored
Merge pull request #70 from seanwevans/claude/semaphore-invariant
Enforce the semaphore count <= max_count invariant
2 parents 874be45 + ff5f41e commit f9615fb

4 files changed

Lines changed: 62 additions & 1 deletion

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ REGRESS = pg_os_basic \
1414
load_unload_module \
1515
modules \
1616
locks_security \
17+
semaphore_validation
1718
schema_install
1819
REGRESS_OPTS = --outputdir=$(CURDIR)/tmp_pg_os_regress
1920
PGXS := $(shell $(PG_CONFIG) --pgxs)

expected/semaphore_validation.out

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-- create_semaphore must reject parameters that would violate the
2+
-- "0 <= count <= max_count, max_count >= 1" invariant.
3+
\set ECHO none
4+
SELECT create_semaphore('ok', 1, 2);
5+
create_semaphore
6+
------------------
7+
8+
(1 row)
9+
10+
SELECT name, count, max_count FROM semaphores WHERE name = 'ok';
11+
name | count | max_count
12+
------+-------+-----------
13+
ok | 1 | 2
14+
(1 row)
15+
16+
SELECT create_semaphore('too_many', 5, 1);
17+
ERROR: Semaphore initial count 5 must be between 0 and the maximum 1
18+
SELECT create_semaphore('negative', -1, 2);
19+
ERROR: Semaphore initial count -1 must be between 0 and the maximum 2
20+
SELECT create_semaphore('no_max', 0, 0);
21+
ERROR: Semaphore maximum must be at least 1 (got 0)
22+
SELECT count(*) AS semaphore_count FROM semaphores;
23+
semaphore_count
24+
-----------------
25+
1
26+
(1 row)
27+

pg_os--1.0.sql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,9 @@ CREATE TABLE IF NOT EXISTS semaphores (
462462
id SERIAL PRIMARY KEY,
463463
name TEXT UNIQUE NOT NULL,
464464
count INTEGER NOT NULL CHECK (count >= 0),
465-
max_count INTEGER NOT NULL CHECK (max_count >= 1)
465+
max_count INTEGER NOT NULL CHECK (max_count >= 1),
466+
-- a semaphore can never hold more permits than its maximum
467+
CHECK (count <= max_count)
466468
);
467469

468470

@@ -533,6 +535,13 @@ GRANT EXECUTE ON FUNCTION unlock_mutex(INTEGER, TEXT) TO PUBLIC;
533535
-- create a semaphore
534536
CREATE OR REPLACE FUNCTION create_semaphore(sem_name TEXT, initial_count INTEGER, max_val INTEGER) RETURNS VOID AS $$
535537
BEGIN
538+
IF max_val < 1 THEN
539+
RAISE EXCEPTION 'Semaphore maximum must be at least 1 (got %)', max_val;
540+
END IF;
541+
IF initial_count < 0 OR initial_count > max_val THEN
542+
RAISE EXCEPTION 'Semaphore initial count % must be between 0 and the maximum %', initial_count, max_val;
543+
END IF;
544+
536545
INSERT INTO semaphores (name, count, max_count)
537546
VALUES (sem_name, initial_count, max_val);
538547
END;

sql/semaphore_validation.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- create_semaphore must reject parameters that would violate the
2+
-- "0 <= count <= max_count, max_count >= 1" invariant.
3+
\set ECHO none
4+
SET client_min_messages TO warning;
5+
DROP EXTENSION IF EXISTS pg_os CASCADE;
6+
CREATE EXTENSION pg_os;
7+
\set ECHO queries
8+
\set VERBOSITY terse
9+
10+
-- a valid semaphore is accepted
11+
SELECT create_semaphore('ok', 1, 2);
12+
SELECT name, count, max_count FROM semaphores WHERE name = 'ok';
13+
14+
\set ON_ERROR_STOP off
15+
-- initial count greater than the maximum is rejected
16+
SELECT create_semaphore('too_many', 5, 1);
17+
-- negative initial count is rejected
18+
SELECT create_semaphore('negative', -1, 2);
19+
-- a maximum below 1 is rejected
20+
SELECT create_semaphore('no_max', 0, 0);
21+
\set ON_ERROR_STOP on
22+
23+
-- none of the invalid semaphores were created
24+
SELECT count(*) AS semaphore_count FROM semaphores;

0 commit comments

Comments
 (0)