aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/libev/libev_vfd.h
diff options
context:
space:
mode:
Diffstat (limited to 'python/gevent/libev/libev_vfd.h')
-rw-r--r--python/gevent/libev/libev_vfd.h223
1 files changed, 223 insertions, 0 deletions
diff --git a/python/gevent/libev/libev_vfd.h b/python/gevent/libev/libev_vfd.h
new file mode 100644
index 0000000..ec16a28
--- /dev/null
+++ b/python/gevent/libev/libev_vfd.h
@@ -0,0 +1,223 @@
+#ifdef _WIN32
+#ifdef _WIN64
+typedef PY_LONG_LONG vfd_socket_t;
+#define vfd_socket_object PyLong_FromLongLong
+#else
+typedef long vfd_socket_t;
+#define vfd_socket_object PyInt_FromLong
+#endif
+#ifdef LIBEV_EMBED
+/*
+ * If libev on win32 is embedded, then we can use an
+ * arbitrary mapping between integer fds and OS
+ * handles. Then by defining special macros libev
+ * will use our functions.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+
+typedef struct vfd_entry_t
+{
+ vfd_socket_t handle; /* OS handle, i.e. SOCKET */
+ int count; /* Reference count, 0 if free */
+ int next; /* Next free fd, -1 if last */
+} vfd_entry;
+
+#define VFD_INCREMENT 128
+static int vfd_num = 0; /* num allocated fds */
+static int vfd_max = 0; /* max allocated fds */
+static int vfd_next = -1; /* next free fd for reuse */
+static PyObject* vfd_map = NULL; /* map OS handle -> virtual fd */
+static vfd_entry* vfd_entries = NULL; /* list of virtual fd entries */
+
+#ifdef WITH_THREAD
+static CRITICAL_SECTION* volatile vfd_lock = NULL;
+static CRITICAL_SECTION* vfd_make_lock()
+{
+ if (vfd_lock == NULL) {
+ /* must use malloc and not PyMem_Malloc here */
+ CRITICAL_SECTION* lock = malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(lock);
+ if (InterlockedCompareExchangePointer(&vfd_lock, lock, NULL) != NULL) {
+ /* another thread initialized lock first */
+ DeleteCriticalSection(lock);
+ free(lock);
+ }
+ }
+ return vfd_lock;
+}
+#define VFD_LOCK_ENTER EnterCriticalSection(vfd_make_lock())
+#define VFD_LOCK_LEAVE LeaveCriticalSection(vfd_lock)
+#define VFD_GIL_DECLARE PyGILState_STATE ___save
+#define VFD_GIL_ENSURE ___save = PyGILState_Ensure()
+#define VFD_GIL_RELEASE PyGILState_Release(___save)
+#else
+#define VFD_LOCK_ENTER
+#define VFD_LOCK_LEAVE
+#define VFD_GIL_DECLARE
+#define VFD_GIL_ENSURE
+#define VFD_GIL_RELEASE
+#endif
+
+/*
+ * Given a virtual fd returns an OS handle or -1
+ * This function is speed critical, so it cannot use GIL
+ */
+static vfd_socket_t vfd_get(int fd)
+{
+ int handle = -1;
+ VFD_LOCK_ENTER;
+ if (vfd_entries != NULL && fd >= 0 && fd < vfd_num)
+ handle = vfd_entries[fd].handle;
+ VFD_LOCK_LEAVE;
+ return handle;
+}
+
+#define EV_FD_TO_WIN32_HANDLE(fd) vfd_get((fd))
+
+/*
+ * Given an OS handle finds or allocates a virtual fd
+ * Returns -1 on failure and sets Python exception if pyexc is non-zero
+ */
+static int vfd_open_(vfd_socket_t handle, int pyexc)
+{
+ VFD_GIL_DECLARE;
+ int fd = -1;
+ unsigned long arg;
+ PyObject* key = NULL;
+ PyObject* value;
+
+ if (!pyexc) {
+ VFD_GIL_ENSURE;
+ }
+ if (ioctlsocket(handle, FIONREAD, &arg) != 0) {
+ if (pyexc)
+ PyErr_Format(PyExc_IOError,
+#ifdef _WIN64
+ "%lld is not a socket (files are not supported)",
+#else
+ "%ld is not a socket (files are not supported)",
+#endif
+ handle);
+ goto done;
+ }
+ if (vfd_map == NULL) {
+ vfd_map = PyDict_New();
+ if (vfd_map == NULL)
+ goto done;
+ }
+ key = vfd_socket_object(handle);
+ /* check if it's already in the dict */
+ value = PyDict_GetItem(vfd_map, key);
+ if (value != NULL) {
+ /* is it safe to use PyInt_AS_LONG(value) here? */
+ fd = PyInt_AsLong(value);
+ if (fd >= 0) {
+ ++vfd_entries[fd].count;
+ goto done;
+ }
+ }
+ /* use the free entry, if available */
+ if (vfd_next >= 0) {
+ fd = vfd_next;
+ vfd_next = vfd_entries[fd].next;
+ VFD_LOCK_ENTER;
+ goto allocated;
+ }
+ /* check if it would be out of bounds */
+ if (vfd_num >= FD_SETSIZE) {
+ /* libev's select doesn't support more that FD_SETSIZE fds */
+ if (pyexc)
+ PyErr_Format(PyExc_IOError, "cannot watch more than %d sockets", (int)FD_SETSIZE);
+ goto done;
+ }
+ /* allocate more space if needed */
+ VFD_LOCK_ENTER;
+ if (vfd_num >= vfd_max) {
+ int newsize = vfd_max + VFD_INCREMENT;
+ vfd_entry* entries = PyMem_Realloc(vfd_entries, sizeof(vfd_entry) * newsize);
+ if (entries == NULL) {
+ VFD_LOCK_LEAVE;
+ if (pyexc)
+ PyErr_NoMemory();
+ goto done;
+ }
+ vfd_entries = entries;
+ vfd_max = newsize;
+ }
+ fd = vfd_num++;
+allocated:
+ /* vfd_lock must be acquired when entering here */
+ vfd_entries[fd].handle = handle;
+ vfd_entries[fd].count = 1;
+ VFD_LOCK_LEAVE;
+ value = PyInt_FromLong(fd);
+ PyDict_SetItem(vfd_map, key, value);
+ Py_DECREF(value);
+done:
+ Py_XDECREF(key);
+ if (!pyexc) {
+ VFD_GIL_RELEASE;
+ }
+ return fd;
+}
+
+#define vfd_open(fd) vfd_open_((fd), 1)
+#define EV_WIN32_HANDLE_TO_FD(handle) vfd_open_((handle), 0)
+
+static void vfd_free_(int fd, int needclose)
+{
+ VFD_GIL_DECLARE;
+ PyObject* key;
+
+ if (needclose) {
+ VFD_GIL_ENSURE;
+ }
+ if (fd < 0 || fd >= vfd_num)
+ goto done; /* out of bounds */
+ if (vfd_entries[fd].count <= 0)
+ goto done; /* free entry, ignore */
+ if (!--vfd_entries[fd].count) {
+ /* fd has just been freed */
+ vfd_socket_t handle = vfd_entries[fd].handle;
+ vfd_entries[fd].handle = -1;
+ vfd_entries[fd].next = vfd_next;
+ vfd_next = fd;
+ if (needclose)
+ closesocket(handle);
+ /* vfd_map is assumed to be != NULL */
+ key = vfd_socket_object(handle);
+ PyDict_DelItem(vfd_map, key);
+ Py_DECREF(key);
+ }
+done:
+ if (needclose) {
+ VFD_GIL_RELEASE;
+ }
+}
+
+#define vfd_free(fd) vfd_free_((fd), 0)
+#define EV_WIN32_CLOSE_FD(fd) vfd_free_((fd), 1)
+
+#else
+/*
+ * If libev on win32 is not embedded in gevent, then
+ * the only way to map vfds is to use the default of
+ * using runtime fds in libev. Note that it will leak
+ * fds, because there's no way of closing them safely
+ */
+#define vfd_get(fd) _get_osfhandle((fd))
+#define vfd_open(fd) _open_osfhandle((fd), 0)
+#define vfd_free(fd)
+#endif
+#else
+/*
+ * On non-win32 platforms vfd_* are noop macros
+ */
+typedef int vfd_socket_t;
+#define vfd_get(fd) (fd)
+#define vfd_open(fd) ((int)(fd))
+#define vfd_free(fd)
+#endif