Prev Up Next

# 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.)

The functions in the module are

> `RawArray(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 setting and getting an element is potentially non-atomic -- use Array() instead to make sure that access is automatically synchronized using a lock.

> `RawValue(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. `*args` is passed on to the constructor for the type.

>   Note that setting and getting the value is potentially non-atomic -- use Value() instead to make sure that access is automatically synchronized using a lock.

>   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`.

> `Array(typecode_or_type, size_or_initializer, **, lock=True)`

>   The same as `RawArray()` except that depending on the value of `lock` a process-safe synchronization wrapper may be returned instead of a raw ctypes array.

>   If `lock` is true (the default) then a new lock object is created to synchronize access to the value. If `lock` is a `Lock` or `RLock` object then that will be used to synchronize access to the value. If `lock` is false then access to the returned object will not be automatically protected by a lock, so it will not necessarily be "process-safe".

>   Note that `lock` is a keyword only argument.

> `Value(typecode_or_type, *args, **, lock=True)`

>   The same as `RawValue()` except that depending on the value of `lock` a process-safe synchronization wrapper may be returned instead of a raw ctypes object.

>   If `lock` is true (the default) then a new lock object is created to synchronize access to the value. If `lock` is a `Lock` or `RLock` object then that will be used to synchronize access to the value. If `lock` is false then access to the returned object will not be automatically protected by a lock, so it will not necessarily be "process-safe".

>   Note that `lock` is a keyword only argument.

> `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.

>   Note that accessing the ctypes object through the wrapper can be a lot slower than accessing the raw ctypes object.

# Equivalences

The table below compares the syntax for creating shared ctypes objects 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) | RawValue(c_double, 2.4) | RawValue('d', 2.4)
MyStruct(4, 6) | RawValue(MyStruct, 4, 6) |
(c_short * 7)() | RawArray(c_short, 7) | RawArray('h', 7)
(c_int * 3)(9, 2, 8) | RawArray(c_int, (9, 2, 8)) | RawArray('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, Lock
    from processing.sharedctypes import Value, Array
    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__':
        lock = Lock()

        n = Value('i', 7)
        x = Value(ctypes.c_double, 1.0/3.0, lock=False)
        s = Array('c', 'hello world', lock=lock)
        A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)

        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)]

Avoid sharing pointers

Although it is posible to store a pointer in shared memory remember that this will refer to a location in the address space of a specific 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.