diff --git a/Include/internal/pycore_qsbr.h b/Include/internal/pycore_qsbr.h index c3680a205542f77..20e643e172b38d7 100644 --- a/Include/internal/pycore_qsbr.h +++ b/Include/internal/pycore_qsbr.h @@ -140,7 +140,7 @@ _Py_qsbr_register(struct _PyThreadStateImpl *tstate, // Disassociates a PyThreadState from the QSBR state and frees the QSBR state. extern void -_Py_qsbr_unregister(struct _PyThreadStateImpl *tstate); +_Py_qsbr_unregister(PyThreadState *tstate); extern void _Py_qsbr_fini(PyInterpreterState *interp); diff --git a/Modules/spinner.c b/Modules/spinner.c new file mode 100644 index 000000000000000..4631f2677bbbba7 --- /dev/null +++ b/Modules/spinner.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include + +// The set_priority function +static PyObject* set_priority(PyObject* self, PyObject* args) { + int policy, priority; + + // Parse the arguments + if (!PyArg_ParseTuple(args, "ii", &policy, &priority)) { + return NULL; + } + + // Get the current thread id + pid_t tid = syscall(SYS_gettid); + + // Set the scheduling policy and priority + struct sched_param param; + param.sched_priority = priority; + if (sched_setscheduler(tid, policy, ¶m) != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + Py_RETURN_NONE; +} + +// The get_priority function +static PyObject* get_priority(PyObject* self, PyObject* args) { + // Get the current thread id + pid_t tid = syscall(SYS_gettid); + + // Get the scheduling policy + int policy = sched_getscheduler(tid); + if (policy == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + // Get the priority + struct sched_param param; + if (sched_getparam(tid, ¶m) != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + return Py_BuildValue("(ii)", policy, param.sched_priority); +} + +// The spin function +static PyObject* spin(PyObject* self, PyObject* args) { + int milliseconds; + double spins; + + // Parse the arguments + if (!PyArg_ParseTuple(args, "i", &milliseconds)) { + return NULL; + } + + // Save the current state and release the GIL + Py_BEGIN_ALLOW_THREADS + + // Get the current time + struct timespec start, now; + clock_gettime(CLOCK_MONOTONIC, &start); + + // Calculate the end time + time_t end_sec = start.tv_sec + milliseconds / 1000; + long end_nsec = start.tv_nsec + (milliseconds % 1000) * 1000000; + if (end_nsec >= 1000000000) { + end_sec++; + end_nsec -= 1000000000; + } + + // Spitn until the end time + spins = 0; + do { + clock_gettime(CLOCK_MONOTONIC, &now); + ++spins; + } while (now.tv_sec < end_sec || (now.tv_sec == end_sec && now.tv_nsec < end_nsec)); + + // Restore the GIL and the saved state + Py_END_ALLOW_THREADS + + return Py_BuildValue("d", spins); +} + +// The module's function table +static PyMethodDef SpinnerMethods[] = { + {"set_priority", set_priority, METH_VARARGS, "Set the current thread's priority."}, + {"get_priority", get_priority, METH_VARARGS, "Get the current thread's priority."}, + {"spin", spin, METH_VARARGS, "Spin the CPU for a specified time."}, + // Add other functions here... + {NULL, NULL, 0, NULL} +}; + +// The module's definition +static struct PyModuleDef spinnermodule = { + PyModuleDef_HEAD_INIT, + "spinner", + "A module that spins the CPU and manages thread priorities.", + -1, + SpinnerMethods +}; + +// The module's initialization function +PyMODINIT_FUNC PyInit_spinner(void) { + PyObject* m; + m = PyModule_Create(&spinnermodule); + if (m == NULL) + return NULL; + PyModule_AddIntConstant(m, "SCHED_IDLE", SCHED_IDLE); + PyModule_AddIntConstant(m, "SCHED_BATCH", SCHED_BATCH); + PyModule_AddIntConstant(m, "SCHED_OTHER", SCHED_OTHER); + PyModule_AddIntConstant(m, "SCHED_FIFO", SCHED_FIFO); + PyModule_AddIntConstant(m, "SCHED_RR", SCHED_FIFO); + return m; +} diff --git a/Python/pystate.c b/Python/pystate.c index b1e085bb8069155..0a226268c558c7f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1785,7 +1785,7 @@ tstate_delete_common(PyThreadState *tstate) HEAD_UNLOCK(runtime); #ifdef Py_GIL_DISABLED - _Py_qsbr_unregister((_PyThreadStateImpl *)tstate); + _Py_qsbr_unregister(tstate); #endif // XXX Unbind in PyThreadState_Clear(), or earlier diff --git a/Python/qsbr.c b/Python/qsbr.c index d7ac8f479cda1ba..1e02ff9c2e45f0e 100644 --- a/Python/qsbr.c +++ b/Python/qsbr.c @@ -231,20 +231,21 @@ _Py_qsbr_register(_PyThreadStateImpl *tstate, PyInterpreterState *interp, } void -_Py_qsbr_unregister(_PyThreadStateImpl *tstate) +_Py_qsbr_unregister(PyThreadState *tstate) { - struct _qsbr_shared *shared = tstate->qsbr->shared; + struct _qsbr_shared *shared = &tstate->interp->qsbr; + struct _PyThreadStateImpl *tstate_imp = (_PyThreadStateImpl*) tstate; PyMutex_Lock(&shared->mutex); // NOTE: we must load (or reload) the thread state's qbsr inside the mutex // because the array may have been resized (changing tstate->qsbr) while // we waited to acquire the mutex. - struct _qsbr_thread_state *qsbr = tstate->qsbr; + struct _qsbr_thread_state *qsbr = tstate_imp->qsbr; assert(qsbr->seq == 0 && "thread state must be detached"); - assert(qsbr->allocated && qsbr->tstate == (PyThreadState *)tstate); + assert(qsbr->allocated && qsbr->tstate == tstate); - tstate->qsbr = NULL; + tstate_imp->qsbr = NULL; qsbr->tstate = NULL; qsbr->allocated = false; qsbr->freelist_next = shared->freelist; diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index a669bc4d1d5c30b..88fe7183e590513 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -24,7 +24,6 @@ race:_PyParkingLot_Park race:_PyType_HasFeature race:assign_version_tag race:gc_restore_tid -race:initialize_new_array race:insertdict race:lookup_tp_dict race:mi_heap_visit_pages