いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-475 (キャンセル可能なreader)(cancelreader)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

標準入力からパイプで流したデータをキャンセル可能にしたいなーって思って、ライブラリ探してたら以下を発見。

github.com

Linuxの場合は、内部で epoll を使って制御してくれているみたいですね。ちょっと試してみました。

以下、サンプルです。

サンプル

package main

import (
    "errors"
    "fmt"
    "log"
    "os"
    "time"

    "github.com/muesli/cancelreader"
)

func main() {
    log.SetFlags(log.Lmicroseconds)
    log.SetOutput(os.Stdout)

    if err := run(); err != nil {
        log.Panic(err)
    }
}

func run() error {
    //
    // 標準入力をキャンセル可能なReaderにする
    //
    // cancelreader.CancelReaderは、Linuxの場合
    // 内部で epoll を利用して制御を行っている。
    //
    // なので、非ブロッキングI/Oが可能なファイルディスクリプタに対してのみ
    // キャンセル可能となっている。非ブロッキングI/Oが可能なものは例えば以下のもの。
    //   - ソケット
    //   - パイプ
    //   - FIFO
    //
    // 通常のファイルは非ブロッキングI/O可能なファイルディスクリプタでは無いことに注意。
    // 通常のファイルをepollで利用すると epoll_ctl() で EPERM が返ってくる。
    // 通常のファイルをキャンセル可能にしたい場合は io_uring などを検討する。
    //

    var (
        reader cancelreader.CancelReader
        err    error
    )
    reader, err = cancelreader.NewReader(os.Stdin)
    if err != nil {
        return fmt.Errorf("cancelreader.NewReader() failed: %w", err)
    }
    defer reader.Close()

    //
    // 3秒後にキャンセル
    //
    go func() {
        time.Sleep(3 * time.Second)
        reader.Cancel()
    }()

    //
    // 500ms毎に1文字読み込み
    //
    var (
        buf [1]byte
    )
    for {
        clear(buf[:])

        if _, err = reader.Read(buf[:]); err != nil {
            if errors.Is(err, cancelreader.ErrCanceled) {
                log.Println("CANCELED")
                break
            }

            return fmt.Errorf("reader.Read() failed: %w", err)
        }

        log.Print(string(buf[:]))
        time.Sleep(500 * time.Millisecond)
    }

    return nil
}

試してみます。

$ task
task: [default] echo -n helloworld | go run main.go
15:09:24.486356 h
15:09:24.987136 e
15:09:25.487775 l
15:09:25.988534 l
15:09:26.489191 o
15:09:26.989851 w
15:09:27.490367 CANCELED

ちゃんと途中でキャンセルがかかっていますね。

参考情報

ja.manpages.org

itkq.jp

blog.ojisan.io

zenn.dev

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。