@@ -6739,10 +6739,13 @@ os_register_at_fork_impl(PyObject *module, PyObject *before,
67396739}
67406740#endif /* HAVE_FORK */
67416741
6742- // Common code to raise a warning if we know there is more than one thread
6743- // running in the process. Best effort, silent if unable to count threads.
6744- // Constraint: Avoids locks. Quick . Never leaves an error set.
6742+ // Common code to raise a warning if we detect there is more than one thread
6743+ // running in the process. Best effort, silent if unable to count threads.
6744+ // Constraint: Quick. Never overcounts . Never leaves an error set.
67456745static void warn_about_fork_with_threads (const char * name ) {
6746+ // TODO: Consider making an `os` module API to return the current number
6747+ // of threads in the process. That'd presumably use this platform code but
6748+ // raise an error rather than using the inaccurate fallback.
67466749 Py_ssize_t num_python_threads = 0 ;
67476750#if defined(__APPLE__ ) && defined(HAVE_GETPID )
67486751 mach_port_t macos_self = mach_task_self ();
@@ -6760,6 +6763,8 @@ static void warn_about_fork_with_threads(const char* name) {
67606763 FILE * proc_stat = fopen ("/proc/self/stat" , "r" );
67616764 if (proc_stat ) {
67626765 size_t n ;
6766+ // Size chosen arbitrarily. ~60% more bytes than a 20th column index
6767+ // observed on the author's workstation.
67636768 char stat_line [160 ];
67646769 n = fread (& stat_line , 1 , 159 , proc_stat );
67656770 stat_line [n ] = '\0' ;
@@ -6777,7 +6782,8 @@ static void warn_about_fork_with_threads(const char* name) {
67776782 }
67786783#endif
67796784 if (num_python_threads <= 0 ) {
6780- // Fall back to just the number of threads this CPython runtime knows about.
6785+ // Fall back to just the number of threads our threading module knows about.
6786+ // An incomplete view of the world, but better than nothing for our purpose.
67816787 PyObject * threading = PyImport_GetModule (& _Py_ID (threading ));
67826788 if (!threading ) {
67836789 PyErr_Clear ();
@@ -6796,19 +6802,16 @@ static void warn_about_fork_with_threads(const char* name) {
67966802 Py_XDECREF (threading_active );
67976803 return ;
67986804 }
6799- // Worst case if someone replaced threading._active or threading._limbo
6800- // with non-dicts, we get -1 from *Length() below and undercount.
6801- // Whatever. That shouldn't happen, we're best effort so we clear errors
6802- // and move on.
6803- assert (PyMapping_Check (threading_active ));
6804- assert (PyMapping_Check (threading_limbo ));
6805+ Py_DECREF (threading );
68056806 // Duplicating what threading.active_count() does but without holding
68066807 // threading._active_limbo_lock so our count could be inaccurate if these
68076808 // dicts are mid-update from another thread. Not a big deal.
6809+ // Worst case if someone replaced threading._active or threading._limbo
6810+ // with non-dicts, we get -1 from *Length() below and undercount.
6811+ // Nobody should, but we're best effort so we clear errors and move on.
68086812 num_python_threads = (PyMapping_Length (threading_active )
68096813 + PyMapping_Length (threading_limbo ));
68106814 PyErr_Clear ();
6811- Py_DECREF (threading );
68126815 Py_DECREF (threading_active );
68136816 Py_DECREF (threading_limbo );
68146817 }
@@ -6817,6 +6820,7 @@ static void warn_about_fork_with_threads(const char* name) {
68176820 PyExc_DeprecationWarning , 1 ,
68186821 "multi-threaded process detected, "
68196822 "use of %s() may lead to deadlocks in the child." , name );
6823+ PyErr_Clear ();
68206824 }
68216825}
68226826
0 commit comments