System V IPC for Python - Semaphores, Shared Memory and Message Queues
This describes the sysv_ipc module which gives Python access
to System V inter-process semaphores, shared memory and message queues
on systems that support them. That includes most (all?) *nix flavors.
The module might work under Windows with a library like
Cygwin.
If you're interested in this module, there are
other low-level IPC extensions
for Python that you might find useful.
You can download
sysv_ipc version 0.4.2
[md5 sum]
which contains the source code, setup.py, installation instructions and
sample code. The class and error names changed
between versions 0.2.1 and 0.3. Sorry about that. You can read about
all of the changes in this version.
You might also want to read
about some known bugs.
I've tested sysv_ipc on
OS X 10.5, Ubuntu 8, OpenSolaris 2008.05 and FreeBSD 6 and 7.
Module sysv_ipc
Module Functions
- remove_semaphore(id)
- Removes the semaphore with the given id.
- remove_shared_memory(id)
- Removes the shared memory with the given id.
- remove_message_queue(id)
- Removes the message queue with the given id.
Module Constants
- IPC_CREAT, IPC_EXCL and IPC_CREX
- IPC_CREAT and IPC_EXCL are flags used when
creating IPC objects. They're
bitwise unique and can be ORed together. IPC_CREX is
shorthand for IPC_CREAT | IPC_EXCL.
When passed to an IPC object's constructor, IPC_CREAT indicates
that you want to create a new object or open an existing one. If you want
the call to fail if an object with that key already exists, specify
the IPC_EXCL flag, too.
- IPC_PRIVATE
- This is a special value that can be passed in place of a key. It implies that
the IPC object should be available only to the creating process or its
child processes (e.g. those created with fork()).
- KEY_MAX
- Keys passed to IPC object constructors must not exceed KEY_MAX.
- SEMAPHORE_VALUE_MAX
- The maximum value of a semaphore.
- PAGE_SIZE
- The operating system's memory page size, in bytes. It's probably a good
idea to make shared memory segments some multiple of this size.
- SEMAPHORE_TIMEOUT_SUPPORTED
- True if the platform supports timed semaphore waits, False otherwise.
- SHM_RND
- You probably don't need this, but it can be used when attaching shared
memory to force the address to be
rounded down to SHMLBA. See your system's man page for shmat()
for more information.
- SHM_HUGETLB, SHM_NORESERVE and SHM_REMAP
- You probably don't need these. They're Linux-specific flags that can
be passed to the SharedMemory
constructor, or to the .attach() function in the case of
SHM_REMAP. See your system's man page for shmget()
and shmat() for more information.
Module Errors
In addition to standard Python errors (e.g. ValueError),
this module raises custom errors. These errors cover
situations specific to IPC.
- Error
- The base error class for all the custom errors in this module. This
error is occasionally raised on its own but you'll almost
always see a more specific error.
- InternalError
- Indicates that something has gone very wrong in the module code. Please
report this to the maintainer.
- PermissionsError
- Indicates that you've attempted something that the permissions on the
IPC object don't allow.
- ExistentialError
- Indicates an error related to the existence or non-existence of
an IPC object.
- BusyError
- Raised when a semaphore call to .P() or .Z() either times out
or would be forced to wait when its block attribute is False.
- NotAttachedError
- Raised when a process attempts to read from or write to a shared memory
segment to which it is not attached.
The Semaphore Class
This is a handle to a semaphore.
Methods
- Semaphore(key, [flags = 0, [mode = 0600, [initial_value = 0]]])
- Creates a new semaphore or opens an existing one.
key must be None,
IPC_PRIVATE or
an integer > 0 and ≤ KEY_MAX. If the key
is None, the module chooses a random unused key.
The flags specify whether you want to create a
new semaphore or open an existing one.
- With flags set to the default of 0, the module attempts
to open an existing semaphore identified by key and raises
a ExistentialError if that semaphore doesn't exist.
- With flags set to IPC_CREAT, the module
opens the semaphore identified by
key or creates a new
one if no such semaphore exists. Using IPC_CREAT by itself
is not recommended. (See Semaphore Initialization.)
- With flags set to
IPC_CREX (IPC_CREAT | IPC_EXCL),
the module
creates a new semaphore identified by key. If a
semaphore with that key already exists, the call raises an
ExistentialError.
The initial_value is ignored unless
both of these flags are specified or
if the semaphore is read-only.
When opening an existing semaphore, mode is ignored.
- acquire([timeout = None, [delta = 1]])
- Waits (conditionally) until the semaphore's value is > 0 and then returns,
decrementing the semaphore.
The timeout (which can be a float) specifies how
many seconds this call should wait, if at all.
The semantics of the timeout changed a little in
version 0.3.
- A timeout of None (the default)
implies no time limit. The call will not return until its wait
condition is satisfied.
- When timeout is 0, the call
raises a BusyError if it can't immediately
acquire the semaphore. Since it will
return immediately if not asked to wait, this can be
thought of as "non-blocking" mode.
- When the timeout is > 0, the call
will wait no longer than timeout
seconds before either returning (having acquired the semaphore)
or raising a BusyError.
On platforms that don't support the semtimedop() API
(OS X doesn't support it), the timeout
is ignored (treated as None).
The module's Boolean constant SEMAPHORE_TIMEOUT_SUPPORTED
is True on platforms that support semtimedop().
When the call returns, the semaphore's value decreases by delta
(or more precisely, abs(delta))
which defaults to 1.
- release([delta = 1])
-
Releases (increments) the semaphore.
The semaphore's value increases by delta
(or more precisely, abs(delta))
which defaults to 1.
- P()
- A synonym for .acquire() that takes the same parameters.
"P" stands for
prolaag or probeer te verlagen
(try to decrease), the original name given by
Edsger Dijkstra.
- V()
- A synonym for .release() that takes the same parameters.
"V" stands for
verhoog (increase), the original name given by
Edsger Dijkstra.
- Z([timeout = None])
- Blocks until zee zemaphore is zero.
Timeout has
the same meaning as described in .acquire().
- remove()
-
Removes (deletes) the semaphore from the system.
As far as I can tell, the effect of deleting a semaphore that
other processes are still using is OS-dependent. Check your system's
man pages for semctl(IPC_RMID).
Attributes
- key (read-only)
- The key passed in the call to the constructor.
- id (read-only)
- The id assigned to this semaphore by the OS.
- value
- The integer value of the semaphore.
- undo
- Defaults to False.
When True, operations that change the
semaphore's value will be undone (reversed) when
the process exits. Note that when a process exits, an undo operation
may imply that a semaphore's value should become negative or
exceed its maximum.
Behavior in this case is system-dependent, which means that
using this flag can make your code non-portable.
- block
-
Defaults to True, which means that calls to acquire() and
release() will not return
until their wait conditions are satisfied.
When False, these calls
will not block but will instead raise an error if they are unable
to return immediately.
- mode
- The semaphore's permission bits.
Tip: the following Python code will display
the mode in octal:
print int(str(my_sem.mode), 8)
- uid
- The semaphore's user id.
- gid
- The semaphore's group id.
- cuid (read-only)
- The semaphore creator's user id.
- cgid (read-only)
- The semaphore creator's group id.
- last_pid (read-only)
- The PID of the process that last called semop() (.P(),
.V() or .Z()) on this semaphore.
- waiting_for_nonzero (read-only)
- The number of processes waiting for the value of the semaphore to become
non-zero (i.e. the number waiting in a call to .P()).
- waiting_for_zero (read-only)
- The number of processes waiting for the value of the semaphore to become
zero (i.e. the number waiting in a call to .Z()).
- o_time (read-only)
- The last time semop() (i.e. .P(), .V() or
.Z()) was called on this semaphore.
The SharedMemory Class
This is a handle to a shared memory segment.
Methods
- SharedMemory(key, [flags = 0, [mode = 0600, [size = 0 or PAGE_SIZE, [init_character = ' ']]]])
- Creates a new shared memory segment or opens an existing one.
The memory is automatically attached.
key must be None,
IPC_PRIVATE or
an integer > 0 and ≤ KEY_MAX. If the key
is None, the module chooses a random unused key.
The flags specify whether you want to create a
new shared memory segment or open an existing one.
The value of size depends on whether
one is opening an existing segment or creating a new one.
- When opening an existing segment, size
must be ≤ the existing segment's size. Zero is
always valid.
- When creating an new segment,
many (most? all?) operating systems insist on a size
> 0.
In addition, some round the size
up to the next multiple of PAGE_SIZE.
This module supplies a default
size of PAGE_SIZE when
IPC_CREX is specified and 0 otherwise.
- attach([address = None, [flags = 0]])
-
Attaches this process to the shared memory. The memory must be attached
before calling .read() or .write(). Note that the
constructor automatically attaches the memory
so you won't need to call this method unless you explicitly detach it
and then want to use it again.
The address parameter allows one to specify (as a Python long) a memory
address at which to attach the segment. Passing None (the default)
is equivalent to passing NULL to shmat(). See that
function's man page for details.
The flags are mostly only relevant if one specifies a specific address.
One exception is the flag SHM_RDONLY which, surprisingly,
attaches the segment read-only.
Note that on some (and perhaps all) platforms, each call to .attach()
increments the system's "attached" count. Thus, if each call to
.attach() isn't paired with a call to .detach(),
the system's "attached" count for the shared memory segment will not
go to zero when the process exits. As a result, the shared memory
segment may not disappear even when its creator calls .remove()
and exits.
- detach()
- Detaches this process from the shared memory.
- read([byte_count = 0, [offset = 0]])
- Reads up to byte_count bytes from the
shared memory segment starting at offset
and returns them as a Python string.
If byte_count is zero (the default) the entire
buffer is returned.
This method will never attempt to read past the end of the shared
memory segment, even when
offset + byte_count
exceeds the memory segment's size. In that case, the bytes
from offset to the end of the segment are returned.
- write(s, [offset = 0])
- Writes the string s to the shared memory,
starting at offset.
At most n bytes will be written, where
n = the segment's size minus offset.
The string may contain embedded NULL bytes ('\0').
- remove()
- Removes (destroys) the shared memory. Note that actual destruction of the
segment only occurs when all processes have detached.
Attributes
- key (read-only)
- The key provided in the constructor.
- id (read-only)
- The id assigned to this semaphore by the OS.
- size (read-only)
- The size of the segment in bytes.
- address (read-only)
- The address of the segment as Python long.
- attached (read-only)
- If True, this segment is currently attached.
- last_attach_time (read-only)
- The last time a process attached this segment.
- last_detach_time (read-only)
- The last time a process detached this segment.
- last_change_time (read-only)
- The last time a process changed the uid, gid or mode on this segment.
- creator_pid (read-only)
- The PID of the process that created this segment.
- last_pid (read-only)
- The PID of the most last process to attach or detach this segment.
- number_attached (read-only)
- The number of processes attached to this segment.
- uid
- The segment's user id.
- gid
- The segment's group id.
- mode
- The shared memory's permission bits.
Tip: the following Python code will display
the mode in octal:
print int(str(my_mem.mode), 8)
- cuid (read-only)
- The segment creator's user id.
- cgid (read-only)
- The segment creator's group id.
The MessageQueue Class
This is a handle to a message queue.
Methods
- MessageQueue(key, [flags = 0, [mode = 0600, [max_message_size = 2048]]])
- Creates a new message queue or opens an existing one.
key must be None,
IPC_PRIVATE or
an integer > 0 and ≤ KEY_MAX. If the key
is None, the module chooses a random unused key.
The flags specify whether you want to create a
new queue or open an existing one.
- With flags set to the
default of 0, the module attempts
to open an existing message queue identified by
key and raises
a ExistentialError if it doesn't exist.
- With flags set to IPC_CREAT, the module
opens the message queue identified
by key or
creates a new one if no such queue exists.
- With flags set to
IPC_CREX (IPC_CREAT | IPC_EXCL),
the module
creates a new message queue identified by
key. If
a queue with that key already exists, the call raises
a ExistentialError.
The max_message_size can be increased
from the default, but be aware of the issues discussed in
Message Queue Limits.
- send(message, [block = True, [type = 1]])
- Puts a message on the queue.
The message string can contain embedded
NULLs (ASCII 0x00).
The block flag specifies whether or
not the call should wait if the message can't be sent (if, for
example, the queue is full). When block
is False, the call will raise a BusyError if
the message can't be sent immediately.
The type is
associated with the message and is relevant when calling
receive(). It must be > 0.
- receive([block = True, [type = 0]])
-
Receives a message from the queue, returning a tuple of
(message, type).
The block flag specifies whether or
not the call should wait if there's no messages of the
specified type to retrieve. When block
is False, the call will raise a BusyError if
a message can't be received immediately.
The type permits some control over
which messages are retrieved.
- When type == 0, the call
returns the first message on the queue regardless of its
type.
- When type > 0, the call
returns the first message of that type.
- When type < 0, the call
returns the first message of the lowest type that is
≤ to the absolute value of type.
- remove()
- Removes (deletes) the message queue.
Attributes
- key (read-only)
- The key provided in the constructor.
- id (read-only)
- The id assigned to this queue by the OS.
- max_size
- The maximum size of the queue in bytes. Only a process with
"appropriate privileges" can increase this value, and on some
systems even that won't work. See
Message Queue Limits for details.
- last_send_time (read-only)
- The last time a message was placed on the queue.
- last_receive_time (read-only)
- The last time a message was received from the queue.
- last_change_time (read-only)
- The last time a process changed the queue's attributes.
- last_send_pid (read-only)
- The id of the most recent process to send a message.
- last_receive_pid (read-only)
- The id of the most recent process to receive a message.
- current_messages (read-only)
- The number of messages currently in the queue.
- uid
- The queue's user id.
- gid
- The queue's group id.
- mode
- The queue's permission bits.
Tip: the following Python code will display
the mode in octal:
print int(str(my_mem.mode), 8)
- cuid (read-only)
- The queue creator's user id.
- cgid (read-only)
- The queue creator's group id.
Supported Features and Differences from SHM
This module is almost, but not quite, a superset of
shm.
Some of the additional features are the ability to override the block
flag on a per-call basis, the ability to change the semaphore's value
in increments > 1 when calling .P() and .V()
and exposure of sem_otime.
Differences that might trip you up are listed below.
- Shm compiles on more platforms and under more versions of Python.
- Attribute names and method signatures are different.
- This module offers neither the functions semaphore_haskey()
nor memory_haskey().
- This module doesn't offer access to ftok() which is
for the best because
ftok is
probably broken on your system.
- This module's default permission on objects is 0600 as opposed
to shm's 0666.
- Shm maintained an internal dictionary of semaphores and shared memory
segments. The object keys served as the dictionary keys.
If you asked for the same object multiple times, shm would
return the same Python object. I'm not convinced this was safe,
particularly in the case where an object may have been destroyed
and another with the same key created in its place.
Usage Tips
Sample Code
This module comes with two demonstration apps. The first (in the
directory demo) shows how to use shared memory and semaphores.
The second (in the directory demo2) shows how to use
message queues.
Semaphore Initialization
When a System V sempahore is created at the C API level, the OS is not required
to initialize the semaphore's value. (This per
the
SUSv3 standard for semget().)
Some (most?) operating systems initialize it to zero, but this behavior
is non-standard and therefore can't be relied upon.
If sempahore creation happens in an predictable, orderly fashion, this isn't a
problem. But a
race condition arises when multiple processes vie to create/open the same semaphore. The
problem lies in the fact that when an application calls semget() with only
the IPC_CREAT flag, the caller can't tell whether or not he has
created a new semaphore or opened an existing one.
This makes it
difficult to create reliable code without using IPC_EXCL.
For instance, imagine processes P1 and P2. They're executing the same code,
and that code intends to share a binary semaphore.
Consider the following sequence of events at the startup of P1 and P2 –
- P1 calls semget(IPC_CREAT) to create the semaphore S.
- P2 calls semget(IPC_CREAT) to open S.
- P1 initializes the semaphore's value to 1.
- P1 calls acquire(), decrementing the value to 0.
- P2, assuming S is a newly-created semaphore that needs to be initialized,
incorrectly sets the semaphore's value to 1.
- P2 calls acquire(), decrementing the value to 0. Both processes
now think they own the lock.
The canonical solution for this race condition is to check the value of
sem_otime (an element in the semid_ds struct that's
populated on call to semctl(IPC_STAT) and which is exposed to
Python by this module) which
is initialized to zero when the semaphore is created and otherwise holds
the time of the last
call to semop().
In Python, each process would run something like this:
try:
sem = sysv_ipc.Semaphore(42, sysv_ipc.CREX)
except sysv_ipc.ExistentialError:
# One of my peers created the semaphore already
sem = sysv_ipc.Semaphore(42)
while not sem.o_time:
time.sleep(.1)
# Now the semaphore is safe to use.
Shared Memory Initialization
With shared memory,
using the IPC_CREAT flag without IPC_EXCL
is problematic unless you know the size of the segment
you're potentially opening.
Why? Because when creating a new segment,
many (most? all?) operating systems demand a non-zero size. However,
when opening an existing segment, zero is the only guaranteed safe value
(again, assuming one doesn't know the size of the segment in advance).
Since IPC_CREAT
can open or create a segment, there's no safe value for the size under
this circumstance.
As a (sort of) side note, the
SUSv3
specification for shmget() says only that the size of a new
segment must not be less than "the system-imposed minimum". I
gather that at one time, some systems set the minimum at zero despite the
fact that it doesn't make much sense to create a zero-length shared memory
segment. I think most modern systems do the sensible thing and insist on
a minimum length of 1.
Message Queue Limits
Python programmers can usually remain blissfully ignorant of memory
allocation issues. Unfortunately, a combination of factors makes them
relevant when dealing with System V message queues.
Some implementations impose extremely stingy limits.
For instance, many BSDish systems (OS X, FreeBSD,
NetBSD, and
OpenBSD)
limit queues to 2048 bytes. Note that that's the total
queue size, not the message size. Two 1k messages would fill the queue.
Those limits can be very difficult to change. At best,
only privileged processes can increase the limit. At worst, the limit
is a kernel parameter and requires a kernel change via a tunable or
a recompile.
This module can't figure out what the limits are, so
it can't cushion them or even report them to you.
On some systems the limits are expressed in header files, on others
they're available through kernel interfaces (like FreeBSD's sysctl).
Under OS X and to some extent OpenSolaris I can't figure out where they're
defined and what I report here is the result of experimentation and educated
guesses formed with the help of Google.
The good news is that this module will still behave as advertised no
matter what these limits are. Nevertheless you might be surprised when a
call to .send() get stuck because a queue is full even though you've
only put 2048 bytes of messages in it.
Here are the limits I've been able to find under my test operating
systems, ordered from best (most generous) to worst (most stingy).
Under OpenSolaris 2008.05 each queue's maximum size defaults
to 64k. A privileged process (e.g. root) can change this through the
max_size attribute of a sysv_ipc.MessageQueue object.
I was able to increase it to 16M and successfully sent sixteen 1M messages to
the queue.
Under Ubuntu 8.04 (and perhaps other Linuxes) each
queue's maximum size defaults to 16k. As with OpenSolaris, I was able to
increase this to 16M, but only for a privileged process.
Under FreeBSD 7 and I think NetBSD and OpenBSD, each
queue's maximum size defaults to 2048 bytes. Furthermore, one can (as root)
set max_size to something larger and FreeBSD doesn't complain, but
it also ignores the change.
OS X is the worst of the lot. Each queue is limited
to 2048 bytes and OS X silently ignores attempts to increase this (just like
FreeBSD). To add insult to injury, there appears to be no way to increase
this limit short of recompiling the kernel.
I'm guessing at this based on the
Darwin
message queue limits.
If you want
to search for these limits on your operating system, the key constants are
MSGSEG, MSGSSZ, MSGTQL, MSGMNB,
MSGMNI and MSGMAX. Under BSD, sysctl kern.ipc
should tell you what you need to know and may allow you to change these
parameters.
Nobody Likes a Mr. Messy
Semaphores and especially shared memory are a little different from most Python objects
and therefore require a little more care on the part of the programmer. When a
program creates a semaphore or shared memory object, it creates something that
resides outside of its own process, just like a file on a hard drive. It
won't go away when your process ends unless you explicitly remove it.
In short, remember to clean up after yourself.
Consult Your Local man Pages
The sysv_ipc module is just a wrapper around your system's API. If your
system's implementation has quirks, the man pages for semget, semctl, semop
shmget, shmat, shmdt and shmctl will probably cover them.
Interesting Tools
Many systems (although not some older versions of OS X) come
with ipcs and ipcrm.
The former shows existing shared memory, semaphores and message queues on your system and
the latter allows you to remove them.
Last But Not Least
For Pythonistas –
Known Bugs
Bugs? My code never has bugs! There are, however, some suboptimal anomalies...
- Under OS X, sys/ipc.h defines two versions of the
ipc_perm struct. This module picks up the old, deprecated
one which stores uid and gid values in 16 bit types. It's definitions
in Python.h that cause this, though, so there's not much
I can do to change it.
- Maximum values for uids, gids, keys and semaphore values are
hardcoded to "safe" values rather than being platform dependent.
It turns out
that it's
really difficult to determine the maximum value that a
typedef-ed variable can hold.
Version History
- Current – 0.4.2 (22 Mar 2009) –
No code changes in this version.
- Fixed broken documentation links to youtube.
- Fixed the project name in the LICENSE file.
- 0.4.1 (12 Feb 2009) –
- Changed status to beta.
- Added automatic generation of keys.
- Added a message queue demo.
- Added str() and repr() support to all
objects.
- Fixed a bug in SharedMemory.write() that could cause
a spurious error of "Attempt to write past end of memory
segment". This bug only occurred on platforms using
different sizes for long and int, or
long and py_size_t
(depending on Python version). Tack to Jesper
for debug help.
- Plugged a memory leak in MessageQueue.receive().
- Added a VERSION attribute to the module.
- 0.4 (28 Jan 2009) –
- Added message queues.
- Fixed a bug where the key attribute of SharedMemory objects
returned garbage.
- Fixed a bug where keys > INT_MAX would get truncated on
platforms where longs are bigger than ints.
- Provided decent inline documentation for object attributes
(available via the help() command).
- Rearranged the code which was suffering growing pains.
- 0.3 (16 Jan 2009) –
This version features a rename of classes and errors
(sorry about breaking the names), some modifications to
semaphore timeouts, and many small fixes.
A semaphore's .block flag now consistently trumps the
timeout. When False, the timeout is irrelevant -- calls
will never block. In prior versions, the flag was ignored
when the timeout was non-zero.
Also, on platforms (such as OS X) where semtimedop() is
not supported, all timeouts are now treated as None.
In other words, when .block is True, all calls
wait as long as necessary.
Other fixes –
- Fixed the poor choices I'd made for class and
error names by removing the leading "SysV" and "SysVIpc".
- Removed dependencies on Python 2.5. The module now works
with Python 2.4.4 and perhaps earlier versions.
- Fixed a bug where I was not following SysV semaphore semantics
for interaction between timeouts and the block flag.
- Fixed compile problems on OpenSolaris 2008.05.
- Got rid of OS X compile warnings.
- Fixed many instances where I was making potentially lossy
conversions between Python values and Unix-specific
types like key_t, pid_t, etc.
- Fixed a bug in the SharedMemory attach() function
that would set an error string but not return an error
value if the passed address was not None or a long.
- Simplified the code a little.
- Restricted the semaphore acquire() and
release() delta to be between SHORT_MIN and
SHORT_MAX since
it
is a short in the SUSv2 spec.
- Fixed a bug where calling acquire() or
release() with a delta of 0 would call
.Z() instead.
- Disallowed byte counts ≤ 0 in
SharedMemory.read()
- Fixed a bug in the SharedMemory init code that
could, under vanishingly rare circumstances, return failure
without setting the error code.
- Removed some dead code relating to the .seq member
of the sem_perm and shm_perm structs.
- Stopped accessing the non-standard key
(a.k.a. _key and __key) element of the
ipc_perm struct.
- Added code to ensure the module doesn't try to create
a string that's larger than Python permits when reading
from shared memory.
- 0.2.1 (3 Jan 2009) –
- Fixed a bug that prevented the module-specific
errors (ExistentialError, etc.) from
being visible in the module.
- Fixed a bug that re-initialized shared memory with
the init character when only IPC_CREAT was specified
and an existing segment was opened.
- Fixed a bug that always defaulted the size of a shared
memory segment to PAGE_SIZE. Updated code and
documentation to use intelligent defaults. Tack to
Jesper for the bug report.
- Several cosmetic changes. (Added more metadata to setup.py,
added a newline to the end of probe_results.h to avoid
compiler complaints, etc.)
- 0.2 (16 Dec 2008) –
Lots of small fixes.
- Fixed a bug where calling shm.remove() on shared
memory that was already removed would cause a SystemError.
(I wasn't setting the Python error before returning.)
- Fixed a couple of bugs that would cause the creation
of a new, read-only shared memory segment to fail.
- Fixed a bug that would cause the creation
of a new, read-only semaphore to fail.
- Added the constant IPC_CREX.
- Renamed (sorry) MAX_KEY to KEY_MAX and
MAX_SEMAPHORE_VALUE to SEMAPHORE_VALUE_MAX
to be consistent with the C naming convention in limits.h.
- Hardcoded SEMAPHORE_VALUE_MAX to 32767 until I can
find a reliable way to determine it at install time.
- Changed prober.py to write out a C header file with
platform-specific definitions in it.
- Replaced OSError in shared memory functions with custom
errors from this module.
- Added code to raise a ValueError when an attempt is made
to assign an out-of-range value to a semaphore.
- Added code to raise a ValueError when an out-of-range key
is passed to a constructor.
- Fixed a bug in the demo program conclusion.c that caused
some informational messages to be printed twice.
- Fixed some documentation bugs.
- 0.1 (4 Dec 2008) – Original (alpha) version.
Future Features/Changes
These are features that may or may not be added depending on technical
difficulty, user interest and so forth.
- Update this documentation with a list of platforms that support semtimedop().
- Find a way to make SEMAPHORE_VALUE_MAX more accurate.
I don't plan on adding support for semaphore sets.