Open
Description
I was looking at if we could use something like the Make jobserver protocol to make sure that precompilation of many packages doesn't overload the users machine, and I translated @haampie Python code from spack/spack#33014 (comment)
into Julia.
module JobServer
"""
mkfifo(path::AbstractString, [mode::Integer]) -> path
Make a FIFO special file (a named pipe) at `path`. Return `path` as-is on success.
`mkfifo` is supported only in Unix platforms.
!!! compat "Julia 1.11"
`mkfifo` requires at least Julia 1.11.
"""
function mkfifo(
path::AbstractString,
mode::Integer = Base.S_IRUSR | Base.S_IWUSR | Base.S_IRGRP | Base.S_IWGRP |
Base.S_IROTH | Base.S_IWOTH,
)
@static if Sys.isunix()
# Default `mode` is compatible with `mkfifo` CLI in coreutils.
ret = ccall(:mkfifo, Cint, (Cstring, Base.Cmode_t), path, mode)
systemerror("mkfifo", ret == -1)
return path
else
# Using normal `error` because `systemerror("mkfifo", ENOTSUP)` does not
# seem to work on Windows.
error("mkfifo: Operation not supported")
end
end
# Inspired by https://github.com/spack/spack/issues/33014#issue-1397776228
function server(cmd, jobs=1)
num_bytes = jobs-1
@info "Starting jobserver" cmd jobs
# Create a FIFO in temporary dir
tmpdir = mktempdir()
fifopath = joinpath(tmpdir, "jobserver")
mkfifo(fifopath)
io = Base.Filesystem.open(fifopath, Base.JL_O_RDWR)
@show bytes_written = write(io, repeat(b"+", num_bytes))
@assert bytes_written == num_bytes
cmd = addenv(cmd, "MAKEFLAGS" => "--jobserver-auth=fifo:$fifopath")
@show cmd
run(cmd, wait=true)
# Verify that all tokens were written back
read_io = Base.Filesystem.open(fifopath, Base.JL_O_RDONLY | Base.JL_O_NONBLOCK)
tokens = read(read_io, jobs)
close(read_io)
close(io)
if length(tokens) == num_bytes
println("All tokens were returned")
else
@error "Expected" num_bytes got=length(tokens)
end
end
end #module
With a modern enough make
mine is GNU Make 4.4.1
julia --project=. -e "using JobServer; JobServer.server(`m
ake`, 8)"
works fine until we hit:
ERROR: SystemError: lseek: Illegal seek
Stacktrace:
[1] systemerror(p::String, errno::Int32; extrainfo::Nothing)
@ Base ./error.jl:176
[2] position
@ ./filesystem.jl:276 [inlined]
[3] bytesavailable
@ ./filesystem.jl:233 [inlined]
[4] read(io::Base.Filesystem.File, nb::Int64)
@ Base.Filesystem ./filesystem.jl:249
[5] server(cmd::Cmd, jobs::Int64)
@ JobServer ~/src/JobServer/src/JobServer.jl:53
[6] top-level scope
@ none:1
On a FIFO queue the bytesavailable
call wants to do a seek. I think maybe the only operation that don't use bytesavailable
is Filesystem.unsafe_read
since even eof
uses bytesavailable
So I have to do:
tokens = Vector{UInt8}(undef, num_bytes)
GC.@preserve tokens begin
Base.Filesystem.unsafe_read(read_io, pointer(tokens), num_bytes)
end
Maybe I am just misunderstanding how I am supposed to work with FIFO in julia.