Skip to content

Commit fa71faf

Browse files
authored
Merge pull request #4954 from readdle/win-fd-set
[Windows] Use `fd_set` according to Winsock2 specifics
2 parents 9c020a9 + d3871a5 commit fa71faf

File tree

2 files changed

+118
-6
lines changed

2 files changed

+118
-6
lines changed

CoreFoundation/RunLoop.subproj/CFSocket.c

+74-6
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,30 @@ CF_INLINE int __CFSocketLastError(void) {
213213
}
214214

215215
CF_INLINE CFIndex __CFSocketFdGetSize(CFDataRef fdSet) {
216+
#if TARGET_OS_WIN32
217+
if (CFDataGetLength(fdSet) == 0) {
218+
return 0;
219+
}
220+
return FD_SETSIZE;
221+
#else
216222
return NBBY * CFDataGetLength(fdSet);
223+
#endif
217224
}
218225

219226
CF_INLINE Boolean __CFSocketFdSet(CFSocketNativeHandle sock, CFMutableDataRef fdSet) {
220227
/* returns true if a change occurred, false otherwise */
221228
Boolean retval = false;
222229
if (INVALID_SOCKET != sock && 0 <= sock) {
230+
fd_set *fds;
231+
#if TARGET_OS_WIN32
232+
if (CFDataGetLength(fdSet) == 0) {
233+
CFDataIncreaseLength(fdSet, sizeof(fd_set));
234+
fds = (fd_set *)CFDataGetMutableBytePtr(fdSet);
235+
FD_ZERO(fds);
236+
} else {
237+
fds = (fd_set *)CFDataGetMutableBytePtr(fdSet);
238+
}
239+
#else
223240
CFIndex numFds = NBBY * CFDataGetLength(fdSet);
224241
fd_mask *fds_bits;
225242
if (sock >= numFds) {
@@ -230,9 +247,11 @@ CF_INLINE Boolean __CFSocketFdSet(CFSocketNativeHandle sock, CFMutableDataRef fd
230247
} else {
231248
fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet);
232249
}
233-
if (!FD_ISSET(sock, (fd_set *)fds_bits)) {
250+
fds = (fd_set *)fds_bits;
251+
#endif
252+
if (!FD_ISSET(sock, fds)) {
234253
retval = true;
235-
FD_SET(sock, (fd_set *)fds_bits);
254+
FD_SET(sock, fds);
236255
}
237256
}
238257
return retval;
@@ -416,6 +435,15 @@ CF_INLINE Boolean __CFSocketFdClr(CFSocketNativeHandle sock, CFMutableDataRef fd
416435
/* returns true if a change occurred, false otherwise */
417436
Boolean retval = false;
418437
if (INVALID_SOCKET != sock && 0 <= sock) {
438+
#if TARGET_OS_WIN32
439+
if (CFDataGetLength(fdSet) > 0) {
440+
fd_set *fds = (fd_set *)CFDataGetMutableBytePtr(fdSet);
441+
if (FD_ISSET(sock, fds)) {
442+
retval = true;
443+
FD_CLR(sock, fds);
444+
}
445+
}
446+
#else
419447
CFIndex numFds = NBBY * CFDataGetLength(fdSet);
420448
fd_mask *fds_bits;
421449
if (sock < numFds) {
@@ -425,6 +453,7 @@ CF_INLINE Boolean __CFSocketFdClr(CFSocketNativeHandle sock, CFMutableDataRef fd
425453
FD_CLR(sock, (fd_set *)fds_bits);
426454
}
427455
}
456+
#endif
428457
}
429458
return retval;
430459
}
@@ -1188,6 +1217,27 @@ static void
11881217
clearInvalidFileDescriptors(CFMutableDataRef d)
11891218
{
11901219
if (d) {
1220+
#if TARGET_OS_WIN32
1221+
if (CFDataGetLength(d) == 0) {
1222+
return;
1223+
}
1224+
1225+
fd_set *fds = (fd_set *)CFDataGetMutableBytePtr(d);
1226+
fd_set invalidFds;
1227+
FD_ZERO(&invalidFds);
1228+
// Gather all invalid sockets into invalidFds set
1229+
for (u_int idx = 0; idx < fds->fd_count; idx++) {
1230+
SOCKET socket = fds->fd_array[idx];
1231+
if (! __CFNativeSocketIsValid(socket)) {
1232+
FD_SET(socket, &invalidFds);
1233+
}
1234+
}
1235+
// Remove invalid sockets from source set
1236+
for (u_int idx = 0; idx < invalidFds.fd_count; idx++) {
1237+
SOCKET socket = invalidFds.fd_array[idx];
1238+
FD_CLR(socket, fds);
1239+
}
1240+
#else
11911241
SInt32 count = __CFSocketFdGetSize(d);
11921242
fd_set* s = (fd_set*) CFDataGetMutableBytePtr(d);
11931243
for (SInt32 idx = 0; idx < count; idx++) {
@@ -1196,14 +1246,13 @@ clearInvalidFileDescriptors(CFMutableDataRef d)
11961246
FD_CLR(idx, s);
11971247
}
11981248
}
1249+
#endif
11991250
}
12001251
}
12011252

12021253
static void
1203-
manageSelectError()
1254+
manageSelectError(SInt32 selectError)
12041255
{
1205-
SInt32 selectError = __CFSocketLastError();
1206-
12071256
__CFSOCKETLOG("socket manager received error %ld from select", (long)selectError);
12081257

12091258
if (EBADF == selectError) {
@@ -1263,8 +1312,15 @@ static void *__CFSocketManager(void * arg)
12631312
SInt32 nrfds, maxnrfds, fdentries = 1;
12641313
SInt32 rfds, wfds;
12651314
fd_set *exceptfds = NULL;
1315+
#if TARGET_OS_WIN32
1316+
fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(fd_set), 0);
1317+
fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(fd_set), 0);
1318+
FD_ZERO(writefds);
1319+
FD_ZERO(readfds);
1320+
#else
12661321
fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0);
12671322
fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0);
1323+
#endif
12681324
fd_set *tempfds;
12691325
SInt32 idx, cnt;
12701326
uint8_t buffer[256];
@@ -1290,6 +1346,11 @@ static void *__CFSocketManager(void * arg)
12901346
free(readBuffer);
12911347
free(writeBuffer);
12921348
#endif
1349+
1350+
#if TARGET_OS_WIN32
1351+
// This parameter is ignored by `select` from Winsock2 API
1352+
maxnrfds = INT_MAX;
1353+
#else
12931354
rfds = __CFSocketFdGetSize(__CFReadSocketsFds);
12941355
wfds = __CFSocketFdGetSize(__CFWriteSocketsFds);
12951356
maxnrfds = __CFMax(rfds, wfds);
@@ -1300,6 +1361,7 @@ static void *__CFSocketManager(void * arg)
13001361
}
13011362
memset(writefds, 0, fdentries * sizeof(fd_mask));
13021363
memset(readfds, 0, fdentries * sizeof(fd_mask));
1364+
#endif
13031365
CFDataGetBytes(__CFWriteSocketsFds, CFRangeMake(0, CFDataGetLength(__CFWriteSocketsFds)), (UInt8 *)writefds);
13041366
CFDataGetBytes(__CFReadSocketsFds, CFRangeMake(0, CFDataGetLength(__CFReadSocketsFds)), (UInt8 *)readfds);
13051367

