1// Copyright 2009 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 "errors" 9 "internal/stringslite" 10 "io" 11 "sync" 12 "syscall" 13 "time" 14) 15 16type FD struct { 17 // Lock sysfd and serialize access to Read and Write methods. 18 fdmu fdMutex 19 20 Destroy func() 21 22 // deadlines 23 rmu sync.Mutex 24 wmu sync.Mutex 25 raio *asyncIO 26 waio *asyncIO 27 rtimer *time.Timer 28 wtimer *time.Timer 29 rtimedout bool // set true when read deadline has been reached 30 wtimedout bool // set true when write deadline has been reached 31 32 // Whether this is a normal file. 33 // On Plan 9 we do not use this package for ordinary files, 34 // so this is always false, but the field is present because 35 // shared code in fd_mutex.go checks it. 36 isFile bool 37} 38 39// We need this to close out a file descriptor when it is unlocked, 40// but the real implementation has to live in the net package because 41// it uses os.File's. 42func (fd *FD) destroy() error { 43 if fd.Destroy != nil { 44 fd.Destroy() 45 } 46 return nil 47} 48 49// Close handles the locking for closing an FD. The real operation 50// is in the net package. 51func (fd *FD) Close() error { 52 if !fd.fdmu.increfAndClose() { 53 return errClosing(fd.isFile) 54 } 55 return nil 56} 57 58// Read implements io.Reader. 59func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) { 60 if err := fd.readLock(); err != nil { 61 return 0, err 62 } 63 defer fd.readUnlock() 64 if len(b) == 0 { 65 return 0, nil 66 } 67 fd.rmu.Lock() 68 if fd.rtimedout { 69 fd.rmu.Unlock() 70 return 0, ErrDeadlineExceeded 71 } 72 fd.raio = newAsyncIO(fn, b) 73 fd.rmu.Unlock() 74 n, err := fd.raio.Wait() 75 fd.raio = nil 76 if isHangup(err) { 77 err = io.EOF 78 } 79 if isInterrupted(err) { 80 err = ErrDeadlineExceeded 81 } 82 return n, err 83} 84 85// Write implements io.Writer. 86func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (int, error) { 87 if err := fd.writeLock(); err != nil { 88 return 0, err 89 } 90 defer fd.writeUnlock() 91 fd.wmu.Lock() 92 if fd.wtimedout { 93 fd.wmu.Unlock() 94 return 0, ErrDeadlineExceeded 95 } 96 fd.waio = newAsyncIO(fn, b) 97 fd.wmu.Unlock() 98 n, err := fd.waio.Wait() 99 fd.waio = nil 100 if isInterrupted(err) { 101 err = ErrDeadlineExceeded 102 } 103 return n, err 104} 105 106// SetDeadline sets the read and write deadlines associated with fd. 107func (fd *FD) SetDeadline(t time.Time) error { 108 return setDeadlineImpl(fd, t, 'r'+'w') 109} 110 111// SetReadDeadline sets the read deadline associated with fd. 112func (fd *FD) SetReadDeadline(t time.Time) error { 113 return setDeadlineImpl(fd, t, 'r') 114} 115 116// SetWriteDeadline sets the write deadline associated with fd. 117func (fd *FD) SetWriteDeadline(t time.Time) error { 118 return setDeadlineImpl(fd, t, 'w') 119} 120 121func setDeadlineImpl(fd *FD, t time.Time, mode int) error { 122 d := t.Sub(time.Now()) 123 if mode == 'r' || mode == 'r'+'w' { 124 fd.rmu.Lock() 125 defer fd.rmu.Unlock() 126 if fd.rtimer != nil { 127 fd.rtimer.Stop() 128 fd.rtimer = nil 129 } 130 fd.rtimedout = false 131 } 132 if mode == 'w' || mode == 'r'+'w' { 133 fd.wmu.Lock() 134 defer fd.wmu.Unlock() 135 if fd.wtimer != nil { 136 fd.wtimer.Stop() 137 fd.wtimer = nil 138 } 139 fd.wtimedout = false 140 } 141 if !t.IsZero() && d > 0 { 142 // Interrupt I/O operation once timer has expired 143 if mode == 'r' || mode == 'r'+'w' { 144 var timer *time.Timer 145 timer = time.AfterFunc(d, func() { 146 fd.rmu.Lock() 147 defer fd.rmu.Unlock() 148 if fd.rtimer != timer { 149 // deadline was changed 150 return 151 } 152 fd.rtimedout = true 153 if fd.raio != nil { 154 fd.raio.Cancel() 155 } 156 }) 157 fd.rtimer = timer 158 } 159 if mode == 'w' || mode == 'r'+'w' { 160 var timer *time.Timer 161 timer = time.AfterFunc(d, func() { 162 fd.wmu.Lock() 163 defer fd.wmu.Unlock() 164 if fd.wtimer != timer { 165 // deadline was changed 166 return 167 } 168 fd.wtimedout = true 169 if fd.waio != nil { 170 fd.waio.Cancel() 171 } 172 }) 173 fd.wtimer = timer 174 } 175 } 176 if !t.IsZero() && d <= 0 { 177 // Interrupt current I/O operation 178 if mode == 'r' || mode == 'r'+'w' { 179 fd.rtimedout = true 180 if fd.raio != nil { 181 fd.raio.Cancel() 182 } 183 } 184 if mode == 'w' || mode == 'r'+'w' { 185 fd.wtimedout = true 186 if fd.waio != nil { 187 fd.waio.Cancel() 188 } 189 } 190 } 191 return nil 192} 193 194// On Plan 9 only, expose the locking for the net code. 195 196// ReadLock wraps FD.readLock. 197func (fd *FD) ReadLock() error { 198 return fd.readLock() 199} 200 201// ReadUnlock wraps FD.readUnlock. 202func (fd *FD) ReadUnlock() { 203 fd.readUnlock() 204} 205 206func isHangup(err error) bool { 207 return err != nil && stringslite.HasSuffix(err.Error(), "Hangup") 208} 209 210func isInterrupted(err error) bool { 211 return err != nil && stringslite.HasSuffix(err.Error(), "interrupted") 212} 213 214// IsPollDescriptor reports whether fd is the descriptor being used by the poller. 215// This is only used for testing. 216func IsPollDescriptor(fd uintptr) bool { 217 return false 218} 219 220// RawControl invokes the user-defined function f for a non-IO 221// operation. 222func (fd *FD) RawControl(f func(uintptr)) error { 223 return errors.New("not implemented") 224} 225 226// RawRead invokes the user-defined function f for a read operation. 227func (fd *FD) RawRead(f func(uintptr) bool) error { 228 return errors.New("not implemented") 229} 230 231// RawWrite invokes the user-defined function f for a write operation. 232func (fd *FD) RawWrite(f func(uintptr) bool) error { 233 return errors.New("not implemented") 234} 235 236func DupCloseOnExec(fd int) (int, string, error) { 237 nfd, err := syscall.Dup(int(fd), -1) 238 if err != nil { 239 return 0, "dup", err 240 } 241 // Plan9 has no syscall.CloseOnExec but 242 // its forkAndExecInChild closes all fds 243 // not related to the fork+exec. 244 return nfd, "", nil 245} 246