Home | History | Annotate | Download | only in poll
      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 
      5 package poll
      6 
      7 import (
      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.
     18 type 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.
     30 type 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.
     38 func 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.
     68 func (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.
     83 func (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.
     90 func runtime_ignoreHangup()
     91 func runtime_unignoreHangup()
     92