commit:     d466b9677a6d659afd25dd8c21cb98ac47347623
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Sat Jan 10 10:18:17 2026 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sun Jan 11 02:27:51 2026 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=d466b967

Scheduler: implicit jobserver slot support

Introduce the support for implicit jobserver slot when emerge is running
under a jobserver client such as GNU make or stevie.  Notably, we're
using the distinction between MAKEFLAGS being set in the calling
environment by the client (which takes precedence over make.conf) vs. it
being set via make.conf.  In the former case, we are assuming that we
can always run one job without acquiring a token.

This fixes the protocol compliance and ensures full build count when
emerge is being run under a jobserver client.  It also enables users to
deliberately do that in order to preacquire a single token and ensure
that emerge process is not blocked from starting any job by other
jobserver clients, and that CPU consumption during dependency
calculation is accounted for.

Bug: https://bugs.gentoo.org/692576
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
Part-of: https://github.com/gentoo/portage/pull/1543

 lib/_emerge/Scheduler.py | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/lib/_emerge/Scheduler.py b/lib/_emerge/Scheduler.py
index 9ad1713280..a6233229af 100644
--- a/lib/_emerge/Scheduler.py
+++ b/lib/_emerge/Scheduler.py
@@ -248,6 +248,7 @@ class Scheduler(PollScheduler):
 
         features = self.settings.features
         self._jobserver_fd = None
+        self._jobserver_has_implicit_token = False
         self._jobserver_tokens = {}
 
         self._fetch_log = os.path.join(
@@ -1639,6 +1640,13 @@ class Scheduler(PollScheduler):
                         out = portage.output.EOutput()
                         out.ewarn(f"Unsupported jobserver type: {flag}")
             if jobserver_path is not None:
+                # If MAKEFLAGS were passed via environment, we're likely 
running
+                # under a jobserver client (make, stevie) and a token was 
acquired
+                # for us, so use an implicit slot. If they were set via 
make.conf,
+                # we are the top-level process and need to acquire tokens for 
all
+                # jobs.
+                if "MAKEFLAGS" in os.environ:
+                    self._jobserver_has_implicit_token = True
                 try:
                     self._jobserver_fd = os.open(
                         jobserver_path, os.O_RDWR | os.O_NONBLOCK
@@ -2130,6 +2138,10 @@ class Scheduler(PollScheduler):
         """
         if self._jobserver_fd is None:
             return b""
+        if self._jobserver_has_implicit_token:
+            # If our implicit slot is free, use it.
+            self._jobserver_has_implicit_token = False
+            return b""
         try:
             return os.read(self._jobserver_fd, 1)
         except BlockingIOError:
@@ -2154,6 +2166,11 @@ class Scheduler(PollScheduler):
         """
         token = self._jobserver_tokens.pop(task_id, None)
         if token is not None and self._jobserver_fd is not None:
+            if token == b"":
+                # This job was running in our implicit slot.
+                assert not self._jobserver_has_implicit_token
+                self._jobserver_has_implicit_token = True
+                return
             try:
                 os.write(self._jobserver_fd, token)
             except OSError as exception:

Reply via email to