1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package poll
6
7import (
8	"runtime"
9	"sync"
10	"syscall"
11)
12
13// asyncIO implements asynchronous cancelable I/O.
14// An asyncIO represents a single asynchronous Read or Write
15// operation. The result is returned on the result channel.
16// The undergoing I/O system call can either complete or be
17// interrupted by a note.
18type asyncIO struct {
19	res chan result
20
21	// mu guards the pid field.
22	mu sync.Mutex
23
24	// pid holds the process id of
25	// the process running the IO operation.
26	pid int
27}
28
29// result is the return value of a Read or Write operation.
30type result struct {
31	n   int
32	err error
33}
34
35// newAsyncIO returns a new asyncIO that performs an I/O
36// operation by calling fn, which must do one and only one
37// interruptible system call.
38func newAsyncIO(fn func([]byte) (int, error), b []byte) *asyncIO {
39	aio := &asyncIO{
40		res: make(chan result, 0),
41	}
42	aio.mu.Lock()
43	go func() {
44		// Lock the current goroutine to its process
45		// and store the pid in io so that Cancel can
46		// interrupt it. We ignore the "hangup" signal,
47		// so the signal does not take down the entire
48		// Go runtime.
49		runtime.LockOSThread()
50		runtime_ignoreHangup()
51		aio.pid = syscall.Getpid()
52		aio.mu.Unlock()
53
54		n, err := fn(b)
55
56		aio.mu.Lock()
57		aio.pid = -1
58		runtime_unignoreHangup()
59		aio.mu.Unlock()
60
61		aio.res <- result{n, err}
62	}()
63	return aio
64}
65
66// Cancel interrupts the I/O operation, causing
67// the Wait function to return.
68func (aio *asyncIO) Cancel() {
69	aio.mu.Lock()
70	defer aio.mu.Unlock()
71	if aio.pid == -1 {
72		return
73	}
74	f, e := syscall.Open("/proc/"+itoa(aio.pid)+"/note", syscall.O_WRONLY)
75	if e != nil {
76		return
77	}
78	syscall.Write(f, []byte("hangup"))
79	syscall.Close(f)
80}
81
82// Wait for the I/O operation to complete.
83func (aio *asyncIO) Wait() (int, error) {
84	res := <-aio.res
85	return res.n, res.err
86}
87
88// The following functions, provided by the runtime, are used to
89// ignore and unignore the "hangup" signal received by the process.
90func runtime_ignoreHangup()
91func runtime_unignoreHangup()
92