Bagaimana cara mencegat keluaran yang tidak buffer dari Proc :: Async di Raku?

Aug 19 2020

Dengan potongan seperti

# Contents of ./run
my $p = Proc::Async.new: @*ARGS; react { whenever Promise.in: 5 { $p.kill               }
    whenever $p.stdout { say "OUT: { .chomp }" } whenever $p.ready      { say "PID: $_" } whenever $p.start      { say "Done"            }
}

dieksekusi seperti

./run raku -e 'react whenever Supply.interval: 1 { .say }'

Saya berharap untuk melihat sesuatu seperti

PID: 1234
OUT: 0
OUT: 1
OUT: 2
OUT: 3
OUT: 4
Done

tapi sebaliknya aku mengerti

PID: 1234
OUT: 0
Done

Saya mengerti bahwa ini ada hubungannya dengan buffering: jika saya mengubah perintah itu menjadi seperti

# The $|++ disables buffering ./run perl -E '$|++; while(1) { state $i; say $i++; sleep 1 }'

Saya mendapatkan hasil yang diinginkan.

Saya tahu bahwa objek TTY IO :: Handle adalah unbuffered , dan dalam hal ini $*OUTproses yang muncul bukanlah satu. Dan saya pernah membaca bahwa IO :: Pipa benda yang buffered "sehingga menulis tanpa membaca tidak segera memblokir" (meskipun saya tidak bisa mengatakan saya sepenuhnya mengerti apa artinya ini).

Tapi apa pun yang saya coba, saya tidak bisa mendapatkan aliran keluaran unbuffered dari Proc :: Async. Bagaimana saya melakukan ini?

Saya sudah mencoba mengikat IO :: Menangani menggunakan terbuka $proc.bind-stdouttetapi saya masih mendapatkan masalah yang sama.

Perhatikan bahwa melakukan sesuatu seperti $proc.bind-stdout: $*OUTitu berhasil, dalam arti bahwa objek Proc :: Async tidak lagi buffer, tetapi itu juga bukan solusi untuk masalah saya, karena saya tidak dapat memanfaatkan output sebelum keluar. Itu menyarankan kepada saya bahwa jika saya dapat mengikat Proc :: Async ke pegangan yang tidak disangga, itu harus melakukan hal yang benar. Tapi aku juga belum bisa membuatnya bekerja.


Untuk klarifikasi: seperti yang disarankan dengan contoh Perl, saya tahu saya dapat memperbaikinya dengan menonaktifkan buffering pada perintah yang akan saya sampaikan sebagai input, tetapi saya mencari cara untuk melakukan ini dari sisi yang membuat Proc: : Objek Async.

Jawaban

3 JonathanWorthington Aug 21 2020 at 05:13

Proc::Asyncitu sendiri tidak melakukan buffering pada data yang diterima. Namun, proses pemijahan dapat melakukan sendiri tergantung pada apa yang mereka hasilkan, dan itulah yang diamati di sini.

Banyak program membuat keputusan tentang buffering keluarannya (antara lain, seperti apakah akan memancarkan kode warna) berdasarkan apakah pegangan keluaran dipasang ke TTY (terminal). Asumsinya adalah bahwa TTY berarti manusia akan mengawasi output, dan dengan demikian latensi lebih disukai daripada throughput, jadi buffering dinonaktifkan (atau dibatasi pada buffering baris). Sebaliknya, jika outputnya berupa pipa atau file, maka asumsinya adalah bahwa latensi tidak begitu penting, dan buffering digunakan untuk mencapai kemenangan throughput yang signifikan (jauh lebih sedikit panggilan sistem untuk menulis data).

Saat kita menelurkan sesuatu dengan Proc::Async, keluaran standar dari proses pemijahan terikat ke pipa - yang bukan TTY. Jadi program yang dipanggil dapat menggunakan ini untuk memutuskan untuk menerapkan buffering keluaran.

Jika Anda ingin memiliki ketergantungan lain, Anda dapat menjalankan program melalui. sesuatu yang memalsukan TTY, seperti unbuffer( expecttampaknya bagian dari paket). Berikut adalah contoh program yang mengalami buffering:

my $proc = Proc::Async.new: 'raku', '-e', 'react whenever Supply.interval(1) { .say }'; react whenever $proc.stdout {
    .print
}

Kami hanya melihat 0dan kemudian harus menunggu lama untuk lebih banyak keluaran. Menjalankannya melalui unbuffer:

my $proc = Proc::Async.new: 'unbuffer', 'raku', '-e', 'react whenever Supply.interval(1) { .say }'; react whenever $proc.stdout {
    .print
}

Berarti kita melihat keluaran angka setiap detik.

Bisakah Raku memberikan solusi bawaan untuk ini suatu hari nanti? Ya - dengan melakukan "keajaiban" yang unbufferdilakukannya sendiri (saya kira mengalokasikan ptysejenis TTY palsu). Ini tidak sepele - meskipun sedang dieksplorasi oleh pengembang libuv ; setidaknya sejauh Rakudo di MoarVM berjalan, saat ada rilis libuv yang tersedia menawarkan fitur seperti itu, kami akan bekerja untuk mengeksposnya.

6 ugexe Aug 19 2020 at 20:11

Anda dapat menyetel .out-bufferpegangan (seperti $*OUTatau $*ERR) ke 0:

$ ./run raku -e '$*OUT.out-buffer = 0; react whenever Supply.interval: 1 { .say }'

PID: 11340
OUT: 0
OUT: 1
OUT: 2
OUT: 3
OUT: 4
Done