# Thread::Sociable

Description | Enhanced replacement for threads::shared

Thread::Sociable > Perl Manuals > Thread-Sociable
Source

### Index

- NAME
- VERSION
- SYNOPSIS
- DESCRIPTION
  - Background
  - An Inconvenient Alternative
    - Drawbacks
      - Requires more internal code to manage variables
      - Requires explicit declaration of lockable variables
      - Requires explicit destruction to free resources
    - Benefits
      - Performance
      - Optimized Scalar Reads
      - `splice()` Support
      - Optimized Resource Allocation
      - Integrated Queueing Solution
      - Transient Sociables
      - Interthread `tie()` Support
      - Software Transactional Memory (aka STM) Support
- ATTRIBUTES
  - - - VARIABLE : Sociable
      - VARIABLE : Lockable
- METHODS
  - - - socialize(VARIABLE)
      - socialize_with_lock(VARIABLE)
      - $id = is_sociable(VARIABLE)
      - $id = is_lockable(VARIABLE)
      - sociable_discard(VARIABLE)
      - sociable_enable_debug()
      - sociable_disable_debug()
      - sociable_enable_warnings()
      - sociable_disable_warnings()
      - ($extent, $himark) = sociable_default_extent()
      - ($extent, $himark) = sociable_default_extent($new_extent)
      - ($extent, $himark) = sociable_default_extent($new_extent, $new_himark)
      - ($extent, $himark) = sociable_extent(@array)
      - ($extent, $himark) = sociable_extent(%hash)
      - ($extent, $himark) = sociable_extent(@array, $new_extent)
      - ($extent, $himark) = sociable_extent(%hash, $new_extent)
      - ($extent, $himark) = sociable_extent(@array, $new_extent, $new_himark)
      - ($extent, $himark) = sociable_extent(%hash, $new_extent, $new_himark)
      - sociable_begin_work(\&xaction_sub [, onCommit => \&commit_sub ] [, onRestart => \&restart_sub ] [, onRollback => \&rollback_sub ])
      - $oldlimit = sociable_stm_limit( [ $newlimit ] )
      - $oldtimeout = sociable_stm_timeout( [ $newtimeout ] )
      - lock(VARIABLE)
      - cond_wait(VARIABLE)
      - cond_wait(CONDVAR, LOCKVAR)
      - cond_timedwait(VARIABLE, ABS_TIMEOUT)
      - cond_timedwait(CONDVAR, ABS_TIMEOUT, LOCKVAR)
      - cond_signal(VARIABLE)
      - cond_broadcast(VARIABLE)
- OBJECTS
- NOTES
- Using Sociably `tie()`'d Variables
- Using STM
- STM Implementation Details
- BUGS AND LIMITATIONS
- Benchmarks
  - - Single 2.4 GHz, WinXP, AS Perl 5.8.6
    - Single 100MHz SPARC, Solaris 10, Perl 5.8.6
    - Single 1.5GHz PPC, OS X 10.4.9, Perl 5.8.6
    - Dual 3GHz Xeon, Win2000 Server, AS Perl 5.8.8
    - Dual 3GHz Xeon, Linux, Perl 5.8.8
    - Dual 3GHz Xeon, Solaris 10, Perl 5.8.8
    - Quad 700MHz Xeon, Win2000 Server, AS Perl 5.8.8
- TO DO
  - - IPC Support
- SEE ALSO
- AUTHOR

# NAME

Thread::Sociable - Less restrictive replacement for threads::shared

# VERSION

0.10

# SYNOPSIS

      use threads;
      use Thread::Sociable;

      my $var : Sociable;
      my $lockvar : Lockable;
      $var = $scalar_value;
      $var = $sociable_ref_value;
      $var = socialize($simple_unshared_ref_value);
      $lockvar = socialize_with_lock($simple_unshared_ref_value);

      my($scalar, @array, %hash);
      socialize($scalar);
      socialize(@array);
      socialize_with_lock(%hash);
      my $bar = &socialize([]);
      $hash{bar} = &socialize({});

      { lock(%hash); ...  }

      cond_wait($scalar);
      cond_timedwait($scalar, time() + 30);
      cond_broadcast(@array);
      cond_signal(%hash);

      my $lockvar : Lockable;
      # condition var != lock var
      cond_wait($var, $lockvar);
      cond_timedwait($var, time()+30, $lockvar);
      #
      # verify the variable is sociable
      #
      print "\$lockvar is sociable\n"
          if is_sociable($lockvar);
      #
      # verify the variable is lockable
      #
      print "\$lockvar is lockable\n"
          if is_lockable($lockvar);
      #
      #    discard the sociable variable; causes
      # $lockvar to be come private again, and
      # all other references in other threads
      # will also become private
      #
      sociable_discard($lockvar);

      print "\$lockvar is not sociable\n"
          unless is_sociable($lockvar);
      #
      #    set default extents
      #
      my ($old_extend, $old_himark) =
          sociable_default_extent($new_extent, $new_himark);
      #
      #    set extents on individual array or hash
      #
      my ($old_extend, $old_himark) =
          sociable_extent(@array, $new_extent, $new_himark);

      my ($old_extend, $old_himark) =
          sociable_extent(%hash, $new_extent, $new_himark);
      #
      # tie a sociable using a Thread::Sociable::Apartment closure container
      #
      sociable_tie $sociable, $tsa_closure;
      #
      # execute a transaction
      #
      sociable_begin_work(sub {
          },
          onCommit => sub {
              print "We're in like Flynn!\n";
          },
          onRestart => sub {
              print "Curses, foiled again!\n";
          },
          onRollback => sub {
              print "OOOOPS!! We're hosed!\n";
          }
      );

