From 20103e0bd255a29d7ef9558fdbdd3a780ddb07da Mon Sep 17 00:00:00 2001 From: epage Date: Sun, 2 Aug 2009 01:40:45 +0000 Subject: [PATCH] Cutting down the thread spawning which should help avoid some bugs and cut down some resources git-svn-id: file:///svnroot/gc-dialer/trunk@370 c39d3808-3fe2-4d86-a59f-b7f623ee9f21 --- src/dc_glade.py | 11 +++-- src/evo_backend.py | 3 ++ src/gc_views.py | 35 +++++++++----- src/gtk_toolbox.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 14 deletions(-) diff --git a/src/dc_glade.py b/src/dc_glade.py index c63b268..997897c 100755 --- a/src/dc_glade.py +++ b/src/dc_glade.py @@ -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): """ diff --git a/src/evo_backend.py b/src/evo_backend.py index 727b898..0b71ebf 100644 --- a/src/evo_backend.py +++ b/src/evo_backend.py @@ -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" diff --git a/src/gc_views.py b/src/gc_views.py index d01ac95..f84e06a 100644 --- a/src/gc_views.py +++ b/src/gc_views.py @@ -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() diff --git a/src/gtk_toolbox.py b/src/gtk_toolbox.py index 1c9dd88..d6db835 100644 --- a/src/gtk_toolbox.py +++ b/src/gtk_toolbox.py @@ -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'), (, 'Goodbye'), (None, 'Meh'), (, 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): -- 1.7.9.5