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