# DESCRIPTION

Within Perl's current ithreads implementation, variables are private to each thread, and each newly created thread gets a private copy of each existing variable. Thread::Sociable permits sharing of variables between threads (and pseudoforks on Win32). While the interface is largely based on threads::shared, a modified backend is provided that minimizes the performance penalty of the highly pessimistic/contentious global shared interpretter locking upon which threads:shared is heavily reliant.

Additionally, Thread::Sociable provides

- array splicing
- inter-thread tie() capability
- an integrated queueing solution
- an apartment threading solution
- software transactional memory aka STM

## Background

The current solution for sharing variables between threads, threads::shared, imposes a significant burden: since all threads::shared variables are managed within a shared Perl interpretter context, a single global lock on that context must be acquired before any thread can do anything to any thread::shared variable. No matter if each thread is referencing completely different variables, and is only doing read operations, they all must first exclusively acquire the global lock before accessing the shared variables (Frankly, it calls into question the point of providing application-level locking primitives...). The problem is exacerbated by the frequent reference count adjustments applied to the shared variables as various threads create and destroy proxy versions and references.

While such overhead may not be significant for a few threads making infrequent accesses to shared variables, for large scale threaded applications, especially those which frequently pass data amongst themselves via either Thread::Queue or Thread::Queue::Duplex (e.g., Thread::Apartment based applications), the performance penalty can be very severe (so much so that using process-based concurrency with, e.g., IPC::Mmap and Storable becomes a viable alternative).

threads::shared's design seems to stem from a "give me convenience or give me death" approach: by reusing the Perl variable management code already available in the shared interpretter, minimal additional code is required. In addition, it preserves the convenience of reference counted reclamation which Perl applications have come to rely on. Alas, to paraphrase, "The wages of convenience is death", or at least performance closely resembling a moribund state.

## An Inconvenient Alternative

Thread::Sociable is based on a more pragmatic view of sharing data between threads. By definition (with a few exceptional platforms most users will never encounter), all threads have access to all other threads' memory. Assuming an internal structure to manage variable state between threads, and support for standard application level locking primitives, there's little need for the heavyweight solution provided by threads::shared...except for the convenience of reusing Perl variable management and reclamation code, and automatic lock structure creation and destruction. Lets look at the drawbacks and benefits of Thread::Sociable:

### Drawbacks

#### Requires more internal code to manage variables

Since it uses its own context structures allocated from the process's native (rather than Perl's) heap, Thread::Sociable cannot simply delegate variable management to a shared interpretter. Fortunately, much of the additional code is simply cut/paste from Perl core (e.g., hash management). The advantage of this approach is that, by careful coding, global locking can be avoided except for variable creation and destruction; beyond that, application of concurrency control to individual variables is at the sole discretion of the application.

Another benefit is that it provides opportunities to add new capabilities, and/or exploit additional external libraries (e.g., Hoard).

#### Requires explicit declaration of lockable variables

Thread::Sociable requires explicit declaration of variables that are to be subjected to locking.

This feature helps avoid the need for frequent interstitial global locks, since the additional locking resources can be established within the global lock during variable creation, rather than at some subsequent arbitrary point in time. In most (nearly all ?) instances, an application will have a priori knowledge whether a variable will need to negotiate concurrent access during its lifetime, so imposing this requirement shouldn't be too much of a burden (and may even be considered a beneficial annotation).

#### Requires explicit destruction to free resources

Sociable variables must be explicitly destroyed to free their resources.

Like all sociable beings, sociable variables enjoy a good party, and don't respond to hints that the party is over: they must be politely told to get out.

Thread::Sociable does not perform reference counting as variables are passed between threads, thereby avoiding the repeated need for locking/unlocking a global context to perform the refcount, and possible destruction when a refcount drops to zero. Instead, applications are responsible for determining when a sociable variable can be discarded. Explicit destruction is a minor inconvenience paid to improve scalability and performance, and likewise may be considered a useful annotation. Furthermore, in many instances, shared variables are intended to be long lived, and thus all the overhead of reference counting - including the global locking - is a waste of time. In fact, many consider the pre-allocation and pooling of a fixed number of such shared resources to be best practice (much like creating a pool of reusable threads).

Note that explicit destruction is NOT required on process exit (or at all), but only when an application determines a sociable variable is no longer needed, and its resources might be recycled for a future variable.

Also note that in the event of an inadvertant access to a discarded sociable variable, the operation will cause the sociable variable to revert to an empty private version. The resource slot for each sociable variable contains a recycle count signature which is verified for each attempted access; if the recycle counts do not match, the proxy variable's "magic" is removed, and the current private contents are then used. However, the `sociable_enable_warnings()` method can be called to cause a Perl warning to be issued whenever such an inadvertant access occurs.

### Benefits

Despite these inconveniences, Thread::Sociable provides some benefits (aside from the performance improvment):

#### Performance

By avoiding the frequent global locking required by threads::shared, Thread::Sociable achieves significant performance improvements. Benchmarks indicate that, in the simplest single CPU, single threaded case, sociable variables are twice as fast to read and 3x faster to write than threads::shared equivalents. When that benchmark is expanded to multithreaded operations, the performance improvement becomes more dramatic, ranging from 4x for a few concurrent threads, to 8x or more for 20+ threads. Finally, when benchmarked on multi-CPU/multicore platforms, the performance differences can be Ax to Bx.

#### Optimized Scalar Reads

