Ah! Things are a lot clearer now. Thanks for explaining that. I get what
you meant, and I actually see reasons why it would cause a crash :-)

I can't speak to specifically about rpyc, since I have never used it, but I
can comment from a general viewpoint about some things:

In your custom signals class, when you perform the connect(), you are
storing the function reference. That can be just fine for a general
function, but for a method and even worse for a method of a QObject, you
will end up with a problem. Storing just the method reference is not enough
to prevent the object it is bound to from being garbage collected. So it is
very possible in situations where you allow an object to be garbage
collected, and there is no mechanism in there to disconnect the signal. it
would just end up potentially calling on a dead object. Qt Signal/Slot
automatically disconnects when QObjects are deleted.

Another thing is that there is no mechanism in your custom signals for when
you are emitting from another thread. In Qt, if you emit from one thread to
a sender in another thread, you have available the QueuedConnection type,
which doesn't run the slot directly, but rather places it into the
receivers event loop to be run in that thread. So your version would need
to be calling those functions on thread safe functions/methods only.

I did some work extending a concept on ActiveState about weakmethods (as in
weakrefs): https://gist.github.com/justinfx/6183367
It has code for doing weakmethods, as well as a system for doing arbitrary
callbacks from another thread into the main threads event loops (kind of
like what you are missing with QueuedConnections). Maybe that code would
help in extending your custom signal design?

Like I said, I don't really know much about the rpyc library, but one thing
that stands out in the example is that you are staring a threaded server,
from within another thread. Is that necessary? Probably unrelated, and I
don't know if a specific crash in this example involves that library or not.




On Wed, Jan 22, 2014 at 8:45 PM, Marcus Ottosson <[email protected]>wrote:

> I think maybe I misunderstood you previously. I thought we *were* talking
>> about using signals to pass data between threads? But you are asking if I
>> have done it without signals?
>>
>
> Yes, I think this is where we may have a misunderstanding. It probably
> stems for my ambiguity in use of the words *pyqtSignal* and the custom
> implementation *Signal *(as in the above article).
>
> What I am trying to convey, is that using *pyqtSignal* works with threads
> (as far as I can tell), but *Signal *(which I'll refer to as
> *CustomSignal *from now on) in an identical scenario may not.
>
> In the above example, I provided both pyqtSignal() and CustomSignal() as
> class attributes for the thread (CustomClass being temporarily commented
> out). I was trying to illustrate that taking out pyqtSignal for
> CustomSignal would cause a crash, but I should have been more explicit, so
> apologies for this.
>
> Now I certainly see why wanted to get to the bottom of this! :) If someone
> made the claim that using Qt's own QThreads with pyqtSignal would be cause
> for crashes, I'd be doing the same thing.
>
> In any case, I may still be completely off in my statement regarding
> passing data across threads causing crashes, so I'm glad we're still on the
> topic. In the three examples you listed, Justin, thread-safety is already
> inherent. What I would love to get a hold on however, is whether it is
> considered safe to pass data from one thread to another, or to simply
> trigger methods from one thread *to* another, by using a class as simple
> as my CustomSignal implementation that has no regard for locking of
> resources during its run.
>
> I'm still having crashes and occasional hang-ups in my original program,
> and made a slightly longer version of in a minimal example, but did not
> succeed in reproducing the crash. Posting as is, just in case, and will
> once again return once I know more.
>
>
> import threading
>
>
> from PyQt5.QtWidgets import *
> from PyQt5.QtCore import *
>
>
> PORT = 18000
>
>
>
>
> class CustomSignal:
>
>
>     def __init__(self):
>         self.__subscribers = []
>
>     def emit(self, *args, **kwargs):
>         for subs in self.__subscribers:
>             subs(*args, **kwargs)
>
>
>     def connect(self, func):
>         self.__subscribers.append(func)
>
>     def disconnect(self, func):
>         try:
>             self.__subscribers.remove(func)
>         except ValueError:
>             print('Warning: function %s not removed '
>                   'from signal %s'%(func,self))
>
>
>
>
> class Window(QWidget):
>
>
>     rpc_show = pyqtSignal()  # Causes no crash
>     # rpc_show = CustomSignal()  # Causes crash
>
>
>
>     def __init__(self, parent=None):
>         super(Window, self).__init__(parent)
>
>
>
>         self.__rpc_server = None
>
>
>         self.rpc_show.connect(self.restore)
>
>
>     def start_rpc(self):
>         from rpyc.utils.server import ThreadedServer
>         from rpyc.core import SlaveService
>
>
>         if self.__rpc_server:
>             self.__rpc_server.close()
>             self.__rpc_server = None
>
>
>         class Service(SlaveService):
>             def exposed_show(self):
>                 self.show_signal.emit()
>
>
>         Service.show_signal = self.rpc_show
>         server = ThreadedServer(Service, port=PORT, reuse_addr=True)
>
>
>         self.__rpc_server = server
>
>
>         def thread():
>             self.__rpc_server.start()
>
>
>         print("Running RPC server")
>         thread = threading.Thread(target=thread, name="rpc")
>         thread.daemon = True
>         thread.start()
>
>
>     def restore(self):
>         self.activateWindow()
>         self.showNormal()
>
>
>
>
> def start_application():
>
>     import sys
>     app = QApplication(sys.argv)
>
>
>     win = Window()
>     win.show()
>
>
>
>     win.start_rpc()
>
>
>     sys.exit(app.exec_())
>
>
>
>
> def request_application():
>     print("Requesting a new Window")
>
>     import socket
>     import rpyc
>
>
>     try:
>         proxy = rpyc.connect('localhost', PORT)
>         proxy.root.show()
>         print("Restored existing instance of Window")
>
>
>     except socket.error:
>         print("Running new instance of Window")
>         start_application()
>
>
>
>
> if __name__ == '__main__':
>     request_application()
>
>  --
> You received this message because you are subscribed to the Google Groups
> "Python Programming for Autodesk Maya" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/python_inside_maya/95bdc1ec-09cd-4086-baee-21715533db5b%40googlegroups.com
> .
>
> For more options, visit https://groups.google.com/groups/opt_out.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/python_inside_maya/CAPGFgA3%2B_t_FCEOnpLD8k_XMYk1GawfzKuKsQUgmC59y-34SXA%40mail.gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to