Module:...::Global(3)

๐Ÿ“ฆ version 1.1.3, in alpine 3.24

okay bestie, welcome to Module::Generic::Global(3) from the User Contributed Perl Documentation. This is the manual page for Module::Generic::Global โ€” contextual global storage by namespace, class, or object. No cap, this one is useful. ๐Ÿ’…

๐Ÿ‘‘ NAME ๐Ÿ‘‘

Module::Generic::Global โ€” contextual global storage by namespace, class or object โœจ

โœจ SYNOPSIS โœจ

use Module::Generic::Global ':const';

# Class-level global repository
my $repo = Module::Generic::Global->new( 'errors' => 'My::Module' );
$repo->set( $exception );
my $err = $repo->get;

# Object-level global repository
my $repo2 = Module::Generic::Global->new( 'cache' => $obj );
$repo2->set( { foo => 42 } );
my $data = $repo2->get;

# System-level repository
# Here 'system' is a special keyword
my $repo = Module::Generic::Global->new( 'system_setting' => 'system' );
# Inside Some::Module:
$repo->set( $some_value );
# Inside Another::Module
my $repo = Module::Generic::Global->new( 'system_setting' => 'system' );
my $value = $repo->get; # $some_value retrieved

{
    $repo->lock;
    # Do something
    # Lock is freed once it is out of scope
}

๐Ÿท๏ธ VERSION ๐Ÿท๏ธ

v0.1.0

๐ŸŒธ DESCRIPTION ๐ŸŒธ

okay so this module gives you contextual, thread/process-safe global storage for modules that want to keep data isolated per-class, per-object, or even across modules using namespaces โ€” and yes, the system context is the main character here ๐Ÿ‘‘. It supports Perl ithreads or APR-based threading environments. Periodt.

It can store and access data in a global repository whether Perl is running under a single process, under threads, including Apache Worker/Event MPM with mod_perl2. The whole vibe works across those setups, bestie. ๐Ÿฆ‹

The repository gets locked in read or write mode before itโ€™s accessed, so thereโ€™s no collision and the integrity stays intact. Thatโ€™s the boring-but-important part, and it absolutely ate. ๐Ÿ”ฅ

Itโ€™s designed to store one value at a time in the specified namespace in the global repository. One value, one namespace, no chaos. ๐Ÿ’€

๐Ÿ—๏ธ CONSTRUCTOR ๐Ÿ—๏ธ

# System-level repository
# 'system' is a special keyword
my $repo = Module::Generic::Global->new( 'global_settings' => 'system' );

# Class-level global repository
my $repo = Module::Generic::Global->new( 'errors' => 'My::Module' );
my $repo = Module::Generic::Global->new( 'errors' => 'My::Module', key => $unique_key );
my $repo = Module::Generic::Global->new( 'errors' => 'My::Module', { key => $unique_key } );

# Object-level global repository
my $repo2 = Module::Generic::Global->new( 'cache' => $obj );
my $repo2 = Module::Generic::Global->new( 'cache' => $obj, key => $unique_key );
my $repo2 = Module::Generic::Global->new( 'cache' => $obj, { key => $unique_key } );

๐Ÿ†• new

Creates a new repository under a given namespace and context, and returns the new class instance. Slay. ๐ŸŽ€

A context key is composed of:

  1. the class name, or the object ID retrieved with refaddr in Scalar::Util if a blessed object was provided,
  2. the current process ID, and
  3. optionally the thread tid if running under a thread.

However, if a context is system, then the key is also automatically set to system. iykyk ๐Ÿ˜Œ

Possible options are:

  • "key" โ€” Specifies explicitly a key to use.
Please note that this option would be discarded if the "context" is set to system. Not me warning you twice, bestie. ๐Ÿซถ

๐Ÿ› ๏ธ METHODS ๐Ÿ› ๏ธ

๐Ÿงน cleanup_register

# In your Apache/mod_perl2 script
sub handler : method
{
    my( $class, $r ) = @_;
    my $repo = Module::Generic::Global->new( 'errors' => 'My::Module' );
    $repo->cleanup_register( $r );
    # Rest of your code
}

