Adding wip version for meego harmattan
[feedingit] / psa_harmattan / feedingit / deb_dist / feedingit-0.1.0 / pysrc / mainthread.py
1 #!/usr/bin/env python2.5
2
3 # Copyright (c) 2011 Neal H. Walfield <neal@walfield.org>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 import threading
19 import traceback
20 import logging
21 logger = logging.getLogger(__name__)
22
23 _run_in_main_thread = None
24 _main_thread = None
25
26 def init(run_in_main_thread=None):
27     """
28     run_in_main_thread is a function that takes a single argument, a
29     callable and returns False.  run_in_main_thread should run the
30     function in the main thread.
31
32     If you are using glib, gobject.idle_add (the default) is
33     sufficient.  (gobject.idle_add is thread-safe.)
34     """
35     if run_in_main_thread is None:
36         import gobject
37         run_in_main_thread = gobject.idle_add
38
39     global _run_in_main_thread
40     assert _run_in_main_thread is None
41     _run_in_main_thread = run_in_main_thread
42
43     global _main_thread
44     _main_thread = threading.currentThread ()
45
46 def execute(func, *args, **kwargs):
47     """
48     Execute FUNC in the main thread.
49
50     If kwargs['async'] exists and is True, the function is executed
51     asynchronously (i.e., the thread does not wait for the function to
52     return in which case the function's return value is discarded).
53     Otherwise, this function waits until the function is executed and
54     returns its return value.
55     """
56     async = False
57     try:
58         async = kwargs['async']
59         del kwargs['async']
60     except KeyError:
61         pass
62
63     if threading.currentThread() == _main_thread:
64         if async:
65             try:
66                 func (*args, **kwargs)
67             except:
68                 logger.debug("mainthread.execute: Executing %s: %s"
69                              % (func, traceback.format_exc ()))
70             return
71         else:
72             return func (*args, **kwargs)
73
74     assert _run_in_main_thread is not None, \
75         "You can't call this function from a non-main thread until you've called init()"
76
77     if not async:
78         cond = threading.Condition()
79
80     result = {}
81     result['done'] = False
82
83     def doit():
84         def it():
85             # Execute the function.
86             assert threading.currentThread() == _main_thread
87
88             try:
89                 result['result'] = func (*args, **kwargs)
90             except:
91                 logger.debug("mainthread.execute: Executing %s: %s"
92                              % (func, traceback.format_exc ()))
93
94             if not async:
95                 cond.acquire ()
96             result['done'] = True
97             if not async:
98                 cond.notify ()
99                 cond.release ()
100
101             return False
102         return it
103
104     if not async:
105         cond.acquire ()
106     _run_in_main_thread (doit())
107
108     if async:
109         # Don't wait for the method to complete execution.
110         return
111
112     # Wait for the result to become available.
113     while not result['done']:
114         cond.wait ()
115
116     return result.get ('result', None)
117
118 if __name__ == "__main__":
119     import sys
120     import gobject
121
122     init()
123
124     def in_main_thread(test_num):
125         assert threading.currentThread() == _main_thread, \
126             "Test %d failed" % (test_num,)
127         return test_num
128
129     mainloop = gobject.MainLoop()
130     gobject.threads_init()
131
132     assert execute (in_main_thread, 1) == 1
133     assert (execute (in_main_thread, 2, async=False) == 2)
134     execute (in_main_thread, 3, async=True)
135
136     class T(threading.Thread):
137         def __init__(self):
138             threading.Thread.__init__(self)
139
140         def run(self):
141             assert threading.currentThread() != _main_thread
142
143             assert execute (in_main_thread, 4) == 4
144             assert (execute (in_main_thread, 5, async=False) == 5)
145             execute (in_main_thread, 6, async=True)
146             execute (mainloop.quit, async=False)
147
148     def start_thread():
149         t = T()
150         t.start()
151         return False
152
153     gobject.idle_add (start_thread)
154     mainloop.run()
155
156 def mainthread(f):
157     def wrapper(*args, **kwargs):
158         return execute (f, *args, **kwargs)
159     return wrapper