An additional optimization is provided to exploit the manner in which Perl's internal scalar data is stored. A sequence counter is maintained for scalar sociables that is incremented each time the variable is written, and is copied to the private proxy version on every read operation. When a proxy read is initiated, the proxy's sequence number is compared to the sociable version, and if the same, no copy operation is performed. While the impact on small size scalars is negligible, access speeds for larger size scalars can be greatly improved by avoiding an unneccesary buffer copy - which may be very useful for certain operations (e.g., complex regular expression evaluation, etc.)

#### `splice()` Support

Thread::Sociable supports array splicing; threads::shared does not.

#### Optimized Resource Allocation

Thread::Sociable allocates sociable context resources in clusters.

In addition, Thread::Sociable supports tunable "extents" for aggregate sociable variables (arrays and hashes). Extents are per-variable pools of sociable scalar elements, and consist of 2 adjustable control values:

- allocation cluster size aka "extent"
  the number of elements allocated and added to a sociable variable's pool when its pool is empty and a new element is added to the array or hash
- high water mark aka "himark"
  the maximum number of elements permitted in the variable's pool. When an element is deleted from an array or hash, its context is placed in the variable's pool; if the pool's size reaches the himark, the (himark - extent) number of elements are returned to the global free pool. By deallocating multiple elements inside a single global context lock, (instead of single element deallocations), global contention can be minimized.

By maintaining a small pool of scalar elements attached individual arrays/hashes, most additions/deletions to/from the aggregate variable can be managed without acquiring the global context lock, thereby reducing thread contention and improving overall performance. A global default extent and himark are applied when the variable is created; the variable's value may then be individually adjusted if needed. Methods are provided to adjust both the global default extent and the per-variable extent. The default extent is 10, and the default himark is 20.

To illustrate the purpose of extents, consider the following examples:

- Loading a large hash
  Assume an application needs to load 5000 elements into a hash. Using the threads::shared approach, the addition of each element requires acquiring the global context lock, allocating the scalar element context, and the releasing the global context lock. If other threads are running and using threads::shared resources, significant contention occurs as the array load operation acquires and releases the global lock 5000 times.
  In contrast, using Thread::Sociable and setting the extent to 5000 after the hash is created, then loading the hash, then restoring the extent to the defaults, only incurs a single global context lock acquire/release cycle.
- Using an array as an interthread queue
  Assume an application uses a threads::shared array as an interthread communication queue (e.g., Thread::Queue ). As elements are enqueued and dequeued, the global context lock is repeatedly acquired and releases, potentially causing thread contention.
  By using Thread::Sociable, a suitable extent and himark can be applied to the array, so that the queue seldom, if ever, needs to acquire the global context lock.

#### Integrated Queueing Solution

Thread::Sociable incorporates significant portions of the Thread::Sociable::DuplexQueue module within its XS code, thereby achieving significant performance improvements for some common uses of thread-shared data, i.e., passing the data between threads. Additionally, the queue management context is optimized for the duplex queueing operations used by Thread::Sociable::Apartment thereby improving performance of inter-apartment method calls. Benchmarks indicate that Thread::Sociable::DuplexQueue is X times faster than the Thread::Queue equivalent operations, and Y times faster the the equivalent Thread::Queue::Duplex and Thread::Queue::Multiplex. Furthermore, tests on round trip times for calls between apartment threaded objects indicate Thread::Sociable::Apartment is Z times faster than Thread::Apartment.

#### Transient Sociables

Thread::Sociable supports transient sociables.

In certain instances (primarily for passing data between threads over a Thread::Sociable::DuplexQueue), a variable only needs to be sociable for a brief time while it is referenced by another sociable variable. Once the reference to the transient sociable variable is removed, the variable is immediately made private in the dereferencing thread, and its sociable resources are freed.

#### Interthread `tie()` Support

Thread::Sociable and Thread::Sociable::Apartment provide an inter-thread `tie` capability

One drawback of threads::shared is the inability to apply `tie`'s to the variables. However, the fault may not lie entirely with threads::shared, as the behavior of a `tie`'d variable with respect to all the threads referencing the variable is nebulous at best. E.g., if Thread A tie's $x, then executes its STORE() method, how should $x's reference in Thread B behave ?

Thread::Sociable (along with Thread::Sociable::Apartment attempts to provide a partial solution, primarily to address issues with apartment threaded versions of DBI (DBIx::Threaded) and Perl/Tk (Tk::Threaded), both of which rely on tie's for important interfaces.

Thread::Sociable introduces the notion of a shadow variable. Essentially, the shadow variable is a private variable within a thread (specifically, an apartment thread in a Thread::Sociable::Apartment container) that is associated with a sociable variable. Thread::Sociable tracks information regarding the shadowed sociable variable in its internal structures that is used to initiate a proxied closure call to the thread with the private shadow variable whenever the shadowed variable is accessed.

E.g., consider the `-textvariable` attribute of a Perl/Tk Tk::Entry() widget. In a nonthreaded application, Perl/Tk uses a form of `tie` to set or update the Entry contents automatically, so an application can simply read or assign to the variable, and the entry's contents are immediately returned or set.

Alas, thread::shared variables can't be tie'd, and so this convenient interface is not available to Tk::Threaded. However, by using shadow variables, and a simple wrapper around the relevant widget constructors, Thread::Sociable can

1. replace the sociable `-textvariable` value with a private shadow variable

2. create a Thread::Sociable::Apartment::Shadow object to wrap the shadow variable

3. provide a proxy for the Shadow object to Thread::Sociable to associate with the sociable variable.

E.g., assuming Tk::Threaded is resident in Thread B, and Thread A creates an Entry widget with `-textvariable =` \$x> (where $x is sociable), Thread B will

- translate the `-textvariable` value into a private `$x_shadow`

- create a Shadow object to contain `$x_shadow`

- associate the Shadow object with `$x`

- Tk can then tie and otherwise manipulate `$x_shadow` as needed.

When Thread A later needs to read the entry widget contents, it simply reads $x, which Thread::Sociable detects as shadowed, and then queues up a proxied closure call to Thread B to `FETCH()` the current value of the shadow, which in turn causes Tk to perform any processing needed to retrieve the contents of the widget.

Likewise, if Thread A writes to $x, Thread::Sociable detects the shadow, and queues a proxied closure call to Thread B to `STORE()` the current value to the shadow, which in turn causes Tk to fill/replace the current contents of the widget.

Similar operations can be performed for tie'd array and hash objects (via tie's PUSH(), POP(), etc. interfaces). In addition, the shadowed behavior is available to all threads, i.e., Threads C, D, and E can also get/set the shadowed $x. Currently, a sociable variable can only be tied by a single thread; a future release will remove this restriction.

