title: Stray threads in Python gstreamer use
body: <p>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.</p>
  
  <p>After a few runs, threading.enumerate() is showing:</p>
  
  <pre><code>[&lt;_MainThread(MainThread, started 140079923849024)&gt;,
   &lt;_DummyThread(Dummy-1, started daemon 140079768815360)&gt;,
   &lt;_DummyThread(Dummy-3, started daemon 140079785338624)&gt;,
   &lt;_DummyThread(Dummy-4, started daemon 140079418832640)&gt;,
   &lt;_DummyThread(Dummy-2, started daemon 140079802386176)&gt;]
  </code></pre>
  
  <p>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:</p>
  
  <pre><code>try:
      time.sleep(899.0)
  except KeyboardInterrupt:
      pass
  
  time.sleep(1.0)
  </code></pre>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>The class code (stripped down to remove irrelevant parts):</p>
  
  <pre><code>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
  </code></pre>
  
  <p>This is instantiated as:</p>
  
  <pre><code>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()
  </code></pre>
  
  <p>An example pipeline can be seen at:  <a href="https://s3.amazonaws.com/beirdo-share/before.png" rel="nofollow">https://s3.amazonaws.com/beirdo-share/before.png</a></p>