mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
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:
@ -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
|
||||||
<programlisting>
|
|
||||||
void RequestAddinShmemSpace(int size)
|
|
||||||
</programlisting>
|
|
||||||
from your <literal>shmem_request_hook</literal>.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
LWLocks are reserved 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:
|
calling:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
int LWLockNewTrancheId(void)
|
void RequestAddinShmemSpace(Size size)
|
||||||
</programlisting>
|
</programlisting>
|
||||||
Next, each individual process using the <literal>tranche_id</literal>
|
Each backend should obtain a pointer to the reserved shared memory by
|
||||||
should associate it with a <literal>tranche_name</literal> by calling:
|
calling:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
void LWLockRegisterTranche(int tranche_id, const char *tranche_name)
|
void *ShmemInitStruct(const char *name, Size size, bool *foundPtr)
|
||||||
</programlisting>
|
</programlisting>
|
||||||
It is also required to call <function>LWLockInitialize</function> once
|
If this function sets <literal>foundPtr</literal> to
|
||||||
per LWLock, passing the <literal>tranche_id</literal> as argument:
|
<literal>false</literal>, the caller should proceed to initialize the
|
||||||
<programlisting>
|
contents of the reserved shared memory. If <literal>foundPtr</literal>
|
||||||
void LWLockInitialize(LWLock *lock, int tranche_id)
|
is set to <literal>true</literal>, the shared memory was already
|
||||||
</programlisting>
|
initialized by another backend, and the caller need not initialize
|
||||||
A complete usage example of <function>LWLockNewTrancheId</function>,
|
further.
|
||||||
<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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To avoid possible race-conditions, each backend should use the LWLock
|
To avoid race conditions, each backend should use the LWLock
|
||||||
<function>AddinShmemInitLock</function> when connecting to and initializing
|
<function>AddinShmemInitLock</function> when initializing its allocation
|
||||||
its allocation of shared memory, as shown here:
|
of shared memory, as shown here:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
static mystruct *ptr = NULL;
|
static mystruct *ptr = NULL;
|
||||||
|
|
||||||
if (!ptr)
|
|
||||||
{
|
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
|
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
|
||||||
ptr = ShmemInitStruct("my struct name", size, &found);
|
ptr = ShmemInitStruct("my struct name", size, &found);
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
initialize contents of shmem area;
|
... initialize contents of shared memory ...
|
||||||
acquire any requested LWLocks using:
|
|
||||||
ptr->locks = GetNamedLWLockTranche("my tranche name");
|
ptr->locks = GetNamedLWLockTranche("my tranche name");
|
||||||
}
|
}
|
||||||
LWLockRelease(AddinShmemInitLock);
|
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>
|
||||||
|
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>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
</sect3>
|
||||||
|
|
||||||
|
<sect3 id="xfunc-addin-lwlocks-after-startup">
|
||||||
|
<title>Requesting LWLocks After Startup</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
It is convenient to use <literal>shmem_startup_hook</literal> which allows
|
There is another, more flexible method of obtaining LWLocks that can be
|
||||||
placing all the code responsible for initializing shared memory in one
|
done after server startup and outside a
|
||||||
place. When using <literal>shmem_startup_hook</literal> the extension
|
<literal>shmem_request_hook</literal>. To do so, first allocate a
|
||||||
still needs to acquire <function>AddinShmemInitLock</function> in order to
|
<literal>tranche_id</literal> by calling:
|
||||||
work properly on all the supported platforms.
|
<programlisting>
|
||||||
|
int LWLockNewTrancheId(void)
|
||||||
|
</programlisting>
|
||||||
|
Next, initialize each LWLock, passing the new
|
||||||
|
<literal>tranche_id</literal> as an argument:
|
||||||
|
<programlisting>
|
||||||
|
void LWLockInitialize(LWLock *lock, int tranche_id)
|
||||||
|
</programlisting>
|
||||||
|
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>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Finally, each backend 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>
|
||||||
|
</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>
|
||||||
|
|
||||||
<sect2 id="xfunc-addin-wait-events">
|
<sect2 id="xfunc-addin-wait-events">
|
||||||
|
Reference in New Issue
Block a user