5
5
* Copyright (C) 2005 Microsoft
6
6
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
7
7
*/
8
+
8
9
package winpipe
9
10
10
11
import (
11
- "errors"
12
12
"io"
13
+ "os"
13
14
"runtime"
14
15
"sync"
15
16
"sync/atomic"
16
17
"time"
18
+ "unsafe"
17
19
18
20
"golang.org/x/sys/windows"
19
21
)
20
22
21
- //sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
22
- //sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
23
- //sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
24
- //sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
25
- //sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
26
-
27
- type atomicBool int32
28
-
29
- func (b * atomicBool ) isSet () bool { return atomic .LoadInt32 ((* int32 )(b )) != 0 }
30
- func (b * atomicBool ) setFalse () { atomic .StoreInt32 ((* int32 )(b ), 0 ) }
31
- func (b * atomicBool ) setTrue () { atomic .StoreInt32 ((* int32 )(b ), 1 ) }
32
- func (b * atomicBool ) swap (new bool ) bool {
33
- var newInt int32
34
- if new {
35
- newInt = 1
36
- }
37
- return atomic .SwapInt32 ((* int32 )(b ), newInt ) == 1
38
- }
39
-
40
- const (
41
- cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
42
- cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
43
- )
44
-
45
- var (
46
- ErrFileClosed = errors .New ("file has already been closed" )
47
- ErrTimeout = & timeoutError {}
48
- )
49
-
50
- type timeoutError struct {}
51
-
52
- func (e * timeoutError ) Error () string { return "i/o timeout" }
53
- func (e * timeoutError ) Timeout () bool { return true }
54
- func (e * timeoutError ) Temporary () bool { return true }
55
-
56
23
type timeoutChan chan struct {}
57
24
58
25
var ioInitOnce sync.Once
@@ -71,21 +38,21 @@ type ioOperation struct {
71
38
}
72
39
73
40
func initIo () {
74
- h , err := createIoCompletionPort (windows .InvalidHandle , 0 , 0 , 0xffffffff )
41
+ h , err := windows . CreateIoCompletionPort (windows .InvalidHandle , 0 , 0 , 0 )
75
42
if err != nil {
76
43
panic (err )
77
44
}
78
45
ioCompletionPort = h
79
46
go ioCompletionProcessor (h )
80
47
}
81
48
82
- // win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
49
+ // file implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
83
50
// It takes ownership of this handle and will close it if it is garbage collected.
84
- type win32File struct {
51
+ type file struct {
85
52
handle windows.Handle
86
53
wg sync.WaitGroup
87
54
wgLock sync.RWMutex
88
- closing atomicBool
55
+ closing uint32 // used as atomic boolean
89
56
socket bool
90
57
readDeadline deadlineHandler
91
58
writeDeadline deadlineHandler
@@ -96,18 +63,18 @@ type deadlineHandler struct {
96
63
channel timeoutChan
97
64
channelLock sync.RWMutex
98
65
timer * time.Timer
99
- timedout atomicBool
66
+ timedout uint32 // used as atomic boolean
100
67
}
101
68
102
- // makeWin32File makes a new win32File from an existing file handle
103
- func makeWin32File (h windows.Handle ) (* win32File , error ) {
104
- f := & win32File {handle : h }
69
+ // makeFile makes a new file from an existing file handle
70
+ func makeFile (h windows.Handle ) (* file , error ) {
71
+ f := & file {handle : h }
105
72
ioInitOnce .Do (initIo )
106
- _ , err := createIoCompletionPort (h , ioCompletionPort , 0 , 0xffffffff )
73
+ _ , err := windows . CreateIoCompletionPort (h , ioCompletionPort , 0 , 0 )
107
74
if err != nil {
108
75
return nil , err
109
76
}
110
- err = setFileCompletionNotificationModes (h , cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS | cFILE_SKIP_SET_EVENT_ON_HANDLE )
77
+ err = windows . SetFileCompletionNotificationModes (h , windows . FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | windows . FILE_SKIP_SET_EVENT_ON_HANDLE )
111
78
if err != nil {
112
79
return nil , err
113
80
}
@@ -116,18 +83,14 @@ func makeWin32File(h windows.Handle) (*win32File, error) {
116
83
return f , nil
117
84
}
118
85
119
- func MakeOpenFile (h windows.Handle ) (io.ReadWriteCloser , error ) {
120
- return makeWin32File (h )
121
- }
122
-
123
86
// closeHandle closes the resources associated with a Win32 handle
124
- func (f * win32File ) closeHandle () {
87
+ func (f * file ) closeHandle () {
125
88
f .wgLock .Lock ()
126
89
// Atomically set that we are closing, releasing the resources only once.
127
- if ! f .closing . swap ( true ) {
90
+ if atomic . SwapUint32 ( & f .closing , 1 ) == 0 {
128
91
f .wgLock .Unlock ()
129
92
// cancel all IO and wait for it to complete
130
- cancelIoEx (f .handle , nil )
93
+ windows . CancelIoEx (f .handle , nil )
131
94
f .wg .Wait ()
132
95
// at this point, no new IO can start
133
96
windows .Close (f .handle )
@@ -137,19 +100,19 @@ func (f *win32File) closeHandle() {
137
100
}
138
101
}
139
102
140
- // Close closes a win32File .
141
- func (f * win32File ) Close () error {
103
+ // Close closes a file .
104
+ func (f * file ) Close () error {
142
105
f .closeHandle ()
143
106
return nil
144
107
}
145
108
146
109
// prepareIo prepares for a new IO operation.
147
110
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
148
- func (f * win32File ) prepareIo () (* ioOperation , error ) {
111
+ func (f * file ) prepareIo () (* ioOperation , error ) {
149
112
f .wgLock .RLock ()
150
- if f .closing . isSet () {
113
+ if atomic . LoadUint32 ( & f .closing ) == 1 {
151
114
f .wgLock .RUnlock ()
152
- return nil , ErrFileClosed
115
+ return nil , os . ErrClosed
153
116
}
154
117
f .wg .Add (1 )
155
118
f .wgLock .RUnlock ()
@@ -164,7 +127,7 @@ func ioCompletionProcessor(h windows.Handle) {
164
127
var bytes uint32
165
128
var key uintptr
166
129
var op * ioOperation
167
- err := getQueuedCompletionStatus (h , & bytes , & key , & op , windows .INFINITE )
130
+ err := windows . GetQueuedCompletionStatus (h , & bytes , & key , ( * * windows . Overlapped )( unsafe . Pointer ( & op )) , windows .INFINITE )
168
131
if op == nil {
169
132
panic (err )
170
133
}
@@ -174,13 +137,13 @@ func ioCompletionProcessor(h windows.Handle) {
174
137
175
138
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
176
139
// the operation has actually completed.
177
- func (f * win32File ) asyncIo (c * ioOperation , d * deadlineHandler , bytes uint32 , err error ) (int , error ) {
140
+ func (f * file ) asyncIo (c * ioOperation , d * deadlineHandler , bytes uint32 , err error ) (int , error ) {
178
141
if err != windows .ERROR_IO_PENDING {
179
142
return int (bytes ), err
180
143
}
181
144
182
- if f .closing . isSet () {
183
- cancelIoEx (f .handle , & c .o )
145
+ if atomic . LoadUint32 ( & f .closing ) == 1 {
146
+ windows . CancelIoEx (f .handle , & c .o )
184
147
}
185
148
186
149
var timeout timeoutChan
@@ -195,20 +158,20 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
195
158
case r = <- c .ch :
196
159
err = r .err
197
160
if err == windows .ERROR_OPERATION_ABORTED {
198
- if f .closing . isSet () {
199
- err = ErrFileClosed
161
+ if atomic . LoadUint32 ( & f .closing ) == 1 {
162
+ err = os . ErrClosed
200
163
}
201
164
} else if err != nil && f .socket {
202
165
// err is from Win32. Query the overlapped structure to get the winsock error.
203
166
var bytes , flags uint32
204
- err = wsaGetOverlappedResult (f .handle , & c .o , & bytes , false , & flags )
167
+ err = windows . WSAGetOverlappedResult (f .handle , & c .o , & bytes , false , & flags )
205
168
}
206
169
case <- timeout :
207
- cancelIoEx (f .handle , & c .o )
170
+ windows . CancelIoEx (f .handle , & c .o )
208
171
r = <- c .ch
209
172
err = r .err
210
173
if err == windows .ERROR_OPERATION_ABORTED {
211
- err = ErrTimeout
174
+ err = os . ErrDeadlineExceeded
212
175
}
213
176
}
214
177
@@ -220,15 +183,15 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
220
183
}
221
184
222
185
// Read reads from a file handle.
223
- func (f * win32File ) Read (b []byte ) (int , error ) {
186
+ func (f * file ) Read (b []byte ) (int , error ) {
224
187
c , err := f .prepareIo ()
225
188
if err != nil {
226
189
return 0 , err
227
190
}
228
191
defer f .wg .Done ()
229
192
230
- if f .readDeadline .timedout . isSet () {
231
- return 0 , ErrTimeout
193
+ if atomic . LoadUint32 ( & f .readDeadline .timedout ) == 1 {
194
+ return 0 , os . ErrDeadlineExceeded
232
195
}
233
196
234
197
var bytes uint32
@@ -247,15 +210,15 @@ func (f *win32File) Read(b []byte) (int, error) {
247
210
}
248
211
249
212
// Write writes to a file handle.
250
- func (f * win32File ) Write (b []byte ) (int , error ) {
213
+ func (f * file ) Write (b []byte ) (int , error ) {
251
214
c , err := f .prepareIo ()
252
215
if err != nil {
253
216
return 0 , err
254
217
}
255
218
defer f .wg .Done ()
256
219
257
- if f .writeDeadline .timedout . isSet () {
258
- return 0 , ErrTimeout
220
+ if atomic . LoadUint32 ( & f .writeDeadline .timedout ) == 1 {
221
+ return 0 , os . ErrDeadlineExceeded
259
222
}
260
223
261
224
var bytes uint32
@@ -265,19 +228,19 @@ func (f *win32File) Write(b []byte) (int, error) {
265
228
return n , err
266
229
}
267
230
268
- func (f * win32File ) SetReadDeadline (deadline time.Time ) error {
231
+ func (f * file ) SetReadDeadline (deadline time.Time ) error {
269
232
return f .readDeadline .set (deadline )
270
233
}
271
234
272
- func (f * win32File ) SetWriteDeadline (deadline time.Time ) error {
235
+ func (f * file ) SetWriteDeadline (deadline time.Time ) error {
273
236
return f .writeDeadline .set (deadline )
274
237
}
275
238
276
- func (f * win32File ) Flush () error {
239
+ func (f * file ) Flush () error {
277
240
return windows .FlushFileBuffers (f .handle )
278
241
}
279
242
280
- func (f * win32File ) Fd () uintptr {
243
+ func (f * file ) Fd () uintptr {
281
244
return uintptr (f .handle )
282
245
}
283
246
@@ -291,7 +254,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
291
254
}
292
255
d .timer = nil
293
256
}
294
- d .timedout . setFalse ( )
257
+ atomic . StoreUint32 ( & d .timedout , 0 )
295
258
296
259
select {
297
260
case <- d .channel :
@@ -306,7 +269,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
306
269
}
307
270
308
271
timeoutIO := func () {
309
- d .timedout . setTrue ( )
272
+ atomic . StoreUint32 ( & d .timedout , 1 )
310
273
close (d .channel )
311
274
}
312
275
0 commit comments