ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
1

ROS Qt Interrupted System Call On Plugin Startup

asked Jul 16 '13

jker gravatar image

updated Jan 28 '14

ngrennan gravatar image

I've recently been working on a python widget for rqt that creates a separate thread to read joystick data in from a pipe. I've tested it as a standalone python program and confirmed that it's getting valid data. However, when I put it into a python plugin for rqt that I've already created and tested, it crashes. After some tweaking of the code, it looks like it crashes at the end of the __init__ function in the plugin with IOError: [Errno 4] Interrupted system call when the pipe read is interrupted.

The initialization is basically:

  1. Create a joystick object that creates a thread to endlessly read in data from /dev/input/js0; start running it
  2. Create thread to endlessly check the Joystick object containing this other thread to look for new data; start running it
  3. Wait 5 seconds, during which joystick data prints out fine as I would expect
  4. The 5 Second wait ends, IOError: [Errno 4] Interrupted system call is thrown, and rqt loads as normal

A snippet of the plugin code is below. The Joystick class being referenced creates a thread in the same manner, opens a pipe using self._pipe = open('/dev/local/js0', 'r'), then reads in raw data for parsing using for character in self._pipe.read(1):

    class Joystick(Plugin):
      # Variables...
      def __init__(self, context):
          # Some initialization code here...
          #...
          # Create the joystick object and run its thread
          self._joystick_reader = LinuxJoystickReader()
          self._joystick_reader.initialize('/dev/input/js0')
          self._joystick_reader.start()
          # Create a local thread and run it to print the joystick data
          self._joystick_thread_run = True
          self._joystick_read_thread = Thread(target=self.pub_joystick, args=())
          self._joystick_read_thread.start()
          # Sleep, during which the data prints out fine
          time.sleep(5)
          # -- ERROR --
          # File "joystick_class.py" line 35, in read_pipe
          # for character in self._pipe.read(1):
          # IOError: [Errno 4] Interrupted system call

       def pub_joystick(self):
         while self._joystick_thread_run:
           if self._joystick_reader.has_new_data():
             pub_str = str(self._joystick_reader.get_data_array()[0]) + ", " + str(self._joystick_reader.get_data_array()[1])
             print pub_str
         self._joystick_reader.stop()
Preview: (hide)

Comments

Btw you should never sleep during construction but I guess this is just part of your debugging code. Even any long running other operation should be deferred or be performed in a separate thread.

Dirk Thomas gravatar image Dirk Thomas  ( Jul 17 '13 )edit

Yes, the sleep is just for debugging purposes (a shot in the dark that paid off), I'm trying to get the separate threads to run on their own without crashing :)

jker gravatar image jker  ( Jul 18 '13 )edit

3 Answers

Sort by » oldest newest most voted
1

answered Jul 18 '13

jker gravatar image

It seems like something in Qt or one of the plugins was interrupting the system call I was making to read the joystick input. Adding additional try/catch code to my joystick implementation solved the problem for me, since it can just continue reading input even if it is interrupted. I was also able to get my plugin working without crashing and without the additional try/catch code by adding my plugin to the GUI last, after all the other plugins were loaded and running.

Got the idea from this StackOverflow thread. I'm still not sure what part interrupted my system call, why it did that, or if it's intentional, but the solution seems to be robust to random interruptions on startup.

Preview: (hide)
2

answered Jul 17 '13

Dirk Thomas gravatar image

Based in the code block you posted I can't spot the problem. But I tried a simple example which works fine for me (including the cleanup).

class MyPlugin(Plugin):

def __init__(self, context):
        super(MyPlugin, self).__init__(context)
        self._thread = threading.Thread(target=self.cb, args=())
        self._thread_running = True
        self._thread.start()

    def cb(self):
        while self._thread_running:
            print 'cb'
            time.sleep(0.1)
        print('thread finished')

    def shutdown_plugin(self):
        self._thread_running = False
        self._thread.join()
Preview: (hide)
0

answered Jul 17 '13

130s gravatar image

This may not be the direct answer to your question.

Although I can't tell why the error occurs after having browsed your code briefly, it might be worth separating Plugin.init and the logic you've currently put in there both from the design of rqt framework and from design of GUI programs points of view. Plugin class (btw I assume you mean Plugin as qt_gui.plugin.Plugin) works as the interface (View in MVC design pattern so to speak) between your own GUI programs and rqt framework, and that's why you are able to load your class on rqt AFTER Plugin.__init__ terminates. I'm not entirely sure the internal logic of it, but creating threads at __init__ might be troublesome from my experience (author of framework @Dirk Thomas can give the clearest explanation if he's available).

Preview: (hide)

Comments

Running threads from within a plugin is fine, if (!) the plugin implements cleanup correctly (which I can't tell from the code snippet above).

Dirk Thomas gravatar image Dirk Thomas  ( Jul 17 '13 )edit

Yes, I flag threads to stop, unregister publishers, etc in def shutdown_plugin(self)

jker gravatar image jker  ( Jul 18 '13 )edit

Question Tools

Stats

Asked: Jul 16 '13

Seen: 746 times

Last updated: Jul 18 '13