Opting for a harder algorithm just to avoid a few lines of compatibility shims seems like very much the wrong tradeoff.
Very likely gmake works on non-posix platforms. Whether there is a requirement that the jobserver also works there I don't know.
It also needs to work on non-linux platforms. So a portable solution (or multiple non-portable ones) is needed.
edit: Oh, it uses `signal.set_wakeup_fd`, interesting.
https://docs.python.org/3/library/signal.html#signal.set_wak...
edit2: it looks like the fd comes from a self-socket, but yeah, it's the same approach. The function is even called `_make_self_pipe`.
https://github.com/python/cpython/blob/bf21e2160d1dc6869fb23...
These OS specific APIs are sometimes required and may make certain usecases a lot easier to implement correctly (or even at all), but a job server for a build system isn't one of those. The make jobs can be required to behave and stay in the process group the job server creates for them just like shell job control. Which can be done with just POSIX API. You just have to blow the dust from the relevant tombs the ancients left us (e.g. Advanced Programming in the UNIX Environment).
Refuse the temptation and don't make infrastructure tools like gmake depend OS specific APIs. Doing so would make the world a worse place for everyone else.
Even if you're going to implement OS-specific APIs you still need a general case for OSes that don't support those APIs or don't support them correctly. That means you still need to solve the original problem regardless, which then means that it doesn't actually save you time and energy to use those APIs but rather creates more development and maintenance work and not less.
If those APIs don't provide you a significant benefit (reliability, performance, etc.) then it's likely not worth implementing them at all if the general case works, and if it doesn't work you have to fix it anyway.
Make is supposed to be the interface, implementations should be able to leverage all the platform capabilities they need, because make is part of that platform.
Forcing lowest common denominator also makes world worse place, as does relying on single specific implementation.
AFAIK, make's jobserver protocol is much older than signalfd.
> June 16th, 2003 (updated September 26th, 2015)
so when it was originally written signalfd(2) would not be available on Linux until 2.6.22 was released in July 2007, 4 years later.
import asyncio
import os
import signal
import sys
async def get_stdin_reader():
loop = asyncio.get_running_loop()
reader = asyncio.StreamReader()
protocol = asyncio.StreamReaderProtocol(reader)
await loop.connect_read_pipe(lambda: protocol, sys.stdin)
return reader
async def amain():
print(f'pid: {os.getpid()}')
sig_queue: asyncio.Queue[None] = asyncio.Queue()
def sig_handler() -> None:
sig_queue.put_nowait(None)
loop= asyncio.get_running_loop()
loop.add_signal_handler(signal.SIGUSR1, sig_handler)
reader = await get_stdin_reader()
task1 = loop.create_task(reader.read(1))
task2 = loop.create_task(sig_queue.get())
pending = {task1, task2}
while True:
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
if task1 in done:
task1 = loop.create_task(reader.read(1))
pending.add(task1)
print('got char on stdin')
if task2 in done:
task2 = loop.create_task(sig_queue.get())
pending.add(task2)
print('got signal')
asyncio.run(amain())