summaryrefslogtreecommitdiff
path: root/_shm/shm_wrapper.py
blob: bf29f65f9f2b0513800020f7c5722b37f238f6af (plain)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# shm_wrapper - A wrapper for the shm module which provides access
# to System V shared memory and semaphores on *nix systems.
#
# Copyright (c) 2007 by Philip Semanchuk
# Contact info at http://NikitaTheSpider.com/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# Python modules
import random
import sys

# Third party modules
import shm

r"""shm_wrapper - A wrapper for the shm module which provides access
to System V shared memory and semaphores on *nix systems.

The module shm is a Python wrapper around system functions like shmget. This
module in turn offers higher-level, more Pythonic access to shared memory and
semaphores.

Full documentation is online at http://NikitaTheSpider.com/python/shm/

"""

def create_memory(size, permissions = 0666, InitCharacter = ' '):
    """ Creates a new shared memory segment. One can destroy it either by calling the
    module-level method remove_memory() or by calling the .remove() method of a handle to
    said memory.
    """
    memory = None

    # I create the memory using a randomly-generated key. I keep trying until I find one
    # that works or until I hit an error.
    while not memory:
        key = random.randint(1, sys.maxint - 1)
        try:
            memory = shm.create_memory(key, size, permissions)
        except shm.error, ExtraData:
            if shm.memory_haskey(key):
                # Oops, bad luck, the key exists. I'll try another. I can't call
                # memory_haskey() before calling create_memory() because that would create
                # a race condition where I could verify a key is not used but then another
                # process could call create_memory() with that key before I got a chance to
                # do so.
                pass
            else:
                # Uh-oh, something fundamental is wrong.
                raise shm.error, ExtraData

    # Here I implicitly discard the memory handle object returned to me by shm and instead
    # return my own handle to the shared memory segment.
    memory = SharedMemoryHandle(key)

    memory.write(InitCharacter[0] * memory.size)

    return memory


def remove_memory(key):
    # Destroys the shared memory segment. Raises KeyError if the key doesn't exist.
    shm.remove_memory(shm.getshmid(key))


class SharedMemoryHandle(object):
    def __init__(self, key):
        self._MemoryHandle = None

        # getshmid will raise a KeyError if there's no memory segment with this key.
        shmid = shm.getshmid(key)
        self._MemoryHandle = shm.memory(shmid)


    def __del__(self):
        if self._MemoryHandle:
            # This will raise an error if the memory has been destroyed.
            try:
                if self._MemoryHandle.attached:
                    self._MemoryHandle.detach()
            except shm.error:
                pass


    def remove(self):
        if self._MemoryHandle:
            if self._MemoryHandle.attached:
                self._MemoryHandle.detach()

            shm.remove_memory(self._MemoryHandle.shmid)
            self._MemoryHandle = None


    def read(self, NumberOfBytes = 0, offset = 0):
        if not self._MemoryHandle.attached:
            self._MemoryHandle.attach()

        if not NumberOfBytes:
            NumberOfBytes = self._MemoryHandle.size - offset

        return self._MemoryHandle.read(NumberOfBytes, offset)


    def write(self, s, offset = 0):
        if not self._MemoryHandle.attached:
            self._MemoryHandle.attach()

        self._MemoryHandle.write(s, offset)


    # Properties start here ================================================================

    # key
    def __get_key(self): return self._MemoryHandle.key
    def __set_key(self, foo): raise AttributeError
    key = property(__get_key, __set_key)

    # size of segment
    def __get_size(self): return self._MemoryHandle.size
    def __set_size(self, foo): raise AttributeError
    size = property(__get_size, __set_size)

    # permissions
    def __get_permissions(self): return self._MemoryHandle.perm
    def __set_permissions(self, permissions): self._MemoryHandle.setperm(permissions)
    permissions = property(__get_permissions, __set_permissions)

    # The number of processes currently attached to this memory segment.
    def __get_number_attached(self): return self._MemoryHandle.nattch
    def __set_number_attached(self, foo): raise AttributeError
    number_attached = property(__get_number_attached, __set_number_attached)

    # segment's uid
    def __get_uid(self): return self._MemoryHandle.uid
    def __set_uid(self, uid): self._MemoryHandle.setuid(uid)
    uid = property(__get_uid, __set_uid)

    # segment's gid
    def __get_gid(self): return self._MemoryHandle.gid
    def __set_gid(self, gid): self._MemoryHandle.setgid(gid)
    gid = property(__get_gid, __set_gid)

    # Creator uid (read-only)
    def __get_creator_uid(self): return self._MemoryHandle.cuid
    def __set_creator_uid(self, foo): raise AttributeError
    creator_uid = property(__get_creator_uid, __set_creator_uid)

    # Creator gid (read-only)
    def __get_creator_gid(self): return self._MemoryHandle.cgid
    def __set_creator_gid(self, foo): raise AttributeError
    creator_gid = property(__get_creator_gid, __set_creator_gid)

    # Creator pid (read-only)
    def __get_creator_pid(self): return self._MemoryHandle.cpid
    def __set_creator_pid(self, foo): raise AttributeError
    creator_pid = property(__get_creator_pid, __set_creator_pid)

    # pid of last process to operate on this segment (read-only)
    def __get_last_pid(self): return self._MemoryHandle.lpid
    def __set_last_pid(self, foo): raise AttributeError
    last_pid = property(__get_last_pid, __set_last_pid)



