Skip to main content

Modules/_multiprocessing/semaphore.c

cpython 3.14 @ ab2d84fe1023/Modules/_multiprocessing/semaphore.c

Modules/_multiprocessing/semaphore.c implements _multiprocessing.SemLock, the C object that backs multiprocessing.Semaphore, multiprocessing.BoundedSemaphore, and multiprocessing.Lock. On POSIX it wraps sem_init/sem_open; on Windows it wraps CreateSemaphoreA. The SemLock object is picklable so it can be passed to child processes via multiprocessing.Process.

Map

LinesSymbolRole
1-60includes, SemLockObject structPOSIX/Windows union of semaphore handle
61-180semlock_newConstructor: POSIX sem_open or sem_init, Windows CreateSemaphore
181-300semlock_acquire, semlock_releasePOSIX sem_timedwait/sem_post, Windows WaitForSingleObject/ReleaseSemaphore
301-400semlock_getvaluesem_getvalue on POSIX; simulated on Windows
401-500semlock_rebuildPickle/unpickle support by re-opening named semaphore
501-600semlock_enter, semlock_exitContext manager protocol wrapping acquire/release
601-700SemLock_Type, PyInit__multiprocessingType registration

Reading

POSIX sem_timedwait with GIL release

semlock_acquire releases the GIL before blocking on the semaphore, using sem_timedwait with a computed absolute timeout. It retries on EINTR.

// CPython: Modules/_multiprocessing/semaphore.c:200 semlock_acquire
static PyObject *
semlock_acquire(SemLockObject *self, PyObject *args, PyObject *kwds)
{
...
do {
Py_BEGIN_ALLOW_THREADS
res = sem_timedwait(self->handle, &deadline);
Py_END_ALLOW_THREADS
if (res == 0)
break;
if (errno != EINTR)
break;
...
} while (1);

semlock_rebuild: cross-process sharing

SemLock objects are shared between processes by name. semlock_rebuild re-opens the named semaphore in the child process using the same name passed via pickle, without resetting the semaphore state.

// CPython: Modules/_multiprocessing/semaphore.c:430 semlock_rebuild
static PyObject *
semlock_rebuild(PyTypeObject *type, PyObject *args)
{
sem_t *handle;
const char *name;
...
handle = sem_open(name, 0);
if (handle == SEM_FAILED) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return newsemlockobject(type, handle, kind, maxvalue, name);
}

Context manager protocol

semlock_enter calls semlock_acquire with the default timeout. semlock_exit calls semlock_release and ignores its return value, matching the Lock.__exit__ contract.

// CPython: Modules/_multiprocessing/semaphore.c:530 semlock_enter
static PyObject *
semlock_enter(SemLockObject *self, PyObject *args)
{
return semlock_acquire(self, args, NULL);
}

static PyObject *
semlock_exit(SemLockObject *self, PyObject *args)
{
return semlock_release(self, NULL, NULL);
}

gopy notes

Not yet ported. Go's sync.Mutex and sync.WaitGroup cover intra-process locking. Cross-process semaphores as used by multiprocessing require OS-level primitives; golang.org/x/sys exposes sem_open on POSIX. A module/_multiprocessing port would be needed to run Lib/test/test_multiprocessing_*.py against gopy.

CPython 3.14 changes

3.14 added SemLock._is_zero to check if a semaphore's value is zero without blocking. The Windows path gained WaitForMultipleObjectsEx for interrupt handling during long waits.