.. include:: header.txt

========================
 Shared ctypes objects
========================

The `processing.sharedctypes` module provides functions for allocating
ctypes objects from shared memory which can be inherited by child
processes.  (See the standard library's documentation for details of
the `ctypes` package.)  

Note that access to a ctypes objects is not protected by any lock, so
it possible for more than one process to try modifying the value of a
shared ctypes object at the same time.  The `synchronized()` function
can be used to create a syncrhonized wrapper for the object which does
not suffer from this problem, but access through the wrapper can be a
lot slower.

In simple cases one can use `processing.Value()` or
`processing.Array()` instead of using the functions in this module.

The functions in the module are

    `Value(typecode_or_type, *args)`
        Returns a ctypes object allocated from shared memory.

        `typecode_or_type` determines the type of the returned object:
        it is either a ctypes type or a one character typecode of the
        kind used by the `array` module.  The remaining arguments are
        passed on to the constructor for the type.

    `Array(typecode_or_type, size_or_initializer)`
        Returns a ctypes array allocated from shared memory.

        `typecode_or_type` determines the type of the elements of the
        returned array: it is either a ctypes type or a one character
        typecode of the kind used by the `array` module.  If
        `size_or_initializer` is an integer then it determines the
        length of the array, and the array will be initially zeroed.
        Otherwise `size_or_initializer` is a sequence which is used to
        initialize the array and whose length determines the length of
        the array.

        Note that an array of `ctypes.c_char` has `value` and
        `rawvalue` attributes which allow one to use it to store and
        retrieve strings -- see documentation for `ctypes`.

    `copy(obj)`
        Returns a ctypes object allocated from shared memory which is
        a copy of the ctypes object `obj`.

    `synchronized(obj, lock=None)`
        Returns a process-safe wrapper object for a ctypes object
        which uses `lock` to synchronize access.  If `lock` is `None`
        then a `processing.RLock` object is created automatically.

        A synchronized wrapper will have two methods in addition to
        those of the object it wraps: `getobj()` returns the wrapped
        object and `getlock()` returns the lock object used for
        synchronization.


Equivalences
============

The table below compares the syntax for creating a shared ctypes
object from shared memory with the normal ctypes syntax.  (In the
table `MyStruct` is some subclass of `ctypes.Structure`.)

==================== ======================== ============================
ctypes               sharedctypes using type  sharedctypes using typecode
==================== ======================== ============================
c_double(2.4)        Value(c_double, 2.4)     Value('d', 2.4)
MyStruct(4, 6)       Value(MyStruct, 4, 6)
(c_short * 7)()      Array(c_short, 7)        Array('h', 7)
(c_int * 3)(9, 2, 8) Array(c_int, (9, 2, 8))  Array('i', (9, 2, 8))
==================== ======================== ============================


Example
=======

Below is an example where a number of ctypes objects are modified by a
child process ::

    from processing import Process
    from processing.sharedctypes import Value, Array, synchronized
    from ctypes import Structure, c_double
    
    class Point(Structure):
        _fields_ = [('x', c_double), ('y', c_double)]

    def modify(n, x, s, A):
        n.value **= 2
        x.value **= 2
        s.value = s.value.upper()
        for p in A:
            p.x **= 2
            p.y **= 2
    
    if __name__ == '__main__':
        n = Value('i', 7)
        x = Value(ctypes.c_double, 1.0/3.0)
        s = Array('c', 'hello world')
        A = Array(Point, [(1.875, -6.25), (-5.75, 2.0), (2.375, 9.5)])

        x = synchronized(x)     # replace x by a synchronized wrapper

        p = Process(target=modify, args=(n, x, s, A))
        p.start()
        p.join()

        print n.value
        print x.value
        print s.value
        print [(p.x, p.y) for p in A]

The results printed are ::

    49
    0.1111111111111111
    HELLO WORLD
    [(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]


.. admonition:: Avoid sharing pointers

    Although it is entirely posible to store a pointer in shared
    memory remember that this will refer to a location in the address
    space of the *current* process.  However, the pointer is quite
    likely to be invalid in the context of a second process and trying
    to dereference the pointer from the second process may cause a
    crash.

.. _Prev: pool-objects.html
.. _Up: processing-ref.html
.. _Next: connection-ref.html
