ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
provsql_shmem.c
Go to the documentation of this file.
1/**
2 * @file provsql_shmem.c
3 * @brief Shared-memory segment lifecycle and LWLock management.
4 *
5 * Implements the functions declared in @c provsql_shmem.h:
6 * - @c provsql_shmem_startup(): allocates (or attaches to) the
7 * @c provsqlSharedState segment, initialises the @c LWLock, and
8 * calls @c pipe(2) to create the two inter-process pipes. Chains
9 * to @c prev_shmem_startup if set.
10 * - @c provsql_memsize(): returns the size of the shared segment.
11 * - @c provsql_shmem_request(): requests shared memory on PG ≥ 15.
12 * - @c provsql_shmem_lock_exclusive() / @c provsql_shmem_lock_shared() /
13 * @c provsql_shmem_unlock(): thin wrappers around
14 * @c LWLockAcquire() / @c LWLockRelease().
15 */
16#include <unistd.h>
17
18#include "postgres.h"
19#include "storage/shmem.h"
20
21#include "provsql_shmem.h"
22#include "provsql_mmap.h"
23
24shmem_startup_hook_type prev_shmem_startup = NULL;
25#if (PG_VERSION_NUM >= 150000)
26shmem_request_hook_type prev_shmem_request = NULL;
27#endif
28
30
31#ifdef PROVSQL_INPROCESS_STORE
32
33/* Single-process build: the circuit store lives in this process, reached
34 through two in-memory FIFOs instead of pipes + a background worker.
35 There is one backend and no shared memory, so the locks are no-ops. */
36
37static provsqlSharedState inproc_state;
38
39/* Write the heap-backed circuit store back to its files before the backend
40 exits, so a later backend (and PGlite's persistence) sees the data. The
41 shared-mmap build does not need this: the kernel flushes the mapping. */
42static void provsql_inproc_atexit(int code, Datum arg)
43{
44 (void) code;
45 (void) arg;
47}
48
49void provsql_inproc_init(void)
50{
51 memset(&inproc_state, 0, sizeof(inproc_state));
52 provsql_shared_state = &inproc_state;
53 /* The write-back hook is registered lazily in provsql_inproc_send, not
54 here: _PG_init runs in the postmaster under shared_preload_libraries,
55 and a forked backend resets the inherited on_proc_exit list, so a hook
56 registered now would never fire in the backend that owns the data. */
57}
58
59bool provsql_fifo_push(provsql_fifo *f, const void *src, size_t n)
60{
61 if(f->tail + n > f->cap) {
62 /* Reclaim already-consumed bytes before growing. */
63 if(f->head > 0) {
64 memmove(f->buf, f->buf + f->head, f->tail - f->head);
65 f->tail -= f->head;
66 f->head = 0;
67 }
68 if(f->tail + n > f->cap) {
69 size_t newcap = f->cap ? f->cap * 2 : 4096;
70 while(newcap < f->tail + n)
71 newcap *= 2;
72 f->buf = realloc(f->buf, newcap);
73 if(!f->buf)
74 provsql_error("ProvSQL: out of memory growing in-process FIFO");
75 f->cap = newcap;
76 }
77 }
78 memcpy(f->buf + f->tail, src, n);
79 f->tail += n;
80 return true;
81}
82
83bool provsql_fifo_pop(provsql_fifo *f, void *dst, size_t n)
84{
85 if(f->tail - f->head < n)
86 return false;
87 memcpy(dst, f->buf + f->head, n);
88 f->head += n;
89 if(f->head == f->tail)
90 f->head = f->tail = 0;
91 return true;
92}
93
94bool provsql_inproc_send(const char *buf, size_t len)
95{
96 static bool atexit_registered = false;
97 char c;
98 Oid db_oid;
99
100 /* Arm the write-back hook in this backend on its first store access
101 (see provsql_inproc_init for why this cannot be done at _PG_init). */
102 if(!atexit_registered) {
103 on_proc_exit(provsql_inproc_atexit, (Datum) 0);
104 atexit_registered = true;
105 }
106
107 provsql_fifo_push(&provsql_shared_state->req, buf, len);
108
109 if(!READM(c, char) || !READM(db_oid, Oid))
110 return false;
111 provsql_mmap_dispatch(c, db_oid);
112 return true;
113}
114
115/* No shared memory and no background worker to set up. */
116void provsql_shmem_startup(void) {}
117Size provsql_memsize(void) { return 0; }
118void provsql_shmem_request(void) {}
119
120/* One backend: nothing to serialise. */
122void provsql_shmem_lock_shared(void) {}
123void provsql_shmem_unlock(void) {}
124
125#else
126
128{
129 bool found;
130 int pipes_b_to_m[2];
131 int pipes_m_to_b[2];
132
135
136 // Reset in case of restart
138
139 LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
140
141 provsql_shared_state = ShmemInitStruct(
142 "provsql",
143 sizeof(provsqlSharedState),
144 &found);
145
146 if(!found) {
147 provsql_shared_state->lock =&(GetNamedLWLockTranche("provsql"))->lock;
148 }
149
150 LWLockRelease(AddinShmemInitLock);
151
152 // Already initialized
153 if(found)
154 return;
155
156 if(pipe(pipes_b_to_m) || pipe(pipes_m_to_b))
157 provsql_error("Cannot create pipe to communicate with MMap worker");
158
159 provsql_shared_state->pipebmr=pipes_b_to_m[0];
160 provsql_shared_state->pipebmw=pipes_b_to_m[1];
161 provsql_shared_state->pipembr=pipes_m_to_b[0];
162 provsql_shared_state->pipembw=pipes_m_to_b[1];
163 provsql_shared_state->kcmcp_endpoint[0]='\0';
164}
165
167{
168 return MAXALIGN(sizeof(provsqlSharedState));
169}
170
172{
173#if (PG_VERSION_NUM >= 150000)
176#endif
177
178 RequestAddinShmemSpace(provsql_memsize());
179
180 RequestNamedLWLockTranche("provsql", 1);
181}
182
184{
185 LWLockAcquire(provsql_shared_state->lock, LW_EXCLUSIVE);
186}
187
189{
190 LWLockAcquire(provsql_shared_state->lock, LW_SHARED);
191}
192
194{
195 LWLockRelease(provsql_shared_state->lock);
196}
197
198#endif /* PROVSQL_INPROCESS_STORE */
void provsql_mmap_dispatch(char c, Oid db_oid)
Handle a single IPC message: read its payload and write its reply.
void destroy_provsql_mmap()
Unmap and close the mmap files.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
Background worker and IPC primitives for mmap-backed circuit storage.
#define READM(var, type)
Read one value of type from the background-to-main pipe.
void provsql_shmem_unlock(void)
Release the ProvSQL LWLock.
void provsql_shmem_lock_exclusive(void)
Acquire the ProvSQL LWLock in exclusive mode.
provsqlSharedState * provsql_shared_state
Pointer to the ProvSQL shared-memory segment (set in provsql_shmem_startup).
void provsql_shmem_request(void)
Request shared memory from PostgreSQL (PG ≥ 15).
shmem_startup_hook_type prev_shmem_startup
Saved pointer to the previous shmem_startup_hook, for chaining.
void provsql_shmem_startup(void)
Initialise the ProvSQL shared-memory segment.
Size provsql_memsize(void)
Return the number of bytes required for the shared-memory segment.
void provsql_shmem_lock_shared(void)
Acquire the ProvSQL LWLock in shared mode.
Shared-memory segment and inter-process pipe management.
shmem_request_hook_type prev_shmem_request
Saved pointer to the previous shmem_request_hook (PG ≥ 15), for chaining.
Shared state stored in the PostgreSQL shared-memory segment.