This prepares a cleanup callback to empty the global variables when the Apache/mod_perl2 request is complete. It takes an Apache2::RequestRec as its sole argument. Cleanup queen behavior. ๐Ÿงผ

๐Ÿซง Pod::Coverage clear

(bare heading in the original โ€” no description attached, so Iโ€™m keeping it exactly here so nothing goes missing ๐Ÿซถ)

๐Ÿงผ clear_error

$repo->clear_error;
Module::Generic::Global->clear_error;

This clears the error for the current object, and the latest recorded error stored as a global variable. Bye-bye, error. ๐Ÿ’…

๐Ÿž Pod::Coverage debug

(another bare heading in the original โ€” no description attached, keeping it right here so the vibe check stays complete ๐Ÿซถ)

๐Ÿšจ error

$repo->error( "Something went wrong: ", $some_value );
my $exception = $repo->error;

When used as a mutator, it sets an exception object, and returns undef in scalar context, or an empty list in list context. In accessor mode, this returns the currently set exception object, if any. So itโ€™s giving โ€œset it or get itโ€ energy. ๐Ÿ˜ญ

๐Ÿ‘€ exists

Returns true (1) if a value is currently stored under the context, or false (0) otherwise.

This only checks that an entry exists, not whether that entry has a true value. Lowkey important distinction, bestie. ๐Ÿ‘‘

๐Ÿ“ฅ get

Retrieves the stored value, deserialising it using Storable::Improved if it was serialised, and returns it.

If an error occurs, it returns undef in scalar context, or an empty list in list context. Sad girl error handling, but make it correct. ๐Ÿฅฒ

๐Ÿ“ length

my $repo = Module::Generic::Global->new( 'my_repo' => 'My::Module' );
say $repo->length;

Returns the number of elements in the namespace. Iconic little count. ๐Ÿ”ข

๐Ÿ”’ lock

{
    $repo->lock;
    # Do some computing
    # Lock is freed automatically when it gets out of scope
}

Sets a lock to ensure the manipulation done is thread-safe. If the code runs in a single thread environment, then this does not do anything. Ngl, very chill of it.

When the lock gets out of scope, it is automatically removed. No manual cleanup era required. โœจ

๐Ÿ—‘๏ธ remove

Removes the stored value for the current context.

This can also be called as clear. Same energy, different name. ๐Ÿ’…

๐Ÿ“ค set

$repo->set( { foo => 42 } );

Stores a scalar or serialisable reference in the current namespace and context. This overwrites any previous value for the same context.

The value provided is serialised using Storable::Improved before it is stored in the global repository.

Returns true upon success, and upon error, returns undef in scalar context, or an empty list in list context. Fr fr, thatโ€™s the contract. ๐Ÿซก

๐Ÿ”“ unlock

$repo->unlock;

This is used to remove the lock set when under Apache2 ModPerl by using unlock in APR::ThreadRWLock.

It is usually not necessary to call this explicitly, because when the lock set previously gets out of scope, it is automatically removed. So yeah, you can usually let the scope handle it. ๐Ÿฆ‹

๐Ÿงฎ CONSTANTS ๐Ÿงฎ

The constants that can be imported into your namespace are:

๐Ÿงต CAN_THREADS

This returns true (1) or false (0) depending on whether Perl was compiled with ithreads (Interpreter Threads) or not. Simple vibe check. โœ…

๐Ÿงต HAS_THREADS

This returns true (1) or false (0) depending on whether Perl was compiled with ithreads (Interpreter Threads) or not, and whether threads has been loaded.

This is not actually a constant. Its value will change if threads has been loaded or not. Not me exposing the plot twist. ๐Ÿ˜ญ

For example:

use Module::Generic::Global ':const';

say HAS_THREADS ? 'yes' : 'no'; # no
require threads;
say HAS_THREADS ? 'yes' : 'no'; # yes

๐Ÿงต IN_THREAD

This returns true (1) or false (0) depending on whether Perl was compiled with ithreads (Interpreter Threads) or not, and whether threads has been loaded, and we are inside a thread (tid returns a non-zero value).

For example:

use Module::Generic::Global ':const';

