#ifndef Py_BUILD_CORE_BUILTIN
#define Py_BUILD_CORE_MODULE …
#endif
#if defined(HAVE_POLL_H) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include "Python.h"
#include "pycore_fileutils.h"
#include "pycore_import.h"
#include "pycore_time.h"
#include <stdbool.h>
#include <stddef.h>
#ifndef MS_WINDOWS
# include <unistd.h>
#endif
#ifdef HAVE_SYS_DEVPOLL_H
#include <sys/resource.h>
#include <sys/devpoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#ifdef __APPLE__
#undef HAVE_BROKEN_POLL
#endif
#if defined(MS_WINDOWS) && !defined(FD_SETSIZE)
#define FD_SETSIZE …
#endif
#if defined(HAVE_POLL_H)
#include <poll.h>
#elif defined(HAVE_SYS_POLL_H)
#include <sys/poll.h>
#endif
#ifdef __sgi
extern void bzero(void *, int);
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef MS_WINDOWS
# ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
# endif
# include <winsock2.h>
#else
#define SOCKET …
#endif
#if defined(__wasi__) && !defined(POLLPRI)
#define POLLPRI …
#endif
#ifdef HAVE_KQUEUE
typedef struct _kqueue_list_item {
struct kqueue_queue_Object *obj;
struct _kqueue_list_item *next;
} _kqueue_list_item, *_kqueue_list;
#endif
_selectstate;
static struct PyModuleDef selectmodule;
static inline _selectstate*
get_select_state(PyObject *module)
{ … }
#define _selectstate_by_type(type) …
pylist;
static void
reap_obj(pylist fd2obj[FD_SETSIZE + 1])
{ … }
static int
seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1])
{ … }
static PyObject *
set2list(fd_set *set, pylist fd2obj[FD_SETSIZE + 1])
{ … }
#undef SELECT_USES_HEAP
#if FD_SETSIZE > 1024
#define SELECT_USES_HEAP
#endif
static PyObject *
select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
PyObject *xlist, PyObject *timeout_obj)
{ … }
#if defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)
pollObject;
static int
update_ufd_array(pollObject *self)
{ … }
static PyObject *
select_poll_register_impl(pollObject *self, int fd, unsigned short eventmask)
{ … }
static PyObject *
select_poll_modify_impl(pollObject *self, int fd, unsigned short eventmask)
{ … }
static PyObject *
select_poll_unregister_impl(pollObject *self, int fd)
{ … }
static PyObject *
select_poll_poll_impl(pollObject *self, PyObject *timeout_obj)
{ … }
static pollObject *
newPollObject(PyObject *module)
{ … }
static void
poll_dealloc(pollObject *self)
{ … }
#ifdef HAVE_SYS_DEVPOLL_H
static PyMethodDef devpoll_methods[];
typedef struct {
PyObject_HEAD
int fd_devpoll;
int max_n_fds;
int n_fds;
struct pollfd *fds;
} devpollObject;
static PyObject *
devpoll_err_closed(void)
{
PyErr_SetString(PyExc_ValueError, "I/O operation on closed devpoll object");
return NULL;
}
static int devpoll_flush(devpollObject *self)
{
int size, n;
if (!self->n_fds) return 0;
size = sizeof(struct pollfd)*self->n_fds;
self->n_fds = 0;
n = _Py_write(self->fd_devpoll, self->fds, size);
if (n == -1)
return -1;
if (n < size) {
PyErr_Format(PyExc_OSError, "failed to write all pollfds. "
"Please, report at https://github.com/python/cpython/issues/. "
"Data to report: Size tried: %d, actual size written: %d.",
size, n);
return -1;
}
return 0;
}
static PyObject *
internal_devpoll_register(devpollObject *self, int fd,
unsigned short events, int remove)
{
if (self->fd_devpoll < 0)
return devpoll_err_closed();
if (remove) {
self->fds[self->n_fds].fd = fd;
self->fds[self->n_fds].events = POLLREMOVE;
if (++self->n_fds == self->max_n_fds) {
if (devpoll_flush(self))
return NULL;
}
}
self->fds[self->n_fds].fd = fd;
self->fds[self->n_fds].events = (signed short)events;
if (++self->n_fds == self->max_n_fds) {
if (devpoll_flush(self))
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
select_devpoll_register_impl(devpollObject *self, int fd,
unsigned short eventmask)
{
return internal_devpoll_register(self, fd, eventmask, 0);
}
static PyObject *
select_devpoll_modify_impl(devpollObject *self, int fd,
unsigned short eventmask)
{
return internal_devpoll_register(self, fd, eventmask, 1);
}
static PyObject *
select_devpoll_unregister_impl(devpollObject *self, int fd)
{
if (self->fd_devpoll < 0)
return devpoll_err_closed();
self->fds[self->n_fds].fd = fd;
self->fds[self->n_fds].events = POLLREMOVE;
if (++self->n_fds == self->max_n_fds) {
if (devpoll_flush(self))
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
{
struct dvpoll dvp;
PyObject *result_list = NULL;
int poll_result, i;
PyObject *value, *num1, *num2;
PyTime_t timeout, ms, deadline = 0;
if (self->fd_devpoll < 0)
return devpoll_err_closed();
if (timeout_obj == Py_None) {
timeout = -1;
ms = -1;
}
else {
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
_PyTime_ROUND_TIMEOUT) < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"timeout must be an integer or None");
}
return NULL;
}
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT);
if (ms < -1 || ms > INT_MAX) {
PyErr_SetString(PyExc_OverflowError, "timeout is too large");
return NULL;
}
}
if (devpoll_flush(self))
return NULL;
dvp.dp_fds = self->fds;
dvp.dp_nfds = self->max_n_fds;
dvp.dp_timeout = (int)ms;
if (timeout >= 0) {
deadline = _PyDeadline_Init(timeout);
}
do {
Py_BEGIN_ALLOW_THREADS
errno = 0;
poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
Py_END_ALLOW_THREADS
if (errno != EINTR)
break;
if (PyErr_CheckSignals())
return NULL;
if (timeout >= 0) {
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
poll_result = 0;
break;
}
ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
dvp.dp_timeout = (int)ms;
}
} while (1);
if (poll_result < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
result_list = PyList_New(poll_result);
if (!result_list)
return NULL;
for (i = 0; i < poll_result; i++) {
num1 = PyLong_FromLong(self->fds[i].fd);
num2 = PyLong_FromLong(self->fds[i].revents);
if ((num1 == NULL) || (num2 == NULL)) {
Py_XDECREF(num1);
Py_XDECREF(num2);
goto error;
}
value = PyTuple_Pack(2, num1, num2);
Py_DECREF(num1);
Py_DECREF(num2);
if (value == NULL)
goto error;
PyList_SET_ITEM(result_list, i, value);
}
return result_list;
error:
Py_DECREF(result_list);
return NULL;
}
static int
devpoll_internal_close(devpollObject *self)
{
int save_errno = 0;
if (self->fd_devpoll >= 0) {
int fd = self->fd_devpoll;
self->fd_devpoll = -1;
Py_BEGIN_ALLOW_THREADS
if (close(fd) < 0)
save_errno = errno;
Py_END_ALLOW_THREADS
}
return save_errno;
}
static PyObject *
select_devpoll_close_impl(devpollObject *self)
{
errno = devpoll_internal_close(self);
if (errno < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
Py_RETURN_NONE;
}
static PyObject*
devpoll_get_closed(devpollObject *self, void *Py_UNUSED(ignored))
{
if (self->fd_devpoll < 0)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
static PyObject *
select_devpoll_fileno_impl(devpollObject *self)
{
if (self->fd_devpoll < 0)
return devpoll_err_closed();
return PyLong_FromLong(self->fd_devpoll);
}
static PyGetSetDef devpoll_getsetlist[] = {
{"closed", (getter)devpoll_get_closed, NULL,
"True if the devpoll object is closed"},
{0},
};
static devpollObject *
newDevPollObject(PyObject *module)
{
devpollObject *self;
int fd_devpoll, limit_result;
struct pollfd *fds;
struct rlimit limit;
limit_result = getrlimit(RLIMIT_NOFILE, &limit);
if (limit_result == -1) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
fd_devpoll = _Py_open("/dev/poll", O_RDWR);
if (fd_devpoll == -1)
return NULL;
fds = PyMem_NEW(struct pollfd, limit.rlim_cur);
if (fds == NULL) {
close(fd_devpoll);
PyErr_NoMemory();
return NULL;
}
self = PyObject_New(devpollObject, get_select_state(module)->devpoll_Type);
if (self == NULL) {
close(fd_devpoll);
PyMem_Free(fds);
return NULL;
}
self->fd_devpoll = fd_devpoll;
self->max_n_fds = limit.rlim_cur;
self->n_fds = 0;
self->fds = fds;
return self;
}
static void
devpoll_dealloc(devpollObject *self)
{
PyObject *type = (PyObject *)Py_TYPE(self);
(void)devpoll_internal_close(self);
PyMem_Free(self->fds);
PyObject_Free(self);
Py_DECREF(type);
}
static PyType_Slot devpoll_Type_slots[] = {
{Py_tp_dealloc, devpoll_dealloc},
{Py_tp_getset, devpoll_getsetlist},
{Py_tp_methods, devpoll_methods},
{0, 0},
};
static PyType_Spec devpoll_Type_spec = {
"select.devpoll",
sizeof(devpollObject),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
devpoll_Type_slots
};
#endif
static PyObject *
select_poll_impl(PyObject *module)
{ … }
#ifdef HAVE_SYS_DEVPOLL_H
static PyObject *
select_devpoll_impl(PyObject *module)
{
return (PyObject *)newDevPollObject(module);
}
#endif
#ifdef __APPLE__
static int select_have_broken_poll(void)
{
int poll_test;
int filedes[2];
struct pollfd poll_struct = { 0, POLLIN|POLLPRI|POLLOUT, 0 };
if (pipe(filedes) < 0) {
return 1;
}
poll_struct.fd = filedes[0];
close(filedes[0]);
close(filedes[1]);
poll_test = poll(&poll_struct, 1, 0);
if (poll_test < 0) {
return 1;
} else if (poll_test == 0 && poll_struct.revents != POLLNVAL) {
return 1;
}
return 0;
}
#endif
#endif
#ifdef HAVE_EPOLL
#ifdef HAVE_SYS_EPOLL_H
#include <sys/epoll.h>
#endif
pyEpoll_Object;
static PyObject *
pyepoll_err_closed(void)
{ … }
static int
pyepoll_internal_close(pyEpoll_Object *self)
{ … }
static PyObject *
newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd)
{ … }
static PyObject *
select_epoll_impl(PyTypeObject *type, int sizehint, int flags)
{ … }
static void
pyepoll_dealloc(pyEpoll_Object *self)
{ … }
static PyObject *
select_epoll_close_impl(pyEpoll_Object *self)
{ … }
static PyObject*
pyepoll_get_closed(pyEpoll_Object *self, void *Py_UNUSED(ignored))
{ … }
static PyObject *
select_epoll_fileno_impl(pyEpoll_Object *self)
{ … }
static PyObject *
select_epoll_fromfd_impl(PyTypeObject *type, int fd)
{ … }
static PyObject *
pyepoll_internal_ctl(int epfd, int op, int fd, unsigned int events)
{ … }
static PyObject *
select_epoll_register_impl(pyEpoll_Object *self, int fd,
unsigned int eventmask)
{ … }
static PyObject *
select_epoll_modify_impl(pyEpoll_Object *self, int fd,
unsigned int eventmask)
{ … }
static PyObject *
select_epoll_unregister_impl(pyEpoll_Object *self, int fd)
{ … }
static PyObject *
select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj,
int maxevents)
{ … }
static PyObject *
select_epoll___enter___impl(pyEpoll_Object *self)
{ … }
static PyObject *
select_epoll___exit___impl(pyEpoll_Object *self, PyObject *exc_type,
PyObject *exc_value, PyObject *exc_tb)
{ … }
static PyGetSetDef pyepoll_getsetlist[] = …;
PyDoc_STRVAR(pyepoll_doc,
"select.epoll(sizehint=-1, flags=0)\n\
\n\
Returns an epolling object\n\
\n\
sizehint must be a positive integer or -1 for the default size. The\n\
sizehint is used to optimize internal data structures. It doesn't limit\n\
the maximum number of monitored events.");
#endif
#ifdef HAVE_KQUEUE
#ifdef HAVE_SYS_EVENT_H
#include <sys/event.h>
#endif
PyDoc_STRVAR(kqueue_event_doc,
"kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0)\n\
\n\
This object is the equivalent of the struct kevent for the C API.\n\
\n\
See the kqueue manpage for more detailed information about the meaning\n\
of the arguments.\n\
\n\
One minor note: while you might hope that udata could store a\n\
reference to a python object, it cannot, because it is impossible to\n\
keep a proper reference count of the object once it's passed into the\n\
kernel. Therefore, I have restricted it to only storing an integer. I\n\
recommend ignoring it and simply using the 'ident' field to key off\n\
of. You could also set up a dictionary on the python side to store a\n\
udata->object mapping.");
typedef struct {
PyObject_HEAD
struct kevent e;
} kqueue_event_Object;
#define kqueue_event_Check …
typedef struct kqueue_queue_Object {
PyObject_HEAD
SOCKET kqfd;
} kqueue_queue_Object;
#if (SIZEOF_UINTPTR_T != SIZEOF_VOID_P)
# error uintptr_t does not match void *!
#elif (SIZEOF_UINTPTR_T == SIZEOF_LONG_LONG)
#define T_UINTPTRT …
#define T_INTPTRT …
#define UINTPTRT_FMT_UNIT …
#define INTPTRT_FMT_UNIT …
#elif (SIZEOF_UINTPTR_T == SIZEOF_LONG)
#define T_UINTPTRT …
#define T_INTPTRT …
#define UINTPTRT_FMT_UNIT …
#define INTPTRT_FMT_UNIT …
#elif (SIZEOF_UINTPTR_T == SIZEOF_INT)
#define T_UINTPTRT …
#define T_INTPTRT …
#define UINTPTRT_FMT_UNIT …
#define INTPTRT_FMT_UNIT …
#else
# error uintptr_t does not match int, long, or long long!
#endif
#if SIZEOF_LONG_LONG == 8
#define T_INT64 …
#define INT64_FMT_UNIT …
#elif SIZEOF_LONG == 8
#define T_INT64 …
#define INT64_FMT_UNIT …
#elif SIZEOF_INT == 8
#define T_INT64 …
#define INT64_FMT_UNIT …
#else
#define INT64_FMT_UNIT …
#endif
#if SIZEOF_LONG_LONG == 4
#define T_UINT32 …
#define UINT32_FMT_UNIT …
#elif SIZEOF_LONG == 4
#define T_UINT32 …
#define UINT32_FMT_UNIT …
#elif SIZEOF_INT == 4
#define T_UINT32 …
#define UINT32_FMT_UNIT …
#else
#define UINT32_FMT_UNIT …
#endif
#ifdef __NetBSD__
#define FILTER_TYPE …
#define FILTER_FMT_UNIT …
#define FLAGS_TYPE …
#define FLAGS_FMT_UNIT …
#define FFLAGS_TYPE …
#define FFLAGS_FMT_UNIT …
#else
#define FILTER_TYPE …
#define FILTER_FMT_UNIT …
#define FLAGS_TYPE …
#define FLAGS_FMT_UNIT …
#define FFLAGS_TYPE …
#define FFLAGS_FMT_UNIT …
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
#define DATA_TYPE …
#define DATA_FMT_UNIT …
#else
#define DATA_TYPE …
#define DATA_FMT_UNIT …
#endif
#define KQ_OFF …
static struct PyMemberDef kqueue_event_members[] = {
{"ident", T_UINTPTRT, KQ_OFF(e.ident)},
{"filter", FILTER_TYPE, KQ_OFF(e.filter)},
{"flags", FLAGS_TYPE, KQ_OFF(e.flags)},
{"fflags", Py_T_UINT, KQ_OFF(e.fflags)},
{"data", DATA_TYPE, KQ_OFF(e.data)},
{"udata", T_UINTPTRT, KQ_OFF(e.udata)},
{NULL}
};
#undef KQ_OFF
static PyObject *
kqueue_event_repr(kqueue_event_Object *s)
{
return PyUnicode_FromFormat(
"<select.kevent ident=%zu filter=%d flags=0x%x fflags=0x%x "
"data=0x%llx udata=%p>",
(size_t)(s->e.ident), (int)s->e.filter, (unsigned int)s->e.flags,
(unsigned int)s->e.fflags, (long long)(s->e.data), (void *)s->e.udata);
}
static int
kqueue_event_init(kqueue_event_Object *self, PyObject *args, PyObject *kwds)
{
PyObject *pfd;
static char *kwlist[] = {"ident", "filter", "flags", "fflags",
"data", "udata", NULL};
static const char fmt[] = "O|"
FILTER_FMT_UNIT FLAGS_FMT_UNIT FFLAGS_FMT_UNIT DATA_FMT_UNIT
UINTPTRT_FMT_UNIT ":kevent";
EV_SET(&(self->e), 0, EVFILT_READ, EV_ADD, 0, 0, 0);
if (!PyArg_ParseTupleAndKeywords(args, kwds, fmt, kwlist,
&pfd, &(self->e.filter), &(self->e.flags),
&(self->e.fflags), &(self->e.data), &(self->e.udata))) {
return -1;
}
if (PyLong_Check(pfd)) {
self->e.ident = PyLong_AsSize_t(pfd);
}
else {
self->e.ident = PyObject_AsFileDescriptor(pfd);
}
if (PyErr_Occurred()) {
return -1;
}
return 0;
}
static PyObject *
kqueue_event_richcompare(kqueue_event_Object *s, kqueue_event_Object *o,
int op)
{
int result;
_selectstate *state = _selectstate_by_type(Py_TYPE(s));
if (!kqueue_event_Check(o, state)) {
Py_RETURN_NOTIMPLEMENTED;
}
#define CMP …
result = CMP(s->e.ident, o->e.ident)
: CMP(s->e.filter, o->e.filter)
: CMP(s->e.flags, o->e.flags)
: CMP(s->e.fflags, o->e.fflags)
: CMP(s->e.data, o->e.data)
: CMP((intptr_t)s->e.udata, (intptr_t)o->e.udata)
: 0;
#undef CMP
Py_RETURN_RICHCOMPARE(result, 0, op);
}
static PyType_Slot kqueue_event_Type_slots[] = {
{Py_tp_doc, (void*)kqueue_event_doc},
{Py_tp_init, kqueue_event_init},
{Py_tp_members, kqueue_event_members},
{Py_tp_new, PyType_GenericNew},
{Py_tp_repr, kqueue_event_repr},
{Py_tp_richcompare, kqueue_event_richcompare},
{0, 0},
};
static PyType_Spec kqueue_event_Type_spec = {
"select.kevent",
sizeof(kqueue_event_Object),
0,
Py_TPFLAGS_DEFAULT,
kqueue_event_Type_slots
};
static PyObject *
kqueue_queue_err_closed(void)
{
PyErr_SetString(PyExc_ValueError, "I/O operation on closed kqueue object");
return NULL;
}
static PyObject*
kqueue_tracking_after_fork(PyObject *module) {
_selectstate *state = get_select_state(module);
_kqueue_list_item *item = state->kqueue_open_list;
state->kqueue_open_list = NULL;
while (item) {
kqueue_queue_Object *obj = item->obj;
assert(obj->kqfd != -1);
obj->kqfd = -1;
_kqueue_list_item *next = item->next;
PyMem_Free(item);
item = next;
}
Py_RETURN_NONE;
}
static PyMethodDef kqueue_tracking_after_fork_def = {
"kqueue_tracking_after_fork", (PyCFunction)kqueue_tracking_after_fork,
METH_NOARGS, "Invalidate open select.kqueue objects after fork."
};
static void
kqueue_tracking_init(PyObject *module) {
_selectstate *state = get_select_state(module);
assert(state->kqueue_open_list == NULL);
PyObject *register_at_fork = NULL, *cb = NULL, *args = NULL,
*kwargs = NULL, *result = NULL;
register_at_fork = _PyImport_GetModuleAttrString("posix",
"register_at_fork");
if (register_at_fork == NULL) {
goto finally;
}
cb = PyCFunction_New(&kqueue_tracking_after_fork_def, module);
if (cb == NULL) {
goto finally;
}
args = PyTuple_New(0);
assert(args != NULL);
kwargs = Py_BuildValue("{sO}", "after_in_child", cb);
if (kwargs == NULL) {
goto finally;
}
result = PyObject_Call(register_at_fork, args, kwargs);
finally:
if (PyErr_Occurred()) {
PyObject *exc = PyErr_GetRaisedException();
PyObject *exctype = (PyObject*)Py_TYPE(exc);
PyErr_WarnFormat(PyExc_RuntimeWarning, 1,
"An exception of type %S was raised while registering an "
"after-fork handler for select.kqueue objects: %S", exctype, exc);
Py_DECREF(exc);
}
Py_XDECREF(register_at_fork);
Py_XDECREF(cb);
Py_XDECREF(args);
Py_XDECREF(kwargs);
Py_XDECREF(result);
state->kqueue_tracking_initialized = true;
}
static int
kqueue_tracking_add_lock_held(_selectstate *state, kqueue_queue_Object *self)
{
assert(self->kqfd >= 0);
_kqueue_list_item *item = PyMem_New(_kqueue_list_item, 1);
if (item == NULL) {
PyErr_NoMemory();
return -1;
}
item->obj = self;
item->next = state->kqueue_open_list;
state->kqueue_open_list = item;
return 0;
}
static int
kqueue_tracking_add(_selectstate *state, kqueue_queue_Object *self)
{
int ret;
PyObject *module = PyType_GetModule(Py_TYPE(self));
Py_BEGIN_CRITICAL_SECTION(module);
if (!state->kqueue_tracking_initialized) {
kqueue_tracking_init(module);
}
ret = kqueue_tracking_add_lock_held(state, self);
Py_END_CRITICAL_SECTION();
return ret;
}
static void
kqueue_tracking_remove_lock_held(_selectstate *state, kqueue_queue_Object *self)
{
_kqueue_list *listptr = &state->kqueue_open_list;
while (*listptr != NULL) {
_kqueue_list_item *item = *listptr;
if (item->obj == self) {
*listptr = item->next;
PyMem_Free(item);
return;
}
listptr = &item->next;
}
assert(0);
}
static void
kqueue_tracking_remove(_selectstate *state, kqueue_queue_Object *self)
{
Py_BEGIN_CRITICAL_SECTION(PyType_GetModule(Py_TYPE(self)));
kqueue_tracking_remove_lock_held(state, self);
Py_END_CRITICAL_SECTION();
}
static int
kqueue_queue_internal_close(kqueue_queue_Object *self)
{
int save_errno = 0;
if (self->kqfd >= 0) {
int kqfd = self->kqfd;
self->kqfd = -1;
_selectstate *state = _selectstate_by_type(Py_TYPE(self));
kqueue_tracking_remove(state, self);
Py_BEGIN_ALLOW_THREADS
if (close(kqfd) < 0)
save_errno = errno;
Py_END_ALLOW_THREADS
}
return save_errno;
}
static PyObject *
newKqueue_Object(PyTypeObject *type, SOCKET fd)
{
kqueue_queue_Object *self;
assert(type != NULL);
allocfunc queue_alloc = PyType_GetSlot(type, Py_tp_alloc);
assert(queue_alloc != NULL);
self = (kqueue_queue_Object *) queue_alloc(type, 0);
if (self == NULL) {
return NULL;
}
if (fd == -1) {
Py_BEGIN_ALLOW_THREADS
self->kqfd = kqueue();
Py_END_ALLOW_THREADS
}
else {
self->kqfd = fd;
}
if (self->kqfd < 0) {
PyErr_SetFromErrno(PyExc_OSError);
Py_DECREF(self);
return NULL;
}
if (fd == -1) {
if (_Py_set_inheritable(self->kqfd, 0, NULL) < 0) {
Py_DECREF(self);
return NULL;
}
}
_selectstate *state = _selectstate_by_type(type);
if (kqueue_tracking_add(state, self) < 0) {
Py_DECREF(self);
return NULL;
}
return (PyObject *)self;
}
static PyObject *
select_kqueue_impl(PyTypeObject *type)
{
return newKqueue_Object(type, -1);
}
static void
kqueue_queue_finalize(kqueue_queue_Object *self)
{
PyObject* error = PyErr_GetRaisedException();
kqueue_queue_internal_close(self);
PyErr_SetRaisedException(error);
}
static PyObject *
select_kqueue_close_impl(kqueue_queue_Object *self)
{
errno = kqueue_queue_internal_close(self);
if (errno < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
Py_RETURN_NONE;
}
static PyObject*
kqueue_queue_get_closed(kqueue_queue_Object *self, void *Py_UNUSED(ignored))
{
if (self->kqfd < 0)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
static PyObject *
select_kqueue_fileno_impl(kqueue_queue_Object *self)
{
if (self->kqfd < 0)
return kqueue_queue_err_closed();
return PyLong_FromLong(self->kqfd);
}
static PyObject *
select_kqueue_fromfd_impl(PyTypeObject *type, int fd)
{
SOCKET s_fd = (SOCKET)fd;
return newKqueue_Object(type, s_fd);
}
static PyObject *
select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist,
int maxevents, PyObject *otimeout)
{
int gotevents = 0;
int nchanges = 0;
int i = 0;
PyObject *seq = NULL, *ei = NULL;
PyObject *result = NULL;
struct kevent *evl = NULL;
struct kevent *chl = NULL;
struct timespec timeoutspec;
struct timespec *ptimeoutspec;
PyTime_t timeout, deadline = 0;
_selectstate *state = _selectstate_by_type(Py_TYPE(self));
if (self->kqfd < 0)
return kqueue_queue_err_closed();
if (maxevents < 0) {
PyErr_Format(PyExc_ValueError,
"Length of eventlist must be 0 or positive, got %d",
maxevents);
return NULL;
}
if (otimeout == Py_None) {
ptimeoutspec = NULL;
}
else {
if (_PyTime_FromSecondsObject(&timeout,
otimeout, _PyTime_ROUND_TIMEOUT) < 0) {
PyErr_Format(PyExc_TypeError,
"timeout argument must be a number "
"or None, got %.200s",
_PyType_Name(Py_TYPE(otimeout)));
return NULL;
}
if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1)
return NULL;
if (timeoutspec.tv_sec < 0) {
PyErr_SetString(PyExc_ValueError,
"timeout must be positive or None");
return NULL;
}
ptimeoutspec = &timeoutspec;
}
if (changelist != Py_None) {
seq = PySequence_Fast(changelist, "changelist is not iterable");
if (seq == NULL) {
return NULL;
}
if (PySequence_Fast_GET_SIZE(seq) > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"changelist is too long");
goto error;
}
nchanges = (int)PySequence_Fast_GET_SIZE(seq);
chl = PyMem_New(struct kevent, nchanges);
if (chl == NULL) {
PyErr_NoMemory();
goto error;
}
_selectstate *state = _selectstate_by_type(Py_TYPE(self));
for (i = 0; i < nchanges; ++i) {
ei = PySequence_Fast_GET_ITEM(seq, i);
if (!kqueue_event_Check(ei, state)) {
PyErr_SetString(PyExc_TypeError,
"changelist must be an iterable of "
"select.kevent objects");
goto error;
}
chl[i] = ((kqueue_event_Object *)ei)->e;
}
Py_CLEAR(seq);
}
if (maxevents) {
evl = PyMem_New(struct kevent, maxevents);
if (evl == NULL) {
PyErr_NoMemory();
goto error;
}
}
if (ptimeoutspec) {
deadline = _PyDeadline_Init(timeout);
}
do {
Py_BEGIN_ALLOW_THREADS
errno = 0;
gotevents = kevent(self->kqfd, chl, nchanges,
evl, maxevents, ptimeoutspec);
Py_END_ALLOW_THREADS
if (errno != EINTR)
break;
if (PyErr_CheckSignals())
goto error;
if (ptimeoutspec) {
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
gotevents = 0;
break;
}
if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1)
goto error;
}
} while (1);
if (gotevents == -1) {
PyErr_SetFromErrno(PyExc_OSError);
goto error;
}
result = PyList_New(gotevents);
if (result == NULL) {
goto error;
}
for (i = 0; i < gotevents; i++) {
kqueue_event_Object *ch;
ch = PyObject_New(kqueue_event_Object, state->kqueue_event_Type);
if (ch == NULL) {
goto error;
}
ch->e = evl[i];
PyList_SET_ITEM(result, i, (PyObject *)ch);
}
PyMem_Free(chl);
PyMem_Free(evl);
return result;
error:
PyMem_Free(chl);
PyMem_Free(evl);
Py_XDECREF(result);
Py_XDECREF(seq);
return NULL;
}
static PyGetSetDef kqueue_queue_getsetlist[] = {
{"closed", (getter)kqueue_queue_get_closed, NULL,
"True if the kqueue handler is closed"},
{0},
};
#endif
#include "clinic/selectmodule.c.h"
#if defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)
static PyMethodDef poll_methods[] = …;
static PyType_Slot poll_Type_slots[] = …;
static PyType_Spec poll_Type_spec = …;
#ifdef HAVE_SYS_DEVPOLL_H
static PyMethodDef devpoll_methods[] = {
SELECT_DEVPOLL_REGISTER_METHODDEF
SELECT_DEVPOLL_MODIFY_METHODDEF
SELECT_DEVPOLL_UNREGISTER_METHODDEF
SELECT_DEVPOLL_POLL_METHODDEF
SELECT_DEVPOLL_CLOSE_METHODDEF
SELECT_DEVPOLL_FILENO_METHODDEF
{NULL, NULL}
};
#endif
#endif
#ifdef HAVE_EPOLL
static PyMethodDef pyepoll_methods[] = …;
static PyType_Slot pyEpoll_Type_slots[] = …;
static PyType_Spec pyEpoll_Type_spec = …;
#endif
#ifdef HAVE_KQUEUE
static PyMethodDef kqueue_queue_methods[] = {
SELECT_KQUEUE_FROMFD_METHODDEF
SELECT_KQUEUE_CLOSE_METHODDEF
SELECT_KQUEUE_FILENO_METHODDEF
SELECT_KQUEUE_CONTROL_METHODDEF
{NULL, NULL},
};
static PyType_Slot kqueue_queue_Type_slots[] = {
{Py_tp_doc, (void*)select_kqueue__doc__},
{Py_tp_getset, kqueue_queue_getsetlist},
{Py_tp_methods, kqueue_queue_methods},
{Py_tp_new, select_kqueue},
{Py_tp_finalize, kqueue_queue_finalize},
{0, 0},
};
static PyType_Spec kqueue_queue_Type_spec = {
"select.kqueue",
sizeof(kqueue_queue_Object),
0,
Py_TPFLAGS_DEFAULT,
kqueue_queue_Type_slots
};
#endif
static PyMethodDef select_methods[] = …;
PyDoc_STRVAR(module_doc,
"This module supports asynchronous I/O on multiple file descriptors.\n\
\n\
*** IMPORTANT NOTICE ***\n\
On Windows, only sockets are supported; on Unix, all file descriptors.");
static int
_select_traverse(PyObject *module, visitproc visit, void *arg)
{ … }
static int
_select_clear(PyObject *module)
{ … }
static void
_select_free(void *module)
{ … }
int
_select_exec(PyObject *m)
{ … }
static PyModuleDef_Slot _select_slots[] = …;
static struct PyModuleDef selectmodule = …;
PyMODINIT_FUNC
PyInit_select(void)
{ … }