Reputation: 424
I have noticed that after I use gstreamer via python (using python-gst-1.0 deb package version 1.2.0-1 from Ubuntu 14.04), I seem to have a stray thread for every encoder run. I have the gstreamer interface within a module I wrote, and it does the gobject.mainloop within the module, and does mainloop.quit(), so I wouldn't expect that it's the mainloop itself.
After a few runs, threading.enumerate() is showing:
[<_MainThread(MainThread, started 140079923849024)>,
<_DummyThread(Dummy-1, started daemon 140079768815360)>,
<_DummyThread(Dummy-3, started daemon 140079785338624)>,
<_DummyThread(Dummy-4, started daemon 140079418832640)>,
<_DummyThread(Dummy-2, started daemon 140079802386176)>]
Thankfully, they are started with daemon, so the program will exit, but I'm at a loss as to how to clean these up. They are affecting the possibility to use Ctrl-C to exit the script, as the KeyboardInterrupt is not always seeming to come to the MainThread. I end my run loop with:
try:
time.sleep(899.0)
except KeyboardInterrupt:
pass
time.sleep(1.0)
This should allow me to abort the loop timeout by hitting Ctrl-C twice rapidly as the first time catches the try/except, and the second one has no handler on the 1s sleep, and thus quits. However, with the stray threads, the second Ctrl-C is somehow never seen at this level, so I need to Ctrl-Z to get to the shell and forcefully kill the script. I don't like it.
Anyone have any idea what this stray thread would be, and how to have it cooperate and die for me? I'm about to break gdb out on a running process to determine what it might be.
The class code (stripped down to remove irrelevant parts):
class GstEncoder:
def __init__(self, metadata, mediainfo):
self.error = None
# used for controlling logic which I removed for clarity
self.metadata = metadata
self.mediainfo = mediainfo
# Create a pipeline in self.pipeline
self.setupPipeline()
# Put in the MainLoop
self.mainloop = GObject.MainLoop()
self.context = self.mainloop.get_context()
self.abort = False
def __del__(self):
logger.info("Dying gasp!")
if self.mainloop.is_running():
self.mainloop.quit()
self.pipeline.unref()
def start(self):
# Set in playing mode
self.pipeline.set_state(Gst.State.PLAYING)
# actually only used in some situations, removed the controlling logic for clarity
GObject.timeout_add_seconds(900, self.timedOut)
GObject.timeout_add_seconds(30, self.progressReport)
try:
self.abort = False
self.mainloop.run()
except KeyboardInterrupt:
logger.warning("Aborted by Ctrl-C")
self.abort = True
self.mainloop.quit()
raise KeyboardInterrupt
# Stop the pipeline
self.pipeline.set_state(Gst.State.NULL)
return self.error
def progressReport(self):
position = self.pipeline.query_position(Gst.Format.TIME)[1]
duration = self.pipeline.query_duration(Gst.Format.TIME)[1]
if self.abort:
return False
percentage = 0.0 if duration == 0 \
else float(position) / float(duration) * 100.0
logger.info("Progress: %s / %s (%.2f%%)" % (Gst.TIME_ARGS(position),
Gst.TIME_ARGS(duration), percentage))
return True
def timedOut(self):
if self.abort:
return False
self.error = "Aborted by watchdog timer"
logger.warning(self.error)
self.abort = True
self.mainloop.quit()
return False
This is instantiated as:
err = None
# Filename, etc is in metadata
encoder = GstEncoder(metadata, mediainfo)
if encoder.error:
err = encoder.error
if not err:
err = encoder.start()
if err:
logger.error(err)
encoder = None
print threading.enumerate()
An example pipeline can be seen at: https://s3.amazonaws.com/beirdo-share/before.png
Upvotes: 1
Views: 2399
Reputation: 5883
I've run into an issue that sounds the same: Ctrl-C triggers a KeyboardInterrupt
but the process doesn't exit due to non-daemon gstreamer threads. The fix was to call GObject.threads_init()
once on application startup.
Upvotes: 1