summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2011-06-08 09:36:13 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2011-06-08 11:38:43 +0100
commit8aabfed148ddbc42ea8ed28d7ea23193f1cdd87d (patch)
treeefb221a126aefddc61af858f78af9605bbed5bf1 /bitbake
parentb34d66225f4df23185870feb83626b1aab38bc11 (diff)
downloadpoky-8aabfed148ddbc42ea8ed28d7ea23193f1cdd87d.tar.gz
bitbake: Add process server from upstream bitbake
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/lib/bb/server/process.py221
1 files changed, 221 insertions, 0 deletions
diff --git a/bitbake/lib/bb/server/process.py b/bitbake/lib/bb/server/process.py
new file mode 100644
index 0000000000..5d7f8aa9de
--- /dev/null
+++ b/bitbake/lib/bb/server/process.py
@@ -0,0 +1,221 @@
1#
2# BitBake Process based server.
3#
4# Copyright (C) 2010 Bob Foerster <robert@erafx.com>
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License version 2 as
8# published by the Free Software Foundation.
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 along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19"""
20 This module implements a multiprocessing.Process based server for bitbake.
21"""
22
23import bb
24import bb.event
25import itertools
26import logging
27import multiprocessing
28import os
29import signal
30import sys
31import time
32from bb.cooker import BBCooker
33from multiprocessing import Event, Process, util
34
35logger = logging.getLogger('BitBake')
36
37class ServerCommunicator():
38 def __init__(self, connection):
39 self.connection = connection
40
41 def runCommand(self, command):
42 # @todo try/except
43 self.connection.send(command)
44
45 while True:
46 # don't let the user ctrl-c while we're waiting for a response
47 try:
48 if self.connection.poll(.5):
49 return self.connection.recv()
50 else:
51 return None
52 except KeyboardInterrupt:
53 pass
54
55
56class EventAdapter():
57 """
58 Adapter to wrap our event queue since the caller (bb.event) expects to
59 call a send() method, but our actual queue only has put()
60 """
61 def __init__(self, queue):
62 self.queue = queue
63
64 def send(self, event):
65 try:
66 self.queue.put(event)
67 except Exception, err:
68 print("EventAdapter puked: %s" % str(err))
69
70
71class ProcessServer(Process):
72 profile_filename = "profile.log"
73 profile_processed_filename = "profile.log.processed"
74
75 def __init__(self, command_channel, event_queue, configuration):
76 Process.__init__(self)
77 self.command_channel = command_channel
78 self.event_queue = event_queue
79 self.event = EventAdapter(event_queue)
80 self.configuration = configuration
81 self.cooker = BBCooker(configuration, self.register_idle_function)
82 self._idlefunctions = {}
83 self.event_handle = bb.event.register_UIHhandler(self)
84 self.quit = False
85
86 self.keep_running = Event()
87 self.keep_running.set()
88
89 for event in bb.event.ui_queue:
90 self.event_queue.put(event)
91
92 def register_idle_function(self, function, data):
93 """Register a function to be called while the server is idle"""
94 assert hasattr(function, '__call__')
95 self._idlefunctions[function] = data
96
97 def run(self):
98 if self.configuration.profile:
99 return self.profile_main()
100 else:
101 return self.main()
102
103 def profile_main(self):
104 import cProfile
105 profiler = cProfile.Profile()
106 try:
107 return profiler.runcall(self.main)
108 finally:
109 profiler.dump_stats(self.profile_filename)
110 self.write_profile_stats()
111 sys.__stderr__.write("Raw profiling information saved to %s and "
112 "processed statistics to %s\n" %
113 (self.profile_filename,
114 self.profile_processed_filename))
115
116 def write_profile_stats(self):
117 import pstats
118 with open(self.profile_processed_filename, 'w') as outfile:
119 stats = pstats.Stats(self.profile_filename, stream=outfile)
120 stats.sort_stats('time')
121 stats.print_stats()
122 stats.print_callers()
123 stats.sort_stats('cumulative')
124 stats.print_stats()
125
126 def main(self):
127 # Ignore SIGINT within the server, as all SIGINT handling is done by
128 # the UI and communicated to us
129 signal.signal(signal.SIGINT, signal.SIG_IGN)
130 while self.keep_running.is_set():
131 try:
132 if self.command_channel.poll():
133 command = self.command_channel.recv()
134 self.runCommand(command)
135
136 self.idle_commands(.1)
137 except Exception:
138 logger.exception('Running command %s', command)
139
140 self.event_queue.cancel_join_thread()
141 bb.event.unregister_UIHhandler(self.event_handle)
142 self.command_channel.close()
143 self.cooker.stop()
144 self.idle_commands(.1)
145
146 def idle_commands(self, delay):
147 nextsleep = delay
148
149 for function, data in self._idlefunctions.items():
150 try:
151 retval = function(self, data, False)
152 if retval is False:
153 del self._idlefunctions[function]
154 elif retval is True:
155 nextsleep = None
156 elif nextsleep is None:
157 continue
158 elif retval < nextsleep:
159 nextsleep = retval
160 except SystemExit:
161 raise
162 except Exception:
163 logger.exception('Running idle function')
164
165 if nextsleep is not None:
166 time.sleep(nextsleep)
167
168 def runCommand(self, command):
169 """
170 Run a cooker command on the server
171 """
172 self.command_channel.send(self.cooker.command.runCommand(command))
173
174 def stop(self):
175 self.keep_running.clear()
176
177 def bootstrap_2_6_6(self):
178 """Pulled from python 2.6.6. Needed to ensure we have the fix from
179 http://bugs.python.org/issue5313 when running on python version 2.6.2
180 or lower."""
181
182 try:
183 self._children = set()
184 self._counter = itertools.count(1)
185 try:
186 sys.stdin.close()
187 sys.stdin = open(os.devnull)
188 except (OSError, ValueError):
189 pass
190 multiprocessing._current_process = self
191 util._finalizer_registry.clear()
192 util._run_after_forkers()
193 util.info('child process calling self.run()')
194 try:
195 self.run()
196 exitcode = 0
197 finally:
198 util._exit_function()
199 except SystemExit, e:
200 if not e.args:
201 exitcode = 1
202 elif type(e.args[0]) is int:
203 exitcode = e.args[0]
204 else:
205 sys.stderr.write(e.args[0] + '\n')
206 sys.stderr.flush()
207 exitcode = 1
208 except:
209 exitcode = 1
210 import traceback
211 sys.stderr.write('Process %s:\n' % self.name)
212 sys.stderr.flush()
213 traceback.print_exc()
214
215 util.info('process exiting with exitcode %d' % exitcode)
216 return exitcode
217
218 # Python versions 2.6.0 through 2.6.2 suffer from a multiprocessing bug
219 # which can result in a bitbake server hang during the parsing process
220 if (2, 6, 0) <= sys.version_info < (2, 6, 3):
221 _bootstrap = bootstrap_2_6_6