[gedit] Implemented asynchronous reading and writing
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: svn-commits-list gnome org
- Subject: [gedit] Implemented asynchronous reading and writing
- Date: Fri, 22 May 2009 15:56:27 -0400 (EDT)
commit cfb6dd10a0de6501a2d12031dfba1e99d2150e5c
Author: Jesse van den Kieboom <jesse icecrew nl>
Date: Fri May 22 21:56:19 2009 +0200
Implemented asynchronous reading and writing
Before, writing to STDIN was done in one go, which fails for large buffers. This is now
done asynchronously with 16K at the time, in an idle loop. Furthermore, STDERR and STDOUT
are now non blocking and use 'read' instead of 'readline' to prevent blocking.
---
plugins/externaltools/tools/capture.py | 85 ++++++++++++++++++++++++++++---
1 files changed, 76 insertions(+), 9 deletions(-)
diff --git a/plugins/externaltools/tools/capture.py b/plugins/externaltools/tools/capture.py
index 56930c8..afee587 100644
--- a/plugins/externaltools/tools/capture.py
+++ b/plugins/externaltools/tools/capture.py
@@ -22,11 +22,14 @@ import os, sys, signal
import locale
import subprocess
import gobject
+import fcntl
class Capture(gobject.GObject):
CAPTURE_STDOUT = 0x01
CAPTURE_STDERR = 0x02
CAPTURE_BOTH = 0x03
+
+ WRITE_BUFFER_SIZE = 0x4000
__gsignals__ = {
'stdout-line' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
@@ -81,6 +84,8 @@ class Capture(gobject.GObject):
popen_args['stderr'] = subprocess.PIPE
self.tried_killing = False
+ self.idle_write_id = 0
+ self.read_buffer = ''
try:
self.pipe = subprocess.Popen(self.command, **popen_args)
@@ -91,25 +96,64 @@ class Capture(gobject.GObject):
# Signal
self.emit('begin-execute')
-
- # IO
- if self.input_text is not None:
- self.pipe.stdin.write(self.input_text)
- self.pipe.stdin.close()
+
if self.flags & self.CAPTURE_STDOUT:
+ # Set non blocking
+ flags = fcntl.fcntl(self.pipe.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK
+ fcntl.fcntl(self.pipe.stdout.fileno(), fcntl.F_SETFL, flags)
+
gobject.io_add_watch(self.pipe.stdout,
gobject.IO_IN | gobject.IO_HUP,
self.on_output)
+
elif self.flags & self.CAPTURE_STDERR:
+ # Set non blocking
+ flags = fcntl.fcntl(self.pipe.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK
+ fcntl.fcntl(self.pipe.stderr.fileno(), fcntl.F_SETFL, flags)
+
gobject.io_add_watch(self.pipe.stderr,
gobject.IO_IN | gobject.IO_HUP,
self.on_output)
+ # IO
+ if self.input_text is not None:
+ # Write async, in chunks of something
+ self.write_buffer = str(self.input_text)
+
+ if self.idle_write_chunk():
+ self.idle_write_id = gobject.idle_add(self.idle_write_chunk)
+
# Wait for the process to complete
gobject.child_watch_add(self.pipe.pid, self.on_child_end)
+ def idle_write_chunk(self):
+ if not self.pipe:
+ self.idle_write_id = 0
+ return False
+
+ try:
+ l = len(self.write_buffer)
+ m = min(l, self.WRITE_BUFFER_SIZE)
+
+ self.pipe.stdin.write(self.write_buffer[:m])
+
+ if m == l:
+ self.write_buffer = ''
+ self.pipe.stdin.close()
+
+ self.idle_write_id = 0
+ return False
+ else:
+ self.write_buffer = self.write_buffer[m:]
+ return True
+ except IOError:
+ self.pipe.stdin.close()
+ self.idle_write_id = 0
+
+ return False
+
def on_output(self, source, condition):
- line = source.readline()
+ line = source.read()
if len(line) > 0:
try:
@@ -119,18 +163,41 @@ class Capture(gobject.GObject):
locale.getdefaultlocale()[1],
'replace')
- if not self.pipe or source == self.pipe.stdout:
- self.emit('stdout-line', line)
+ self.read_buffer += line
+ lines = self.read_buffer.splitlines(True)
+
+ if not lines[-1].endswith("\n"):
+ self.read_buffer = lines[-1]
+ lines = lines[0:-1]
else:
- self.emit('stderr-line', line)
+ self.read_buffer = ''
+
+ for line in lines:
+ if not self.pipe or source == self.pipe.stdout:
+ self.emit('stdout-line', line)
+ else:
+ self.emit('stderr-line', line)
+
return True
else:
+ if self.read_buffer:
+ if source == self.pipe.stdout:
+ self.emit('stdout-line', self.read_buffer)
+ else:
+ self.emit('stderr-line', self.read_buffer)
+
+ self.read_buffer = ''
+
self.pipe = None
return False
def stop(self, error_code = -1):
if self.pipe is not None:
+ if self.idle_write_id:
+ gobject.source_remove(self.idle_write_id)
+ self.idle_write_id = 0
+
if not self.tried_killing:
os.kill(self.pipe.pid, signal.SIGTERM)
self.tried_killing = True
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]