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 switch err := syscall.CancelIoEx(fd.Sysfd, nil); err {
88 case nil, syscall.ERROR_NOT_FOUND:
89 case windows.ERROR_INVALID_HANDLE:
90
91
92 default:
93 panic(err)
94 }
95 }
96 if !retry {
97 return
98 }
99
100
101
102
103 runtime.Gosched()
104 }
105 }
106
107
108
109 func runtime_startThread(func())
110
111
112
113
114 func runtime_threadHandle() syscall.Handle
115
116 type syncIORequest struct {
117 fd *FD
118 submit func(syscall.Handle, []byte, *uint32, *syscall.Overlapped) error
119 buf []byte
120 }
121
122 type syncIOResult struct {
123 qty uint32
124 err error
125 }
126
127 type syncIOWorker struct {
128 state *syncIOWorkerState
129 }
130
131
132
133 type syncIOWorkerState struct {
134 requests chan syncIORequest
135 results chan syncIOResult
136 thread syscall.Handle
137 }
138
139
140
141
142 var idleSyncIOWorkers sync.Pool
143
144 func acquireSyncIOWorker() *syncIOWorker {
145
146
147 inBubble := synctest.IsInBubble()
148 if !inBubble {
149 if worker := idleSyncIOWorkers.Get(); worker != nil {
150 return worker.(*syncIOWorker)
151 }
152 }
153 state := &syncIOWorkerState{
154 requests: make(chan syncIORequest),
155 results: make(chan syncIOResult),
156 }
157 runtime_startThread(state.run)
158 worker := &syncIOWorker{state: state}
159 if !inBubble {
160 runtime.AddCleanup(worker, (*syncIOWorkerState).stop, state)
161 }
162 return worker
163 }
164
165 func (worker *syncIOWorker) release() {
166 if synctest.IsInBubble() {
167 worker.state.stop()
168 } else {
169 idleSyncIOWorkers.Put(worker)
170 }
171 runtime.KeepAlive(worker)
172 }
173
174 func (worker *syncIOWorkerState) stop() {
175 close(worker.requests)
176 }
177
178 func (worker *syncIOWorkerState) run() {
179 worker.thread = runtime_threadHandle()
180
181
182 var result syncIOResult
183 for request := range worker.requests {
184 slot := request.fd.ioCancel.begin(worker)
185 if request.fd.closing() {
186 result.err = errClosing(request.fd.isFile)
187 } else {
188 result.err = request.submit(request.fd.Sysfd, request.buf, &result.qty, nil)
189 }
190
191
192 request.fd.ioCancel.end(slot)
193
194 request = syncIORequest{}
195 worker.results <- result
196 result = syncIOResult{}
197 }
198 }
199
200
201
202
203 func (fd *FD) execSyncIO(submit func(syscall.Handle, []byte, *uint32, *syscall.Overlapped) error, buf []byte) (uint32, error) {
204 worker := acquireSyncIOWorker()
205 worker.state.requests <- syncIORequest{fd, submit, buf}
206 result := <-worker.state.results
207 worker.release()
208 return result.qty, result.err
209 }
210
View as plain text