Skip to content

Working with FIFO file-descriptors #52122

Open
@vchuravy

Description

@vchuravy

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    filesystemUnderlying file system and functions that use it

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions