python: Add non-blocking Xenstore watch bindings
cover python: Add non-blocking Xenstore watch bindings
Expose xs_fileno() and xs_check_watch() to Python. These functions
make it posible to write event-driven Xenstore clients in Python:
#!/usr/bin/env python
import xen.lowlevel.xs
import sys
import errno
from select import select
import time
# Connect to XenStore and set watch
xsh = xen.lowlevel.xs.xs()
xsh.watch("/foo", "footoken")
xsh.watch("/bar", "bartoken")
# Start polling loop
xsfd = xsh.fileno()
while True:
readable, writable, exceptional = select([xsfd], [], [xsfd], 1.0) print "%d tick" % time.time()
if readable:
while True:
watch = xsh.check_watch()
if not watch:
break
path, token = watch
print "%d watch fired: path=%s, token=%s" % (time.time(),
path, token)
value = xsh.read("", path)
print "%d read %s = %s" % (time.time(), path, value)
if exceptional:
print "select error"
The polling loop can be simplified further by wrapping the call to xsh.check_watch() in a generator, but this is easier to do in Python than in the C bindings.
Euan Harris (3):
python: Add wrapper for xs_fileno()
python: Extract registered watch search logic from xspy_read_watch()
python: Add binding for non-blocking xs_check_watch()
tools/python/xen/lowlevel/xs/xs.c | 109 ++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 23 deletions(-)
Signature · 7 lines
1.8.3.1 _______________________________________________ Xen-devel mailing list [email protected] https://lists.xen.org/xen-devel
PATCH 1/3 python: Add binding for xs_fileno()
xs_fileno() returns a file descriptor which receives events when Xenstore
watches fire. Exposing this in the Python bindings is a prerequisite
for writing event-driven clients in Python.
--- tools/python/xen/lowlevel/xs/xs.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
tools/python/xen/lowlevel/xs/xs.c +20 -0
@@ -453,6 +453,25 @@ static PyObject *xspy_watch(XsHandle *self, PyObject *args) } +#define xspy_fileno_doc "\n" \ + "Return the FD to poll for notifications when watches fire.\n" \ + "Returns: [int] file descriptor.\n" \ + "\n" + +static PyObject *xspy_fileno (XsHandle *self) +{ +struct xs_handle *xh =xshandle (self); +int fd; + +if (!xh) +return NULL ; + + fd =xs_fileno (xh); + +return PyInt_FromLong (fd); +} + +#define xspy_read_watch_doc "\n" \ "Read a watch notification.\n" \ "\n" \
@@ -887,6 +906,7 @@ static PyMethodDef xshandle_methods[] = {XSPY_METH (release_domain,METH_VARARGS ),XSPY_METH (close,METH_NOARGS ),XSPY_METH (get_domain_path,METH_VARARGS ), +XSPY_METH (fileno,METH_NOARGS ), { NULL /* Sentinel. */ }, };
Signature · 7 lines
1.8.3.1 _______________________________________________ Xen-devel mailing list [email protected] https://lists.xen.org/xen-devel
Re: [PATCH 1/3] python: Add binding for xs_fileno()
Quoted 5 lines · Euan Harris
xs_fileno()returns a file descriptor which receives events when Xenstore
watches fire. Exposing this in the Python bindings is a prerequisite
for writing event-driven clients in Python.Signed-off-by Euan Harris <[email protected]>
Quoted 50 lines
--- tools/python/xen/lowlevel/xs/xs.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)tools/python/xen/lowlevel/xs/xs.c +20
-0@@ -453,6 +453,25 @@ static PyObject *xspy_watch(XsHandle *self, PyObject *args) } +#define xspy_fileno_doc "\n" \ + "Return the FD to poll for notifications when watches fire.\n" \ + "Returns: [int] file descriptor.\n" \ + "\n" + +static PyObject *xspy_fileno (XsHandle *self) +{ +struct xs_handle *xh =xshandle (self); +int fd; + +if (!xh) +return NULL ; + + fd =xs_fileno (xh); + +return PyInt_FromLong (fd); +} + +#define xspy_read_watch_doc "\n" \ "Read a watch notification.\n" \ "\n" \@@ -887,6 +906,7 @@ static PyMethodDef xshandle_methods[] = {XSPY_METH (release_domain,METH_VARARGS ),XSPY_METH (close,METH_NOARGS ),XSPY_METH (get_domain_path,METH_VARARGS ), +XSPY_METH (fileno,METH_NOARGS ), { NULL /* Sentinel. */ }, };Signature · 7 lines
1.8.3.1 _______________________________________________ Xen-devel mailing list [email protected] https://lists.xen.org/xen-devel
_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
Re: [PATCH 1/3] python: Add binding for xs_fileno()
In reply to Euan HarrisQuoted 5 lines · Euan Harris
xs_fileno()returns a file descriptor which receives events when Xenstore
watches fire. Exposing this in the Python bindings is a prerequisite
for writing event-driven clients in Python.Signed-off-by Euan Harris <[email protected]>
_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
PATCH 2/3 python: Extract registered watch search logic from xspy_read_watch()
In reply to Euan HarrisWhen a watch fires, xspy_read_watch() checks whether the client has registered interest in the path which changed and, if so, returns the
path and a client-supplied token. The binding for xs_check_watch()
needs to do the same, so this patch extracts the search code into a separate function.
--- tools/python/xen/lowlevel/xs/xs.c | 61 ++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-)
tools/python/xen/lowlevel/xs/xs.c +38 -23
@@ -77,6 +77,8 @@ static inline struct xs_handle *xshandle(XsHandle *self)static void remove_watch (XsHandle *xsh,PyObject *token); +static PyObject *match_watch_by_token (XsHandle *self,char **xsval); +static PyObject *none (bool result); static int parse_transaction_path(XsHandle *self, PyObject *args,
@@ -484,8 +486,6 @@ static PyObject *xspy_read_watch(XsHandle *self, PyObject *args)struct xs_handle *xh =xshandle (self);PyObject *val =NULL ;char **xsval; -PyObject *token; -int i;unsigned int num;if (!xh)
@@ -497,32 +497,20 @@ again:Py_END_ALLOW_THREADS if (!xsval) {PyErr_SetFromErrno (xs_error); - goto exit; - } -if (sscanf (xsval[XS_WATCH_TOKEN ],"%li" , (unsigned long *)&token) !=1 ) { -xs_set_error (EINVAL ); - goto exit; - } -for (i =0 ; i <PyList_Size (self->watches); i++) { -if (token ==PyList_GetItem (self->watches, i)) -break ; - } -if (i ==PyList_Size (self->watches)) { -/* We do not have a registered watch for the one that has just fired. -Ignore this -- a watch that has been recently deregistered can still -have watches in transit. This is a blocking method, so go back to -read again. -*/ -free (xsval); - goto again; +return val; } -/* Create tuple (path, token). */ - val =Py_BuildValue ("(sO)" , xsval[XS_WATCH_PATH ], token); - exit: + + val =match_watch_by_token (self, xsval);free (xsval); + +if (!val && errno ==EAGAIN ) { + goto again; + } +return val; } +#define xspy_unwatch_doc "\n" \ "Stop watching a path.\n" \ " path [string] : xenstore path.\n" \
@@ -868,6 +856,33 @@ static int parse_transaction_path(XsHandle *self, PyObject *args, } +static PyObject *match_watch_by_token (XsHandle *self,char **xsval) +{ +PyObject *token; +int i; + +if (sscanf (xsval[XS_WATCH_TOKEN ],"%li" , (unsigned long *)&token) !=1 ) { +xs_set_error (EINVAL ); +return NULL ; + } +for (i =0 ; i <PyList_Size (self->watches); i++) { +if (token ==PyList_GetItem (self->watches, i)) +break ; + } +if (i ==PyList_Size (self->watches)) { +/* We do not have a registered watch for the one that has just fired. +Ignore this -- a watch that has been recently deregistered can still +have watches in transit. +*/ +xs_set_error (EAGAIN ); +return NULL ; + } + +/* Create tuple (path, token). */ +return Py_BuildValue ("(sO)" , xsval[XS_WATCH_PATH ], token); +} + +static PyObject *none (bool result) {if (result) {
Signature · 7 lines
1.8.3.1 _______________________________________________ Xen-devel mailing list [email protected] https://lists.xen.org/xen-devel
Re: [PATCH 2/3] python: Extract registered watch search logic from xspy_read_watch()
Quoted 54 lines · Euan Harris
tools/python/xen/lowlevel/xs/xs.c | 61 ++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-)tools/python/xen/lowlevel/xs/xs.c +5
-23@@ -77,6 +77,8 @@ static inline struct xs_handle *xshandle(XsHandle *self)static void remove_watch (XsHandle *xsh,PyObject *token); +static PyObject *match_watch_by_token (XsHandle *self,char **xsval); +static PyObject *none (bool result); static int parse_transaction_path(XsHandle *self, PyObject *args,@@ -484,8 +486,6 @@ static PyObject *xspy_read_watch(XsHandle *self, PyObject *args)struct xs_handle *xh =xshandle (self);PyObject *val =NULL ;char **xsval; -PyObject *token; -int i;unsigned int num;if (!xh)@@ -497,32 +497,20 @@ again:Py_END_ALLOW_THREADS if (!xsval) {PyErr_SetFromErrno (xs_error); - goto exit; - } -if (sscanf (xsval[XS_WATCH_TOKEN ],"%li" , (unsigned long *)&token) !=1 ) { -xs_set_error (EINVAL ); - goto exit; - } -for (i =0 ; i <PyList_Size (self->watches); i++) { -if (token ==PyList_GetItem (self->watches, i)) -break ; - } -if (i ==PyList_Size (self->watches)) { -/* We do not have a registered watch for the one that has just fired. -Ignore this -- a watch that has been recently deregistered can still -have watches in transit. This is a blocking method, so go back to -read again. -*/ -free (xsval); - goto again; +return val; } -/* Create tuple (path, token). */ - val =Py_BuildValue ("(sO)" , xsval[XS_WATCH_PATH ], token); - exit: + + val =match_watch_by_token (self, xsval);
tools/python/xen/lowlevel/xs/xs.c again: · line 513
Quoted 10 lines
tools/python/xen/lowlevel/xs/xs.c +6
-0free (xsval); + +if (!val && errno ==EAGAIN ) { + goto again; + } +return val; } +
Stray blank line.
Quoted 4 lines
tools/python/xen/lowlevel/xs/xs.c +0
-0#define xspy_unwatch_doc "\n" \ "Stop watching a path.\n" \ " path [string] : xenstore path.\n" \
tools/python/xen/lowlevel/xs/xs.c static int parse_transaction_path(XsHandle *self, PyObject *args, · line 865
Quoted 10 lines
tools/python/xen/lowlevel/xs/xs.c +7
-0@@ -868,6 +856,33 @@ static int parse_transaction_path(XsHandle *self, PyObject *args, } +static PyObject *match_watch_by_token (XsHandle *self,char **xsval) +{ +PyObject *token; +int i; + +if (sscanf (xsval[XS_WATCH_TOKEN ],"%li" , (unsigned long *)&token) !=1 ) { +xs_set_error (EINVAL );
Please fix indentation here.
With the comments addressed:
_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
PATCH 3/3 python: Add binding for non-blocking xs_check_watch()
In reply to Euan Harrisxs_check_watch() checks for watch notifications without blocking. Together with the binding for xs_fileno(), this makes it possible to write event-driven clients in Python.
--- tools/python/xen/lowlevel/xs/xs.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
tools/python/xen/lowlevel/xs/xs.c +28 -0
@@ -474,6 +474,33 @@ static PyObject *xspy_fileno(XsHandle *self) } +#define xspy_check_watch_doc "\n" \ + "Check for watch notifications without blocking.\n" \ + "\n" \ + "Returns: [tuple] (path, token).\n" \ + " None if no watches have fired.\n" \ + "Raises xen.lowlevel.xs.Error on error.\n" \ + "\n" + +static PyObject *xspy_check_watch (XsHandle *self,PyObject *args) +{ +struct xs_handle *xh =xshandle (self); +PyObject *val =NULL ; +char **xsval; + +if (!xh) +return NULL ; + + xsval =xs_check_watch (xh); +if (!xsval) { +return none (errno ==EAGAIN ); + } + + val =match_watch_by_token (self, xsval); +free (xsval); +return val; +} +#define xspy_read_watch_doc "\n" \ "Read a watch notification.\n" \ "\n" \
@@ -912,6 +939,7 @@ static PyMethodDef xshandle_methods[] = {XSPY_METH (set_permissions,METH_VARARGS ),XSPY_METH (watch,METH_VARARGS ),XSPY_METH (read_watch,METH_NOARGS ), +XSPY_METH (check_watch,METH_NOARGS ),XSPY_METH (unwatch,METH_VARARGS ),XSPY_METH (transaction_start,METH_NOARGS ), XSPY_METH(transaction_end, METH_VARARGS | METH_KEYWORDS),
Signature · 7 lines
1.8.3.1 _______________________________________________ Xen-devel mailing list [email protected] https://lists.xen.org/xen-devel
Re: [PATCH 3/3] python: Add binding for non-blocking xs_check_watch()
Quoted 5 lines · Euan Harris
xs_check_watch()checks for watch notifications without blocking. Together with the binding forxs_fileno(), this makes it possible to write event-driven clients in Python.Signed-off-by Euan Harris <[email protected]>
_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel