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