I'm using the pyxs
Python Xenstore client module to write an Upstart daemon that monitors a bunch of output pins on a GPIO controller on a box. The basic structure of the daemon, after startup, is to export the relevant pins, add the corresponding Xenstore paths for the pins, and add and monitor watches for each of the Xenstore paths. The watches part is threaded - for each watch a thread is created with a target worker method that monitors the watch for changes. According to the PyXS docs you basically have to do something like:
# monitor is a pyxs.client.Client.Monitor object, and watch adds a
# watch to the given path
monitor.watch(path, path_token)
# wait for events on the watched path - returns a pair if there is an
# event, the first is the event path and the second is the path token
monitor.wait(sleep=...)
My question is whether the call to wait
blocks if no sleep=<time>
argument is specified - it is not clear from the PyXS docs whether this is the case or not.
The code is roughly something like this:
from pyxs.client import Client
from pyxs.exceptions import PyXSError
from threading import Thread
...
class gpiod(object):
def __init__(self,...):
...
# stores pin numbers using descriptive labels as keys
self._gpio_pins = {}
# stores Xenstore paths for the pins, using the pin labels as keys
self._xenstore_paths = {}
self._xenstore_client = Client()
self._xenstore_monitor = self._xenstore_client.monitor()
# stores threads that monitor the watches added for the paths
self._xenstore_watchers = {}
self.start()
self.run()
def _watch_xenstore_path(self, watch_path):
"""
A worker method that is a target for a watch thread.
"""
while True:
try:
path_change = self._xenstore_monitor.wait(sleep=1)
while not path_change:
path_change = self._xenstore_monitor.wait(sleep=1)
if path_change[0] == watch_path:
# write changed value to the pin
except PyXSError as e:
# log the error
def start(self):
# load config
...
# export relevant GPIO output pins (using system calls to sysfs)
...
# create Xenstore paths using _xenstore_client
...
# set watches on the paths
for path in self._xenstore_paths:
self._xenstore_monitor.watch(wpath=path, token=path)
self._xenstore_watchers.update(
{pin_label: Thread(target=self._watch_xenstore_path, args=(path,))}
)
def run(self):
for watcher in self._xenstore_watchers:
watcher.start()
From the pyxs
documentation:
wait(sleep=None)
Waits for any of the watched paths to generate an event, which is a (path, token) pair, where the first element is event path, i.e. the actual path that was modified and second element is a token, passed to the watch(). Parameters: sleep (float) – number of seconds to sleep between event checks.
That means, the sleep
parameters is actually to separate even checks on the wait
method and if you look at the code from github:
def wait(self, sleep=None):
"""Waits for any of the watched paths to generate an event,
which is a ``(path, token)`` pair, where the first element
is event path, i.e. the actual path that was modified and
second element is a token, passed to the :meth:`watch`.
:param float sleep: number of seconds to sleep between event
checks.
"""
while True:
if self.client.events:
packet = self.client.events.popleft()
return Event(*packet.payload.split("\x00")[:-1])
# Executing a noop, hopefuly we'll get some events queued
# in the meantime. Note: I know it sucks, but it seems like
# there's no other way ...
self.client.execute_command(Op.DEBUG, "")
if sleep is not None: #sleep here is to provide tap gap between event check
time.sleep(sleep)
You'll see that sleep
parameters is only to provide tap gap between event check where as the while True
loop keeps going until you get un event
or if you want: How fast to check for event
, the smaller sleep
the faster the regular check.
So, in conclusion:
The call to wait
method already blocks, sleep
is only to give time separation between event
checks.