fd_plan9.mx raw
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
5 package poll
6
7 import (
8 "errors"
9 "internal/stringslite"
10 "io"
11 "sync"
12 "syscall"
13 "time"
14 )
15
16 type 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.
42 func (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.
51 func (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.
59 func (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.
86 func (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.
107 func (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.
112 func (fd *FD) SetReadDeadline(t time.Time) error {
113 return setDeadlineImpl(fd, t, 'r')
114 }
115
116 // SetWriteDeadline sets the write deadline associated with fd.
117 func (fd *FD) SetWriteDeadline(t time.Time) error {
118 return setDeadlineImpl(fd, t, 'w')
119 }
120
121 func 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.
197 func (fd *FD) ReadLock() error {
198 return fd.readLock()
199 }
200
201 // ReadUnlock wraps FD.readUnlock.
202 func (fd *FD) ReadUnlock() {
203 fd.readUnlock()
204 }
205
206 func isHangup(err error) bool {
207 return err != nil && stringslite.HasSuffix(err.Error(), "Hangup")
208 }
209
210 func 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.
216 func IsPollDescriptor(fd uintptr) bool {
217 return false
218 }
219
220 // RawControl invokes the user-defined function f for a non-IO
221 // operation.
222 func (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.
227 func (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.
232 func (fd *FD) RawWrite(f func(uintptr) bool) error {
233 return errors.New("not implemented")
234 }
235
236 func DupCloseOnExec(fd int) (int, []byte, 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