fd_io_plan9.mx raw

   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  	"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.
  19  type 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.
  31  type 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.
  39  func newAsyncIO(fn func([]byte) (int, error), b []byte) *asyncIO {
  40  	aio := &asyncIO{
  41  		res: 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.
  69  func (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.
  84  func (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.
  91  func runtime_ignoreHangup()
  92  func runtime_unignoreHangup()
  93