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