[Python-checkins] r75958 - in python/trunk: Doc/library/thread.rst Lib/test/test_support.py Lib/test/test_thread.py Misc/NEWS Modules/threadmodule.c

Brett Cannon brett at python.org
Fri Oct 30 21:13:11 CET 2009


If the new function is private, why document it?

On Fri, Oct 30, 2009 at 10:27, antoine.pitrou
<python-checkins at python.org> wrote:
> Author: antoine.pitrou
> Date: Fri Oct 30 18:07:08 2009
> New Revision: 75958
>
> Log:
> Issue #7222: Make thread "reaping" more reliable so that reference
> leak-chasing test runs give sensible results. The previous method of
> reaping threads could return successfully while some Thread objects were
> still referenced. This also introduces a new private function:
> :func: hread._count().
>
>
>
> Modified:
>   python/trunk/Doc/library/thread.rst
>   python/trunk/Lib/test/test_support.py
>   python/trunk/Lib/test/test_thread.py
>   python/trunk/Misc/NEWS
>   python/trunk/Modules/threadmodule.c
>
> Modified: python/trunk/Doc/library/thread.rst
> ==============================================================================
> --- python/trunk/Doc/library/thread.rst (original)
> +++ python/trunk/Doc/library/thread.rst Fri Oct 30 18:07:08 2009
> @@ -112,6 +112,20 @@
>
>    .. versionadded:: 2.5
>
> +
> +.. function:: _count()
> +
> +   Return the number of currently running Python threads, excluding the main
> +   thread.  The returned number comprises all threads created through
> +   :func:`start_new_thread` as well as :class:`threading.Thread`, and not
> +   yet finished.
> +
> +   This function is meant for internal and specialized purposes only. In
> +   most applications :func:`threading.enumerate()` should be used instead.
> +
> +   .. versionadded:: 2.7
> +
> +
>  Lock objects have the following methods:
>
>
>
> Modified: python/trunk/Lib/test/test_support.py
> ==============================================================================
> --- python/trunk/Lib/test/test_support.py       (original)
> +++ python/trunk/Lib/test/test_support.py       Fri Oct 30 18:07:08 2009
> @@ -952,24 +952,29 @@
>  #=======================================================================
>  # Threading support to prevent reporting refleaks when running regrtest.py -R
>
> +# NOTE: we use thread._count() rather than threading.enumerate() (or the
> +# moral equivalent thereof) because a threading.Thread object is still alive
> +# until its __bootstrap() method has returned, even after it has been
> +# unregistered from the threading module.
> +# thread._count(), on the other hand, only gets decremented *after* the
> +# __bootstrap() method has returned, which gives us reliable reference counts
> +# at the end of a test run.
> +
>  def threading_setup():
> -    import threading
> -    return len(threading._active), len(threading._limbo)
> +    import thread
> +    return thread._count(),
>
> -def threading_cleanup(num_active, num_limbo):
> -    import threading
> +def threading_cleanup(nb_threads):
> +    import thread
>     import time
>
>     _MAX_COUNT = 10
> -    count = 0
> -    while len(threading._active) != num_active and count < _MAX_COUNT:
> -        count += 1
> -        time.sleep(0.1)
> -
> -    count = 0
> -    while len(threading._limbo) != num_limbo and count < _MAX_COUNT:
> -        count += 1
> +    for count in range(_MAX_COUNT):
> +        n = thread._count()
> +        if n == nb_threads:
> +            break
>         time.sleep(0.1)
> +    # XXX print a warning in case of failure?
>
>  def reap_threads(func):
>     @functools.wraps(func)
>
> Modified: python/trunk/Lib/test/test_thread.py
> ==============================================================================
> --- python/trunk/Lib/test/test_thread.py        (original)
> +++ python/trunk/Lib/test/test_thread.py        Fri Oct 30 18:07:08 2009
> @@ -4,6 +4,7 @@
>  from test import test_support
>  import thread
>  import time
> +import weakref
>
>
>  NUMTASKS = 10
> @@ -101,6 +102,32 @@
>
>             thread.stack_size(0)
>
> +    def test__count(self):
> +        # Test the _count() function.
> +        orig = thread._count()
> +        mut = thread.allocate_lock()
> +        mut.acquire()
> +        started = []
> +        def task():
> +            started.append(None)
> +            mut.acquire()
> +            mut.release()
> +        thread.start_new_thread(task, ())
> +        while not started:
> +            time.sleep(0.01)
> +        self.assertEquals(thread._count(), orig + 1)
> +        # Allow the task to finish.
> +        mut.release()
> +        # The only reliable way to be sure that the thread ended from the
> +        # interpreter's point of view is to wait for the function object to be
> +        # destroyed.
> +        done = []
> +        wr = weakref.ref(task, lambda _: done.append(None))
> +        del task
> +        while not done:
> +            time.sleep(0.01)
> +        self.assertEquals(thread._count(), orig)
> +
>
>  class Barrier:
>     def __init__(self, num_threads):
>
> Modified: python/trunk/Misc/NEWS
> ==============================================================================
> --- python/trunk/Misc/NEWS      (original)
> +++ python/trunk/Misc/NEWS      Fri Oct 30 18:07:08 2009
> @@ -1525,6 +1525,12 @@
>  Tests
>  -----
>
> +- Issue #7222: Make thread "reaping" more reliable so that reference
> +  leak-chasing test runs give sensible results. The previous method of
> +  reaping threads could return successfully while some Thread objects were
> +  still referenced. This also introduces a new private function:
> +  :func:`thread._count()`.
> +
>  - Issue #7151: fixed regrtest -j so that output to stderr from a test no
>   longer runs the risk of causing the worker thread to fail.
>
>
> Modified: python/trunk/Modules/threadmodule.c
> ==============================================================================
> --- python/trunk/Modules/threadmodule.c (original)
> +++ python/trunk/Modules/threadmodule.c Fri Oct 30 18:07:08 2009
> @@ -14,7 +14,7 @@
>  #include "pythread.h"
>
>  static PyObject *ThreadError;
> -
> +static long nb_threads = 0;
>
>  /* Lock objects */
>
> @@ -439,6 +439,7 @@
>        tstate = PyThreadState_New(boot->interp);
>
>        PyEval_AcquireThread(tstate);
> +       nb_threads++;
>        res = PyEval_CallObjectWithKeywords(
>                boot->func, boot->args, boot->keyw);
>        if (res == NULL) {
> @@ -463,6 +464,7 @@
>        Py_DECREF(boot->args);
>        Py_XDECREF(boot->keyw);
>        PyMem_DEL(boot_raw);
> +       nb_threads--;
>        PyThreadState_Clear(tstate);
>        PyThreadState_DeleteCurrent();
>        PyThread_exit_thread();
> @@ -606,6 +608,18 @@
>  A thread's identity may be reused for another thread after it exits.");
>
>  static PyObject *
> +thread__count(PyObject *self)
> +{
> +       return PyInt_FromLong(nb_threads);
> +}
> +
> +PyDoc_STRVAR(_count_doc,
> +"_count() -> integer\n\
> +\n\
> +Return the number of currently running (sub)threads.\n\
> +This excludes the main thread.");
> +
> +static PyObject *
>  thread_stack_size(PyObject *self, PyObject *args)
>  {
>        size_t old_size;
> @@ -678,6 +692,8 @@
>         METH_NOARGS, interrupt_doc},
>        {"get_ident",           (PyCFunction)thread_get_ident,
>         METH_NOARGS, get_ident_doc},
> +       {"_count",              (PyCFunction)thread__count,
> +        METH_NOARGS, _count_doc},
>        {"stack_size",          (PyCFunction)thread_stack_size,
>                                METH_VARARGS,
>                                stack_size_doc},
> @@ -735,6 +751,8 @@
>        if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0)
>                return;
>
> +       nb_threads = 0;
> +
>        /* Initialize the C thread library */
>        PyThread_init_thread();
>  }
> _______________________________________________
> Python-checkins mailing list
> Python-checkins at python.org
> http://mail.python.org/mailman/listinfo/python-checkins
>


More information about the Python-checkins mailing list