Commit 7feb7da1 authored by Jason Madden's avatar Jason Madden

Optimize CFFI backend to use less memory by taking advantage of the libev...

Optimize CFFI backend to use less memory by taking advantage of the libev 'data' pointer. Also simplifies the callback code.
parent 5ed67937
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
This means that it is now traceable and not exactly as atomic as the This means that it is now traceable and not exactly as atomic as the
Cython version, though the overall semantics should remain the same. Cython version, though the overall semantics should remain the same.
Reported in :issue:`704` by Shaun Crampton. Reported in :issue:`704` by Shaun Crampton.
- PyPy: Optimize the CFFI backend to use less memory (two pointers per
watcher).
1.1rc2 (Dec 11, 2015) 1.1rc2 (Dec 11, 2015)
===================== =====================
......
...@@ -85,25 +85,51 @@ struct ev_loop { ...@@ -85,25 +85,51 @@ struct ev_loop {
int activecnt; int activecnt;
...; ...;
}; };
// Watcher types
// base for all watchers
struct ev_watcher{...;};
struct ev_io { struct ev_io {
int fd; int fd;
int events; int events;
void* data;
...; ...;
}; };
struct ev_timer { struct ev_timer {
double at; double at;
void* data;
...;
};
struct ev_signal {
void* data;
...;
};
struct ev_idle {
void* data;
...;
};
struct ev_prepare {
void* data;
...;
};
struct ev_check {
void* data;
...;
};
struct ev_fork {
void* data;
...;
};
struct ev_async {
void* data;
...; ...;
}; };
struct ev_signal {...;};
struct ev_idle {...;};
struct ev_prepare {...;};
struct ev_check {...;};
struct ev_fork {...;};
struct ev_async {...;};
struct ev_child { struct ev_child {
int pid; int pid;
int rpid; int rpid;
int rstatus; int rstatus;
void* data;
...; ...;
}; };
struct stat { struct stat {
...@@ -116,6 +142,7 @@ struct ev_stat { ...@@ -116,6 +142,7 @@ struct ev_stat {
const char* path; const char* path;
struct stat prev; struct stat prev;
double interval; double interval;
void* data;
...; ...;
}; };
...@@ -141,7 +168,7 @@ void ev_io_start(struct ev_loop*, struct ev_io*); ...@@ -141,7 +168,7 @@ void ev_io_start(struct ev_loop*, struct ev_io*);
void ev_io_stop(struct ev_loop*, struct ev_io*); void ev_io_stop(struct ev_loop*, struct ev_io*);
void ev_feed_event(struct ev_loop*, void*, int); void ev_feed_event(struct ev_loop*, void*, int);
void ev_timer_init(struct ev_timer*, void (*callback)(struct ev_loop *_loop, struct ev_timer *w, int revents), double, double); void ev_timer_init(struct ev_timer*, void *callback, double, double);
void ev_timer_start(struct ev_loop*, struct ev_timer*); void ev_timer_start(struct ev_loop*, struct ev_timer*);
void ev_timer_stop(struct ev_loop*, struct ev_timer*); void ev_timer_stop(struct ev_loop*, struct ev_timer*);
void ev_timer_again(struct ev_loop*, struct ev_timer*); void ev_timer_again(struct ev_loop*, struct ev_timer*);
...@@ -234,6 +261,7 @@ _watcher_types = [ ...@@ -234,6 +261,7 @@ _watcher_types = [
_source = """ _source = """
// passed to the real C compiler // passed to the real C compiler
#define LIBEV_EMBED 1 #define LIBEV_EMBED 1
#ifdef _WIN32 #ifdef _WIN32
#define EV_STANDALONE 1 #define EV_STANDALONE 1
#include "libev_vfd.h" #include "libev_vfd.h"
...@@ -258,55 +286,41 @@ _cdef += _cbs ...@@ -258,55 +286,41 @@ _cdef += _cbs
_source += _cbs _source += _cbs
_watcher_type = None _watcher_type = None
for _watcher_type in _watcher_types: # We use a single C callback for every watcher type, which in turn calls the
_cdef += """ # Python callbacks. The ev_watcher pointer type can be used for every watcher type
struct gevent_%s { # because they all start with the same members---libev itself relies on this. Each
// recall that the address of a struct is the # watcher types has a 'void* data' that stores the CFFI handle to the Python watcher
// same as the address of its first member, so # object.
// this struct is interchangable with the ev_XX _cdef += """
// that is its first member. static void _gevent_generic_callback(struct ev_loop* loop, struct ev_watcher* watcher, int revents);
struct %s watcher; """
// the CFFI handle to the Python watcher object
void* handle; _source += """
...; static void _gevent_generic_callback(struct ev_loop* loop, struct ev_watcher* watcher, int revents)
}; {
static void _gevent_%s_callback(struct ev_loop* loop, struct %s* watcher, int revents); void* handle = watcher->data;
""" % (_watcher_type, _watcher_type, _watcher_type, _watcher_type) int cb_result = python_callback(handle, revents);
switch(cb_result) {
_source += """ case -1:
struct gevent_%s { // in case of exception, call self.loop.handle_error;
struct %s watcher; // this function is also responsible for stopping the watcher
void* handle; // and allowing memory to be freed
}; python_handle_error(handle, revents);
""" % (_watcher_type, _watcher_type) break;
case 0:
_source += """ // Code to stop the event. Note that if python_callback
static void _gevent_%s_callback(struct ev_loop* loop, struct %s* watcher, int revents) // has disposed of the last reference to the handle,
{ // `watcher` could now be invalid/disposed memory!
// invoke self.callback() if (!ev_is_active(watcher)) {
void* handle = ((struct gevent_%s *)watcher)->handle; python_stop(handle);
int cb_result = python_callback(handle, revents); }
switch(cb_result) { break;
case -1: default:
// in case of exception, call self.loop.handle_error; assert(cb_result == 1);
// this function is also responsible for stopping the watcher // watcher is already stopped and dead, nothing to do.
// and allowing memory to be freed
python_handle_error(handle, revents);
break;
case 0:
// Code to stop the event. Note that if python_callback
// has disposed of the last reference to the handle,
// `watcher` could now be invalid/disposed memory!
if (!ev_is_active(watcher)) {
python_stop(handle);
}
break;
default:
assert(cb_result == 1);
// watcher is already stopped and dead, nothing to do.
}
} }
""" % (_watcher_type, _watcher_type, _watcher_type) }
"""
thisdir = os.path.dirname(os.path.abspath(__file__)) thisdir = os.path.dirname(os.path.abspath(__file__))
include_dirs = [ include_dirs = [
......
...@@ -719,9 +719,9 @@ class watcher(object): ...@@ -719,9 +719,9 @@ class watcher(object):
self._args = None self._args = None
self._callback = None self._callback = None
self._handle = ffi.new_handle(self) self._handle = ffi.new_handle(self)
self._gwatcher = ffi.new(self._watcher_struct_pointer_type)
self._watcher = ffi.addressof(self._gwatcher.watcher) self._watcher = ffi.new(self._watcher_struct_pointer_type)
self._gwatcher.handle = self._handle self._watcher.data = self._handle
if priority is not None: if priority is not None:
libev.ev_set_priority(self._watcher, priority) libev.ev_set_priority(self._watcher, priority)
self._watcher_init(self._watcher, self._watcher_init(self._watcher,
...@@ -752,9 +752,9 @@ class watcher(object): ...@@ -752,9 +752,9 @@ class watcher(object):
def _init_subclasses(cls): def _init_subclasses(cls):
for subclass in cls.__subclasses__(): for subclass in cls.__subclasses__():
watcher_type = subclass._watcher_type watcher_type = subclass._watcher_type
subclass._watcher_struct_pointer_type = ffi.typeof('struct gevent_' + watcher_type + '*') subclass._watcher_struct_pointer_type = ffi.typeof('struct ' + watcher_type + '*')
subclass._watcher_callback = ffi.addressof(libev, subclass._watcher_callback = ffi.addressof(libev,
'_gevent_' + watcher_type + '_callback') '_gevent_generic_callback')
for name in 'start', 'stop', 'init': for name in 'start', 'stop', 'init':
ev_name = watcher_type + '_' + name ev_name = watcher_type + '_' + name
watcher_name = '_watcher' + '_' + name watcher_name = '_watcher' + '_' + name
...@@ -777,7 +777,7 @@ class watcher(object): ...@@ -777,7 +777,7 @@ class watcher(object):
result += " args=%r" % (self.args, ) result += " args=%r" % (self.args, )
if self.callback is None and self.args is None: if self.callback is None and self.args is None:
result += " stopped" result += " stopped"
result += " handle=%s" % (self._gwatcher.handle) result += " handle=%s" % (self._watcher.data)
return result + ">" return result + ">"
def _format(self): def _format(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment