14
14
15
15
#include "Python.h"
16
16
#include "pycore_fileutils.h" // _Py_set_inheritable()
17
+ #include "pycore_import.h" // _PyImport_GetModuleAttrString()
17
18
#include "pycore_time.h" // _PyTime_t
18
19
20
+ #include <stdbool.h>
19
21
#include <stddef.h> // offsetof()
20
22
#ifndef MS_WINDOWS
21
23
# include <unistd.h> // close()
@@ -75,13 +77,26 @@ extern void bzero(void *, int);
75
77
# define POLLPRI 0
76
78
#endif
77
79
80
+ #ifdef HAVE_KQUEUE
81
+ // Linked list to track kqueue objects with an open fd, so
82
+ // that we can invalidate them at fork;
83
+ typedef struct _kqueue_list_item {
84
+ struct kqueue_queue_Object * obj ;
85
+ struct _kqueue_list_item * next ;
86
+ } _kqueue_list_item , * _kqueue_list ;
87
+ #endif
88
+
78
89
typedef struct {
79
90
PyObject * close ;
80
91
PyTypeObject * poll_Type ;
81
92
PyTypeObject * devpoll_Type ;
82
93
PyTypeObject * pyEpoll_Type ;
94
+ #ifdef HAVE_KQUEUE
83
95
PyTypeObject * kqueue_event_Type ;
84
96
PyTypeObject * kqueue_queue_Type ;
97
+ _kqueue_list kqueue_open_list ;
98
+ bool kqueue_tracking_initialized ;
99
+ #endif
85
100
} _selectstate ;
86
101
87
102
static struct PyModuleDef selectmodule ;
@@ -1754,7 +1769,7 @@ typedef struct {
1754
1769
1755
1770
#define kqueue_event_Check (op , state ) (PyObject_TypeCheck((op), state->kqueue_event_Type))
1756
1771
1757
- typedef struct {
1772
+ typedef struct kqueue_queue_Object {
1758
1773
PyObject_HEAD
1759
1774
SOCKET kqfd ; /* kqueue control fd */
1760
1775
} kqueue_queue_Object ;
@@ -1940,13 +1955,116 @@ kqueue_queue_err_closed(void)
1940
1955
return NULL ;
1941
1956
}
1942
1957
1958
+ static PyObject *
1959
+ kqueue_tracking_after_fork (PyObject * module ) {
1960
+ _selectstate * state = get_select_state (module );
1961
+ _kqueue_list_item * item = state -> kqueue_open_list ;
1962
+ state -> kqueue_open_list = NULL ;
1963
+ while (item ) {
1964
+ // Safety: we hold the GIL, and references are removed from this list
1965
+ // before the object is deallocated.
1966
+ kqueue_queue_Object * obj = item -> obj ;
1967
+ assert (obj -> kqfd != -1 );
1968
+ obj -> kqfd = -1 ;
1969
+ _kqueue_list_item * next = item -> next ;
1970
+ PyMem_Free (item );
1971
+ item = next ;
1972
+ }
1973
+ Py_RETURN_NONE ;
1974
+ }
1975
+
1976
+ static PyMethodDef kqueue_tracking_after_fork_def = {
1977
+ "kqueue_tracking_after_fork" , (PyCFunction )kqueue_tracking_after_fork ,
1978
+ METH_NOARGS , "Invalidate open select.kqueue objects after fork."
1979
+ };
1980
+
1981
+ static void
1982
+ kqueue_tracking_init (PyObject * module ) {
1983
+ _selectstate * state = get_select_state (module );
1984
+ assert (state -> kqueue_open_list == NULL );
1985
+ // Register a callback to invalidate kqueues with open fds after fork.
1986
+ PyObject * register_at_fork = NULL , * cb = NULL , * args = NULL ,
1987
+ * kwargs = NULL , * result = NULL ;
1988
+ register_at_fork = _PyImport_GetModuleAttrString ("posix" ,
1989
+ "register_at_fork" );
1990
+ if (register_at_fork == NULL ) {
1991
+ goto finally ;
1992
+ }
1993
+ cb = PyCFunction_New (& kqueue_tracking_after_fork_def , module );
1994
+ if (cb == NULL ) {
1995
+ goto finally ;
1996
+ }
1997
+ args = PyTuple_New (0 );
1998
+ assert (args != NULL );
1999
+ kwargs = Py_BuildValue ("{sO}" , "after_in_child" , cb );
2000
+ if (kwargs == NULL ) {
2001
+ goto finally ;
2002
+ }
2003
+ result = PyObject_Call (register_at_fork , args , kwargs );
2004
+
2005
+ finally :
2006
+ if (PyErr_Occurred ()) {
2007
+ // There are a few reasons registration can fail, especially if someone
2008
+ // touched posix.register_at_fork. But everything else still works so
2009
+ // instead of raising we issue a warning and move along.
2010
+ PyObject * exc = PyErr_GetRaisedException ();
2011
+ PyObject * exctype = (PyObject * )Py_TYPE (exc );
2012
+ PyErr_WarnFormat (PyExc_RuntimeWarning , 1 ,
2013
+ "An exception of type %S was raised while registering an "
2014
+ "after-fork handler for select.kqueue objects: %S" , exctype , exc );
2015
+ Py_DECREF (exc );
2016
+ }
2017
+ Py_XDECREF (register_at_fork );
2018
+ Py_XDECREF (cb );
2019
+ Py_XDECREF (args );
2020
+ Py_XDECREF (kwargs );
2021
+ Py_XDECREF (result );
2022
+ state -> kqueue_tracking_initialized = true;
2023
+ }
2024
+
2025
+ static int
2026
+ kqueue_tracking_add (_selectstate * state , kqueue_queue_Object * self ) {
2027
+ if (!state -> kqueue_tracking_initialized ) {
2028
+ kqueue_tracking_init (PyType_GetModule (Py_TYPE (self )));
2029
+ }
2030
+ assert (self -> kqfd >= 0 );
2031
+ _kqueue_list_item * item = PyMem_New (_kqueue_list_item , 1 );
2032
+ if (item == NULL ) {
2033
+ PyErr_NoMemory ();
2034
+ return -1 ;
2035
+ }
2036
+ item -> obj = self ;
2037
+ item -> next = state -> kqueue_open_list ;
2038
+ state -> kqueue_open_list = item ;
2039
+ return 0 ;
2040
+ }
2041
+
2042
+ static void
2043
+ kqueue_tracking_remove (_selectstate * state , kqueue_queue_Object * self ) {
2044
+ _kqueue_list * listptr = & state -> kqueue_open_list ;
2045
+ while (* listptr != NULL ) {
2046
+ _kqueue_list_item * item = * listptr ;
2047
+ if (item -> obj == self ) {
2048
+ * listptr = item -> next ;
2049
+ PyMem_Free (item );
2050
+ return ;
2051
+ }
2052
+ listptr = & item -> next ;
2053
+ }
2054
+ // The item should be in the list when we remove it,
2055
+ // and it should only be removed once at close time.
2056
+ assert (0 );
2057
+ }
2058
+
1943
2059
static int
1944
2060
kqueue_queue_internal_close (kqueue_queue_Object * self )
1945
2061
{
1946
2062
int save_errno = 0 ;
1947
2063
if (self -> kqfd >= 0 ) {
1948
2064
int kqfd = self -> kqfd ;
1949
2065
self -> kqfd = -1 ;
2066
+ _selectstate * state = _selectstate_by_type (Py_TYPE (self ));
2067
+ kqueue_tracking_remove (state , self );
1950
2068
Py_BEGIN_ALLOW_THREADS
1951
2069
if (close (kqfd ) < 0 )
1952
2070
save_errno = errno ;
@@ -1987,6 +2105,13 @@ newKqueue_Object(PyTypeObject *type, SOCKET fd)
1987
2105
return NULL ;
1988
2106
}
1989
2107
}
2108
+
2109
+ _selectstate * state = _selectstate_by_type (type );
2110
+ if (kqueue_tracking_add (state , self ) < 0 ) {
2111
+ Py_DECREF (self );
2112
+ return NULL ;
2113
+ }
2114
+
1990
2115
return (PyObject * )self ;
1991
2116
}
1992
2117
@@ -2017,13 +2142,11 @@ select_kqueue_impl(PyTypeObject *type)
2017
2142
}
2018
2143
2019
2144
static void
2020
- kqueue_queue_dealloc (kqueue_queue_Object * self )
2145
+ kqueue_queue_finalize (kqueue_queue_Object * self )
2021
2146
{
2022
- PyTypeObject * type = Py_TYPE ( self );
2147
+ PyObject * error = PyErr_GetRaisedException ( );
2023
2148
kqueue_queue_internal_close (self );
2024
- freefunc kqueue_free = PyType_GetSlot (type , Py_tp_free );
2025
- kqueue_free ((PyObject * )self );
2026
- Py_DECREF ((PyObject * )type );
2149
+ PyErr_SetRaisedException (error );
2027
2150
}
2028
2151
2029
2152
/*[clinic input]
@@ -2357,11 +2480,11 @@ static PyMethodDef kqueue_queue_methods[] = {
2357
2480
};
2358
2481
2359
2482
static PyType_Slot kqueue_queue_Type_slots [] = {
2360
- {Py_tp_dealloc , kqueue_queue_dealloc },
2361
2483
{Py_tp_doc , (void * )select_kqueue__doc__ },
2362
2484
{Py_tp_getset , kqueue_queue_getsetlist },
2363
2485
{Py_tp_methods , kqueue_queue_methods },
2364
2486
{Py_tp_new , select_kqueue },
2487
+ {Py_tp_finalize , kqueue_queue_finalize },
2365
2488
{0 , 0 },
2366
2489
};
2367
2490
@@ -2406,8 +2529,11 @@ _select_traverse(PyObject *module, visitproc visit, void *arg)
2406
2529
Py_VISIT (state -> poll_Type );
2407
2530
Py_VISIT (state -> devpoll_Type );
2408
2531
Py_VISIT (state -> pyEpoll_Type );
2532
+ #ifdef HAVE_KQUEUE
2409
2533
Py_VISIT (state -> kqueue_event_Type );
2410
2534
Py_VISIT (state -> kqueue_queue_Type );
2535
+ // state->kqueue_open_list only holds borrowed refs
2536
+ #endif
2411
2537
return 0 ;
2412
2538
}
2413
2539
@@ -2420,8 +2546,10 @@ _select_clear(PyObject *module)
2420
2546
Py_CLEAR (state -> poll_Type );
2421
2547
Py_CLEAR (state -> devpoll_Type );
2422
2548
Py_CLEAR (state -> pyEpoll_Type );
2549
+ #ifdef HAVE_KQUEUE
2423
2550
Py_CLEAR (state -> kqueue_event_Type );
2424
2551
Py_CLEAR (state -> kqueue_queue_Type );
2552
+ #endif
2425
2553
return 0 ;
2426
2554
}
2427
2555
@@ -2570,6 +2698,8 @@ _select_exec(PyObject *m)
2570
2698
} while (0)
2571
2699
2572
2700
#ifdef HAVE_KQUEUE
2701
+ state -> kqueue_open_list = NULL ;
2702
+
2573
2703
state -> kqueue_event_Type = (PyTypeObject * )PyType_FromModuleAndSpec (
2574
2704
m , & kqueue_event_Type_spec , NULL );
2575
2705
if (state -> kqueue_event_Type == NULL ) {
0 commit comments