Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
colesbury committed Nov 27, 2023
1 parent ce91111 commit 1ebcb70
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 5 deletions.
8 changes: 4 additions & 4 deletions Include/internal/pycore_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,12 @@ typedef struct {
} _PyRWMutex;

// Read lock
extern void _PyRWMutex_RLock(_PyRWMutex *rwmutex);
extern void _PyRWMutex_RUnlock(_PyRWMutex *rwmutex);
PyAPI_FUNC(void) _PyRWMutex_RLock(_PyRWMutex *rwmutex);
PyAPI_FUNC(void) _PyRWMutex_RUnlock(_PyRWMutex *rwmutex);

// Write lock
extern void _PyRWMutex_Lock(_PyRWMutex *rwmutex);
extern void _PyRWMutex_Unlock(_PyRWMutex *rwmutex);
PyAPI_FUNC(void) _PyRWMutex_Lock(_PyRWMutex *rwmutex);
PyAPI_FUNC(void) _PyRWMutex_Unlock(_PyRWMutex *rwmutex);


#ifdef __cplusplus
Expand Down
77 changes: 76 additions & 1 deletion Modules/_testinternalcapi/test_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,85 @@ test_lock_once(PyObject *self, PyObject *obj)
Py_RETURN_NONE;
}

struct test_rwlock_data {
_PyRWMutex rw;
PyEvent step1;
PyEvent step2;
PyEvent step3;
PyEvent done;
};

static void
rdlock_thread(void *arg)
{
struct test_rwlock_data *test_data = arg;

// First acquire the lock in read mode
_PyRWMutex_RLock(&test_data->rw);
PyEvent_Wait(&test_data->step1);
printf("unlocking\n");
_PyRWMutex_RUnlock(&test_data->rw);

_PyRWMutex_RLock(&test_data->rw);
PyEvent_Wait(&test_data->step3);
_PyRWMutex_RUnlock(&test_data->rw);

_PyEvent_Notify(&test_data->done);
}
static void
wrlock_thread(void *arg)
{
struct test_rwlock_data *test_data = arg;

// First acquire the lock in write mode
_PyRWMutex_Lock(&test_data->rw);
PyEvent_Wait(&test_data->step2);
_PyRWMutex_Unlock(&test_data->rw);

_PyEvent_Notify(&test_data->done);
}

static void
wait_until(uintptr_t *ptr, uintptr_t value)
{
// wait up to two seconds for *ptr == value
int iters = 0;
uintptr_t bits;
do {
pysleep(10);
bits = _Py_atomic_load_uintptr(ptr);
iters++;
} while (bits != value && iters < 200);
}

static PyObject *
test_lock_rwlock(PyObject *self, PyObject *obj)
{
_PyRWMutex rwlock = {0};
struct test_rwlock_data test_data = {0};

// Start two readers
PyThread_start_new_thread(rdlock_thread, &test_data);
PyThread_start_new_thread(rdlock_thread, &test_data);

// wait up to two seconds for the threads to attempt to read-lock "rw"
wait_until(&test_data.rw.bits, 8);
assert(test_data.rw.bits == 8);

// start writer (while readers hold lock)
PyThread_start_new_thread(wrlock_thread, &test_data);
wait_until(&test_data.rw.bits, 10);
assert(test_data.rw.bits == 10);

// readers release lock, writer should re-acquire it
_PyEvent_Notify(&test_data.step1);
wait_until(&test_data.rw.bits, 3);
assert(test_data.rw.bits == 3);


_PyEvent_Notify(&test_data.step2);
_PyEvent_Notify(&test_data.step3);
PyEvent_Wait(&test_data.done);

Py_RETURN_NONE;
}

Expand Down
2 changes: 2 additions & 0 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -2016,6 +2016,7 @@ _PyThreadState_Park(PyThreadState *tstate)
}


#ifdef Py_GIL_DISABLED
// Interpreter for _Py_FOR_EACH_THREAD(). For global stop-the-world events,
// we start with the first interpreter and then iterate over all interpreters.
// For per-interpreter stop-the-world events, we only operate on the one
Expand Down Expand Up @@ -2142,6 +2143,7 @@ start_the_world(struct _stoptheworld_state *stw)
}
PyMutex_Unlock(&stw->mutex);
}
#endif // Py_GIL_DISABLED

void
_PyRuntimeState_StopTheWorld(_PyRuntimeState *runtime)
Expand Down

0 comments on commit 1ebcb70

Please sign in to comment.