diff options
author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-01-04 12:32:35 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-01-05 11:50:17 +0000 |
commit | a19687acd12497d727203e63d74b2703387f34a6 (patch) | |
tree | 813515cdfb0c807435db26acb6bafc2d351f48f1 /bitbake/lib/bb | |
parent | 2c6f0b9228b47459dd1b67d578792cd017006128 (diff) | |
download | poky-a19687acd12497d727203e63d74b2703387f34a6.tar.gz |
bitbake: lib/bb: Update thread/process locks to use a timeout
The thread/process locks we use translate to futexes in Linux. If a
process dies holding the lock, anything else trying to take the lock
will hang indefinitely. An example would be the OOM killer taking out
a parser process.
To avoid bitbake processes just hanging indefinitely, add a timeout to
our lock calls using a context manager. If we can't obtain the lock
after waiting 5 minutes, hard exit out using os._exit(1). Use _exit()
to avoid locking in any other places trying to write error messages to
event handler queues (which also need locks).
Whilst a bit harsh, this should mean we stop having lots of long running
processes in cases where things are never going to work out and also
avoids hanging builds on the autobuilder.
(Bitbake rev: d2a3f662b0eed900fc012a392bfa0a365df0df9b)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb')
-rw-r--r-- | bitbake/lib/bb/cooker.py | 4 | ||||
-rw-r--r-- | bitbake/lib/bb/event.py | 6 | ||||
-rw-r--r-- | bitbake/lib/bb/server/process.py | 16 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/uievent.py | 28 | ||||
-rw-r--r-- | bitbake/lib/bb/utils.py | 13 |
5 files changed, 36 insertions, 31 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py index a5a635858c..738849d189 100644 --- a/bitbake/lib/bb/cooker.py +++ b/bitbake/lib/bb/cooker.py | |||
@@ -251,14 +251,14 @@ class BBCooker: | |||
251 | self.notifier = pyinotify.Notifier(self.watcher, self.notifications) | 251 | self.notifier = pyinotify.Notifier(self.watcher, self.notifications) |
252 | 252 | ||
253 | def process_inotify_updates(self): | 253 | def process_inotify_updates(self): |
254 | with self.inotify_threadlock: | 254 | with bb.utils.lock_timeout(self.inotify_threadlock): |
255 | for n in [self.confignotifier, self.notifier]: | 255 | for n in [self.confignotifier, self.notifier]: |
256 | if n and n.check_events(timeout=0): | 256 | if n and n.check_events(timeout=0): |
257 | # read notified events and enqueue them | 257 | # read notified events and enqueue them |
258 | n.read_events() | 258 | n.read_events() |
259 | 259 | ||
260 | def process_inotify_updates_apply(self): | 260 | def process_inotify_updates_apply(self): |
261 | with self.inotify_threadlock: | 261 | with bb.utils.lock_timeout(self.inotify_threadlock): |
262 | for n in [self.confignotifier, self.notifier]: | 262 | for n in [self.confignotifier, self.notifier]: |
263 | if n and n.check_events(timeout=0): | 263 | if n and n.check_events(timeout=0): |
264 | n.read_events() | 264 | n.read_events() |
diff --git a/bitbake/lib/bb/event.py b/bitbake/lib/bb/event.py index 7826541a64..8b05f93e2f 100644 --- a/bitbake/lib/bb/event.py +++ b/bitbake/lib/bb/event.py | |||
@@ -184,7 +184,7 @@ def fire_ui_handlers(event, d): | |||
184 | ui_queue.append(event) | 184 | ui_queue.append(event) |
185 | return | 185 | return |
186 | 186 | ||
187 | with _thread_lock: | 187 | with bb.utils.lock_timeout(_thread_lock): |
188 | errors = [] | 188 | errors = [] |
189 | for h in _ui_handlers: | 189 | for h in _ui_handlers: |
190 | #print "Sending event %s" % event | 190 | #print "Sending event %s" % event |
@@ -315,7 +315,7 @@ def set_eventfilter(func): | |||
315 | _eventfilter = func | 315 | _eventfilter = func |
316 | 316 | ||
317 | def register_UIHhandler(handler, mainui=False): | 317 | def register_UIHhandler(handler, mainui=False): |
318 | with _thread_lock: | 318 | with bb.utils.lock_timeout(_thread_lock): |
319 | bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 | 319 | bb.event._ui_handler_seq = bb.event._ui_handler_seq + 1 |
320 | _ui_handlers[_ui_handler_seq] = handler | 320 | _ui_handlers[_ui_handler_seq] = handler |
321 | level, debug_domains = bb.msg.constructLogOptions() | 321 | level, debug_domains = bb.msg.constructLogOptions() |
@@ -329,7 +329,7 @@ def unregister_UIHhandler(handlerNum, mainui=False): | |||
329 | if mainui: | 329 | if mainui: |
330 | global _uiready | 330 | global _uiready |
331 | _uiready = False | 331 | _uiready = False |
332 | with _thread_lock: | 332 | with bb.utils.lock_timeout(_thread_lock): |
333 | if handlerNum in _ui_handlers: | 333 | if handlerNum in _ui_handlers: |
334 | del _ui_handlers[handlerNum] | 334 | del _ui_handlers[handlerNum] |
335 | return | 335 | return |
diff --git a/bitbake/lib/bb/server/process.py b/bitbake/lib/bb/server/process.py index ac7749d36c..b5f6faf6fb 100644 --- a/bitbake/lib/bb/server/process.py +++ b/bitbake/lib/bb/server/process.py | |||
@@ -113,7 +113,7 @@ class ProcessServer(): | |||
113 | def register_idle_function(self, function, data): | 113 | def register_idle_function(self, function, data): |
114 | """Register a function to be called while the server is idle""" | 114 | """Register a function to be called while the server is idle""" |
115 | assert hasattr(function, '__call__') | 115 | assert hasattr(function, '__call__') |
116 | with self._idlefuncsLock: | 116 | with bb.utils.lock_timeout(self._idlefuncsLock): |
117 | self._idlefuns[function] = data | 117 | self._idlefuns[function] = data |
118 | serverlog("Registering idle function %s" % str(function)) | 118 | serverlog("Registering idle function %s" % str(function)) |
119 | 119 | ||
@@ -379,7 +379,7 @@ class ProcessServer(): | |||
379 | 379 | ||
380 | def idle_thread(self): | 380 | def idle_thread(self): |
381 | def remove_idle_func(function): | 381 | def remove_idle_func(function): |
382 | with self._idlefuncsLock: | 382 | with bb.utils.lock_timeout(self._idlefuncsLock): |
383 | del self._idlefuns[function] | 383 | del self._idlefuns[function] |
384 | self.idle_cond.notify_all() | 384 | self.idle_cond.notify_all() |
385 | 385 | ||
@@ -387,7 +387,7 @@ class ProcessServer(): | |||
387 | nextsleep = 0.1 | 387 | nextsleep = 0.1 |
388 | fds = [] | 388 | fds = [] |
389 | 389 | ||
390 | with self._idlefuncsLock: | 390 | with bb.utils.lock_timeout(self._idlefuncsLock): |
391 | items = list(self._idlefuns.items()) | 391 | items = list(self._idlefuns.items()) |
392 | 392 | ||
393 | for function, data in items: | 393 | for function, data in items: |
@@ -743,7 +743,7 @@ class BBUIEventQueue: | |||
743 | self.t.start() | 743 | self.t.start() |
744 | 744 | ||
745 | def getEvent(self): | 745 | def getEvent(self): |
746 | with self.eventQueueLock: | 746 | with bb.utils.lock_timeout(self.eventQueueLock): |
747 | if len(self.eventQueue) == 0: | 747 | if len(self.eventQueue) == 0: |
748 | return None | 748 | return None |
749 | 749 | ||
@@ -758,7 +758,7 @@ class BBUIEventQueue: | |||
758 | return self.getEvent() | 758 | return self.getEvent() |
759 | 759 | ||
760 | def queue_event(self, event): | 760 | def queue_event(self, event): |
761 | with self.eventQueueLock: | 761 | with bb.utils.lock_timeout(self.eventQueueLock): |
762 | self.eventQueue.append(event) | 762 | self.eventQueue.append(event) |
763 | self.eventQueueNotify.set() | 763 | self.eventQueueNotify.set() |
764 | 764 | ||
@@ -794,7 +794,7 @@ class ConnectionReader(object): | |||
794 | return self.reader.poll(timeout) | 794 | return self.reader.poll(timeout) |
795 | 795 | ||
796 | def get(self): | 796 | def get(self): |
797 | with self.rlock: | 797 | with bb.utils.lock_timeout(self.rlock): |
798 | res = self.reader.recv_bytes() | 798 | res = self.reader.recv_bytes() |
799 | return multiprocessing.reduction.ForkingPickler.loads(res) | 799 | return multiprocessing.reduction.ForkingPickler.loads(res) |
800 | 800 | ||
@@ -815,7 +815,7 @@ class ConnectionWriter(object): | |||
815 | 815 | ||
816 | def _send(self, obj): | 816 | def _send(self, obj): |
817 | gc.disable() | 817 | gc.disable() |
818 | with self.wlock: | 818 | with bb.utils.lock_timeout(self.wlock): |
819 | self.writer.send_bytes(obj) | 819 | self.writer.send_bytes(obj) |
820 | gc.enable() | 820 | gc.enable() |
821 | 821 | ||
@@ -828,7 +828,7 @@ class ConnectionWriter(object): | |||
828 | # pthread_sigmask block/unblock would be nice but doesn't work, https://bugs.python.org/issue47139 | 828 | # pthread_sigmask block/unblock would be nice but doesn't work, https://bugs.python.org/issue47139 |
829 | process = multiprocessing.current_process() | 829 | process = multiprocessing.current_process() |
830 | if process and hasattr(process, "queue_signals"): | 830 | if process and hasattr(process, "queue_signals"): |
831 | with process.signal_threadlock: | 831 | with bb.utils.lock_timeout(process.signal_threadlock): |
832 | process.queue_signals = True | 832 | process.queue_signals = True |
833 | self._send(obj) | 833 | self._send(obj) |
834 | process.queue_signals = False | 834 | process.queue_signals = False |
diff --git a/bitbake/lib/bb/ui/uievent.py b/bitbake/lib/bb/ui/uievent.py index d595f172ac..adbe698939 100644 --- a/bitbake/lib/bb/ui/uievent.py +++ b/bitbake/lib/bb/ui/uievent.py | |||
@@ -70,30 +70,22 @@ class BBUIEventQueue: | |||
70 | self.t.start() | 70 | self.t.start() |
71 | 71 | ||
72 | def getEvent(self): | 72 | def getEvent(self): |
73 | 73 | with bb.utils.lock_timeout(self.eventQueueLock): | |
74 | self.eventQueueLock.acquire() | 74 | if not self.eventQueue: |
75 | 75 | return None | |
76 | if not self.eventQueue: | 76 | item = self.eventQueue.pop(0) |
77 | self.eventQueueLock.release() | 77 | if not self.eventQueue: |
78 | return None | 78 | self.eventQueueNotify.clear() |
79 | 79 | return item | |
80 | item = self.eventQueue.pop(0) | ||
81 | |||
82 | if not self.eventQueue: | ||
83 | self.eventQueueNotify.clear() | ||
84 | |||
85 | self.eventQueueLock.release() | ||
86 | return item | ||
87 | 80 | ||
88 | def waitEvent(self, delay): | 81 | def waitEvent(self, delay): |
89 | self.eventQueueNotify.wait(delay) | 82 | self.eventQueueNotify.wait(delay) |
90 | return self.getEvent() | 83 | return self.getEvent() |
91 | 84 | ||
92 | def queue_event(self, event): | 85 | def queue_event(self, event): |
93 | self.eventQueueLock.acquire() | 86 | with bb.utils.lock_timeout(self.eventQueueLock): |
94 | self.eventQueue.append(event) | 87 | self.eventQueue.append(event) |
95 | self.eventQueueNotify.set() | 88 | self.eventQueueNotify.set() |
96 | self.eventQueueLock.release() | ||
97 | 89 | ||
98 | def send_event(self, event): | 90 | def send_event(self, event): |
99 | self.queue_event(pickle.loads(event)) | 91 | self.queue_event(pickle.loads(event)) |
diff --git a/bitbake/lib/bb/utils.py b/bitbake/lib/bb/utils.py index 0df522b372..8c79159573 100644 --- a/bitbake/lib/bb/utils.py +++ b/bitbake/lib/bb/utils.py | |||
@@ -1841,3 +1841,16 @@ def mkstemp(suffix=None, prefix=None, dir=None, text=False): | |||
1841 | else: | 1841 | else: |
1842 | prefix = tempfile.gettempprefix() + entropy | 1842 | prefix = tempfile.gettempprefix() + entropy |
1843 | return tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir, text=text) | 1843 | return tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir, text=text) |
1844 | |||
1845 | # If we don't have a timeout of some kind and a process/thread exits badly (for example | ||
1846 | # OOM killed) and held a lock, we'd just hang in the lock futex forever. It is better | ||
1847 | # we exit at some point than hang. 5 minutes with no progress means we're probably deadlocked. | ||
1848 | @contextmanager | ||
1849 | def lock_timeout(lock): | ||
1850 | held = lock.acquire(timeout=5*60) | ||
1851 | try: | ||
1852 | if not held: | ||
1853 | os._exit(1) | ||
1854 | yield held | ||
1855 | finally: | ||
1856 | lock.release() | ||