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.