Source file
src/internal/poll/fd_sync_windows.go
1
2
3
4
5 package poll
6
7 import (
8 "internal/synctest"
9 "internal/syscall/windows"
10 "runtime"
11 "sync"
12 "sync/atomic"
13 "syscall"
14 )
15
16
17
18
19 type ioCancelState struct {
20 canceling atomic.Bool
21 cancelDone sync.WaitGroup
22
23 workers [2]atomic.Pointer[syncIOWorkerState]
24 }
25
26
27
28 func (s *ioCancelState) begin(worker *syncIOWorkerState) int {
29 for i := range s.workers {
30 if s.workers[i].CompareAndSwap(nil, worker) {
31 return i
32 }
33 }
34
35 panic("too many synchronous I/O workers")
36 }
37
38 func (s *ioCancelState) end(slot int) {
39
40 s.workers[slot].Store(nil)
41 if s.canceling.Load() {
42 s.cancelDone.Wait()
43 }
44 }
45
46
47
48
49
50 func (fd *FD) cancelIO() {
51 s := &fd.ioCancel
52
53
54
55 s.cancelDone.Add(1)
56 s.canceling.Store(true)
57 defer s.cancelDone.Done()
58
59
60
61 var canceled [2]bool
62 first := true
63 for {
64 var retry bool
65 for i := range s.workers {
66 worker := s.workers[i].Load()
67 if worker == nil || canceled[i] {
68 continue
69 }
70 switch err := windows.CancelSynchronousIo(worker.thread); err {
71 case nil:
72
73
74 canceled[i] = true
75 case syscall.ERROR_NOT_FOUND:
76
77 retry = true
78 default:
79 panic(err)
80 }
81 }
82 if first {
83 first = false
84
85
86
87 if err := syscall.CancelIoEx(fd.Sysfd, nil); err != nil && err != syscall.ERROR_NOT_FOUND {
88 panic(err)
89 }
90 }
91 if !retry {
92 return
93 }
94
95
96
97
98 runtime.Gosched()
99 }
100 }
101
102
103
104 func runtime_startThread(func())
105
106
107
108
109 func runtime_threadHandle() syscall.Handle
110
111 type syncIORequest struct {
112 fd *FD
113 submit func(syscall.Handle, []byte, *uint32, *syscall.Overlapped) error
114 buf []byte
115 }
116
117 type syncIOResult struct {
118 qty uint32
119 err error
120 }
121
122 type syncIOWorker struct {
123 state *syncIOWorkerState
124 }
125
126
127
128 type syncIOWorkerState struct {
129 requests chan syncIORequest
130 results chan syncIOResult
131 thread syscall.Handle
132 }
133
134
135
136
137 var idleSyncIOWorkers sync.Pool
138
139 func acquireSyncIOWorker() *syncIOWorker {
140
141
142 inBubble := synctest.IsInBubble()
143 if !inBubble {
144 if worker := idleSyncIOWorkers.Get(); worker != nil {
145 return worker.(*syncIOWorker)
146 }
147 }
148 state := &syncIOWorkerState{
149 requests: make(chan syncIORequest),
150 results: make(chan syncIOResult),
151 }
152 runtime_startThread(state.run)
153 worker := &syncIOWorker{state: state}
154 if !inBubble {
155 runtime.AddCleanup(worker, (*syncIOWorkerState).stop, state)
156 }
157 return worker
158 }
159
160 func (worker *syncIOWorker) release() {
161 if synctest.IsInBubble() {
162 worker.state.stop()
163 } else {
164 idleSyncIOWorkers.Put(worker)
165 }
166 runtime.KeepAlive(worker)
167 }
168
169 func (worker *syncIOWorkerState) stop() {
170 close(worker.requests)
171 }
172
173 func (worker *syncIOWorkerState) run() {
174 worker.thread = runtime_threadHandle()
175
176
177 var result syncIOResult
178 for request := range worker.requests {
179 slot := request.fd.ioCancel.begin(worker)
180 if request.fd.closing() {
181 result.err = errClosing(request.fd.isFile)
182 } else {
183 result.err = request.submit(request.fd.Sysfd, request.buf, &result.qty, nil)
184 }
185
186
187 request.fd.ioCancel.end(slot)
188
189 request = syncIORequest{}
190 worker.results <- result
191 result = syncIOResult{}
192 }
193 }
194
195
196
197
198 func (fd *FD) execSyncIO(submit func(syscall.Handle, []byte, *uint32, *syscall.Overlapped) error, buf []byte) (uint32, error) {
199 worker := acquireSyncIOWorker()
200 worker.state.requests <- syncIORequest{fd, submit, buf}
201 result := <-worker.state.results
202 worker.release()
203 return result.qty, result.err
204 }
205
View as plain text