blob: 49590ab13c416cbc249da33024fef8accc376e9c [file] [log] [blame]
Ian Lance Taylor3792db52017-02-10 14:59:38 -08001// 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 "io"
9 "sync/atomic"
10 "time"
11)
12
13type atomicBool int32
14
15func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
16func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
17func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
18
19type FD struct {
20 // Lock sysfd and serialize access to Read and Write methods.
21 fdmu fdMutex
22
23 Destroy func()
24
25 // deadlines
26 raio *asyncIO
27 waio *asyncIO
28 rtimer *time.Timer
29 wtimer *time.Timer
30 rtimedout atomicBool // set true when read deadline has been reached
31 wtimedout atomicBool // set true when write deadline has been reached
Ian Lance Taylorfb4b4342017-04-07 15:53:19 -070032
33 // Whether this is a normal file.
34 // On Plan 9 we do not use this package for ordinary files,
35 // so this is always false, but the field is present because
36 // shared code in fd_mutex.go checks it.
37 isFile bool
Ian Lance Taylor3792db52017-02-10 14:59:38 -080038}
39
40// We need this to close out a file descriptor when it is unlocked,
41// but the real implementation has to live in the net package because
42// it uses os.File's.
43func (fd *FD) destroy() error {
44 if fd.Destroy != nil {
45 fd.Destroy()
46 }
47 return nil
48}
49
50// Close handles the locking for closing an FD. The real operation
51// is in the net package.
52func (fd *FD) Close() error {
53 if !fd.fdmu.increfAndClose() {
Ian Lance Taylorfb4b4342017-04-07 15:53:19 -070054 return errClosing(fd.isFile)
Ian Lance Taylor3792db52017-02-10 14:59:38 -080055 }
56 return nil
57}
58
Mikio Hara7c3fa412017-04-12 12:40:07 +090059// Read implements io.Reader.
Dave Cheney84cf1f02017-02-14 09:18:12 +110060func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) {
Ian Lance Taylor3792db52017-02-10 14:59:38 -080061 if fd.rtimedout.isSet() {
62 return 0, ErrTimeout
63 }
64 if err := fd.readLock(); err != nil {
65 return 0, err
66 }
67 defer fd.readUnlock()
68 if len(b) == 0 {
69 return 0, nil
70 }
71 fd.raio = newAsyncIO(fn, b)
Dave Cheney84cf1f02017-02-14 09:18:12 +110072 n, err := fd.raio.Wait()
Ian Lance Taylor3792db52017-02-10 14:59:38 -080073 fd.raio = nil
74 if isHangup(err) {
75 err = io.EOF
76 }
77 if isInterrupted(err) {
78 err = ErrTimeout
79 }
Dave Cheney84cf1f02017-02-14 09:18:12 +110080 return n, err
Ian Lance Taylor3792db52017-02-10 14:59:38 -080081}
82
Mikio Hara7c3fa412017-04-12 12:40:07 +090083// Write implements io.Writer.
Dave Cheney84cf1f02017-02-14 09:18:12 +110084func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (int, error) {
Ian Lance Taylor3792db52017-02-10 14:59:38 -080085 if fd.wtimedout.isSet() {
86 return 0, ErrTimeout
87 }
88 if err := fd.writeLock(); err != nil {
89 return 0, err
90 }
91 defer fd.writeUnlock()
92 fd.waio = newAsyncIO(fn, b)
Dave Cheney84cf1f02017-02-14 09:18:12 +110093 n, err := fd.waio.Wait()
Ian Lance Taylor3792db52017-02-10 14:59:38 -080094 fd.waio = nil
95 if isInterrupted(err) {
96 err = ErrTimeout
97 }
Dave Cheney84cf1f02017-02-14 09:18:12 +110098 return n, err
Ian Lance Taylor3792db52017-02-10 14:59:38 -080099}
100
Mikio Hara38409f52017-03-06 18:39:02 +0900101// SetDeadline sets the read and write deadlines associated with fd.
Ian Lance Taylor3792db52017-02-10 14:59:38 -0800102func (fd *FD) SetDeadline(t time.Time) error {
103 return setDeadlineImpl(fd, t, 'r'+'w')
104}
105
Mikio Hara38409f52017-03-06 18:39:02 +0900106// SetReadDeadline sets the read deadline associated with fd.
Ian Lance Taylor3792db52017-02-10 14:59:38 -0800107func (fd *FD) SetReadDeadline(t time.Time) error {
108 return setDeadlineImpl(fd, t, 'r')
109}
110
Mikio Hara38409f52017-03-06 18:39:02 +0900111// SetWriteDeadline sets the write deadline associated with fd.
Ian Lance Taylor3792db52017-02-10 14:59:38 -0800112func (fd *FD) SetWriteDeadline(t time.Time) error {
113 return setDeadlineImpl(fd, t, 'w')
114}
115
116func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
117 d := t.Sub(time.Now())
118 if mode == 'r' || mode == 'r'+'w' {
119 fd.rtimedout.setFalse()
120 }
121 if mode == 'w' || mode == 'r'+'w' {
122 fd.wtimedout.setFalse()
123 }
124 if t.IsZero() || d < 0 {
125 // Stop timer
126 if mode == 'r' || mode == 'r'+'w' {
127 if fd.rtimer != nil {
128 fd.rtimer.Stop()
129 }
130 fd.rtimer = nil
131 }
132 if mode == 'w' || mode == 'r'+'w' {
133 if fd.wtimer != nil {
134 fd.wtimer.Stop()
135 }
136 fd.wtimer = nil
137 }
138 } else {
139 // Interrupt I/O operation once timer has expired
140 if mode == 'r' || mode == 'r'+'w' {
141 fd.rtimer = time.AfterFunc(d, func() {
142 fd.rtimedout.setTrue()
143 if fd.raio != nil {
144 fd.raio.Cancel()
145 }
146 })
147 }
148 if mode == 'w' || mode == 'r'+'w' {
149 fd.wtimer = time.AfterFunc(d, func() {
150 fd.wtimedout.setTrue()
151 if fd.waio != nil {
152 fd.waio.Cancel()
153 }
154 })
155 }
156 }
157 if !t.IsZero() && d < 0 {
158 // Interrupt current I/O operation
159 if mode == 'r' || mode == 'r'+'w' {
160 fd.rtimedout.setTrue()
161 if fd.raio != nil {
162 fd.raio.Cancel()
163 }
164 }
165 if mode == 'w' || mode == 'r'+'w' {
166 fd.wtimedout.setTrue()
167 if fd.waio != nil {
168 fd.waio.Cancel()
169 }
170 }
171 }
172 return nil
173}
174
175// On Plan 9 only, expose the locking for the net code.
176
Mikio Hara38409f52017-03-06 18:39:02 +0900177// ReadLock wraps FD.readLock.
Ian Lance Taylor3792db52017-02-10 14:59:38 -0800178func (fd *FD) ReadLock() error {
179 return fd.readLock()
180}
181
Mikio Hara38409f52017-03-06 18:39:02 +0900182// ReadUnlock wraps FD.readUnlock.
Ian Lance Taylor3792db52017-02-10 14:59:38 -0800183func (fd *FD) ReadUnlock() {
184 fd.readUnlock()
185}
186
187func isHangup(err error) bool {
188 return err != nil && stringsHasSuffix(err.Error(), "Hangup")
189}
190
191func isInterrupted(err error) bool {
192 return err != nil && stringsHasSuffix(err.Error(), "interrupted")
193}
Ian Lance Taylor45a5f792017-02-15 14:26:42 -0800194
Mikio Hara38409f52017-03-06 18:39:02 +0900195// PollDescriptor returns the descriptor being used by the poller,
196// or ^uintptr(0) if there isn't one. This is only used for testing.
Ian Lance Taylor45a5f792017-02-15 14:26:42 -0800197func PollDescriptor() uintptr {
198 return ^uintptr(0)
199}