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

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

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.