<div>I am writing a tool for work that will run various software development aids such as message capturing and diagnostic control of system processes.<div>My current design strategy is to implement these aids as plugins (not twisted plugins) to a generic plugin runner rather than stand alone applications.</div>

<div>The plugins are spawned as separate processes by the plugin-runner and communicate using Perspective Broker during the plugin installation and shutdown phases.</div><div><br></div><div>Some plugins may take a while to shutdown as they need to close sockets, finalize files, move large files, etc.</div>

<div>I would like to instruct the plugin to shutdown and be told when it is finally ready to be shut down.</div><div><br></div><div>To date all my callRemote methods have effectively returned immediately. For example:</div>

<div><br></div><div>def remote_get_name(self):</div><div>    return <a href="http://self.name" target="_blank">self.name</a></div><div><br></div><div>My question to the list was going to be: Is there a pattern/example I could follow where I could call plugin.callRemote(&quot;shutdown&quot;) on a plugin and not return a result to the plugin-runner until the plugin has completed all it&#39;s, potentially, long running activities? However, while I was trying to write a small code snippet that would demonstrate what I was wanting, I think I got it working.</div>

<div>I think the simple answer to my question is to just return a deferred as the result to the callRemote(&quot;shutdown&quot;) method and trigger it as normal.</div><div>Google is my friend but I could not find examples of this usage. Is there any references to this usage in the twisted docs?</div>

<div><br></div><div>Below (and attached in case the formatting goes screwy) is a short example which emulates a long running shutdown activity performed by the plugin prior to shutdown.</div><div>It seems to delay the processing of the callRemote(&quot;shutdown&quot;) result until the plugin has completed it&#39;s long running activity.</div>

<div>I have omitted the separate process stuff as it didn&#39;t seem relevant for this snippet.</div><div><br></div><div><div>Is the following code snippet the standard/normal way to defer the return result of a callRemote method call?</div>

<div>If this is the normal way, how does triggering the deferred on the plugin (client) side also trigger the same/copy deferred returned to the plugin-runner (server)?</div><div>Is this PB magic, somehow managing deferreds across the PB interface?</div>

<div><br></div><div>Regards,</div><div>Chris</div><div><br></div><div><br></div></div><div><br></div><div><div><div>from twisted.internet import reactor, defer</div><div>from twisted.spread import pb</div><div>import datetime</div>

<div><br></div><div>class PluginClient(pb.Referenceable):</div><div>    &quot;&quot;&quot; Plugin client interface exposed to PluginServer&#39;s &quot;&quot;&quot;</div><div>    </div><div>    def __init__(self, shutdownCallback):</div>

<div>        self.shutdownHandler = shutdownCallback</div><div><br></div><div>    def remote_shutdown(self):</div><div>        &quot;&quot;&quot; Instruct Plugin to shutdown &quot;&quot;&quot;</div><div>        print &quot;plugin instructed to shutdown&quot;</div>

<div>        d = defer.Deferred()</div><div>        self.shutdownHandler(d)</div><div>        return d</div><div>        </div><div>class PluginServer(pb.Root):</div><div>    &quot;&quot;&quot; Plugin server interface exposed to PluginClient&#39;s &quot;&quot;&quot;</div>

<div><br></div><div>    def remote_set_client_perspective(self, pluginRef):</div><div>        print &quot;plugin-runner got client reference&quot;</div><div>        reactor.callLater(1.0, self.shutdown_plugin, pluginRef)</div>

<div>    </div><div>    def shutdown_plugin(self, pluginRef):</div><div>        </div><div>        def pluginShutdownCompleted(result, startTime):</div><div>            endTime = datetime.datetime.now()</div><div>            print &quot;Plugin shutdown took %s to complete.&quot; % (endTime - startTime)</div>

<div>            return result</div><div>        </div><div>        print &quot;plugin-runner asking plugin to shutdown&quot;</div><div>        d = pluginRef.callRemote(&quot;shutdown&quot;)</div><div>        d.addCallback(pluginShutdownCompleted, datetime.datetime.now())</div>

<div>        d.addCallback(self.shutdown)</div><div>    </div><div>    def shutdown(self, _):</div><div>        reactor.stop()</div><div><br></div><div><br></div><div>def startPluginServer(port):</div><div>    &quot;&quot;&quot; Start a plugin communications server &quot;&quot;&quot;</div>

<div>    print &quot;starting server&quot;</div><div>    reactor.listenTCP(port=port,</div><div>                      factory=pb.PBServerFactory(PluginServer()),</div><div>                      interface=&#39;localhost&#39;)</div>

<div><br></div><div><br></div><div>def startPluginClient(port, shutdownHandler):</div><div>    &quot;&quot;&quot; Start a plugin communications client &quot;&quot;&quot;</div><div>    </div><div>    def gotServerPerspective(serverPerspective, pluginPerspective):</div>

<div>        &quot;&quot;&quot; Give the plugin-runner this client&#39;s perspective &quot;&quot;&quot;</div><div>        serverPerspective.callRemote(&quot;set_client_perspective&quot;, pluginPerspective)</div><div>        return serverPerspective</div>

<div><br></div><div>    print &quot;starting plugin&quot;</div><div>    client = PluginClient(shutdownHandler)</div><div>    factory = pb.PBClientFactory()</div><div>    reactor.connectTCP(host=&#39;localhost&#39;, port=port, factory=factory)</div>

<div>    return factory.getRootObject().addCallback(gotServerPerspective, client)</div><div><br></div><div>if __name__ == &quot;__main__&quot;:</div><div>    port = 42155</div><div>    </div><div>    def longRunningAction(d, countDown=10):</div>

<div>        &quot;&quot;&quot; Emulate long running shutdown activities &quot;&quot;&quot;</div><div>        print &quot;shuting down in %i seconds&quot; % countDown</div><div>        if countDown == 0:</div><div>            d.callback(True)</div>

<div>        else:</div><div>            countDown -= 1</div><div>            reactor.callLater(1, longRunningAction, d, countDown)</div><div><br></div><div>    </div><div>    # start plugin-runner</div><div>    reactor.callWhenRunning(startPluginServer, port)</div>

<div>    # start plugin</div><div>    reactor.callLater(2.0, startPluginClient, port, longRunningAction)</div><div>    reactor.run()</div></div></div><div><br></div><div><br></div></div>