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