summaryrefslogtreecommitdiff
path: root/demo/premise.py
diff options
context:
space:
mode:
Diffstat (limited to 'demo/premise.py')
-rw-r--r--demo/premise.py118
1 files changed, 118 insertions, 0 deletions
diff --git a/demo/premise.py b/demo/premise.py
new file mode 100644
index 0000000..55711e0
--- /dev/null
+++ b/demo/premise.py
@@ -0,0 +1,118 @@
+# Python modules
+import time
+import mmap
+import os
+import sys
+PY_MAJOR_VERSION = sys.version_info[0]
+# hashlib is only available in Python >= 2.5. I still want to support
+# older Pythons so I import md5 if hashlib is not available. Fortunately
+# md5 can masquerade as hashlib for my purposes.
+try:
+ import hashlib
+except ImportError:
+ import md5 as hashlib
+
+# 3rd party modules
+import posix_ipc
+
+# Utils for this demo
+import utils
+
+
+utils.say("Oooo 'ello, I'm Mrs. Premise!")
+
+params = utils.read_params()
+
+# Create the shared memory and the semaphore.
+memory = posix_ipc.SharedMemory(params["SHARED_MEMORY_NAME"], posix_ipc.O_CREX,
+ size=params["SHM_SIZE"])
+semaphore = posix_ipc.Semaphore(params["SEMAPHORE_NAME"], posix_ipc.O_CREX)
+
+# MMap the shared memory
+mapfile = mmap.mmap(memory.fd, memory.size)
+
+# Once I've mmapped the file descriptor, I can close it without
+# interfering with the mmap.
+memory.close_fd()
+
+# I seed the shared memory with a random string (the current time).
+what_i_wrote = time.asctime()
+utils.write_to_memory(mapfile, what_i_wrote)
+
+for i in range(params["ITERATIONS"]):
+ utils.say("iteration %d" % i)
+ if not params["LIVE_DANGEROUSLY"]:
+ # Release the semaphore...
+ utils.say("Releasing the semaphore")
+ semaphore.release()
+ # ...and wait for it to become available again. In real code
+ # I might want to sleep briefly before calling .acquire() in
+ # order to politely give other processes an opportunity to grab
+ # the semaphore while it is free so as to avoid starvation. But
+ # this code is meant to be a stress test that maximizes the
+ # opportunity for shared memory corruption and politeness is
+ # not helpful in stress tests.
+ utils.say("Waiting to acquire the semaphore")
+ semaphore.acquire()
+
+ s = utils.read_from_memory(mapfile)
+
+ # I keep checking the shared memory until something new has
+ # been written.
+ while s == what_i_wrote:
+ # Nothing new; give Mrs. Conclusion another chance to respond.
+ if not params["LIVE_DANGEROUSLY"]:
+ utils.say("Releasing the semaphore")
+ semaphore.release()
+ utils.say("Waiting to acquire the semaphore")
+ semaphore.acquire()
+
+ s = utils.read_from_memory(mapfile)
+
+ # What I read must be the md5 of what I wrote or something's
+ # gone wrong.
+ if PY_MAJOR_VERSION > 2:
+ what_i_wrote = what_i_wrote.encode()
+
+ try:
+ assert(s == hashlib.md5(what_i_wrote).hexdigest())
+ except AssertionError:
+ utils.raise_error(AssertionError,
+ "Shared memory corruption after %d iterations." % i)
+
+ # MD5 the reply and write back to Mrs. Conclusion.
+ if PY_MAJOR_VERSION > 2:
+ s = s.encode()
+ what_i_wrote = hashlib.md5(s).hexdigest()
+ utils.write_to_memory(mapfile, what_i_wrote)
+
+utils.say("")
+utils.say("%d iterations complete" % (i + 1))
+
+# Announce for one last time that the semaphore is free again so that
+# Mrs. Conclusion can exit.
+if not params["LIVE_DANGEROUSLY"]:
+ utils.say("")
+ utils.say("Final release of the semaphore followed by a 5 second pause")
+ semaphore.release()
+ time.sleep(5)
+ # ...before beginning to wait until it is free again.
+ # Technically, this is bad practice. It's possible that on a
+ # heavily loaded machine, Mrs. Conclusion wouldn't get a chance
+ # to acquire the semaphore. There really ought to be a loop here
+ # that waits for some sort of goodbye message but for purposes of
+ # simplicity I'm skipping that.
+ utils.say("Final wait to acquire the semaphore")
+ semaphore.acquire()
+
+utils.say("Destroying semaphore and shared memory.")
+mapfile.close()
+# I could call memory.unlink() here but in order to demonstrate
+# unlinking at the module level I'll do it that way.
+posix_ipc.unlink_shared_memory(params["SHARED_MEMORY_NAME"])
+
+semaphore.release()
+
+# I could also unlink the semaphore by calling
+# posix_ipc.unlink_semaphore() but I'll do it this way instead.
+semaphore.unlink()