1- // +build windows
2-
31/* SPDX-License-Identifier: MIT
42 *
53 * Copyright (C) 2005 Microsoft
64 * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
75 */
6+
7+ // +build windows
8+
89package winpipe
910
1011import (
11- "errors"
1212 "io"
13+ "os"
1314 "runtime"
1415 "sync"
1516 "sync/atomic"
1617 "time"
18+ "unsafe"
1719
1820 "golang.org/x/sys/windows"
1921)
2022
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-
5623type timeoutChan chan struct {}
5724
5825var ioInitOnce sync.Once
@@ -71,21 +38,21 @@ type ioOperation struct {
7138}
7239
7340func initIo () {
74- h , err := createIoCompletionPort (windows .InvalidHandle , 0 , 0 , 0xffffffff )
41+ h , err := windows . CreateIoCompletionPort (windows .InvalidHandle , 0 , 0 , 0 )
7542 if err != nil {
7643 panic (err )
7744 }
7845 ioCompletionPort = h
7946 go ioCompletionProcessor (h )
8047}
8148
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.
8350// It takes ownership of this handle and will close it if it is garbage collected.
84- type win32File struct {
51+ type file struct {
8552 handle windows.Handle
8653 wg sync.WaitGroup
8754 wgLock sync.RWMutex
88- closing atomicBool
55+ closing uint32 // used as atomic boolean
8956 socket bool
9057 readDeadline deadlineHandler
9158 writeDeadline deadlineHandler
@@ -96,18 +63,18 @@ type deadlineHandler struct {
9663 channel timeoutChan
9764 channelLock sync.RWMutex
9865 timer * time.Timer
99- timedout atomicBool
66+ timedout uint32 // used as atomic boolean
10067}
10168
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 }
10572 ioInitOnce .Do (initIo )
106- _ , err := createIoCompletionPort (h , ioCompletionPort , 0 , 0xffffffff )
73+ _ , err := windows . CreateIoCompletionPort (h , ioCompletionPort , 0 , 0 )
10774 if err != nil {
10875 return nil , err
10976 }
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 )
11178 if err != nil {
11279 return nil , err
11380 }
@@ -116,18 +83,14 @@ func makeWin32File(h windows.Handle) (*win32File, error) {
11683 return f , nil
11784}
11885
119- func MakeOpenFile (h windows.Handle ) (io.ReadWriteCloser , error ) {
120- return makeWin32File (h )
121- }
122-
12386// closeHandle closes the resources associated with a Win32 handle
124- func (f * win32File ) closeHandle () {
87+ func (f * file ) closeHandle () {
12588 f .wgLock .Lock ()
12689 // 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 {
12891 f .wgLock .Unlock ()
12992 // cancel all IO and wait for it to complete
130- cancelIoEx (f .handle , nil )
93+ windows . CancelIoEx (f .handle , nil )
13194 f .wg .Wait ()
13295 // at this point, no new IO can start
13396 windows .Close (f .handle )
@@ -137,19 +100,19 @@ func (f *win32File) closeHandle() {
137100 }
138101}
139102
140- // Close closes a win32File .
141- func (f * win32File ) Close () error {
103+ // Close closes a file .
104+ func (f * file ) Close () error {
142105 f .closeHandle ()
143106 return nil
144107}
145108
146109// prepareIo prepares for a new IO operation.
147110// 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 ) {
149112 f .wgLock .RLock ()
150- if f .closing . isSet () {
113+ if atomic . LoadUint32 ( & f .closing ) == 1 {
151114 f .wgLock .RUnlock ()
152- return nil , ErrFileClosed
115+ return nil , os . ErrClosed
153116 }
154117 f .wg .Add (1 )
155118 f .wgLock .RUnlock ()
@@ -164,7 +127,7 @@ func ioCompletionProcessor(h windows.Handle) {
164127 var bytes uint32
165128 var key uintptr
166129 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 )
168131 if op == nil {
169132 panic (err )
170133 }
@@ -174,13 +137,13 @@ func ioCompletionProcessor(h windows.Handle) {
174137
175138// asyncIo processes the return value from ReadFile or WriteFile, blocking until
176139// 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 ) {
178141 if err != windows .ERROR_IO_PENDING {
179142 return int (bytes ), err
180143 }
181144
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 )
184147 }
185148
186149 var timeout timeoutChan
@@ -195,20 +158,20 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
195158 case r = <- c .ch :
196159 err = r .err
197160 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
200163 }
201164 } else if err != nil && f .socket {
202165 // err is from Win32. Query the overlapped structure to get the winsock error.
203166 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 )
205168 }
206169 case <- timeout :
207- cancelIoEx (f .handle , & c .o )
170+ windows . CancelIoEx (f .handle , & c .o )
208171 r = <- c .ch
209172 err = r .err
210173 if err == windows .ERROR_OPERATION_ABORTED {
211- err = ErrTimeout
174+ err = os . ErrDeadlineExceeded
212175 }
213176 }
214177
@@ -220,15 +183,15 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
220183}
221184
222185// Read reads from a file handle.
223- func (f * win32File ) Read (b []byte ) (int , error ) {
186+ func (f * file ) Read (b []byte ) (int , error ) {
224187 c , err := f .prepareIo ()
225188 if err != nil {
226189 return 0 , err
227190 }
228191 defer f .wg .Done ()
229192
230- if f .readDeadline .timedout . isSet () {
231- return 0 , ErrTimeout
193+ if atomic . LoadUint32 ( & f .readDeadline .timedout ) == 1 {
194+ return 0 , os . ErrDeadlineExceeded
232195 }
233196
234197 var bytes uint32
@@ -247,15 +210,15 @@ func (f *win32File) Read(b []byte) (int, error) {
247210}
248211
249212// Write writes to a file handle.
250- func (f * win32File ) Write (b []byte ) (int , error ) {
213+ func (f * file ) Write (b []byte ) (int , error ) {
251214 c , err := f .prepareIo ()
252215 if err != nil {
253216 return 0 , err
254217 }
255218 defer f .wg .Done ()
256219
257- if f .writeDeadline .timedout . isSet () {
258- return 0 , ErrTimeout
220+ if atomic . LoadUint32 ( & f .writeDeadline .timedout ) == 1 {
221+ return 0 , os . ErrDeadlineExceeded
259222 }
260223
261224 var bytes uint32
@@ -265,19 +228,19 @@ func (f *win32File) Write(b []byte) (int, error) {
265228 return n , err
266229}
267230
268- func (f * win32File ) SetReadDeadline (deadline time.Time ) error {
231+ func (f * file ) SetReadDeadline (deadline time.Time ) error {
269232 return f .readDeadline .set (deadline )
270233}
271234
272- func (f * win32File ) SetWriteDeadline (deadline time.Time ) error {
235+ func (f * file ) SetWriteDeadline (deadline time.Time ) error {
273236 return f .writeDeadline .set (deadline )
274237}
275238
276- func (f * win32File ) Flush () error {
239+ func (f * file ) Flush () error {
277240 return windows .FlushFileBuffers (f .handle )
278241}
279242
280- func (f * win32File ) Fd () uintptr {
243+ func (f * file ) Fd () uintptr {
281244 return uintptr (f .handle )
282245}
283246
@@ -291,7 +254,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
291254 }
292255 d .timer = nil
293256 }
294- d .timedout . setFalse ( )
257+ atomic . StoreUint32 ( & d .timedout , 0 )
295258
296259 select {
297260 case <- d .channel :
@@ -306,7 +269,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
306269 }
307270
308271 timeoutIO := func () {
309- d .timedout . setTrue ( )
272+ atomic . StoreUint32 ( & d .timedout , 1 )
310273 close (d .channel )
311274 }
312275
0 commit comments