[Python-checkins] cpython: expose sched.h functions (closes #12655)

benjamin.peterson python-checkins at python.org
Wed Aug 3 00:30:42 CEST 2011


http://hg.python.org/cpython/rev/89e92e684b37
changeset:   71704:89e92e684b37
user:        Benjamin Peterson <benjamin at python.org>
date:        Tue Aug 02 17:30:04 2011 -0500
summary:
  expose sched.h functions (closes #12655)

files:
  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
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -2744,6 +2744,155 @@
    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
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -829,6 +829,138 @@
         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 @@
             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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -251,6 +251,11 @@
 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
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -105,6 +105,10 @@
 #include <sys/sendfile.h>
 #endif
 
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
@@ -1605,6 +1609,7 @@
 static int initialized;
 static PyTypeObject StatResultType;
 static PyTypeObject StatVFSResultType;
+static PyTypeObject SchedParamType;
 static newfunc structseq_new;
 
 static PyObject *
@@ -4544,6 +4549,542 @@
 }
 #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, &param))
+        return NULL;
+    if (sched_setscheduler(pid, policy, &param))
+        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, &param))
+        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, &param))
+        return NULL;
+    if (sched_setparam(pid, &param))
+        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("<cpu_set with %li entries>", 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 @@
 #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 @@
 #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 @@
     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();
@@ -10335,6 +10911,12 @@
         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__)
     Py_INCREF((PyObject*) &WaitidResultType);
@@ -10345,6 +10927,8 @@
     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
--- a/configure
+++ b/configure
@@ -6092,7 +6092,7 @@
 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 @@
 
 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
--- a/configure.in
+++ b/configure.in
@@ -1301,7 +1301,7 @@
 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
--- 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 <sched.h> header file. */
+#undef HAVE_SCHED_H
+
 /* Define to 1 if you have the `select' function. */
 #undef HAVE_SELECT
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list