From 94b580d423524188bcf7136f9829fb68eb6d15d3 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 2 Aug 2011 17:30:04 -0500 Subject: [PATCH] expose sched.h functions (closes #12655) --- Doc/library/os.rst | 149 +++++++++++ Lib/test/test_posix.py | 133 +++++++++- Misc/NEWS | 5 + Modules/posixmodule.c | 584 +++++++++++++++++++++++++++++++++++++++++ configure | 6 +- configure.in | 2 +- pyconfig.h.in | 3 + 7 files changed, 877 insertions(+), 5 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 42dba3076c1..47e6a1bf25a 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2744,6 +2744,155 @@ used to determine the disposition of a process. Availability: Unix. +Interface to the scheduler +-------------------------- + +These functions control how a process is allocated CPU time by the operating +system. They are only available on some Unix platforms. For more detailed +information, consult your Unix manpages. + +.. versionadded:: 3.3 + +The following scheduling policies are exposed if they are a supported by the +operating system. + +.. data:: SCHED_OTHER + + The default scheduling policy. + +.. data:: SCHED_BATCH + + Scheduling policy for CPU-intensive processes that tries to preserve + interactivity on the rest of the computer. + +.. data:: SCHED_IDLE + + Scheduling policy for extremely low priority background tasks. + +.. data:: SCHED_SPORADIC + + Scheduling policy for sporadic server programs. + +.. data:: SCHED_FIFO + + A First In First Out scheduling policy. + +.. data:: SCHED_RR + + A round-robin scheduling policy. + +.. data:: SCHED_RESET_ON_FORK + + This flag can OR'ed with any other scheduling policy. When a process with + this flag set forks, its child's scheduling policy and priority are reset to + the default. + + +.. class:: sched_param(sched_priority) + + This class represents tunable scheduling parameters used in + :func:`sched_setparam`, :func:`sched_setscheduler`, and + :func:`sched_getparam`. It is immutable. + + At the moment, there is only one possible parameter: + + .. attribute:: sched_priority + + The scheduling priority for a scheduling policy. + + +.. function:: sched_get_priority_min(policy) + + Get the minimum priority value for *policy*. *policy* is one of the + scheduling policy constants above. + + +.. function:: sched_get_priority_max(policy) + + Get the maximum priority value for *policy*. *policy* is one of the + scheduling policy constants above. + + +.. function:: sched_setscheduler(pid, policy, param) + + Set the scheduling policy for the process with PID *pid*. A *pid* of 0 means + the calling process. *policy* is one of the scheduling policy constants + above. *param* is a :class:`sched_param` instance. + + +.. function:: sched_getscheduler(pid) + + Return the scheduling policy for the process with PID *pid*. A *pid* of 0 + means the calling process. The result is one of the scheduling policy + constants above. + + +.. function:: sched_setparam(pid, param) + + Set a scheduling parameters for the process with PID *pid*. A *pid* of 0 means + the calling process. *param* is a :class:`sched_param` instance. + + +.. function:: sched_getparam(pid) + + Return the scheduling parameters as a :class:`sched_param` instance for the + process with PID *pid*. A *pid* of 0 means the calling process. + + +.. function:: sched_rr_get_interval(pid) + + Return the round-robin quantum in seconds for the process with PID *pid*. A + *pid* of 0 means the calling process. + + +.. function:: sched_yield() + + Voluntarily relinquish the CPU. + + +.. class:: cpu_set(ncpus) + + :class:`cpu_set` represents a set of CPUs on which a process is eligible to + run. *ncpus* is the number of CPUs the set should describe. Methods on + :class:`cpu_set` allow CPUs to be add or removed. + + :class:`cpu_set` supports the AND, OR, and XOR bitwise operations. For + example, given two cpu_sets, ``one`` and ``two``, ``one | two`` returns a + :class:`cpu_set` containing the cpus enabled both in ``one`` and ``two``. + + .. method:: set(i) + + Enable CPU *i*. + + .. method:: clear(i) + + Remove CPU *i*. + + .. method:: isset(i) + + Return ``True`` if CPU *i* is enabled in the set. + + .. method:: count() + + Return the number of enabled CPUs in the set. + + .. method:: zero() + + Clear the set completely. + + +.. function:: sched_setaffinity(pid, mask) + + Restrict the process with PID *pid* to a set of CPUs. *mask* is a + :class:`cpu_set` instance. + + +.. function:: sched_getaffinity(pid, size) + + Return the :class:`cpu_set` the process with PID *pid* is restricted to. The + result will contain *size* CPUs. + + .. _os-path: Miscellaneous System Information diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index ce3866971af..b649cd9e69f 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -829,6 +829,138 @@ class PosixTester(unittest.TestCase): finally: posix.close(f) + requires_sched_h = unittest.skipUnless(hasattr(posix, 'sched_yield'), + "don't have scheduling support") + + @requires_sched_h + def test_sched_yield(self): + # This has no error conditions (at least on Linux). + posix.sched_yield() + + @requires_sched_h + def test_sched_priority(self): + # Round-robin usually has interesting priorities. + pol = posix.SCHED_RR + lo = posix.sched_get_priority_min(pol) + hi = posix.sched_get_priority_max(pol) + self.assertIsInstance(lo, int) + self.assertIsInstance(hi, int) + self.assertGreaterEqual(hi, lo) + self.assertRaises(OSError, posix.sched_get_priority_min, -23) + self.assertRaises(OSError, posix.sched_get_priority_max, -23) + + @requires_sched_h + def test_get_and_set_scheduler_and_param(self): + possible_schedulers = [sched for name, sched in posix.__dict__.items() + if name.startswith("SCHED_")] + mine = posix.sched_getscheduler(0) + self.assertIn(mine, possible_schedulers) + try: + init = posix.sched_getscheduler(1) + except OSError as e: + if e.errno != errno.EPERM: + raise + else: + self.assertIn(init, possible_schedulers) + self.assertRaises(OSError, posix.sched_getscheduler, -1) + self.assertRaises(OSError, posix.sched_getparam, -1) + param = posix.sched_getparam(0) + self.assertIsInstance(param.sched_priority, int) + posix.sched_setscheduler(0, mine, param) + posix.sched_setparam(0, param) + self.assertRaises(OSError, posix.sched_setparam, -1, param) + self.assertRaises(OSError, posix.sched_setscheduler, -1, mine, param) + self.assertRaises(TypeError, posix.sched_setscheduler, 0, mine, None) + self.assertRaises(TypeError, posix.sched_setparam, 0, 43) + param = posix.sched_param(None) + self.assertRaises(TypeError, posix.sched_setparam, 0, param) + large = 214748364700 + param = posix.sched_param(large) + self.assertRaises(OverflowError, posix.sched_setparam, 0, param) + param = posix.sched_param(sched_priority=-large) + self.assertRaises(OverflowError, posix.sched_setparam, 0, param) + + @requires_sched_h + def test_sched_rr_get_interval(self): + interval = posix.sched_rr_get_interval(0) + self.assertIsInstance(interval, float) + # Reasonable constraints, I think. + self.assertGreaterEqual(interval, 0.) + self.assertLess(interval, 1.) + + @requires_sched_h + def test_sched_affinity(self): + mask = posix.sched_getaffinity(0, 1024) + self.assertGreaterEqual(mask.count(), 1) + self.assertIsInstance(mask, posix.cpu_set) + self.assertRaises(OSError, posix.sched_getaffinity, -1, 1024) + empty = posix.cpu_set(10) + posix.sched_setaffinity(0, mask) + self.assertRaises(OSError, posix.sched_setaffinity, 0, empty) + self.assertRaises(OSError, posix.sched_setaffinity, -1, mask) + + @requires_sched_h + def test_cpu_set_basic(self): + s = posix.cpu_set(10) + self.assertEqual(len(s), 10) + self.assertEqual(s.count(), 0) + s.set(0) + s.set(9) + self.assertTrue(s.isset(0)) + self.assertTrue(s.isset(9)) + self.assertFalse(s.isset(5)) + self.assertEqual(s.count(), 2) + s.clear(0) + self.assertFalse(s.isset(0)) + self.assertEqual(s.count(), 1) + s.zero() + self.assertFalse(s.isset(0)) + self.assertFalse(s.isset(9)) + self.assertEqual(s.count(), 0) + self.assertRaises(ValueError, s.set, -1) + self.assertRaises(ValueError, s.set, 10) + self.assertRaises(ValueError, s.clear, -1) + self.assertRaises(ValueError, s.clear, 10) + self.assertRaises(ValueError, s.isset, -1) + self.assertRaises(ValueError, s.isset, 10) + + @requires_sched_h + def test_cpu_set_cmp(self): + self.assertNotEqual(posix.cpu_set(11), posix.cpu_set(12)) + l = posix.cpu_set(10) + r = posix.cpu_set(10) + self.assertEqual(l, r) + l.set(1) + self.assertNotEqual(l, r) + r.set(1) + self.assertEqual(l, r) + + @requires_sched_h + def test_cpu_set_bitwise(self): + l = posix.cpu_set(5) + l.set(0) + l.set(1) + r = posix.cpu_set(5) + r.set(1) + r.set(2) + b = l & r + self.assertEqual(b.count(), 1) + self.assertTrue(b.isset(1)) + b = l | r + self.assertEqual(b.count(), 3) + self.assertTrue(b.isset(0)) + self.assertTrue(b.isset(1)) + self.assertTrue(b.isset(2)) + b = l ^ r + self.assertEqual(b.count(), 2) + self.assertTrue(b.isset(0)) + self.assertFalse(b.isset(1)) + self.assertTrue(b.isset(2)) + b = l + b |= r + self.assertIs(b, l) + self.assertEqual(l.count(), 3) + class PosixGroupsTester(unittest.TestCase): def setUp(self): @@ -864,7 +996,6 @@ class PosixGroupsTester(unittest.TestCase): posix.setgroups(groups) self.assertListEqual(groups, posix.getgroups()) - def test_main(): try: support.run_unittest(PosixTester, PosixGroupsTester) diff --git a/Misc/NEWS b/Misc/NEWS index 11205971b93..95aec07fee4 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -251,6 +251,11 @@ Core and Builtins Library ------- +- Issue #12655: Expose functions from sched.h in the os module: sched_yield(), + sched_setscheduler(), sched_getscheduler(), sched_setparam(), + sched_get_min_priority(), sched_get_max_priority(), sched_rr_get_interval(), + sched_getaffinity(), sched_setaffinity(). + - Issues #11104, #8688: Fix the behavior of distutils' sdist command with manually-maintained MANIFEST files. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index ba80f5742d9..ef8f82eb4c4 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -105,6 +105,10 @@ corresponding Unix manual entries for more information on calls."); #include #endif +#ifdef HAVE_SCHED_H +#include +#endif + #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) #ifdef HAVE_SYS_SOCKET_H #include @@ -1605,6 +1609,7 @@ static PyTypeObject WaitidResultType; static int initialized; static PyTypeObject StatResultType; static PyTypeObject StatVFSResultType; +static PyTypeObject SchedParamType; static newfunc structseq_new; static PyObject * @@ -4544,6 +4549,542 @@ posix_fork(PyObject *self, PyObject *noargs) } #endif +#ifdef HAVE_SCHED_H + +PyDoc_STRVAR(posix_sched_get_priority_max__doc__, +"sched_get_priority_max(policy)\n\n\ +Get the maximum scheduling priority for *policy*."); + +static PyObject * +posix_sched_get_priority_max(PyObject *self, PyObject *args) +{ + int policy, max; + + if (!PyArg_ParseTuple(args, "i:sched_get_priority_max", &policy)) + return NULL; + max = sched_get_priority_max(policy); + if (max < 0) + return posix_error(); + return PyLong_FromLong(max); +} + +PyDoc_STRVAR(posix_sched_get_priority_min__doc__, +"sched_get_priority_min(policy)\n\n\ +Get the minimum scheduling priority for *policy*."); + +static PyObject * +posix_sched_get_priority_min(PyObject *self, PyObject *args) +{ + int policy, min; + + if (!PyArg_ParseTuple(args, "i:sched_get_priority_min", &policy)) + return NULL; + min = sched_get_priority_min(policy); + if (min < 0) + return posix_error(); + return PyLong_FromLong(min); +} + +PyDoc_STRVAR(posix_sched_getscheduler__doc__, +"sched_getscheduler(pid)\n\n\ +Get the scheduling policy for the process with a PID of *pid*.\n\ +Passing a PID of 0 returns the scheduling policy for the calling process."); + +static PyObject * +posix_sched_getscheduler(PyObject *self, PyObject *args) +{ + pid_t pid; + int policy; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID ":sched_getscheduler", &pid)) + return NULL; + policy = sched_getscheduler(pid); + if (policy < 0) + return posix_error(); + return PyLong_FromLong(policy); +} + +static PyObject * +sched_param_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *res, *priority; + static char *kwlist[] = {"sched_priority"}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:sched_param", kwlist, &priority)) + return NULL; + res = PyStructSequence_New(type); + if (!res) + return NULL; + Py_INCREF(priority); + PyStructSequence_SET_ITEM(res, 0, priority); + return res; +} + +PyDoc_STRVAR(sched_param__doc__, +"sched_param(sched_priority): A scheduling parameter.\n\n\ +Current has only one field: sched_priority"); + +static PyStructSequence_Field sched_param_fields[] = { + {"sched_priority", "the scheduling priority"}, + {0} +}; + +static PyStructSequence_Desc sched_param_desc = { + "sched_param", /* name */ + sched_param__doc__, /* doc */ + sched_param_fields, + 1 +}; + +static int +convert_sched_param(PyObject *param, struct sched_param *res) +{ + long priority; + + if (Py_TYPE(param) != &SchedParamType) { + PyErr_SetString(PyExc_TypeError, "must have a sched_param object"); + return 0; + } + priority = PyLong_AsLong(PyStructSequence_GET_ITEM(param, 0)); + if (priority == -1 && PyErr_Occurred()) + return 0; + if (priority > INT_MAX || priority < INT_MIN) { + PyErr_SetString(PyExc_OverflowError, "sched_priority out of range"); + return 0; + } + res->sched_priority = Py_SAFE_DOWNCAST(priority, long, int); + return 1; +} + +PyDoc_STRVAR(posix_sched_setscheduler__doc__, +"sched_setscheduler(pid, policy, param)\n\n\ +Set the scheduling policy, *policy*, for *pid*.\n\ +If *pid* is 0, the calling process is changed.\n\ +*param* is an instance of sched_param."); + +static PyObject * +posix_sched_setscheduler(PyObject *self, PyObject *args) +{ + pid_t pid; + int policy; + struct sched_param param; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "iO&:sched_setscheduler", + &pid, &policy, &convert_sched_param, ¶m)) + return NULL; + if (sched_setscheduler(pid, policy, ¶m)) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_sched_getparam__doc__, +"sched_getparam(pid) -> sched_param\n\n\ +Returns scheduling parameters for the process with *pid* as an instance of the\n\ +sched_param class. A PID of 0 means the calling process."); + +static PyObject * +posix_sched_getparam(PyObject *self, PyObject *args) +{ + pid_t pid; + struct sched_param param; + PyObject *res, *priority; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID ":sched_getparam", &pid)) + return NULL; + if (sched_getparam(pid, ¶m)) + return posix_error(); + res = PyStructSequence_New(&SchedParamType); + if (!res) + return NULL; + priority = PyLong_FromLong(param.sched_priority); + if (!priority) { + Py_DECREF(res); + return NULL; + } + PyStructSequence_SET_ITEM(res, 0, priority); + return res; +} + +PyDoc_STRVAR(posix_sched_setparam__doc__, +"sched_setparam(pid, param)\n\n\ +Set scheduling parameters for a process with PID *pid*.\n\ +A PID of 0 means the calling process."); + +static PyObject * +posix_sched_setparam(PyObject *self, PyObject *args) +{ + pid_t pid; + struct sched_param param; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O&:sched_setparam", + &pid, &convert_sched_param, ¶m)) + return NULL; + if (sched_setparam(pid, ¶m)) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_sched_rr_get_interval__doc__, +"sched_rr_get_interval(pid) -> float\n\n\ +Return the round-robin quantum for the process with PID *pid* in seconds."); + +static PyObject * +posix_sched_rr_get_interval(PyObject *self, PyObject *args) +{ + pid_t pid; + struct timespec interval; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID ":sched_rr_get_interval", &pid)) + return NULL; + if (sched_rr_get_interval(pid, &interval)) + return posix_error(); + return PyFloat_FromDouble((double)interval.tv_sec + 1e-9*interval.tv_nsec); +} + +PyDoc_STRVAR(posix_sched_yield__doc__, +"sched_yield()\n\n\ +Voluntarily relinquish the CPU."); + +static PyObject * +posix_sched_yield(PyObject *self, PyObject *noargs) +{ + if (sched_yield()) + return posix_error(); + Py_RETURN_NONE; +} + +typedef struct { + PyObject_HEAD; + Py_ssize_t size; + int ncpus; + cpu_set_t *set; +} Py_cpu_set; + +static PyTypeObject cpu_set_type; + +static void +cpu_set_dealloc(Py_cpu_set *set) +{ + assert(set->set); + CPU_FREE(set->set); + Py_TYPE(set)->tp_free(set); +} + +static Py_cpu_set * +make_new_cpu_set(PyTypeObject *type, Py_ssize_t size) +{ + Py_cpu_set *set; + + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "negative size"); + return NULL; + } + set = (Py_cpu_set *)type->tp_alloc(type, 0); + if (!set) + return NULL; + set->ncpus = size; + set->size = CPU_ALLOC_SIZE(size); + set->set = CPU_ALLOC(size); + if (!set->set) { + type->tp_free(set); + PyErr_NoMemory(); + return NULL; + } + CPU_ZERO_S(set->size, set->set); + return set; +} + +static PyObject * +cpu_set_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + int size; + + if (!_PyArg_NoKeywords("cpu_set()", kwargs) || + !PyArg_ParseTuple(args, "i:cpu_set", &size)) + return NULL; + return (PyObject *)make_new_cpu_set(type, size); +} + +static PyObject * +cpu_set_repr(Py_cpu_set *set) +{ + return PyUnicode_FromFormat("", set->ncpus); +} + +static Py_ssize_t +cpu_set_len(Py_cpu_set *set) +{ + return set->ncpus; +} + +static int +_get_cpu(Py_cpu_set *set, const char *requester, PyObject *args) +{ + int cpu; + if (!PyArg_ParseTuple(args, requester, &cpu)) + return -1; + if (cpu < 0) { + PyErr_SetString(PyExc_ValueError, "cpu < 0 not valid"); + return -1; + } + if (cpu >= set->ncpus) { + PyErr_SetString(PyExc_ValueError, "cpu too large for set"); + return -1; + } + return cpu; +} + +PyDoc_STRVAR(cpu_set_set_doc, +"cpu_set.set(i)\n\n\ +Add CPU *i* to the set."); + +static PyObject * +cpu_set_set(Py_cpu_set *set, PyObject *args) +{ + int cpu = _get_cpu(set, "i|set", args); + if (cpu == -1) + return NULL; + CPU_SET_S(cpu, set->size, set->set); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(cpu_set_count_doc, +"cpu_set.count() -> int\n\n\ +Return the number of CPUs active in the set."); + +static PyObject * +cpu_set_count(Py_cpu_set *set, PyObject *noargs) +{ + return PyLong_FromLong(CPU_COUNT_S(set->size, set->set)); +} + +PyDoc_STRVAR(cpu_set_clear_doc, +"cpu_set.clear(i)\n\n\ +Remove CPU *i* from the set."); + +static PyObject * +cpu_set_clear(Py_cpu_set *set, PyObject *args) +{ + int cpu = _get_cpu(set, "i|clear", args); + if (cpu == -1) + return NULL; + CPU_CLR_S(cpu, set->size, set->set); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(cpu_set_isset_doc, +"cpu_set.isset(i) -> bool\n\n\ +Test if CPU *i* is in the set."); + +static PyObject * +cpu_set_isset(Py_cpu_set *set, PyObject *args) +{ + int cpu = _get_cpu(set, "i|isset", args); + if (cpu == -1) + return NULL; + if (CPU_ISSET_S(cpu, set->size, set->set)) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(cpu_set_zero_doc, +"cpu_set.zero()\n\n\ +Clear the cpu_set."); + +static PyObject * +cpu_set_zero(Py_cpu_set *set, PyObject *noargs) +{ + CPU_ZERO_S(set->size, set->set); + Py_RETURN_NONE; +} + +static PyObject * +cpu_set_richcompare(Py_cpu_set *set, Py_cpu_set *other, int op) +{ + int eq; + + if ((op != Py_EQ && op != Py_NE) || Py_TYPE(other) != &cpu_set_type) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + eq = set->ncpus == other->ncpus && CPU_EQUAL_S(set->size, set->set, other->set); + if ((op == Py_EQ) ? eq : !eq) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +#define CPU_SET_BINOP(name, op) \ + static PyObject * \ + do_cpu_set_##name(Py_cpu_set *left, Py_cpu_set *right, Py_cpu_set *res) { \ + if (res) { \ + Py_INCREF(res); \ + } \ + else { \ + res = make_new_cpu_set(&cpu_set_type, left->size); \ + if (!res) \ + return NULL; \ + } \ + if (Py_TYPE(right) != &cpu_set_type || left->size != right->size) { \ + Py_DECREF(res); \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + assert(left->size == right->size == res->size); \ + op(res->size, res->set, left->set, right->set); \ + return (PyObject *)res; \ + } \ + static PyObject * \ + cpu_set_##name(Py_cpu_set *left, Py_cpu_set *right) { \ + return do_cpu_set_##name(left, right, NULL); \ + } \ + static PyObject * \ + cpu_set_i##name(Py_cpu_set *left, Py_cpu_set *right) { \ + return do_cpu_set_##name(left, right, left); \ + } \ + +CPU_SET_BINOP(and, CPU_AND_S) +CPU_SET_BINOP(or, CPU_OR_S) +CPU_SET_BINOP(xor, CPU_XOR_S) +#undef CPU_SET_BINOP + +PyDoc_STRVAR(cpu_set_doc, +"cpu_set(size)\n\n\ +Create an empty mask of CPUs."); + +static PyNumberMethods cpu_set_as_number = { + 0, /*nb_add*/ + 0, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_negative*/ + 0, /*nb_positive*/ + 0, /*nb_absolute*/ + 0, /*nb_bool*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + (binaryfunc)cpu_set_and, /*nb_and*/ + (binaryfunc)cpu_set_xor, /*nb_xor*/ + (binaryfunc)cpu_set_or, /*nb_or*/ + 0, /*nb_int*/ + 0, /*nb_reserved*/ + 0, /*nb_float*/ + 0, /*nb_inplace_add*/ + 0, /*nb_inplace_subtract*/ + 0, /*nb_inplace_multiply*/ + 0, /*nb_inplace_remainder*/ + 0, /*nb_inplace_power*/ + 0, /*nb_inplace_lshift*/ + 0, /*nb_inplace_rshift*/ + (binaryfunc)cpu_set_iand, /*nb_inplace_and*/ + (binaryfunc)cpu_set_ixor, /*nb_inplace_xor*/ + (binaryfunc)cpu_set_ior, /*nb_inplace_or*/ +}; + +static PySequenceMethods cpu_set_as_sequence = { + (lenfunc)cpu_set_len, /* sq_length */ +}; + +static PyMethodDef cpu_set_methods[] = { + {"clear", (PyCFunction)cpu_set_clear, METH_VARARGS, cpu_set_clear_doc}, + {"count", (PyCFunction)cpu_set_count, METH_NOARGS, cpu_set_count_doc}, + {"isset", (PyCFunction)cpu_set_isset, METH_VARARGS, cpu_set_isset_doc}, + {"set", (PyCFunction)cpu_set_set, METH_VARARGS, cpu_set_set_doc}, + {"zero", (PyCFunction)cpu_set_zero, METH_NOARGS, cpu_set_zero_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject cpu_set_type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "posix.cpu_set", /* tp_name */ + sizeof(Py_cpu_set), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)cpu_set_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)cpu_set_repr, /* tp_repr */ + &cpu_set_as_number, /* tp_as_number */ + &cpu_set_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + PyObject_HashNotImplemented, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + cpu_set_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)cpu_set_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + cpu_set_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + cpu_set_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + +PyDoc_STRVAR(posix_sched_setaffinity__doc__, +"sched_setaffinity(pid, cpu_set)\n\n\ +Set the affinity of the process with PID *pid* to *cpu_set*."); + +static PyObject * +posix_sched_setaffinity(PyObject *self, PyObject *args) +{ + pid_t pid; + Py_cpu_set *cpu_set; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O!|sched_setaffinity", + &pid, &cpu_set_type, &cpu_set)) + return NULL; + if (sched_setaffinity(pid, cpu_set->size, cpu_set->set)) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_sched_getaffinity__doc__, +"sched_getaffinity(pid, ncpus) -> cpu_set\n\n\ +Return the affinity of the process with PID *pid*.\n\ +The returned cpu_set will be of size *ncpus*."); + +static PyObject * +posix_sched_getaffinity(PyObject *self, PyObject *args) +{ + pid_t pid; + int ncpus; + Py_cpu_set *res; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "i|sched_getaffinity", + &pid, &ncpus)) + return NULL; + res = make_new_cpu_set(&cpu_set_type, ncpus); + if (!res) + return NULL; + if (sched_getaffinity(pid, res->size, res->set)) { + Py_DECREF(res); + return posix_error(); + } + return (PyObject *)res; +} + +#endif /* HAVE_SCHED_H */ + /* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */ /* IRIX has both /dev/ptc and /dev/ptmx, use ptmx */ #if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX) @@ -9506,6 +10047,18 @@ static PyMethodDef posix_methods[] = { #ifdef HAVE_FORK {"fork", posix_fork, METH_NOARGS, posix_fork__doc__}, #endif /* HAVE_FORK */ +#ifdef HAVE_SCHED_H + {"sched_get_priority_max", posix_sched_get_priority_max, METH_VARARGS, posix_sched_get_priority_max__doc__}, + {"sched_get_priority_min", posix_sched_get_priority_min, METH_VARARGS, posix_sched_get_priority_min__doc__}, + {"sched_getparam", posix_sched_getparam, METH_VARARGS, posix_sched_getparam__doc__}, + {"sched_getscheduler", posix_sched_getscheduler, METH_VARARGS, posix_sched_getscheduler__doc__}, + {"sched_rr_get_interval", posix_sched_rr_get_interval, METH_VARARGS, posix_sched_rr_get_interval__doc__}, + {"sched_setparam", posix_sched_setparam, METH_VARARGS, posix_sched_setparam__doc__}, + {"sched_setscheduler", posix_sched_setscheduler, METH_VARARGS, posix_sched_setscheduler__doc__}, + {"sched_yield", posix_sched_yield, METH_NOARGS, posix_sched_yield__doc__}, + {"sched_setaffinity", posix_sched_setaffinity, METH_VARARGS, posix_sched_setaffinity__doc__}, + {"sched_getaffinity", posix_sched_getaffinity, METH_VARARGS, posix_sched_getaffinity__doc__}, +#endif #if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) {"openpty", posix_openpty, METH_NOARGS, posix_openpty__doc__}, #endif /* HAVE_OPENPTY || HAVE__GETPTY || HAVE_DEV_PTMX */ @@ -10243,6 +10796,24 @@ all_ins(PyObject *d) #endif #endif +#ifdef HAVE_SCHED_H + if (ins(d, "SCHED_FIFO", (long)SCHED_FIFO)) return -1; + if (ins(d, "SCHED_RR", (long)SCHED_RR)) return -1; +#ifdef SCHED_SPORADIC + if (ins(d, "SCHED_SPORADIC", (long)SCHED_SPORADIC) return -1; +#endif + if (ins(d, "SCHED_OTHER", (long)SCHED_OTHER)) return -1; +#ifdef SCHED_BATCH + if (ins(d, "SCHED_BATCH", (long)SCHED_BATCH)) return -1; +#endif +#ifdef SCHED_IDLE + if (ins(d, "SCHED_IDLE", (long)SCHED_IDLE)) return -1; +#endif +#ifdef SCHED_RESET_ON_FORK + if (ins(d, "SCHED_RESET_ON_FORK", (long)SCHED_RESET_ON_FORK)) return -1; +#endif +#endif + #if defined(PYOS_OS2) if (insertvalues(d)) return -1; #endif @@ -10305,6 +10876,11 @@ INITFUNC(void) Py_INCREF(PyExc_OSError); PyModule_AddObject(m, "error", PyExc_OSError); + if (PyType_Ready(&cpu_set_type) < 0) + return NULL; + Py_INCREF(&cpu_set_type); + PyModule_AddObject(m, "cpu_set", (PyObject *)&cpu_set_type); + #ifdef HAVE_PUTENV if (posix_putenv_garbage == NULL) posix_putenv_garbage = PyDict_New(); @@ -10334,6 +10910,12 @@ INITFUNC(void) # else ticks_per_second = 60; /* magic fallback value; may be bogus */ # endif +#endif + +#ifdef HAVE_SCHED_H + sched_param_desc.name = MODNAME ".sched_param"; + PyStructSequence_InitType(&SchedParamType, &sched_param_desc); + SchedParamType.tp_new = sched_param_new; #endif } #if defined(HAVE_WAITID) && !defined(__APPLE__) @@ -10345,6 +10927,8 @@ INITFUNC(void) Py_INCREF((PyObject*) &StatVFSResultType); PyModule_AddObject(m, "statvfs_result", (PyObject*) &StatVFSResultType); + Py_INCREF(&SchedParamType); + PyModule_AddObject(m, "sched_param", (PyObject *)&SchedParamType); initialized = 1; #ifdef __APPLE__ diff --git a/configure b/configure index 17fecec3942..ca13d706d0b 100755 --- a/configure +++ b/configure @@ -6092,7 +6092,7 @@ fi for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ fcntl.h grp.h \ ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ -shadow.h signal.h stdint.h stropts.h termios.h \ +sched.h shadow.h signal.h stdint.h stropts.h termios.h \ unistd.h utime.h \ sys/audioio.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \ sys/lock.h sys/mkdev.h sys/modem.h \ @@ -14418,8 +14418,8 @@ esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. -config_files="`echo $ac_config_files`" -config_headers="`echo $ac_config_headers`" +config_files="$ac_config_files" +config_headers="$ac_config_headers" _ACEOF diff --git a/configure.in b/configure.in index 95ceab8d971..86e1a98f6cc 100644 --- a/configure.in +++ b/configure.in @@ -1301,7 +1301,7 @@ AC_HEADER_STDC AC_CHECK_HEADERS(asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ fcntl.h grp.h \ ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ -shadow.h signal.h stdint.h stropts.h termios.h \ +sched.h shadow.h signal.h stdint.h stropts.h termios.h \ unistd.h utime.h \ sys/audioio.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \ sys/lock.h sys/mkdev.h sys/modem.h \ diff --git a/pyconfig.h.in b/pyconfig.h.in index da6197cfa73..e9a4cace9ec 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -650,6 +650,9 @@ /* Define to 1 if you have the `round' function. */ #undef HAVE_ROUND +/* Define to 1 if you have the header file. */ +#undef HAVE_SCHED_H + /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT