doc: Reorganize section for shared memory and LWLocks.

Presently, this section meanders through a few different features,
and the text itself is terse.  This commit attempts to improve
matters by splitting the section into smaller sections and by
expanding the text for clarity.  This is preparatory work for a
follow-up commit that will introduce a way for libraries to use
shared memory without needing to request it at startup time.

Reviewed-by: Aleksander Alekseev, Bharath Rupireddy, Abhijit Menon-Sen
Discussion: https://postgr.es/m/20240112041430.GA3557928%40nathanxps13
Discussion: https://postgr.es/m/20231205034647.GA2705267%40nathanxps13
This commit is contained in:
Nathan Bossart 2024-01-19 11:18:32 -06:00
parent 448a3331d9
commit 964152c476
1 changed files with 116 additions and 70 deletions

View File

@ -3397,90 +3397,136 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
</sect2>
<sect2 id="xfunc-shared-addin">
<title>Shared Memory and LWLocks</title>
<title>Shared Memory</title>
<para>
Add-ins can reserve LWLocks and an allocation of shared memory on server
startup. The add-in's shared library must be preloaded by specifying
it in
<xref linkend="guc-shared-preload-libraries"/><indexterm><primary>shared_preload_libraries</primary></indexterm>.
The shared library should register a <literal>shmem_request_hook</literal>
in its <function>_PG_init</function> function. This
<literal>shmem_request_hook</literal> can reserve LWLocks or shared memory.
Shared memory is reserved by calling:
<sect3 id="xfunc-shared-addin-at-startup">
<title>Requesting Shared Memory at Startup</title>
<para>
Add-ins can reserve shared memory on server startup. To do so, the
add-in's shared library must be preloaded by specifying it in
<xref linkend="guc-shared-preload-libraries"/><indexterm><primary>shared_preload_libraries</primary></indexterm>.
The shared library should also register a
<literal>shmem_request_hook</literal> in its
<function>_PG_init</function> function. This
<literal>shmem_request_hook</literal> can reserve shared memory by
calling:
<programlisting>
void RequestAddinShmemSpace(int size)
void RequestAddinShmemSpace(Size size)
</programlisting>
from your <literal>shmem_request_hook</literal>.
</para>
<para>
LWLocks are reserved by calling:
Each backend should obtain a pointer to the reserved shared memory by
calling:
<programlisting>
void *ShmemInitStruct(const char *name, Size size, bool *foundPtr)
</programlisting>
If this function sets <literal>foundPtr</literal> to
<literal>false</literal>, the caller should proceed to initialize the
contents of the reserved shared memory. If <literal>foundPtr</literal>
is set to <literal>true</literal>, the shared memory was already
initialized by another backend, and the caller need not initialize
further.
</para>
<para>
To avoid race conditions, each backend should use the LWLock
<function>AddinShmemInitLock</function> when initializing its allocation
of shared memory, as shown here:
<programlisting>
static mystruct *ptr = NULL;
bool found;
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
ptr = ShmemInitStruct("my struct name", size, &amp;found);
if (!found)
{
... initialize contents of shared memory ...
ptr->locks = GetNamedLWLockTranche("my tranche name");
}
LWLockRelease(AddinShmemInitLock);
</programlisting>
<literal>shmem_startup_hook</literal> provides a convenient place for the
initialization code, but it is not strictly required that all such code
be placed in this hook. Each backend will execute the registered
<literal>shmem_startup_hook</literal> shortly after it attaches to shared
memory. Note that add-ins should still acquire
<function>AddinShmemInitLock</function> within this hook, as shown in the
example above.
</para>
<para>
An example of a <literal>shmem_request_hook</literal> and
<literal>shmem_startup_hook</literal> can be found in
<filename>contrib/pg_stat_statements/pg_stat_statements.c</filename> in
the <productname>PostgreSQL</productname> source tree.
</para>
</sect3>
</sect2>
<sect2 id="xfunc-addin-lwlocks">
<title>LWLocks</title>
<sect3 id="xfunc-addin-lwlocks-at-startup">
<title>Requesting LWLocks at Startup</title>
<para>
Add-ins can reserve LWLocks on server startup. As with shared memory,
the add-in's shared library must be preloaded by specifying it in
<xref linkend="guc-shared-preload-libraries"/><indexterm><primary>shared_preload_libraries</primary></indexterm>,
and the shared library should register a
<literal>shmem_request_hook</literal> in its
<function>_PG_init</function> function. This
<literal>shmem_request_hook</literal> can reserve LWLocks by calling:
<programlisting>
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
</programlisting>
from your <literal>shmem_request_hook</literal>. This will ensure that an array of
<literal>num_lwlocks</literal> LWLocks is available under the name
<literal>tranche_name</literal>. Use <function>GetNamedLWLockTranche</function>
to get a pointer to this array.
</para>
<para>
An example of a <literal>shmem_request_hook</literal> can be found in
<filename>contrib/pg_stat_statements/pg_stat_statements.c</filename> in the
<productname>PostgreSQL</productname> source tree.
</para>
<para>
There is another, more flexible method of obtaining LWLocks. First,
allocate a <literal>tranche_id</literal> from a shared counter by
calling:
This ensures that an array of <literal>num_lwlocks</literal> LWLocks is
available under the name <literal>tranche_name</literal>. A pointer to
this array can be obtained by calling:
<programlisting>
LWLockPadded *GetNamedLWLockTranche(const char *tranche_name)
</programlisting>
</para>
</sect3>
<sect3 id="xfunc-addin-lwlocks-after-startup">
<title>Requesting LWLocks After Startup</title>
<para>
There is another, more flexible method of obtaining LWLocks that can be
done after server startup and outside a
<literal>shmem_request_hook</literal>. To do so, first allocate a
<literal>tranche_id</literal> by calling:
<programlisting>
int LWLockNewTrancheId(void)
</programlisting>
Next, each individual process using the <literal>tranche_id</literal>
should associate it with a <literal>tranche_name</literal> by calling:
<programlisting>
void LWLockRegisterTranche(int tranche_id, const char *tranche_name)
</programlisting>
It is also required to call <function>LWLockInitialize</function> once
per LWLock, passing the <literal>tranche_id</literal> as argument:
Next, initialize each LWLock, passing the new
<literal>tranche_id</literal> as an argument:
<programlisting>
void LWLockInitialize(LWLock *lock, int tranche_id)
</programlisting>
A complete usage example of <function>LWLockNewTrancheId</function>,
<function>LWLockInitialize</function> and
<function>LWLockRegisterTranche</function> can be found in
<filename>contrib/pg_prewarm/autoprewarm.c</filename> in the
<productname>PostgreSQL</productname> source tree.
</para>
<para>
To avoid possible race-conditions, each backend should use the LWLock
<function>AddinShmemInitLock</function> when connecting to and initializing
its allocation of shared memory, as shown here:
Similar to shared memory, each backend should ensure that only one
process allocates a new <literal>tranche_id</literal> and initializes
each new LWLock. One way to do this is to only call these functions in
your shared memory initialization code with the
<function>AddinShmemInitLock</function> held exclusively.
</para>
<para>
Finally, each backend using the <literal>tranche_id</literal> should
associate it with a <literal>tranche_name</literal> by calling:
<programlisting>
static mystruct *ptr = NULL;
if (!ptr)
{
bool found;
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
ptr = ShmemInitStruct("my struct name", size, &amp;found);
if (!found)
{
initialize contents of shmem area;
acquire any requested LWLocks using:
ptr->locks = GetNamedLWLockTranche("my tranche name");
}
LWLockRelease(AddinShmemInitLock);
}
void LWLockRegisterTranche(int tranche_id, const char *tranche_name)
</programlisting>
</para>
<para>
It is convenient to use <literal>shmem_startup_hook</literal> which allows
placing all the code responsible for initializing shared memory in one
place. When using <literal>shmem_startup_hook</literal> the extension
still needs to acquire <function>AddinShmemInitLock</function> in order to
work properly on all the supported platforms.
</para>
</para>
<para>
A complete usage example of <function>LWLockNewTrancheId</function>,
<function>LWLockInitialize</function>, and
<function>LWLockRegisterTranche</function> can be found in
<filename>contrib/pg_prewarm/autoprewarm.c</filename> in the
<productname>PostgreSQL</productname> source tree.
</para>
</sect3>
</sect2>
<sect2 id="xfunc-addin-wait-events">