[PATCH xorg-gtest 6/6] device: use inotify to wait for the device to appear
Peter Hutterer
peter.hutterer at who-t.net
Wed Aug 29 16:54:55 PDT 2012
On Wed, Aug 29, 2012 at 04:26:47PM -0700, Chase Douglas wrote:
> On 08/29/2012 03:25 PM, Peter Hutterer wrote:
> >On Wed, Aug 29, 2012 at 01:09:32PM -0700, Chase Douglas wrote:
> >>On 08/28/2012 11:14 PM, Peter Hutterer wrote:
> >>>uinput can be too slow, leaving us with GuessDeviceNode() failing and an
> >>>empty device string. Use inotify to tell us when a /dev/input/event device
> >>>appeared and then continue. However, even when inotify tells us the device
> >>>is there, the EVIOCGNAME may still fail on the device, so we need to keep
> >>>the backup GuessDeviceNode() in place.
> >>>
> >>>This leaves us with a race condition - if a different device pops up while
> >>>we're waiting, then we may still not get the device name. Chance of that
> >>>happening is tiny.
> >>>
> >>>Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> >>>---
> >>> src/device.cpp | 59 +++++++++++++++++++++++++++++++++++++-
> >>> test/.gitignore | 1 +
> >>> test/Makefile.am | 10 +++++--
> >>> test/PIXART-USB-OPTICAL-MOUSE.desc | 41 ++++++++++++++++++++++++++
> >>> test/device-test.cpp | 33 +++++++++++++++++++++
> >>> 5 files changed, 141 insertions(+), 3 deletions(-)
> >>> create mode 100644 test/PIXART-USB-OPTICAL-MOUSE.desc
> >>> create mode 100644 test/device-test.cpp
> >>>
> >>>diff --git a/src/device.cpp b/src/device.cpp
> >>>index 232d3ae..ea98d17 100644
> >>>--- a/src/device.cpp
> >>>+++ b/src/device.cpp
> >>>@@ -31,6 +31,8 @@
> >>> #include <linux/input.h>
> >>> #include <fcntl.h>
> >>> #include <dirent.h>
> >>>+#include <sys/inotify.h>
> >>>+#include <poll.h>
> >>>
> >>> #include <stdexcept>
> >>>
> >>>@@ -110,6 +112,48 @@ void xorg::testing::evemu::Device::GuessDeviceNode(time_t ctime) {
> >>> free(event_devices);
> >>> }
> >>>
> >>>+static std::string wait_for_inotify(int fd)
> >>>+{
> >>>+ std::string devnode;
> >>>+ bool found = false;
> >>>+ struct pollfd pfd;
> >>>+
> >>>+ pfd.fd = fd;
> >>>+ pfd.events = POLLIN;
> >>>+
> >>>+ char buf[1024];
> >>>+ size_t bufidx = 0;
> >>>+
> >>>+ while (!found && poll(&pfd, 1, 2000) > 0) {
> >>>+ ssize_t r;
> >>>+
> >>>+ r = read(fd, buf + bufidx, sizeof(buf) - bufidx);
> >>>+ if (r == -1 && errno != EAGAIN) {
> >>>+ std::cerr << "inotify read failed with: " << std::string(strerror(errno)) << std::endl;
> >>>+ break;
> >>>+ }
> >>>+
> >>>+ bufidx += r;
> >>>+
> >>>+ struct inotify_event *e = reinterpret_cast<struct inotify_event*>(buf);
> >>>+
> >>>+ while (bufidx > sizeof(*e) && bufidx >= sizeof(*e) + e->len) {
> >>>+ if (strncmp(e->name, "event", 5) == 0) {
> >>>+ devnode = DEV_INPUT_DIR + std::string(e->name);
> >>>+ found = true;
> >>>+ break;
> >>>+ }
> >>>+
> >>>+ /* this packet didn't contain what we're looking for */
> >>>+ int len = sizeof(*e) + e->len;
> >>>+ memmove(buf, buf + len, bufidx - len);
> >>>+ bufidx -= len;
> >>>+ }
> >>>+ }
> >>>+
> >>>+ return devnode;
> >>>+}
> >>>+
> >>> xorg::testing::evemu::Device::Device(const std::string& path)
> >>> : d_(new Private) {
> >>> static const char UINPUT_NODE[] = "/dev/uinput";
> >>>@@ -132,6 +176,14 @@ xorg::testing::evemu::Device::Device(const std::string& path)
> >>>
> >>> fclose(fp);
> >>>
> >>>+ int ifd = inotify_init1(IN_NONBLOCK);
> >>>+ if (ifd == -1 || inotify_add_watch(ifd, DEV_INPUT_DIR, IN_CREATE) == -1) {
> >>>+ std::cerr << "Failed to create inotify watch" << std::endl;
> >>>+ if (ifd != -1)
> >>>+ close(ifd);
> >>>+ ifd = -1;
> >>>+ }
> >>>+
> >>> d_->fd = open(UINPUT_NODE, O_WRONLY);
> >>> if (d_->fd < 0) {
> >>> evemu_delete(d_->device);
> >>>@@ -145,7 +197,12 @@ xorg::testing::evemu::Device::Device(const std::string& path)
> >>> throw std::runtime_error("Failed to create evemu device");
> >>> }
> >>>
> >>>- GuessDeviceNode(d_->ctime);
> >>>+ if (ifd != -1) {
> >>>+ std::string devnode = wait_for_inotify(ifd);
> >>>+ if (event_is_device(devnode, evemu_get_name(d_->device), d_->ctime))
> >>>+ d_->device_node = devnode;
> >>>+ close(ifd);
> >>
> >>We could instead loop around here waiting until the correct device
> >>is seen, potentially with a timeout? I'm guessing you've already
> >>thought of that, so I'm interested to hear your thoughts.
> >
> >the problem here is the failed ioctl(EVIOCGNAME). inotify gives us the right
> >device, but because the ioctl fails we cannot be sure it really is our
> >device.
> >
> >the alternative here is to loop until the ioctl succeeds but I don't know if
> >that can hang the test, and putting a loop limit on it just means we'll need
> >GuessDeviceNode() again.
> >
> >you can try it, remove the GuessDeviceNode() call and run the new test in a
> >loop
> >
> > while [ $? -eq 0 ]; do sudo ./device-test; done
> >
> >it'll run anywhere from a minute to several minutes before it fails.
>
> Hmm... strange. Oh well. This shouldn't be a huge concern when
> running the tests.
actually, it is. that's why I started running the test as above. I hit the
issue in real test runs (i.e. make check) after around 30-40 tests, but
inconsistently. the while loop above was just a quicker way to reproduce,
and it ruled out any issues with the tests themselves.
> Reviewed-by: Chase Douglas <chase.douglas at canonical.com>
thanks!
Cheers,
Peter
More information about the xorg-devel
mailing list