// 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 (
"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(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()