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
pull/152/head
Nathan Bossart 2 years ago
parent 448a3331d9
commit 964152c476
  1. 160
      doc/src/sgml/xfunc.sgml

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

Loading…
Cancel
Save