1# Python modules
2import time
3import hashlib
4
5# 3rd party modules
6import sysv_ipc
7
8# Utils for this demo
9import utils
10
11utils.say("Oooo 'ello, I'm Mrs. Premise!")
12
13params = utils.read_params()
14
15# Create the semaphore & shared memory. I read somewhere that semaphores
16# and shared memory have separate key spaces, so one can safely use the
17# same key for each. This seems to be true in my experience.
18
19# For purposes of simplicity, this demo code makes no allowance for the
20# failure of the semaphore or memory constructors. This is unrealistic
21# because one can never predict whether or not a given key will be available,
22# so your code must *always* be prepared for these functions to fail.
23
24semaphore = sysv_ipc.Semaphore(params["KEY"], sysv_ipc.IPC_CREX)
25memory = sysv_ipc.SharedMemory(params["KEY"], sysv_ipc.IPC_CREX)
26
27# I seed the shared memory with a random value which is the current time.
28what_i_wrote = time.asctime()
29s = what_i_wrote
30
31utils.write_to_memory(memory, what_i_wrote)
32
33for i in range(0, params["ITERATIONS"]):
34    utils.say("iteration %d" % i)
35    if not params["LIVE_DANGEROUSLY"]:
36        # Releasing the semaphore...
37        utils.say("releasing the semaphore")
38        semaphore.release()
39        # ...and wait for it to become available again. In real code it'd be
40        # wise to sleep briefly before calling .acquire() in order to be
41        # polite and give other processes an opportunity to grab the semaphore
42        # while it is free and thereby avoid starvation. But this code is meant
43        # to be a stress test that maximizes the opportunity for shared memory
44        # corruption, and politeness has no place in that.
45        utils.say("acquiring the semaphore...")
46        semaphore.acquire()
47
48    s = utils.read_from_memory(memory)
49
50    # I keep checking the shared memory until something new has been written.
51    while s == what_i_wrote:
52        if not params["LIVE_DANGEROUSLY"]:
53            utils.say("releasing the semaphore")
54            semaphore.release()
55            utils.say("acquiring the semaphore...")
56            semaphore.acquire()
57
58        # Once the call to .acquire() completes, I own the shared resource and
59        # I'm free to read from the memory.
60        s = utils.read_from_memory(memory)
61
62    # What I read must be the md5 of what I wrote or something's gone wrong.
63    what_i_wrote = what_i_wrote.encode()
64
65    try:
66        assert(s == hashlib.md5(what_i_wrote).hexdigest())
67    except AssertionError:
68        raise AssertionError("Shared memory corruption after %d iterations." % i)
69
70    # MD5 the reply and write back to Mrs. Conclusion.
71    s = s.encode()
72    what_i_wrote = hashlib.md5(s).hexdigest()
73    utils.write_to_memory(memory, what_i_wrote)
74
75
76# Announce for one last time that the semaphore is free again so that
77# Mrs. Conclusion can exit.
78if not params["LIVE_DANGEROUSLY"]:
79    utils.say("Final release of the semaphore followed by a 5 second pause")
80    semaphore.release()
81    time.sleep(5)
82    # ...before beginning to wait until it is free again.
83    utils.say("Final acquisition of the semaphore")
84    semaphore.acquire()
85
86utils.say("Destroying semaphore and shared memory")
87# It'd be more natural to call memory.remove() and semaphore.remove() here,
88# but I'll use the module-level functions instead to demonstrate their use.
89sysv_ipc.remove_shared_memory(memory.id)
90sysv_ipc.remove_semaphore(semaphore.id)
91