// Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package poll import ( "internal/itoa" "runtime" "sync" "syscall" ) // asyncIO implements asynchronous cancelable I/O. // An asyncIO represents a single asynchronous Read or Write // operation. The result is returned on the result channel. // The undergoing I/O system call can either complete or be // interrupted by a note. type asyncIO struct { res chan result // mu guards the pid field. mu sync.Mutex // pid holds the process id of // the process running the IO operation. pid int } // result is the return value of a Read or Write operation. type result struct { n int err error } // newAsyncIO returns a new asyncIO that performs an I/O // operation by calling fn, which must do one and only one // interruptible system call. func newAsyncIO(fn func([]byte) (int, error), b []byte) *asyncIO { aio := &asyncIO{ res: make(chan result, 0), } aio.mu.Lock() go func() { // Lock the current goroutine to its process // and store the pid in io so that Cancel can // interrupt it. We ignore the "hangup" signal, // so the signal does not take down the entire // Go runtime. runtime.LockOSThread() runtime_ignoreHangup() aio.pid = syscall.Getpid() aio.mu.Unlock() n, err := fn(b) aio.mu.Lock() aio.pid = -1 runtime_unignoreHangup() aio.mu.Unlock() aio.res <- result{n, err} }() return aio } // Cancel interrupts the I/O operation, causing // the Wait function to return. func (aio *asyncIO) Cancel() { aio.mu.Lock() defer aio.mu.Unlock() if aio.pid == -1 { return } f, e := syscall.Open("/proc/"+itoa.Itoa(aio.pid)+"/note", syscall.O_WRONLY) if e != nil { return } syscall.Write(f, []byte("hangup")) syscall.Close(f) } // Wait for the I/O operation to complete. func (aio *asyncIO) Wait() (int, error) { res := <-aio.res return res.n, res.err } // The following functions, provided by the runtime, are used to // ignore and unignore the "hangup" signal received by the process. func runtime_ignoreHangup() func runtime_unignoreHangup()