Home > Backend Development > Golang > os.OpenFile with O_RDONLY hangs on named pipe without writer

os.OpenFile with O_RDONLY hangs on named pipe without writer

王林
Release: 2024-02-11 08:33:08
forward
1274 people have browsed it

带有 O_RDONLY 的 os.OpenFile 挂在没有编写器的命名管道上

php editor Banana introduces you to a special operation method, which is to use the os.OpenFile function with O_RDONLY to mount on a named pipe without a writer. This operation method can realize the reading operation of the named pipe, allowing you to easily obtain the data information in the named pipe without using a writer. This technique is simple to understand and easy to operate, making it an excellent choice for developers when dealing with named pipes. Next, we will introduce you in detail how to use this method to implement the reading operation of the named pipe.

Question content

I'm writing a daemon that should receive notifications from ad-hoc cli commands and choose to do so via unix named pipes. I wrote a short package that on one hand generates a separate goroutine to read from nodes and send received notifications to channels (playground with unit tests):

type Writer struct {
    f *os.File
}

func NewWriter(ipc string) (*Writer, error) {
    f, err := os.OpenFile(ipc, os.O_WRONLY, 0600)

    if err != nil {
        return nil, fmt.Errorf("writer: open file: %w", err)
    }

    return &Writer{f: f}, nil
}

func (w *Writer) WriteString(str string) (int, error) {
    return w.f.WriteString(fmt.Sprint(str, "\n"))

}

func (w *Writer) Close() error {
    return w.f.Close()
}

type Reader struct {
    f    *os.File
    rmFn func() error
    quit chan struct{}
    done *sync.WaitGroup
}

func NewReader(ipc string) (*Reader, error) {
    err := syscall.Mkfifo(ipc, 0640)
    if err != nil {
        return nil, fmt.Errorf("reader: create fifo: %w", err)
    }

    f, err := os.OpenFile(ipc, os.O_RDONLY, 0640)
    if err != nil {
        return nil, fmt.Errorf("reader: open fifo: %w", err)
    }
    return &Reader{
        f:    f,
        quit: make(chan struct{}),
        done: &sync.WaitGroup{},
        rmFn: func() error {
            return os.Remove(ipc)
        },
    }, nil
}

func (r *Reader) PollRead() <-chan string {
    reader := bufio.NewReader(r.f)
    out := make(chan string)
    r.done.Add(1)
    go func() {
        defer r.done.Done()
        for {
            line, err := reader.ReadBytes('\n')
            if err != nil {
                fmt.Printf("error reading from named pipe: %v\n", err)
                return
            }

            nline := string(line)
            nline = strings.TrimRight(nline, "\n")
            select {
            case out <- nline:
            case <-r.quit:
                close(out)
                return
            }
        }
    }()

    return out
}

func (r *Reader) Close() error {
    close(r.quit)
    r.done.Wait()
    err := r.f.Close()
    if err != nil {
        return fmt.Errorf("error closing named pipe: %v", err)
    }

    err = r.rmFn()
    if err != nil {
        return fmt.Errorf("error removing named pipe: %v", err)
    }
    return nil
}
Copy after login

This does seem to work, but it suffers from a peculiar behavior where no readers can open the file before any writers can open the file, which seems to be Based on other things I've read on the topic to reverse the behavior; the usual complaint is that the writer hangs because there aren't any readers, however, here the reader cannot be instantiated in the first place.

Solution

This is posix system interface:

o_nonblock When opening a fifo with o_rdonly or o_wronly set: if o_nonblock is set, read-only open() will return without Delay. If there is no process, write-only open() will return an error The file is currently open for reading.

If o_nonblock is cleared, read-only open() will block Called until the thread opens the file for writing. an open() Write-only should block the calling thread until the thread is opened File for reading.

When opening a supported block special or character special file Non-blocking open:

If o_nonblock is set, the open() function will return without Prevent the device from being ready or available. subsequent behavior Device properties are device-specific.

If o_nonblock is cleared, the open() function will block the call l> The thread does not return until the device is ready or available.

Otherwise, the o_nonblock flag does not cause an error, but it is It is not specified whether the file status flag contains o_nonblock logo.

So the solution is to add the syscall.o_nonblock flag to the openfile call:

f, err := os.OpenFile(ipc, os.O_RDONLY|syscall.O_NONBLOCK, 0640)
Copy after login

Edit: As discussed in the comments, this solution is not portable to a darwin environment. A more portable solution is to open the file on the reader side using o_rdwr.

The above is the detailed content of os.OpenFile with O_RDONLY hangs on named pipe without writer. For more information, please follow other related articles on the PHP Chinese website!

source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template