def create_semaphore(InitialValue = 1, permissions = 0666):
    """ Creates a new semaphore. One can destroy it either by calling the
    module-level method remove_semaphore() or by calling the .remove() method of a
    handle to said semaphore.
    """
    semaphore = None

    # I create the semaphore using a randomly-generated key. I keep trying until I find one
    # that works or until I hit an error.
    while not semaphore:
        key = random.randint(1, sys.maxint - 1)
        try:
            semaphore = shm.create_semaphore(key, InitialValue, permissions)
        except shm.error, ExtraData:
            if shm.semaphore_haskey(key):
                # Oops, bad luck, the key exists. I'll try another. I can't call
                # memory_haskey() before calling create_semaphore() because that would create
                # a race condition where I could verify a key is not used but then another
                # process could call create_semaphore() with that key before I got a chance to
                # do so.
                pass
            else:
                # Uh-oh, something fundamental is wrong.
                raise ExtraData

    # Here I implicitly discard the semaphore object returned to me by shm and instead
    # return my own handle to the semaphore.
    return SemaphoreHandle(key)


def remove_semaphore(key):
    # Destroys the semaphore. Raises KeyError if the key doesn't exist.
    shm.remove_semaphore(shm.getsemid(key))


class SemaphoreHandle(object):
    def __init__(self, key):
        # getsemid will raise a KeyError if appropriate.
        self._SemaphoreHandle = shm.semaphore(shm.getsemid(key))


    def remove(self):
        shm.remove_semaphore(self._SemaphoreHandle.semid)
        self._SemaphoreHandle = None


    def P(self):
        # P = prolaag = probeer te verlagen (try to decrease)
        self._SemaphoreHandle.P()


    def V(self):
        # V = verhoog (increase)
        self._SemaphoreHandle.V()


    def Z(self):
        # Z = block until Zee semaphore is Zero
        self._SemaphoreHandle.Z()


    # Properties start here ================================================================
    def __get_key(self): return self._SemaphoreHandle.key
    def __set_key(self, foo): raise AttributeError
    key = property(__get_key, __set_key)


    def __get_value(self): return self._SemaphoreHandle.val
    def __set_value(self, value): self._semaphore.setval(value)
    value = property(__get_value, __set_value)


    def __get_WaitingForZero(self): return self._SemaphoreHandle.zcnt
    def __set_WaitingForZero(self, foo): raise AttributeError
    WaitingForZero = property(__get_WaitingForZero, __set_WaitingForZero)


    def __get_WaitingForNonZero(self): return self._SemaphoreHandle.ncnt
    def __set_WaitingForNonZero(self, foo): raise AttributeError
    WaitingForNonZero = property(__get_WaitingForNonZero, __set_WaitingForNonZero)


    def __get_blocking(self): return self._SemaphoreHandle.blocking
    def __set_blocking(self, block): self._SemaphoreHandle.setblocking(block)
    blocking = property(__get_blocking, __set_blocking)


    def __get_undo(self): raise AttributeError
    def __set_undo(self, undo): self._SemaphoreHandle.setundo(undo)
    undo = property(__get_undo, __set_undo)


    # segment's uid
    def __get_uid(self): return self._SemaphoreHandle.uid
    def __set_uid(self, uid): self._SemaphoreHandle.setuid(uid)
    uid = property(__get_uid, __set_uid)


    # segment's gid
    def __get_gid(self): return self._SemaphoreHandle.gid
    def __set_gid(self, gid): self._SemaphoreHandle.setgid(gid)
    gid = property(__get_gid, __set_gid)


    # Creator uid (read-only)
    def __get_creator_uid(self): return self._SemaphoreHandle.cuid
    def __set_creator_uid(self, foo): raise AttributeError
    creator_uid = property(__get_creator_uid, __set_creator_uid)


    # Creator gid (read-only)
    def __get_creator_gid(self): return self._SemaphoreHandle.cgid
    def __set_creator_gid(self, foo): raise AttributeError
    creator_gid = property(__get_creator_gid, __set_creator_gid)


    # Creator pid -- since semaphores have a lot of the same properties as memory
    # objects, one would expect creator PID to be exposed here, but it isn't
    # made available by the system (true AFAICT for BSDs, OS X and Solaris).


    # pid of last process to operate on this segment (read-only)
    def __get_last_pid(self): return self._SemaphoreHandle.lpid
    def __set_last_pid(self, foo): raise AttributeError
    last_pid = property(__get_last_pid, __set_last_pid)