2009-12-29 11 views
0

J'utilise python-dbus et cherrypy pour surveiller les périphériques USB et fournir un service REST qui conservera l'état sur les périphériques USB insérés. J'ai écrit et débogué ces services indépendamment, et ils fonctionnent comme prévu.Numéro de fusion DBus-Cherrypy

Maintenant, je fusionne les services en une seule application. Mon problème est: Je n'arrive pas à obtenir les deux services (cherrypy et dbus) pour commencer ensemble. L'un ou l'autre bloque ou sort de la portée, ou n'est pas initialisé.

J'ai essayé d'encapsuler chacun dans son propre thread, et il suffit d'appeler start sur eux. Cela a des problèmes bizarres.

class RESTThread(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     cherrypy.config.update({ 'server.socket_host': HVR_Common.DBUS_SERVER_ADDR, 'server.socket_port': HVR_Common.DBUS_SERVER_PORT, }) 
     cherrypy.quickstart(USBRest()) 

class DBUSThread(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     DBusGMainLoop(set_as_default=True) 
     loop = gobject.MainLoop() 
     DeviceAddedListener() 
     print 'Starting DBus' 
     loop.run() 

print 'DBus Python Started' 
if __name__ == '__main__': 
    # Start up REST 

    print 'Starting REST' 
    rs = RESTThread() 
    rs.start() 

    db = DBUSThread() 
    db.start() 

    #cherrypy.config.update({ 'server.socket_host': HVR_Common.DBUS_SERVER_ADDR, 'server.socket_port': HVR_Common.DBUS_SERVER_PORT, }) 
    #cherrypy.quickstart(USBRest()) 

    while True: 
     x = 1 

Lorsque ce code est exécuté, le code Cherrypy n'est pas entièrement initialisé. Quand un périphérique USB est inséré, cherrypy continue à s'initialiser (comme si les threads sont liés d'une façon ou d'une autre), mais ne fonctionne pas (ne sert pas de données ou même de faire des connexions sur le port) Je n'ai pas trouvé un moyen de démarrer cherrypy de telle sorte qu'il inits, et retourne, afin que je puisse initier les choses DBus et être en mesure de faire sortir la porte.

Ma question ultime est: Y a-t-il un moyen de démarrer et de ne pas bloquer Cherrypy mais de continuer à travailler? Je veux me débarrasser des threads dans cet exemple et init à la fois cherrypy et dbus dans le fil principal.

+0

J'ai essayé ceci: http://stackoverflow.com/questions/510821/how-to-write-a-functional-test-for-a-dbus-service-written-in-python/762079#762079 Maintenant cherrypy fonctionne très bien, mais le loop.run() ne semble pas fonctionner. – Therealstubot

Répondre

3

J'ai compris cela. Apparemment, il y a un tas de problèmes de contention de thread dans glib. Si vous créez une application avec DBusGMainLoop, vous ne pouvez pas créer un autre thread dans votre application. Le nouveau thread bloque immédiatement lorsque start() est appelé. Aucune quantité de massage n'obtiendra le nouveau thread à exécuter.

J'ai trouvé un site qui avait une référence obscure à dbus.mainloop.glib.threads_init(), et comment cela doit être appelé avant d'initialiser un nouveau thread. Cependant, un nouveau problème est découvert lorsque cela est tenté. Une exception est levée qui indique que g_thread_init() doit être appelé avant que dbus.mainloop.glib.threads_init() soit appelé. Plus de recherche a découvert une autre référence obscure à gobject.threads_init(). Cela semblait aller, donc après beaucoup d'expérimentation, j'ai découvert la bonne séquence.

Voici la solution.

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 
gobject.threads_init() 
dbus.mainloop.glib.threads_init()  
DBUSMAINLOOP = gobject.MainLoop() 

print 'Creating DBus Thread' 
DBUSLOOPTHREAD = threading.Thread(name='glib_mainloop', target=DBUSMAINLOOP.run) 
DBUSLOOPTHREAD.start() 

print 'Starting REST' 
cherrypy.config.update({ 'server.socket_host': Common.DBUS_SERVER_ADDR, 'server.socket_port': Common.DBUS_SERVER_PORT, }) 
cherrypy.quickstart(USBRest()) 

Gosh quel cauchemar. Maintenant, pour le rendre meilleur.

3

Oui; n'utilisez pas cherrypy.quickstart. Au lieu de cela, décompressez-le:

cherrypy.config.update(conf) 
cherrypy.tree.mount(USBREST()) 
cherrypy.engine.start() 

Quickstart fait ci-dessus, mais se termine en appelant engine.block(). Si votre programme a une boucle principale autre que CherryPy, omettez l'appel à engine.block et ça devrait aller. Cependant, lorsque votre boucle principale étrangère se termine, vous voulez toujours appeler cherrypy.engine.stop():

loop = gobject.MainLoop() 
try: 
    loop.run() 
finally: 
    cherrypy.engine.stop() 

Il y a quelques autres, comme si gotchas CherryPy doit gérer Ctrl-C et d'autres signaux, et si elle devrait autoreload. Ces comportements sont à vous, et sont tous assez faciles à activer/désactiver. Voir le code source cherrypy.quickstart() pour certains d'entre eux.

+0

J'ai essayé cela, plusieurs façons sans succès. J'ai réussi à comprendre le problème.Apparemment, le démarrage d'une boucle glib (pour le messager DBus) ne laissera pas démarrer d'autres threads dans l'application jusqu'à ce qu'une séquence d'appels thread_init() soit faite. Je vais développer cela dans le message original. Votre solution fonctionnerait dans les cas où une nouvelle boucle glib ne serait pas présente. – Therealstubot