diff --git a/lib/bb/utils.py b/lib/bb/utils.py
index 5486f9599..083242268 100644
--- a/lib/bb/utils.py
+++ b/lib/bb/utils.py
@@ -1876,6 +1876,15 @@ def path_is_descendant(descendant, ancestor):
 
     return False
 
+# Recomputing the sets in signal.py is expensive (bitbake -pP idle)
+# so try and use _signal directly to avoid it
+valid_signals = signal.valid_signals()
+try:
+    import _signal
+    sigmask = _signal.pthread_sigmask
+except ImportError:
+    sigmask = signal.pthread_sigmask
+
 # If we don't have a timeout of some kind and a process/thread exits badly (for example
 # OOM killed) and held a lock, we'd just hang in the lock futex forever. It is better
 # we exit at some point than hang. 5 minutes with no progress means we're probably deadlocked.
@@ -1885,7 +1894,7 @@ def path_is_descendant(descendant, ancestor):
 @contextmanager
 def lock_timeout(lock):
     try:
-        s = signal.pthread_sigmask(signal.SIG_BLOCK, signal.valid_signals())
+        s = sigmask(signal.SIG_BLOCK, valid_signals)
         held = lock.acquire(timeout=5*60)
         if not held:
             bb.server.process.serverlog("Couldn't get the lock for 5 mins, timed out, exiting.\n%s" % traceback.format_stack())
@@ -1893,16 +1902,16 @@ def lock_timeout(lock):
         yield held
     finally:
         lock.release()
-        signal.pthread_sigmask(signal.SIG_SETMASK, s)
+        sigmask(signal.SIG_SETMASK, s)
 
 # A version of lock_timeout without the check that the lock was locked and a shorter timeout
 @contextmanager
 def lock_timeout_nocheck(lock):
     try:
-        s = signal.pthread_sigmask(signal.SIG_BLOCK, signal.valid_signals())
+        s = sigmask(signal.SIG_BLOCK, valid_signals)
         l = lock.acquire(timeout=10)
         yield l
     finally:
         if l:
             lock.release()
-        signal.pthread_sigmask(signal.SIG_SETMASK, s)
+        sigmask(signal.SIG_SETMASK, s)
