summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2023-01-04 12:32:35 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-01-05 11:50:17 +0000
commita19687acd12497d727203e63d74b2703387f34a6 (patch)
tree813515cdfb0c807435db26acb6bafc2d351f48f1
parent2c6f0b9228b47459dd1b67d578792cd017006128 (diff)
downloadpoky-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>
-rwxr-xr-xbitbake/bin/bitbake-worker9
-rw-r--r--bitbake/lib/bb/cooker.py4
-rw-r--r--bitbake/lib/bb/event.py6
-rw-r--r--bitbake/lib/bb/server/process.py16
-rw-r--r--bitbake/lib/bb/ui/uievent.py28
-rw-r--r--bitbake/lib/bb/utils.py13
6 files changed, 40 insertions, 36 deletions
diff --git a/bitbake/bin/bitbake-worker b/bitbake/bin/bitbake-worker
index ed266f0ac2..a3ea5d9618 100755
--- a/bitbake/bin/bitbake-worker
+++ b/bitbake/bin/bitbake-worker
@@ -121,11 +121,10 @@ def worker_child_fire(event, d):
121 121
122 data = b"<event>" + pickle.dumps(event) + b"</event>" 122 data = b"<event>" + pickle.dumps(event) + b"</event>"
123 try: 123 try:
124 worker_pipe_lock.acquire() 124 with bb.utils.lock_timeout(worker_pipe_lock):
125 while(len(data)): 125 while(len(data)):
126 written = worker_pipe.write(data) 126 written = worker_pipe.write(data)
127 data = data[written:] 127 data = data[written:]
128 worker_pipe_lock.release()
129 except IOError: 128 except IOError:
130 sigterm_handler(None, None) 129 sigterm_handler(None, None)
131 raise 130 raise
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
317def register_UIHhandler(handler, mainui=False): 317def 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
1849def 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()