title: Browsing avahi services with python misses services
body: <p>I need a class which gives me a list of avahi services of a certain type which a currently available. Therefor I run a <code>gobject.MainLoop()</code> (line 23-25) in a separate thread an add a browser for each service I am interested in (line 27,28). This works in principle.</p>
  
  <p>My problem is, that I do not get always all services. Sometimes all services that are available are listed, sometimes none of them, sometimes just a few. My guess is, that the browser starts iterating the services (line 36) before the appropriate signals are connected (line 41-44), but I have no clue how to fix this. Below a minimal example which shows the failure. </p>
  
  <p>Most examples I have seen on the net (e.g.: <a href="https://stackoverflow.com/questions/5126413">Stopping the Avahi service and return a list of elements</a>) run the MainLoop after the browser is set up and the signals are connected. The loop is then quit when the "AllForNow" signal is received. This is not an option for me as the Browser should keep running and listen for new or removed services (which works reliable by the way, just the initial query is problematic).</p>
  
  <pre><code>#!/usr/bin/python
  import dbus
  from dbus.mainloop.glib import DBusGMainLoop
  import avahi
  import gobject
  import threading
  
  gobject.threads_init()
  dbus.mainloop.glib.threads_init()
  
  class ZeroconfBrowser:
      def __init__(self):
          self.service_browsers = set()
          self.services = {}
          self.lock = threading.Lock()
  
          loop = DBusGMainLoop(set_as_default=True)
          self._bus = dbus.SystemBus(mainloop=loop)
          self.server = dbus.Interface(
                  self._bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), 
                  avahi.DBUS_INTERFACE_SERVER)
  
          thread = threading.Thread(target=gobject.MainLoop().run)
          thread.daemon = True
          thread.start()
  
          self.browse("_ssh._tcp")
          self.browse("_http._tcp")
  
      def browse(self, service):
          if service in self.service_browsers:
              return
          self.service_browsers.add(service)
  
          with self.lock:
              browser = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME, 
                      self.server.ServiceBrowserNew(avahi.IF_UNSPEC, 
                              avahi.PROTO_UNSPEC, service, 'local', dbus.UInt32(0))),
                      avahi.DBUS_INTERFACE_SERVICE_BROWSER)
  
              browser.connect_to_signal("ItemNew", self.item_new)
              browser.connect_to_signal("ItemRemove", self.item_remove)
              browser.connect_to_signal("AllForNow", self.all_for_now)
              browser.connect_to_signal("Failure", self.failure)
  
      def resolved(self, interface, protocol, name, service, domain, host, 
              aprotocol, address, port, txt, flags):
          print "resolved", interface, protocol, name, service, domain, flags
  
      def failure(self, exception):
          print "Browse error:", exception
  
      def item_new(self, interface, protocol, name, stype, domain, flags):
          with self.lock:
              self.server.ResolveService(interface, protocol, name, stype,
                      domain, avahi.PROTO_UNSPEC, dbus.UInt32(0),
                      reply_handler=self.resolved, error_handler=self.resolve_error)
  
      def item_remove(self, interface, protocol, name, service, domain, flags):
          print "removed", interface, protocol, name, service, domain, flags
  
      def all_for_now(self):
          print "all for now"
  
      def resolve_error(self, *args, **kwargs):
          with self.lock:
              print "Resolve error:", args, kwargs
  
  import time
  def main():
      browser = ZeroconfBrowser()
      while True:
          time.sleep(3)
          for key, value in browser.services.items():
              print key, str(value)
  
  if __name__ == '__main__':
      main()
  </code></pre>