@@ -1345,7 +1407,13 @@ static void *__CFSocketManager(void * arg)
13451407
}
13461408
#endif
13471409

1410+
SInt32 error = 0;
13481411
nrfds = select(maxnrfds, readfds, writefds, exceptfds, pTimeout);
1412+
if (nrfds < 0) {
1413+
// Store error as early as possible, as the code below could
1414+
// reset it and make late check unreliable.
1415+
error = __CFSocketLastError();
1416+
}
13491417

13501418
#if defined(LOG_CFSOCKET) && defined(DEBUG_POLLING_SELECT)
13511419
__CFSOCKETLOG("socket manager woke from select, ret=%ld", (long)nrfds);
@@ -1434,7 +1502,7 @@ static void *__CFSocketManager(void * arg)
14341502
}
14351503

14361504
if (0 > nrfds) {
1437-
manageSelectError();
1505+
manageSelectError(error);
14381506
continue;
14391507
}
14401508
if (FD_ISSET(__CFWakeupSocketPair[1], readfds)) {

Tests/Foundation/Tests/TestSocketPort.swift

+44
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,55 @@ class TestSocketPort : XCTestCase {
106106
}
107107
}
108108

109+
func testSendingMultipleMessagesRemoteToLocal() throws {
110+
var localPorts = [SocketPort]()
111+
var remotePorts = [SocketPort]()
112+
var delegates = [TestPortDelegateWithBlock]()
113+
114+
let data = Data("I cannot weave".utf8)
115+
116+
for _ in 0..<128 {
117+
let local = try XCTUnwrap(SocketPort(tcpPort: 0))
118+
let tcpPort = try UInt16(XCTUnwrap(tcpOrUdpPort(of: local)))
119+
let remote = try XCTUnwrap(SocketPort(remoteWithTCPPort: tcpPort, host: "localhost"))
120+
121+
let received = expectation(description: "Message received")
122+
123+
let localDelegate = TestPortDelegateWithBlock { message in
124+
XCTAssertEqual(message.components as? [AnyHashable], [data as NSData])
125+
received.fulfill()
126+
}
127+
128+
localPorts.append(local)
129+
remotePorts.append(remote)
130+
delegates.append(localDelegate)
131+
132+
local.setDelegate(localDelegate)
133+
local.schedule(in: .main, forMode: .default)
134+
remote.schedule(in: .main, forMode: .default)
135+
}
136+
137+
withExtendedLifetime(delegates) {
138+
for remote in remotePorts {
139+
let sent = remote.send(before: Date(timeIntervalSinceNow: 5), components: NSMutableArray(array: [data]), from: nil, reserved: 0)
140+
XCTAssertTrue(sent)
141+
}
142+
waitForExpectations(timeout: 5.0)
143+
}
144+
145+
for port in localPorts + remotePorts {
146+
port.setDelegate(nil)
147+
port.remove(from: .main, forMode: .default)
148+
port.invalidate()
149+
}
150+
}
151+
109152
static var allTests: [(String, (TestSocketPort) -> () throws -> Void)] {
110153
return [
111154
("testRemoteSocketPortsAreUniqued", testRemoteSocketPortsAreUniqued),
112155
("testInitPicksATCPPort", testInitPicksATCPPort),
113156
("testSendingOneMessageRemoteToLocal", testSendingOneMessageRemoteToLocal),
157+
("testSendingMultipleMessagesRemoteToLocal", testSendingMultipleMessagesRemoteToLocal),
114158
]
115159
}
116160
}

0 commit comments

Comments
 (0)