#### Software Transactional Memory (aka STM) Support

Thread::Sociable provides an STM implementation (currently supporting x86, x64, SPARC, and PowerPC platforms) to simplify development of concurrent applications.

# ATTRIBUTES

The following variable attributes are provided:

#### VARIABLE : Sociable

#### VARIABLE : Lockable

`Sociable` creates the specified VARIABLE, and applies `socialize()` to it. Likewise, `Lockable` creates the specified VARIABLE, and applies `socialize_with_lock()` to it.

# METHODS

Note that all these methods are exported into the `use`'ing package.

#### socialize(VARIABLE)

#### socialize_with_lock(VARIABLE)

`socialize` takes a value and marks it as shared. You can share a scalar, array, hash, scalar ref, array ref, or hash ref. `socialize` will return the shared rvalue, but always as a reference. `socialize_with_lock` is the same as socialize, but also creates lock structures for the sociable variable. All descriptions of sociable() apply equally to sociable_with_lock().

A variable can also be marked as sociable at compile time by using the `: Sociable` attribute, e.g., `my $var : Sociable;`, or sociably lockable via the `: Lockable` attribute: e.g., `my $var : Lockable;`.

Due to problems with Perl's prototyping, if you want to socialize a newly created reference, you need to use the `&socialize([])` and `&socialize({})` syntax.

The only values that can be assigned to a sociable scalar are other scalar values, or sociable refs:

      my $var : Sociable;
      $var = 1;              # ok
      $var = [];             # error
      $var = &socialize([]);     # ok

`socialize` will traverse up references exactly one level. `socialize(\$a)` is equivalent to `socialize($a)`, while `socialize(\\$a)` is not. This means that you must create nested sociable data structures by first creating individual sociable leaf nodes, and then adding them to a sociable hash or array.

      my %hash : Sociable;
      $hash{'meaning'} = &socialize([]);
      $hash{'meaning'}[0] = &socialize({});
      $hash{'meaning'}[0]{'life'} = 42;

#### $id = is_sociable(VARIABLE)

`is_sociable` checks if the specified variable is sociable or not. If sociable, returns the variable's internal ID (similar to refaddr()). Otherwise, returns `undef`.

      if (is_sociable($var)) {
          print("\$var is sociable\n");
      } else {
          print("\$var is not sociable\n");
      }

#### $id = is_lockable(VARIABLE)

`is_lockable` checks if the specified variable is lockable or not. If lockable, returns the variable's internal ID (similar to refaddr()). Otherwise, returns `undef`.

      if (is_lockable($var)) {
          print("\$var is lockable\n");
      } else {
          print("\$var is not lockable\n");
      }

#### sociable_discard(VARIABLE)

Explicitly destroys the specified sociable variable, returning its resources to the sociable pool to be recycled for any future sociable variable creation. Removes the "magic" from the private proxy version of the variable and clears it, so that any future attempt to reference the variable will use the existing private version. Likewise, any subsequent reference the sociable in another thread will remove the magic from that thread's private proxy.

If `sociable_enable_warnings()` has been called, any subsequent reference to the sociable in any thread will also cause a Perl warning to be generated.

#### sociable_enable_debug()

#### sociable_disable_debug()

Enable/disable copious diagnostic messages from Thread::Sociable internals. Only useful if Thread::Sociable has been compiled with the SOCIABLE_DEBUG preprocessor symbol defined.

#### sociable_enable_warnings()

#### sociable_disable_warnings()

Enable/disable generation of Perl warnings on attempts to reference a sociable that has been discarded.

#### ($extent, $himark) = sociable_default_extent()

#### ($extent, $himark) = sociable_default_extent($new_extent)

#### ($extent, $himark) = sociable_default_extent($new_extent, $new_himark)

Read or set the global default extent values. The first form simply reads the current values. The second form sets the default extent to the specified value, and sets the himark to `max(10, $new_extent * 1.1)`. The last form sets both the default extent and himark to the specified values. If `$new_himark` is less than `$new_extent`, the specified value is ignored, and the himark is set to `max(10, $new_extent * 1.1)`. Setting extent to zero turns off the extent optimization behavior. These default values are applied to sociable hashes and arrays when they are created; the values of individual variables may then be altered using the `sociable_extent()` method. The initial default values are (10, 20).

#### ($extent, $himark) = sociable_extent(@array)

#### ($extent, $himark) = sociable_extent(%hash)

#### ($extent, $himark) = sociable_extent(@array, $new_extent)

#### ($extent, $himark) = sociable_extent(%hash, $new_extent)

#### ($extent, $himark) = sociable_extent(@array, $new_extent, $new_himark)

#### ($extent, $himark) = sociable_extent(%hash, $new_extent, $new_himark)

