blob: 1529bc6fde91113c47e9ff70b4d9792d8cad6409 [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
32}
33
34// We need this to close out a file descriptor when it is unlocked,
35// but the real implementation has to live in the net package because
36// it uses os.File's.
37func (fd *FD) destroy() error {
38 if fd.Destroy != nil {
39 fd.Destroy()
40 }
41 return nil
42}
43
44// Close handles the locking for closing an FD. The real operation
45// is in the net package.
46func (fd *FD) Close() error {
47 if !fd.fdmu.increfAndClose() {
48 return ErrClosing
49 }
50 return nil
51}
52
Dave Cheney84cf1f02017-02-14 09:18:12 +110053func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) {
Ian Lance Taylor3792db52017-02-10 14:59:38 -080054 if fd.rtimedout.isSet() {
55 return 0, ErrTimeout
56 }
57 if err := fd.readLock(); err != nil {
58 return 0, err
59 }
60 defer fd.readUnlock()
61 if len(b) == 0 {
62 return 0, nil
63 }
64 fd.raio = newAsyncIO(fn, b)
Dave Cheney84cf1f02017-02-14 09:18:12 +110065 n, err := fd.raio.Wait()
Ian Lance Taylor3792db52017-02-10 14:59:38 -080066 fd.raio = nil
67 if isHangup(err) {
68 err = io.EOF
69 }
70 if isInterrupted(err) {
71 err = ErrTimeout
72 }
Dave Cheney84cf1f02017-02-14 09:18:12 +110073 return n, err
Ian Lance Taylor3792db52017-02-10 14:59:38 -080074}
75
Dave Cheney84cf1f02017-02-14 09:18:12 +110076func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (int, error) {
Ian Lance Taylor3792db52017-02-10 14:59:38 -080077 if fd.wtimedout.isSet() {
78 return 0, ErrTimeout
79 }
80 if err := fd.writeLock(); err != nil {
81 return 0, err
82 }
83 defer fd.writeUnlock()
84 fd.waio = newAsyncIO(fn, b)
Dave Cheney84cf1f02017-02-14 09:18:12 +110085 n, err := fd.waio.Wait()
Ian Lance Taylor3792db52017-02-10 14:59:38 -080086 fd.waio = nil
87 if isInterrupted(err) {
88 err = ErrTimeout
89 }
Dave Cheney84cf1f02017-02-14 09:18:12 +110090 return n, err
Ian Lance Taylor3792db52017-02-10 14:59:38 -080091}
92
93func (fd *FD) SetDeadline(t time.Time) error {
94 return setDeadlineImpl(fd, t, 'r'+'w')
95}
96
97func (fd *FD) SetReadDeadline(t time.Time) error {
98 return setDeadlineImpl(fd, t, 'r')
99}
100
101func (fd *FD) SetWriteDeadline(t time.Time) error {
102 return setDeadlineImpl(fd, t, 'w')
103}
104
105func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
106 d := t.Sub(time.Now())
107 if mode == 'r' || mode == 'r'+'w' {
108 fd.rtimedout.setFalse()
109 }
110 if mode == 'w' || mode == 'r'+'w' {
111 fd.wtimedout.setFalse()
112 }
113 if t.IsZero() || d < 0 {
114 // Stop timer
115 if mode == 'r' || mode == 'r'+'w' {
116 if fd.rtimer != nil {
117 fd.rtimer.Stop()
118 }
119 fd.rtimer = nil
120 }
121 if mode == 'w' || mode == 'r'+'w' {
122 if fd.wtimer != nil {
123 fd.wtimer.Stop()
124 }
125 fd.wtimer = nil
126 }
127 } else {
128 // Interrupt I/O operation once timer has expired
129 if mode == 'r' || mode == 'r'+'w' {
130 fd.rtimer = time.AfterFunc(d, func() {
131 fd.rtimedout.setTrue()
132 if fd.raio != nil {
133 fd.raio.Cancel()
134 }
135 })
136 }
137 if mode == 'w' || mode == 'r'+'w' {
138 fd.wtimer = time.AfterFunc(d, func() {
139 fd.wtimedout.setTrue()
140 if fd.waio != nil {
141 fd.waio.Cancel()
142 }
143 })
144 }
145 }
146 if !t.IsZero() && d < 0 {
147 // Interrupt current I/O operation
148 if mode == 'r' || mode == 'r'+'w' {
149 fd.rtimedout.setTrue()
150 if fd.raio != nil {
151 fd.raio.Cancel()
152 }
153 }
154 if mode == 'w' || mode == 'r'+'w' {
155 fd.wtimedout.setTrue()
156 if fd.waio != nil {
157 fd.waio.Cancel()
158 }
159 }
160 }
161 return nil
162}
163
164// On Plan 9 only, expose the locking for the net code.
165
166func (fd *FD) ReadLock() error {
167 return fd.readLock()
168}
169
170func (fd *FD) ReadUnlock() {
171 fd.readUnlock()
172}
173
174func isHangup(err error) bool {
175 return err != nil && stringsHasSuffix(err.Error(), "Hangup")
176}
177
178func isInterrupted(err error) bool {
179 return err != nil && stringsHasSuffix(err.Error(), "interrupted")
180}