File: sharedctypes.txt

package info (click to toggle)
multiprocess 0.70.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,900 kB
  • sloc: python: 70,095; ansic: 7,509; makefile: 16
file content (166 lines) | stat: -rw-r--r-- 6,343 bytes parent folder | download | duplicates (38)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
.. 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.)  

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


.. admonition:: 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.

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