Read or set the extent values on the specified array or hash. The first form simply reads the current values. The second form sets the extent to the specified value, and sets the himark to `max(10, $new_extent * 1.1)`. The last form sets both the extent and himark to the specified values. If `$new_himark` is less than `$new_extent`, the specified value is ignored, and the himark is set to `max(10, $new_extent * 1.1)`. If the specified hash or array is not sociable, the operation is silently ignored, unless the `sociable_enable_warnings()` is in effect, in which case a warning will be generated. Setting extent to zero turns off the extent optimization behavior.

#### sociable_begin_work(\&xaction_sub [, onCommit => \&commit_sub ] [, onRestart => \&restart_sub ] [, onRollback => \&rollback_sub ])

Execute `&xaction_sub` in a STM transaction. All sociable variable references executed within `&xaction_sub` will be performed as "dirty" reads, and logged writes, with a final reconcile and commit applied upon return from `&xaction_sub`. At commit time, if any read-first variable's sequence number has changed since its initial read, or any written variable cannot be acquired for write, the transaction will be restarted; otherwise, all logged write operations will be applied. If any fatal error occurs within `&xaction_sub`, the transaction will be rolled back and not restarted. Rollback can be initiated by any general Perl error, including by explicitly executing a `die` (or croak) statement.

During commit processing, if an `onCommit` closure is provided, it will be called after the reconcile step has completed without causing restart, but before any writes are committed. An `onCommit` closure may induce restart or rollback.

At any time during the transaction, if a restart is required, any provided `onRestart` closure will be called after any acquired variables are released, to permit the application to perform any private state cleanup. `onRestart` closures may induce rollback.

Likewise, if a rollback is initiated, any provided `onRollback` closure will be called after any acquired variables are released, with the associated `die` message passed as a parameter.

#### $oldlimit = sociable_stm_limit( [ $newlimit ] )

Adjust the maximum permitted number of concurrent transactions. Thread::Sociable maintains a pre-allocated pool of transaction context structures in order to avoid memory faults in the event of thread death or other logic errors. The number of contexts created in the pool is determined by this limit. If a new transaction is started, and no free transaction context is available, the transaction treats the condition as a restart with some random backoff period. This limit can be used to throttle the amount of contention. The default limit is 100. If the $newlimit argument is omitted, the current limit is returned; otherwise, contexts are added or removed to satisfy the $newlimit, and the $oldlimit value is returned. Note that, if $newlimit is less than the current number of inuse contexts, the in-use contexts will not be discarded until they are freed.

#### $oldtimeout = sociable_stm_timeout( [ $newtimeout ] )

Adjust the maximum lifetime of a transaction. In future, Thread::Sociable will provide a transaction lifetime monitor to kill transactions that have not completed within this timeout (in order to detect and remove hung threads). If the $newtimeout argument is omitted, the current timeout limit is returned; otherwise, the timeout is set to $newtimeout, and the previous timeout value is returned. The timeout values have seconds precision; the default value is 1 minute. A value of zero indicates no timeout (i.e., infinite transaction lifetime permitted).

#### lock(VARIABLE)

#### cond_wait(VARIABLE)

#### cond_wait(CONDVAR, LOCKVAR)

#### cond_timedwait(VARIABLE, ABS_TIMEOUT)

#### cond_timedwait(CONDVAR, ABS_TIMEOUT, LOCKVAR)

#### cond_signal(VARIABLE)

#### cond_broadcast(VARIABLE)

These methods are identical to their threads::shared counterparts, except

- locking an individual element of an array or hash (e.g., `lock($array[23]);`) is not currently supported.
- Only variables created with socialize_with_lock() or via the Lockable attribute can be lock()'d.

# OBJECTS

Thread::Sociable exports a version of bless() that works on shared objects such that blessings propagate across threads.

      # Create a shared 'foo' object
      my $foo;
      socialize($foo);
      $foo = &socialize({});
      bless($foo, 'foo');

      # Create a shared 'bar' object
      my $bar;
      socialize($bar);
      $bar = &socialize({});
      bless($bar, 'bar');

      # Put 'bar' inside 'foo'
      $foo->{'bar'} = $bar;

      # Rebless the objects via a thread
      threads->create(sub {
          # Rebless the outer object
          bless($foo, 'yin');

          # Cannot directly rebless the inner object
          #bless($foo->{'bar'}, 'yang');

          # Retrieve and rebless the inner object
          my $obj = $foo->{'bar'};
          bless($obj, 'yang');
          $foo->{'bar'} = $obj;

      })->join();

      print(ref($foo),          "\n");    # Prints 'yin'
      print(ref($foo->{'bar'}), "\n");    # Prints 'yang'
      print(ref($bar),          "\n");    # Also prints 'yang'

# NOTES

Thread::Sociable is designed to disable itself silently if threads are not available. If you want access to threads, you must `use threads` before you `use Thread::Sociable`.

# Using Sociably `tie()`'d Variables

A future release will permit multiple threads to sociably tie a single variable. In that scenario, when a thread writes to the variable, a "multicast" operation is performed, wherein the STORE operation to applied to all the tying threads.

Read operations are a bit more problematic, due to the need to merge the FETCHed values from multiple threads into a single return value. In order to address this need, Thread::Sociable uses the read context to determine how to return the result. If a tied variable is read in list context, a hash mapping thread ID's to returned values is returned; in scalar context, only the returned value of the last responding thread is returned.

Applications using sociably tied variables must be vigilant regarding locking schemes: if a thread initiating a STORE/FETCH operation holds a lock which must be acquired by any of the tying threads, deadlock will result, and the STORE/FETCH operation will hang.