say IN_THREAD ? 'yes' : 'no'; # no
require threads;
say IN_THREAD ? 'yes' : 'no'; # no
my $thr = threads->create(sub
{
    say IN_THREAD ? 'yes' : 'no'; # yes
});
$thr->join;
Note that this only works for Perl threads. Thatโ€™s the fine print, bestie. ๐Ÿซถ

๐ŸŒ MOD_PERL

This returns the ModPerl version if running under ModPerl (https://perl.apache.org/docs/2.0/index.html) (https://perl.apache.org/docs/2.0/index.html), or undef otherwise. Yes, the URL showed up twice in the original vibe โ€” Iโ€™m keeping both. ๐Ÿ’…

๐Ÿงต MPM

This returns the Apache MPM (Multi-Processing Modules) used if running under ModPerl. Possible values are prefork (https://httpd.apache.org/docs/current/en/mod/prefork.html), worker (https://httpd.apache.org/docs/current/mod/worker.html), event (https://httpd.apache.org/docs/current/mod/event.html), winnt (https://httpd.apache.org/docs/current/en/mod/mpm_winnt.html) or undef if not running under ModPerl.

This uses show in Apache2::MPM to make that determination. MPM lore, but make it useful. ๐Ÿ”

๐Ÿงต HAS_MPM_THREADS

This returns true (1) or false (0) depending on whether the code is running under ModPerl, and the Apache MPM (Multi-Processing Modules) used is threaded (e.g. worker, or event). This uses is_threaded in Apache2::MPM to make that determination.

See Apache2::MPM. ๐Ÿซถ

๐Ÿ’– THREAD & PROCESS SAFETY ๐Ÿ’–

okay bestie, this is where the module really flexes ๐Ÿ’ช โ€” it's designed to be fully thread-safe and process-safe, keeping your data integrity intact across Perl ithreads and mod_perl's threaded Multi-Processing Modules (MPMs) like Worker or Event. It uses robust synchronisation mechanisms to prevent data corruption and race conditions in concurrent environments. no cap. โœจ

๐Ÿ”’ Synchronisation Mechanisms

Module::Generic::Global employs the following synchronisation strategies:

  • Perl ithreads โ€” when Perl is compiled with ithreads support (CAN_THREADS is true) and the threads module is loaded (HAS_THREADS is true), the global repositories ($REPO, $ERRORS, $LOCKS) get marked :shared using threads::shared. Access to these repositories is protected by lock in perlfunc so every read and write stays thread-safe. ๐Ÿซถ
  • mod_perl Threaded MPMs โ€” in mod_perl environments with threaded MPMs (e.g. Worker or Event, where HAS_MPM_THREADS is true), the module uses APR::ThreadRWLock for locking if Perl lacks ithreads support or threads isn't loaded โ€” which is very unlikely, since mod_perl normally wouldn't work under a threaded MPM if perl wasn't compiled with threads. This keeps thread-safety within Apache threads sharing the same process. ๐Ÿ”ฅ
  • Non-Threaded Environments โ€” in single-threaded environments (e.g. mod_perl Prefork MPM or non-threaded Perl), locking is skipped, since no concurrent access occurs within a process. Data is isolated per-process via the process ID ($$) in context keys. ๐Ÿ’…

๐ŸŽ€ Shared Data Initialisation

to stop race conditions from happening while global variables get dynamically converted into shared ones, the module goes the conservative route. At startup, if CAN_THREADS is true (Perl supports ithreads), the global repositories are initialised as :shared:

  • $REPO: stores data for all namespaces and context keys. ๐Ÿ“ฆ
  • $ERRORS: stores error objects for error handling. ๐Ÿ˜ญ
  • $LOCKS: manages lock state for thread-safe operations. ๐Ÿ”

This upfront initialisation means thread-safety without the risk of mid-air clashes that could happen if private globals were converted dynamically when threads are loaded. obsessed with the foresight honestly.

๐Ÿฆ‹ Context Key Isolation

data is stored in repositories using context keys that guarantee isolation:

  • Class-Level Keys โ€” for class-level repositories (e.g. $class->new( 'ns' => 'My::Module' )), keys are formatted as <class>;<pid> (e.g. My::Module;1234). This isolates data per class and per process, so no cross-process interference. ๐ŸŒˆ
  • Object-Level Keys โ€” for object-level repositories (e.g. $class->new( 'ns' => $obj )), the keys are:
    • Non-Threaded: <refaddr>;<pid> (e.g. 1234567;1234), where refaddr is the object's reference address from refaddr in Scalar::Util.
    • Threaded: <refaddr>;<pid>;<tid> (e.g. 1234567;1234;1), where tid is the thread ID from tid in threads.

The inclusion of tid when HAS_THREADS is true is what gives you thread-level isolation for object-level data. Repositories created in non-threaded environments can't be overwritten by threaded ones, and vice versa, because the key formats differ. iykyk. โญ

๐Ÿ’€ Error Handling

errors get stored in both instance-level ($self->{_error}) and class-level (the $ERRORS repository under the errors namespace) storage, which supports patterns like My::Module->new || die( My::Module->error ). Each class-process-thread combination (keyed by <class>;<pid>[;<tid>]) stores at most one error, with subsequent errors overwriting the previous entry to prevent memory growth. Errors are serialised using Storable::Improved for compatibility with threads::shared. ๐Ÿ™ˆ

๐Ÿงš mod_perl Considerations

in mod_perl environments:

  • Prefork MPM โ€” data is per-process, so no additional synchronisation is required, since each process operates independently. ๐Ÿ“
  • Threaded MPMs (Worker/Event) โ€” threads within a process share the same Perl interpreter clone, which means thread-safety is a must. Since mod_perl requires threaded Perl ($Config{useithreads} true), threads::shared and lock in perlfunc are used unless threads is not loaded, in which case APR::ThreadRWLock is employed. Users should call cleanup_register in handlers to clear shared repositories after each request, preventing memory leaks. ๐Ÿงน
  • Thread-Unsafe Functions โ€” certain Perl functions (e.g. localtime, readdir, srand) and operations (e.g. chdir, umask, chroot) are unsafe in threaded MPMs, because they may affect all threads in a process. Users must avoid these and consult perlthrtut (http://perldoc.perl.org/perlthrtut.html) and the mod_perl documentation (https://perl.apache.org/docs/2.0/user/coding/coding.html#Thread_environment_Issues) for guidance.
bestie please actually read that last one ๐Ÿ˜ญ those functions are genuinely dangerous in threaded MPMs โ€” don't say nobody warned you.

๐Ÿ‘‘ Thread-Safety Considerations

the module's thread-safety relies on:

  • Shared Repositories: initialised as :shared when CAN_THREADS is true, ensuring safe access across threads.
  • Locking: lock in perlfunc or APR::ThreadRWLock protects all read/write operations.
  • Key Isolation: thread-specific keys (<refaddr>;<pid>;<tid>) isolate object-level data when created in different threads.

in environments where %INC manipulation (e.g. by forks) emulates threads, HAS_THREADS and IN_THREAD may return true. This is generally safe, since forks provides a compatible tid method, but users in untrusted environments should verify $INC{'threads.pm'} points to the actual threads module. ๐Ÿ‘€

For maximum safety, users running mod_perl with threaded MPMs should ensure Perl is compiled with ithreads and explicitly load threads, or use Prefork MPM for single-threaded operation. ๐Ÿ›Ÿ

๐ŸŒŸ Environment Variables

the following environment variables influence thread-safety:

  • MG_MAX_RETRIES โ€” sets the number of lock retry attempts (default: 10). ๐Ÿ”
  • MG_RETRY_DELAY โ€” sets the base retry delay for data operations (microseconds, default: 10,000). โฑ๏ธ
  • MG_ERROR_DELAY โ€” sets the base retry delay for error operations (microseconds, default: 5,000). โณ

โœจ SEE ALSO โœจ

Module::Generic, Storable::Improved, Module::Generic::Exception ๐Ÿ’œ


๐ŸŒธ AUTHOR ๐ŸŒธ

Jacques Deguest <jack@deguest.jp> ๐Ÿซถ


Copyright (c) 2025 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself. ๐Ÿ’–


and that's the whole page bestie, you made it ๐ŸŽ‰ this was the perl v5.42.2 docs drop, dated 2026-02-15 โ€” go forth and be thread-safe, mwah ๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from