Cutting down the thread spawning which should help avoid some bugs and cut down some...
authorepage <eopage@byu.net>
Sun, 2 Aug 2009 01:40:45 +0000 (01:40 +0000)
committerepage <eopage@byu.net>
Sun, 2 Aug 2009 01:40:45 +0000 (01:40 +0000)
git-svn-id: file:///svnroot/gc-dialer/trunk@370 c39d3808-3fe2-4d86-a59f-b7f623ee9f21

src/dc_glade.py
src/evo_backend.py
src/gc_views.py
src/gtk_toolbox.py

index c63b268..997897c 100755 (executable)
@@ -162,6 +162,13 @@ class Dialcentral(object):
                self._window.set_default_size(800, 300)
                self._window.show_all()
 
+               self._loginSink = gtk_toolbox.threaded_stage(
+                       gtk_toolbox.comap(
+                               self.attempt_login,
+                               gtk_toolbox.null_sink(),
+                       )
+               )
+
                backgroundSetup = threading.Thread(target=self._idle_setup)
                backgroundSetup.setDaemon(True)
                backgroundSetup.start()
@@ -352,9 +359,7 @@ class Dialcentral(object):
                                self._errorDisplay.push_exception(e)
 
        def _spawn_attempt_login(self, *args):
-               backgroundLogin = threading.Thread(target=self.attempt_login, args=args)
-               backgroundLogin.setDaemon(True)
-               backgroundLogin.start()
+               self._loginSink.send(args)
 
        def refresh_session(self):
                """
index 727b898..0b71ebf 100644 (file)
@@ -84,6 +84,9 @@ class EvolutionAddressBook(object):
        def contact_source_short_name(contactId):
                return "Evo"
 
+       def clear_caches(self):
+               pass
+
        @staticmethod
        def factory_name():
                return "Evolution"
index d01ac95..f84e06a 100644 (file)
@@ -700,6 +700,13 @@ class RecentCallsView(object):
                self._window = gtk_toolbox.find_parent_window(self._recentview)
                self._phoneTypeSelector = PhoneTypeSelector(widgetTree, self._backend)
 
+               self._updateSink = gtk_toolbox.threaded_stage(
+                       gtk_toolbox.comap(
+                               self._idly_populate_recentview,
+                               gtk_toolbox.null_sink(),
+                       )
+               )
+
        def enable(self):
                assert self._backend.is_authed()
                self._recentview.set_model(self._recentmodel)
@@ -731,9 +738,7 @@ class RecentCallsView(object):
        def update(self, force = False):
                if not force and self._isPopulated:
                        return
-               backgroundPopulate = threading.Thread(target=self._idly_populate_recentview)
-               backgroundPopulate.setDaemon(True)
-               backgroundPopulate.start()
+               self._updateSink.send(())
 
        def clear(self):
                self._isPopulated = False
@@ -842,6 +847,13 @@ class MessagesView(object):
                self._window = gtk_toolbox.find_parent_window(self._messageview)
                self._phoneTypeSelector = PhoneTypeSelector(widgetTree, self._backend)
 
+               self._updateSink = gtk_toolbox.threaded_stage(
+                       gtk_toolbox.comap(
+                               self._idly_populate_messageview,
+                               gtk_toolbox.null_sink(),
+                       )
+               )
+
        def enable(self):
                assert self._backend.is_authed()
                self._messageview.set_model(self._messagemodel)
@@ -873,9 +885,7 @@ class MessagesView(object):
        def update(self, force = False):
                if not force and self._isPopulated:
                        return
-               backgroundPopulate = threading.Thread(target=self._idly_populate_messageview)
-               backgroundPopulate.setDaemon(True)
-               backgroundPopulate.start()
+               self._updateSink.send(())
 
        def clear(self):
                self._isPopulated = False
@@ -972,6 +982,13 @@ class ContactsView(object):
                self._window = gtk_toolbox.find_parent_window(self._contactsview)
                self._phoneTypeSelector = PhoneTypeSelector(widgetTree, self._backend)
 
+               self._updateSink = gtk_toolbox.threaded_stage(
+                       gtk_toolbox.comap(
+                               self._idly_populate_contactsview,
+                               gtk_toolbox.null_sink(),
+                       )
+               )
+
        def enable(self):
                assert self._backend.is_authed()
 
@@ -1034,15 +1051,11 @@ class ContactsView(object):
        def update(self, force = False):
                if not force and self._isPopulated:
                        return
-               backgroundPopulate = threading.Thread(target=self._idly_populate_contactsview)
-               backgroundPopulate.setDaemon(True)
-               backgroundPopulate.start()
+               self._updateSink.send(())
 
        def clear(self):
                self._isPopulated = False
                self._contactsmodel.clear()
-
-       def clear_caches(self):
                for factory in self._addressBookFactories:
                        factory.clear_caches()
                self._addressBook.clear_caches()
index 1c9dd88..d6db835 100644 (file)
@@ -7,6 +7,8 @@ import traceback
 import functools
 import contextlib
 import warnings
+import threading
+import Queue
 
 import gobject
 import gtk
@@ -80,6 +82,135 @@ def synchronous_gtk_message(original_func):
        return immediate_func
 
 
+def autostart(func):
+       """
+       >>> @autostart
+       ... def grep_sink(pattern):
+       ...     print "Looking for %s" % pattern
+       ...     while True:
+       ...             line = yield
+       ...             if pattern in line:
+       ...                     print line,
+       >>> g = grep_sink("python")
+       Looking for python
+       >>> g.send("Yeah but no but yeah but no")
+       >>> g.send("A series of tubes")
+       >>> g.send("python generators rock!")
+       python generators rock!
+       >>> g.close()
+       """
+
+       @functools.wraps(func)
+       def start(*args, **kwargs):
+               cr = func(*args, **kwargs)
+               cr.next()
+               return cr
+
+       return start
+
+
+@autostart
+def null_sink():
+       """
+       Good for uses like with cochain to pick up any slack
+       """
+       while True:
+               item = yield
+
+
+@autostart
+def comap(function, target):
+       """
+       >>> p = printer_sink()
+       >>> cm = comap(lambda x: x+1, p)
+       >>> cm.send(0)
+       1
+       >>> cm.send(1.0)
+       2.0
+       >>> cm.send(-2)
+       -1
+       >>> # cm.throw(RuntimeError, "Goodbye")
+       >>> # cm.send(0)
+       >>> # cm.send(1.0)
+       >>> # cm.close()
+       """
+       while True:
+               try:
+                       item = yield
+                       mappedItem = function(*item)
+                       target.send(mappedItem)
+               except StandardError, e:
+                       target.throw(e.__class__, e.message)
+
+
+@autostart
+def queue_sink(queue):
+       """
+       >>> q = Queue.Queue()
+       >>> qs = queue_sink(q)
+       >>> qs.send("Hello")
+       >>> qs.send("World")
+       >>> qs.throw(RuntimeError, "Goodbye")
+       >>> qs.send("Meh")
+       >>> qs.close()
+       >>> print [i for i in _flush_queue(q)]
+       [(None, 'Hello'), (None, 'World'), (<type 'exceptions.RuntimeError'>, 'Goodbye'), (None, 'Meh'), (<type 'exceptions.GeneratorExit'>, None)]
+       """
+       while True:
+               try:
+                       item = yield
+                       queue.put((None, item))
+               except StandardError, e:
+                       queue.put((e.__class__, e.message))
+               except GeneratorExit:
+                       queue.put((GeneratorExit, None))
+                       raise
+
+
+def decode_item(item, target):
+       if item[0] is None:
+               target.send(item[1])
+               return False
+       elif item[0] is GeneratorExit:
+               target.close()
+               return True
+       else:
+               target.throw(item[0], item[1])
+               return False
+
+
+def nonqueue_source(queue, target):
+       """
+       >>> q = Queue.Queue()
+       >>> for i in [
+       ...     (None, 'Hello'),
+       ...     (None, 'World'),
+       ...     (GeneratorExit, None),
+       ...     ]:
+       ...     q.put(i)
+       >>> qs = queue_source(q, printer_sink())
+       Hello
+       """
+       isDone = False
+       while not isDone:
+               item = queue.get()
+               isDone = decode_item(item, target)
+               while not queue.empty():
+                       queue.get_nowait()
+
+
+def threaded_stage(target, thread_factory = threading.Thread):
+       messages = Queue.Queue()
+
+       run_source = functools.partial(nonqueue_source, messages, target)
+       thread = thread_factory(target=run_source)
+       thread.setDaemon(True)
+       thread.start()
+
+       # Sink running in current thread
+       return queue_sink(messages)
+
+
 class LoginWindow(object):
 
        def __init__(self, widgetTree):