As noted in the following section, operations on sociably tied variables are not restartable/abortable inside STM transactions. However, as for thread-private variable, it may be possible to provide some level of recoverability via the onRestart/onRollback closures.

# Using STM

Currently, STM is only supported on x86, x64, SPARC, and PowerPC CPU platforms (due to the availability of spinlock code). Support for other CPUs may be possible, assuming appropriate spinlock code is provided (patches welcome).

An STM transaction will be restarted if any of the following occur during the transaction:

- Another thread modifies a scalar previously read by the current thread (including an individual array or hash element)
- Another thread adds or deletes elements from a sociable array previously read by the current thread, regardless whether the current transaction accesses the added/removed elements, due to potential conflict with a prior `scalar @array` or `$#array` reference.
- Another thread adds or deletes an element from a sociable hash previously read by the current thread, regardless whether the current transaction accesses the added/removed elements, due to potential conflict with a prior `keys %hash` reference.
- The transaction explicitly `die`'s with the message "Thread::Sociable STM RESTART".
- The contention manager forces a restart to avoid livelocks See "STM Implementation Details" below.

Restart and rollback cannot rollback any external effects of, or changes to, thread-private variables or I/O operations. E.g., if an application writes to a file during a transaction which later restarts or rolls back, the file write operation is not rolled back. Applications can, however, provide logic to perform such rollbacks via the `onRestart` and `onRollback` closure arguments.

Threads spawned within a transaction do not inherit the transaction state.

Processes forked within a transaction will inherit any current transactions within the current threads; however, as the new process's address space is completely separate from parent process, the transactions of one process do not effect the transactions of another.

`onCommit` closures may induce either restart or rollback by executing an appropriate `die`. `onRestart` closures may induce rollback by executing a `die`. `onRollback` closures cannot induce a restart.

Attempting to open a new transaction in `onCommit`, `onRestart`, or `onRollback` closures will generate a fatal error. Any access to sociable variables within those closures will not be transactional, and will reference the sociable value, not any existing private transactional value.

Transactions may be nested; however,

- Commits of sequential inner transactions are not applied until the outermost transaction commits.
- Restarts or rollbacks of inner transactions will cascade to all transactions within the thread, including previously completed, but uncommitted, preceding and inner transactions.

As each pending transaction is committed, restarted, or rolled back, any associated `onCmmit`, `onRestart`, or `onRollback` closures will be invoked.

If a transaction's `onCommit` closure induces restart or rollback, or if a transaction's `onRestart` closure induces rollback, the `onRestart` or `onRollback` closures of all pending transactions will be executed, including

- prior completed, but uncommitted, transactions
- inner transactions
- transactions which have already executed their `onCommit` or `onRestart` closures

Note that certain pathological constructs should be avoided, e.g., consider

        sub lots_of_pending_xactions {
            for (1..1000) {
                sociable_begin_work(sub {
                    #
                    # do some stuff
                    #
                    },
                    onRestart => sub {
                    #
                    #    do some restart stuff
                    #
                    },
                    onRollback => sub {
                    #
                    #    do some rollback stuff
                    #
                    }
                );
            }
        }

        sociable_begin_work(sub {
            lots_of_pending_xactions()
            #
            #    do some stuff
            #
            }
        );

This code would cause up to 1000 inner transactions to be completed, but not committed. More importantly, each iteration would add an additional onRestart and onRollback closure to the transaction log; if a restart or rollback occurs, each of the registered closure instances will be executed.

In such cases, applications should use a more conservative strategy if possible, e.g.

        sub lots_of_pending_xactions {
            my $already_registered = 0;
            for (1..1000) {
                sociable_begin_work(sub {
                    #
                    # do some stuff
                    #
                    },
                    onRestart => $already_registered ?
                        sub {
                        #
                        #    do some restart stuff
                        #
                        } : undef,
                    onRollback =>  $already_registered ?
                        sub {
                        #
                        #    do some rollback stuff
                        #
                        } : undef
                );
                $already_registered = 1;
            }
        }

This modification results in only adding a single instance of the closures.

