[PATCH xorg-gtest 6/6] device: use inotify to wait for the device to appear

Chase Douglas chase.douglas at canonical.com
Wed Aug 29 16:26:47 PDT 2012


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.

Reviewed-by: Chase Douglas <chase.douglas at canonical.com>


More information about the xorg-devel mailing list