16
16
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
17
17
│ PERFORMANCE OF THIS SOFTWARE. │
18
18
╚─────────────────────────────────────────────────────────────────────────────*/
19
+ #include "libc/assert.h"
19
20
#include "libc/calls/createfileflags.internal.h"
20
21
#include "libc/calls/internal.h"
21
22
#include "libc/calls/sig.internal.h"
22
23
#include "libc/calls/struct/sigset.h"
23
24
#include "libc/calls/syscall_support-nt.internal.h"
25
+ #include "libc/errno.h"
24
26
#include "libc/intrin/fds.h"
25
27
#include "libc/intrin/weaken.h"
26
28
#include "libc/nt/enum/filetype.h"
27
29
#include "libc/nt/errors.h"
28
30
#include "libc/nt/events.h"
29
31
#include "libc/nt/files.h"
32
+ #include "libc/nt/process.h"
30
33
#include "libc/nt/runtime.h"
31
34
#include "libc/nt/struct/overlapped.h"
32
35
#include "libc/nt/synchronization.h"
33
36
#include "libc/nt/thread.h"
37
+ #include "libc/sock/internal.h"
34
38
#include "libc/stdio/sysparam.h"
35
39
#include "libc/sysv/consts/sicode.h"
36
40
#include "libc/sysv/errfuns.h"
@@ -47,8 +51,6 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
47
51
int64_t handle , sigset_t waitmask ,
48
52
bool32 ReadOrWriteFile (int64_t , void * , uint32_t , uint32_t * ,
49
53
struct NtOverlapped * )) {
50
- int sig ;
51
- uint32_t exchanged ;
52
54
struct Fd * f = g_fds .p + fd ;
53
55
54
56
// pread() and pwrite() perform an implicit lseek() operation, so
@@ -60,105 +62,153 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
60
62
if (pwriting && !seekable )
61
63
return espipe ();
62
64
63
- // determine if we need to lock a file descriptor across processes
65
+ // determine if we need to lock file descriptor
64
66
bool locked = isdisk && !pwriting && f -> cursor ;
65
- if (locked )
66
- __cursor_lock (f -> cursor );
67
-
68
- RestartOperation :
69
- // when a file is opened in overlapped mode win32 requires that we
70
- // take over full responsibility for managing our own file pointer
71
- // which is fine, because the one win32 has was never very good in
72
- // the sense that it behaves so differently from linux, that using
73
- // win32 i/o required more compatibilty toil than doing it by hand
74
- if (!pwriting ) {
75
- if (seekable && f -> cursor ) {
76
- offset = f -> cursor -> shared -> pointer ;
77
- } else {
78
- offset = 0 ;
79
- }
80
- }
81
67
82
- bool eagained = false;
83
- // check for signals and cancelation
84
- if (_check_cancel () == -1 ) {
68
+ for (;;) {
69
+ int got_sig = 0 ;
70
+ bool got_eagain = false;
71
+ uint32_t other_error = 0 ;
72
+
73
+ // create event handle for overlapped i/o
74
+ intptr_t event ;
75
+ if (!(event = CreateEvent (0 , 1 , 0 , 0 )))
76
+ return __winerr ();
77
+
78
+ // ensure iops are ordered across threads and processes if seeking
85
79
if (locked )
86
- __cursor_unlock (f -> cursor );
87
- return -1 ; // ECANCELED
88
- }
89
- if (_weaken (__sig_get ) && (sig = _weaken (__sig_get )(waitmask )))
90
- goto HandleInterrupt ;
91
-
92
- // signals have already been fully blocked by caller
93
- // perform i/o operation with atomic signal/cancel checking
94
- struct NtOverlapped overlap = {.hEvent = CreateEvent (0 , 1 , 0 , 0 ),
95
- .Pointer = offset };
96
- bool32 ok = ReadOrWriteFile (handle , data , size , 0 , & overlap );
97
- if (!ok && GetLastError () == kNtErrorIoPending ) {
98
- // win32 says this i/o operation needs to block
99
- if (f -> flags & _O_NONBLOCK ) {
100
- // abort the i/o operation if file descriptor is in non-blocking mode
101
- CancelIoEx (handle , & overlap );
102
- eagained = true;
103
- } else {
104
- // wait until i/o either completes or is canceled by another thread
105
- // we avoid a race condition by having a second mask for unblocking
106
- struct PosixThread * pt ;
107
- pt = _pthread_self ();
108
- pt -> pt_blkmask = waitmask ;
109
- pt -> pt_iohandle = handle ;
110
- pt -> pt_ioverlap = & overlap ;
111
- atomic_store_explicit (& pt -> pt_blocker , PT_BLOCKER_IO ,
112
- memory_order_release );
113
- WaitForSingleObject (overlap .hEvent , -1u );
114
- atomic_store_explicit (& pt -> pt_blocker , 0 , memory_order_release );
80
+ __cursor_lock (f -> cursor );
81
+
82
+ // when a file is opened in overlapped mode win32 requires that we
83
+ // take over full responsibility for managing our own file pointer
84
+ // which is fine, because the one win32 has was never very good in
85
+ // the sense that it behaves so differently from linux, that using
86
+ // win32 i/o required more compatibilty toil than doing it by hand
87
+ if (!pwriting ) {
88
+ if (seekable && f -> cursor ) {
89
+ offset = f -> cursor -> shared -> pointer ;
90
+ } else {
91
+ offset = 0 ;
92
+ }
115
93
}
116
- ok = true;
117
- }
118
- if (ok )
119
- ok = GetOverlappedResult (handle , & overlap , & exchanged , true);
120
- CloseHandle (overlap .hEvent );
121
-
122
- // if i/o succeeded then return its result
123
- if (ok ) {
124
- if (!pwriting && seekable && f -> cursor )
125
- f -> cursor -> shared -> pointer = offset + exchanged ;
126
- if (locked )
127
- __cursor_unlock (f -> cursor );
128
- return exchanged ;
129
- }
130
94
131
- // only raise EINTR or EAGAIN if I/O got canceled
132
- if (GetLastError () == kNtErrorOperationAborted ) {
133
- // raise EAGAIN if it's due to O_NONBLOCK mmode
134
- if (eagained ) {
135
- if (locked )
136
- __cursor_unlock (f -> cursor );
137
- return eagain ();
95
+ // initiate asynchronous i/o operation with win32
96
+ struct NtOverlapped overlap = {.hEvent = event , .Pointer = offset };
97
+ bool32 ok = ReadOrWriteFile (handle , data , size , 0 , & overlap );
98
+ if (!ok && GetLastError () == kNtErrorIoPending ) {
99
+ if (f -> flags & _O_NONBLOCK ) {
100
+ // immediately back out of blocking i/o if non-blocking
101
+ CancelIoEx (handle , & overlap );
102
+ got_eagain = true;
103
+ } else {
104
+ // atomic block on i/o completion, signal, or cancel
105
+ // it's not safe to acknowledge cancelation from here
106
+ // it's not safe to call any signal handlers from here
107
+ intptr_t sem ;
108
+ if ((sem = CreateSemaphore (0 , 0 , 1 , 0 ))) {
109
+ // installing semaphore before sig get makes wait atomic
110
+ struct PosixThread * pt = _pthread_self ();
111
+ pt -> pt_semaphore = sem ;
112
+ pt -> pt_blkmask = waitmask ;
113
+ atomic_store_explicit (& pt -> pt_blocker , PT_BLOCKER_SEM ,
114
+ memory_order_release );
115
+ if (_is_canceled ()) {
116
+ CancelIoEx (handle , & overlap );
117
+ } else if (_weaken (__sig_get ) &&
118
+ (got_sig = _weaken (__sig_get )(waitmask ))) {
119
+ CancelIoEx (handle , & overlap );
120
+ } else {
121
+ intptr_t hands [] = {event , sem };
122
+ uint32_t wi = WaitForMultipleObjects (2 , hands , 0 , -1u );
123
+ if (wi == 1 ) { // semaphore was signaled by signal enqueue
124
+ CancelIoEx (handle , & overlap );
125
+ if (_weaken (__sig_get ))
126
+ got_sig = _weaken (__sig_get )(waitmask );
127
+ } else if (wi == -1u ) {
128
+ other_error = GetLastError ();
129
+ CancelIoEx (handle , & overlap );
130
+ }
131
+ }
132
+ atomic_store_explicit (& pt -> pt_blocker , 0 , memory_order_release );
133
+ CloseHandle (sem );
134
+ } else {
135
+ other_error = GetLastError ();
136
+ CancelIoEx (handle , & overlap );
137
+ }
138
+ }
139
+ ok = true;
138
140
}
139
- // otherwise it must be due to a kill() via __sig_cancel()
140
- if (_weaken (__sig_relay ) && (sig = _weaken (__sig_get )(waitmask ))) {
141
- HandleInterrupt :
141
+ uint32_t exchanged = 0 ;
142
+ if (ok )
143
+ ok = GetOverlappedResult (handle , & overlap , & exchanged , true);
144
+ uint32_t io_error = GetLastError ();
145
+ CloseHandle (event );
146
+
147
+ // check if i/o completed
148
+ // this could forseeably happen even if CancelIoEx was called
149
+ if (ok ) {
150
+ if (!pwriting && seekable && f -> cursor )
151
+ f -> cursor -> shared -> pointer = offset + exchanged ;
142
152
if (locked )
143
153
__cursor_unlock (f -> cursor );
144
- int handler_was_called = _weaken (__sig_relay )(sig , SI_KERNEL , waitmask );
145
- if (_check_cancel () == -1 )
146
- return -1 ; // possible if we SIGTHR'd
147
- if (locked )
148
- __cursor_lock (f -> cursor );
149
- // read() is @restartable unless non-SA_RESTART hands were called
150
- if (!(handler_was_called & SIG_HANDLED_NO_RESTART ))
151
- goto RestartOperation ;
154
+ if (got_sig ) // swallow dequeued signal
155
+ _weaken (__sig_relay )(got_sig , SI_KERNEL , waitmask );
156
+ return exchanged ;
152
157
}
158
+
159
+ // it's now safe to unlock cursor
153
160
if (locked )
154
161
__cursor_unlock (f -> cursor );
155
- return eintr ();
156
- }
157
162
158
- // read() and write() have generally different error-handling paths
159
- if (locked )
160
- __cursor_unlock (f -> cursor );
161
- return -2 ;
163
+ // check if i/o failed
164
+ if (io_error != kNtErrorOperationAborted ) {
165
+ if (got_sig ) // swallow dequeued signal
166
+ _weaken (__sig_relay )(got_sig , SI_KERNEL , waitmask );
167
+ // read() and write() have different error paths
168
+ SetLastError (io_error );
169
+ return -2 ;
170
+ }
171
+
172
+ // the i/o operation was successfully canceled
173
+ if (got_eagain ) {
174
+ unassert (!got_sig );
175
+ return eagain ();
176
+ }
177
+
178
+ // it's now reasonable to report semaphore creation error
179
+ if (other_error ) {
180
+ unassert (!got_sig );
181
+ errno = __dos2errno (other_error );
182
+ return -1 ;
183
+ }
184
+
185
+ // check for thread cancelation and acknowledge
186
+ if (_check_cancel () == -1 )
187
+ return -1 ;
188
+
189
+ // if signal module has been linked, then
190
+ if (_weaken (__sig_get )) {
191
+
192
+ // gobble up all unmasked pending signals
193
+ // it's now safe to recurse into signal handlers
194
+ int handler_was_called = 0 ;
195
+ do {
196
+ if (got_sig )
197
+ handler_was_called |=
198
+ _weaken (__sig_relay )(got_sig , SI_KERNEL , waitmask );
199
+ } while ((got_sig = _weaken (__sig_get )(waitmask )));
200
+
201
+ // check if SIGTHR handler was called
202
+ if (_check_cancel () == -1 )
203
+ return -1 ;
204
+
205
+ // check if signal handler without SA_RESTART was called
206
+ if (handler_was_called & SIG_HANDLED_NO_RESTART )
207
+ return eintr ();
208
+ }
209
+
210
+ // otherwise try the i/o operation again
211
+ }
162
212
}
163
213
164
214
#endif /* __x86_64__ */
0 commit comments