Certain sociable objects may be safely used with the classic locking/critical section technique while inside an STM transaction (notably, TSDQ's used for TSA interfaces). In general, however, mingling classic locking with STM is probably a very bad idea.

Operations on sociably `tie()`'d variables are not logged within a transaction, and therefore cannot be restarted/rolled back.

Transactional access to arrays or hashes is problematic: some applications may only access a few elements of a given array or hash, while others may iterate over the entire array or hash. The former scenario is reasonably well tolerated within an STM, with a restart probability comparable to accessing simple scalars. The latter scenario, however, can lead to restart starvation for large arrays or hashes which are subject to frequent updates, with significant heap thrashing and spinlocking congestion. STM users are advised to keep these issues in mind, and seek alternative designs where possible. Performing large splices on arrays, especially with a replacement list, is a particularly "heavyweight" operation within a transaction, and should be avoided if possible.

Currently, creation of sociable variables within a transaction is not transactional, i.e., if the transaction restarts or rolls back, the sociable variable will not be discarded until an explicit `sociable_discard()` call.

Creating or accessing references to sociable variables is not considered transactional access; an access is only logged on a read or write of a sociable variable's contents.

Passing a sociable reference to, or receiving a sociable reference from, another thread (e.g., via a TSDQ operation) does not convey any transactional context to the recipient/sender. Subsequent accesses to the sociable variable within the transaction will be logged as usual, but no notification is provided to the sender/receiver upon commit/restart/rollback, nor is any "two phase commit" protocol provided to coordinate such activities of communicationg threads.

Two different conflict detection protocols are supported:

Eager

  Eager detection attempts to detect conflicts on each access to sociable variables during the transaction, and will initiate restart as early as possible. This protocol may be preferable in highly contentions environments by avoiding additional STM operations which would later be discarded due to restart at commit.

Lazy

  Lazy detection ignores conflicts until commit. In so doing, the overhead of variable access during the transaction is reduced, which may be preferable in minimal contention environments with short transactions.

Likewise, two commit protocols are supported:

Locked

  Locked commit first acquires a global mutex (not a spinlock) before acquiring the spinlocks of all accessed sociable variables. Once the spinlocks are acquired (or a conflict is detected), the mutex is released, and commit (or rollback) can proceed. The mutex is required to avoid deadlock between concurrently committing transactions which may attempt to spinlock the same variables, but in different orders. By requiring the mutex, only one transaction at a time can ever hold more than 1 spinlock at a time. However, the mutex may impede progress of non-overlapping transactions.

Lockfree

  Prior to acquiring spinlocks, Lockfree commit first applies a sort to the addresses of the accessed sociable variables. By acquiring spinlocks in a sorted order, competing commits are guaranteed to avoid deadlock. For very large transactions, the sort overhead may be significant; however, other non-overlapping transactions are not impeded (as for the Locked commit).

The detection protocol is set using the `sociable_stm_eager()` and `sociable_stm_lazy()` methods; `sociable_stm_locked()` and `sociable_stm_lockfree()` are used to select the commit protocol. Note that the conflict and commit protocol settings are global, i.e., changing the protocol in any thread changes it for all threads (as the complementary protocols are not compatible).

The current protocol settings can be determined by calling `sociable_stm_protocol()`, which return a hashref with keys Commits (set to either "locked" or "lockfree"), and Conflicts (set to either "eager" or "lazy").

The advantages of any protocol combination are not as yet empirically known. Locked commit may help avoid restarts for long duration transactions by avoiding competition for spinlocks at commit time; but highly concurrent environments using small non-overlapping transactions may benefit from the ability to concurrently commit.

A lazy copy algorithm is used to acquire array or hash contents. Individual elements of a structure are only copied from the sociable version when they are first referenced, thereby avoiding significant copy overhead for transactions which only reference a few elements. However, this approach may be detrimental for transactions which iterate over an entire array or hash (due to possibly increased the probability of restarts). A future release may provide selectable lazy or eager copy support.

# STM Implementation Details

Thread::Sociable currently uses invisible reads, with selectable eager or lazy conflict detection, and selectable locked or lockfree commits (default is eager, lockfree). Eager conflict detection is implemented using a modified "Polka" Contention Manager dubbed "Sociable Contention Manager":

- Each thread has a "karma" counter in its transaction context, which is incremented for each first access to a sociable variable within a transaction, and cleared on commit or rollback, but retained across a restart.
- Each sociable variable has both a sequence number, and an "owner" pointer field, which points to the transaction context of the current owner of the variable.
- On first access to a sociable variable, the accessing thread attempts to acquire ownership. If the variable is not owned, or the current owner's karma is less than the current thread's karma, then the variable is acquired by the current thread. If the variable is acquired or the current operation is a read, the transaction proceeds; otherwise, a restart is initiated. Note that read operations do not require ownership, but will attempt ownership in order to avoid future restarts induced by a shorter duration writer. Should the thread fail to acquire the variable on a read operation, it will once again attempt to acquire ownership on any subsequent write operation, and induce restart if the variable cannot be acquired at that time.
  Additionally, the current sequence number of the variable is recorded.
- Sociable references require an additional mapping within the transaction. Since different sociable variables may reference the same sociable variable, the thread-private transactional versions of the reference variables must be mapped to the same private version of the referent (in order for any updates through one reference to be reflected in any read through a different reference). Therefore, a hash is used to map the referent sociable's address to thread private copy.
  Consider

      	my $a : Sociable;
      	my $b : Sociable;
      	my @ary : Sociable;
      	$a = \@ary;
      	$b = \@ary;

      	sociable_begin_work(
      		sub {
      			$a->[1] = 20;
      			$b->[2] = 30;
      			if ($a->[2] == 30) {
      				print "proper update";
      			}
      			else {
      				print "improper update";
      			}
      		}
      	);

  On the first reference to $a, a private copy of its contents - a reference to `@ary` - is made. The dereference of the private copy will then create a (shallow) private copy of @ary. Then the reference to $b creates a private copy of its contents - again, a reference to `@ary` -, and the dereference would cause another private copy of @ary. With 2 different copies of @ary, the conditional would indicate "improper copy". Keeping an additional map of the private copy of the referent, keyed by the sociable version's address, assures that the updates are applied to the same private version.
- In order to commit, a thread must have ownership of any variables which it has written, and, for variables which were only read, the sequence number must not have changed since the first access (i.e., has not yet been written by another transaction). If those conditions hold at commit, the sequence numbers of variables which are written are incremented, the writes are copied to the sociable versions, and ownership is released. If those conditions are violated, a restart is initiated.
- Eager conflict detection is implemented for readonly variables by testing the sequence number of each accessed variable (or structure element) each time it is read (though only the thread-private value of the variable - created on first access - is used). If the sequence number has changed since first access, the transaction is restarted immediately. Note that variables that are written before any read access are not subject to eager restart. For variables which have been written, the ownership of the variable is tested and a restart initiated if the current thread is no longer the owner.
  For highly concurrent environments with medium to high probability of transaction restarts, eager detection may be preferable to lazy detection, as the eager method's overhead is not significant relative to the normal overhead of Perl and Thread::Sociable variable manipulation. The eager method may improve performance by avoiding additional read/write operations that would later be discarded under the lazy method.
- Lazy conflict detection simply skips the sequence number and ownership checks until the commit phase. Lazy detection may perform better in environments with low concurrency and low restart probability.
- Commits are applied using the selected protocol (see above). If a locked protocol is selected, a global mutex is acquired before acquiring the spinlocks of each referenced variable, after which the mutex is released. If the lockfree protocol is selected, then the entries in the WAL are sorted in ascending order before spinlocks are acquired. The sequence number and ownership tests described above occur while the spinlocks are held; the spinlocks are released when all variables have been committed, or a restart is initiated.
- On a transaction restart, a thread `yield()` is executed to provide a pseudo-random restart backoff delay, which various studies indicate improve performance/reduce contention. While such studies usually rely on exponential backoff spinlocks, yield() should provide a similar delay mechanism that automatically adapts to the processing load of the system.

The Sociable contention manager helps avoid livelock* with minimal access overhead. Sociable CM approximates the "Polka" Contention Manager described in http://www.cs.rochester.edu/u/scott/papers/2005_PODC_CM.pdf, which indicates that Polka provides near optimal contention management across various types of contention.

The Sociable Contention Manager differs from the Polka implementation in that

- Polka uses a more complex conflict resolution algorithm: if the number of attempts by the current thread to acquire a variable exceeds the difference between the current thread's karma and the current owner's karma, then the current thread will acquire the variable. Sociable CM simply compares the karma values, with ownership granted if the current thread's karma is greater than the current owner's. The current thread increments its karma regardless whether the acquisition succeeds, so the accumulation effect is similar.
- Polka uses exponential backoff spinlocks to delay transaction restarts, whereas Sociable relies on `threads-`yield()> to inject a pseudorandom delay, which frees CPU resources for other threads to execute.

* Livelock occurs when some threads' (usually long-duration) transactions are repeatedly restarted due to conflicts created by other thread's (usually short duration) transactions modifying variables referenced by the livelocked transaction.

# BUGS AND LIMITATIONS

When `socialize` is used on arrays, hashes, array refs or hash refs, any data they contain will be lost.

      my @arr = qw(foo bar baz);
      socialize(@arr);
      # @arr is now empty (i.e., == ());

      # Create a 'foo' object
      my $foo = { 'data' => 99 };
      bless($foo, 'foo');

      # Share the object
      socialize($foo);        # Contents are now wiped out
      print("ERROR: \$foo is empty\n")
          if (! exists($foo->{'data'}));

Therefore, populate such variables after declaring them as sociable. (Scalar and scalar refs are not affected by this problem.)

Care must be exersized when socializing an object. Unless the class itself has been written to support socialization, an object's destructor may get called multiple times, once for each thread's scope exit. Additionally, the contents of hash or array based objects will be lost (due to the above limitation) if the object is socialized after its members have been populated.

Due to significant performance impact, taking refs of individual array or hash elements (e.g., $x = \$ary[34]; $y = \$hash{SomeKey};) is not fully supported. While the reference may be returned, it will not be a reference to a sociable value, and any future dereference through such references will not apply to the sociable values, but only to the thread local copy of the data. A future release will support a Refable attribute and associated sociable_refable() method to declare arrays and hashes that will return shared references for elements, with an associated performance impact.

slice() of a sociable array or hash does not produce a sociable variable. A future Refable support may add support for sociable slices.

Taking references to the elements of sociable arrays and hashes does not autovivify the elements, and neither does slicing a sociable array/hash over non-existent indices/keys autovivify the elements.

`socialize()` allows you to `socialize($hashref->{key})` without giving any error message. But the `$hashref->{key}` is not sociable, causing the error "locking can only be used on sociable values" to occur when you attempt to `lock($hasref->{key})`.

Mixing `lock()` and STM is probably a very, very, very bad idea.

View existing bug reports at, and submit any new bugs, problems, patches, etc. to: http://rt.cpan.org/NoAuth/Bugs.html?Dist=Thread-Sociable

# Benchmarks

A set of simple benchmark test scripts (included in the ./bench directory) were executed against both this version of Thread::Sociable and threads::shared 1.03. The following table provides the results for various operations, using the specified number of threads on the specified platforms:

### Single 2.4 GHz, WinXP, AS Perl 5.8.6

### Single 100MHz SPARC, Solaris 10, Perl 5.8.6

### Single 1.5GHz PPC, OS X 10.4.9, Perl 5.8.6

### Dual 3GHz Xeon, Win2000 Server, AS Perl 5.8.8

### Dual 3GHz Xeon, Linux, Perl 5.8.8

### Dual 3GHz Xeon, Solaris 10, Perl 5.8.8

### Quad 700MHz Xeon, Win2000 Server, AS Perl 5.8.8

# TO DO

### IPC Support

Thread::Sociable could be extended to provide an interprocess shared variable solution by replacing the CRT heap with a shared memory heap manager.

# SEE ALSO

Thread::Sociable::DuplexQueue

Thread::Sociable::Apartment

threads::shared

threads

perlthrtut

Some spinlock code was borrowed from Parrot, which also provides an STM implementation.

Perl threads mailing list: http://lists.cpan.org/showlist.cgi?name=iThreads

For in-depth information regarding, STM, I recommend the University of Rochester Synchronization Group's Publication list at http://www.cs.rochester.edu/research/synchronization/pubs.shtml.

# AUTHOR

Dean Arnold mailto:darnold@presicient.com

Based on threads::shared by

Artur Bergman <sky AT crucially DOT net>

and its CPAN version produced by Jerry D. Hedden <jdhedden AT cpan DOT org>.

Permission is granted to use this software under the same terms as Perl itself. Refer to the Perl Artistic License for details.