Gobject io monitoring + nonblocking reads

I am having a problem using a monitor io_add_watchin python (via gobject). I want to do a non-blocking read of the entire buffer after each notification. Here's the code (abbreviated bit):

class SomeApp(object):

   def __init__(self):
      # some other init that does a lot of stderr debug writes
      fl = fcntl.fcntl(0, fcntl.F_GETFL, 0)
      fcntl.fcntl(0, fcntl.F_SETFL, fl | os.O_NONBLOCK)
      print "hooked", gobject.io_add_watch(0, gobject.IO_IN | gobject.IO_PRI, self.got_message, [""])
      self.app = gobject.MainLoop()

   def run(self):
      print "ready"
      self.app.run()

   def got_message(self, fd, condition, data):
      print "reading now"
      data[0] += os.read(0, 1024)
      print "got something", fd, condition, data
      return True

gobject.threads_init()
SomeApp().run()

Here's the trick - when I run the program without activated debugging output, I do not receive calls got_message. When I write a lot of material for stderr, the problem disappears. If I do not write anything other than the fingerprints visible in this code, I do not receive stdin messsage signals. Another interesting thing: when I try to run the same application with debugging stderr, but through strace(to check if there are any fcntl / ioctl calls that I missed), the problem reappears.

, : stderr strace, io_watch . strace io_watch .

" init" , , , "hooked 2", "ctrl + c" "ready", get_message, EAGAIN, .

Strace, stdin:

ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
fcntl(0, F_GETFL)                       = 0xa002 (flags O_RDWR|O_ASYNC|O_LARGEFILE)
fcntl(0, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE) = 0
fcntl(0, F_GETFL)                       = 0xa802 (flags O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE)

- , ?


EDIT: . , . "" :

...
      rpipe, wpipe = os.pipe()
      stopped = threading.Event()
      self.stdreader = threading.Thread(name = "reader", target = self.std_read_loop, args = (wpipe, stopped))
      self.stdreader.start()
      new_data = ""
      print "hooked", gobject.io_add_watch(rpipe, gobject.IO_IN | gobject.IO_PRI, self.got_message, [new_data])

   def std_read_loop(self, wpipe, stop_event):
      while True:
         try:
            new_data = os.read(0, 1024)
            while len(new_data) > 0:
               l = os.write(wpipe, new_data)
               new_data = new_data[l:]
         except OSError, e:
            if stop_event.isSet():
               break
            time.sleep(0.1)
...

, , . , :

  • "" -
  • it fugly

, - , ?

+3
3

, , , , .

, , io_add_watch(). , Python fcntl docs :

fd . , , sys.stdin.fileno() , sys.stdin, fileno(), .

, , , , STDIN FD == 0. .

, FD , , , , . , fcntl , , GTK?

, , , GTK GUI, . , , io_add_watch() :

proc = subprocess.Popen(command, stdout = subprocess.PIPE)
gobject.io_add_watch(proc.stdout, glib.IO_IN, self.write_to_buffer )

, , FD, io_add_watch().

, gobject.io_add_watch(), gobject.MainLoop(). , , io_add_watch, IO_IN.

+2

, TRUE .

0

, , stderr? , ?

In addition, I suppose you will probably have to repeatedly call os.read()in your handler until it gives any data, in case> 1024 bytes become ready between calls.

Have you tried using the module selectin the background thread to emulate a function gio? It works? What platform is this and what kind of FD are you dealing with? (file? socket? pipe?)

0
source

Source: https://habr.com/ru/post/1720413/


All Articles