io_add_watch
Python で (gobject 経由で) モニターを使用する際に問題が発生しました。通知のたびにバッファ全体のノンブロッキング読み取りを実行したいと考えています。コードは次のとおりです (少し短縮されています)。
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()
トリックは次のとおりです。デバッグ出力をアクティブにせずにプログラムを実行すると、got_message
呼び出しが行われません。最初に stderr に多くのものを書き込むと、問題はなくなります。このコードに表示される出力以外に何も記述しないと、stdin メッセージ シグナルを取得できません。もう 1 つの興味深い点は、stderr デバッグを有効にして同じアプリを実行しようとするとstrace
(見逃した fcntl / ioctl 呼び出しがあるかどうかを確認するために)、問題が再び発生することです。
つまり、strace を使用せずに最初に stderr に大量の書き込みを行うと、io_watch
機能します。strace でたくさん書くか、まったく書かないとうまくいきio_watch
ません。
「some other init」部分には時間がかかるため、「hooked 2」出力が表示される前にテキストを入力し、「ready」後に「ctrl + c」を押すと、get_message
コールバックが呼び出されますが、読み取り呼び出しは EAGAIN をスローします。バッファが空のようです。
標準入力に関連する Strace ログ:
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)
ここで何が起こっているかについて誰か考えがありますか?
編集:別の手がかり。アプリをリファクタリングして、別のスレッドで読み取りを行い、パイプ経由で戻そうとしました。それは「一種の」作品です:
...
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)
...
同じテキストを新しいパイプに入れるだけで、すべてが機能し始めるのは驚くべきことです。問題はそれです:
- 最初の行はまったく「認識」されていません-2行目以降の行のみが表示されます
- それは醜いです
多分それはなぜそれが起こっているのかについて他の誰かの手がかりを与えるでしょうか?