Move download management from frontends to rss_sqlite.py.
[feedingit] / src / 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
21 _run_in_main_thread = None
22 _main_thread = None
23
24 def init(run_in_main_thread=None):
25     """
26     run_in_main_thread is a function that takes a single argument, a
27     callable and returns False.  run_in_main_thread should run the
28     function in the main thread.
29
30     If you are using glib, gobject.idle_add (the default) is
31     sufficient.  (gobject.idle_add is thread-safe.)
32     """
33     if run_in_main_thread is None:
34         import gobject
35         run_in_main_thread = gobject.idle_add
36
37     global _run_in_main_thread
38     assert _run_in_main_thread is None
39     _run_in_main_thread = run_in_main_thread
40
41     global _main_thread
42     _main_thread = threading.currentThread ()
43
44 def execute(func, *args, **kwargs):
45     """
46     Execute FUNC in the main thread.
47
48     If kwargs['async'] exists and is True, the function is executed
49     asynchronously (i.e., the thread does not wait for the function to
50     return in which case the function's return value is discarded).
51     Otherwise, this function waits until the function is executed and
52     returns its return value.
53     """
54     async = False
55     try:
56         async = kwargs['async']
57         del kwargs['async']
58     except KeyError:
59         pass
60
61     if threading.currentThread() == _main_thread:
62         if async:
63             try:
64                 func (*args, **kwargs)
65             except:
66                 print ("mainthread.execute: Executing %s: %s"
67                        % (func, traceback.format_exc ()))
68             return
69         else:
70             return func (*args, **kwargs)
71
72     assert _run_in_main_thread is not None, \
73         "You can't call this function from a non-main thread until you've called init()"
74
75     if not async:
76         cond = threading.Condition()
77
78     result = {}
79     result['done'] = False
80
81     def doit():
82         def it():
83             # Execute the function.
84             assert threading.currentThread() == _main_thread
85
86             try:
87                 result['result'] = func (*args, **kwargs)
88             except:
89                 print ("mainthread.execute: Executing %s: %s"
90                        % (func, traceback.format_exc ()))
91
92             if not async:
93                 cond.acquire ()
94             result['done'] = True
95             if not async:
96                 cond.notify ()
97                 cond.release ()
98
99             return False
100         return it
101
102     if not async:
103         cond.acquire ()
104     _run_in_main_thread (doit())
105
106     if async:
107         # Don't wait for the method to complete execution.
108         return
109
110     # Wait for the result to become available.
111     while not result['done']:
112         cond.wait ()
113
114     return result.get ('result', None)
115
116 if __name__ == "__main__":
117     import sys
118     import gobject
119
120     init()
121
122     def in_main_thread(test_num):
123         assert threading.currentThread() == _main_thread, \
124             "Test %d failed" % (test_num,)
125         return test_num
126
127     mainloop = gobject.MainLoop()
128     gobject.threads_init()
129
130     assert execute (in_main_thread, 1) == 1
131     assert (execute (in_main_thread, 2, async=False) == 2)
132     execute (in_main_thread, 3, async=True)
133
134     class T(threading.Thread):
135         def __init__(self):
136             threading.Thread.__init__(self)
137
138         def run(self):
139             assert threading.currentThread() != _main_thread
140
141             assert execute (in_main_thread, 4) == 4
142             assert (execute (in_main_thread, 5, async=False) == 5)
143             execute (in_main_thread, 6, async=True)
144             execute (mainloop.quit, async=False)
145
146     def start_thread():
147         t = T()
148         t.start()
149         return False
150
151     gobject.idle_add (start_thread)
152     mainloop.run()
153
154 def mainthread(f):
155     def wrapper(*args, **kwargs):
156         return execute (f, *args, **kwargs)
157     return wrapper