| ... | ... | @@ -1261,7 +1261,7 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA { |
| 1261 | 1261 | .WSASYSNOTREADY => return error.SystemNotAvailable, |
| 1262 | 1262 | .WSAVERNOTSUPPORTED => return error.VersionNotSupported, |
| 1263 | 1263 | .WSAEINPROGRESS => return error.BlockingOperationInProgress, |
| 1264 | | .WSAEPROCLIM => return error.SystemResources, |
| 1264 | .WSAEPROCLIM => return error.ProcessFdQuotaExceeded, |
| 1265 | 1265 | else => |err| return unexpectedWSAError(err), |
| 1266 | 1266 | }, |
| 1267 | 1267 | }; |
| ... | ... | @@ -1280,6 +1280,30 @@ pub fn WSACleanup() !void { |
| 1280 | 1280 | }; |
| 1281 | 1281 | } |
| 1282 | 1282 | |
| 1283 | var wsa_startup_mutex: std.Thread.Mutex = .{}; |
| 1284 | |
| 1285 | /// Microsoft requires WSAStartup to be called to initialize, or else |
| 1286 | /// WSASocketW will return WSANOTINITIALISED. |
| 1287 | /// Since this is a standard library, we do not have the luxury of |
| 1288 | /// putting initialization code anywhere, because we would not want |
| 1289 | /// to pay the cost of calling WSAStartup if there ended up being no |
| 1290 | /// networking. Also, if Zig code is used as a library, Zig is not in |
| 1291 | /// charge of the start code, and we couldn't put in any initialization |
| 1292 | /// code even if we wanted to. |
| 1293 | /// The documentation for WSAStartup mentions that there must be a |
| 1294 | /// matching WSACleanup call. It is not possible for the Zig Standard |
| 1295 | /// Library to honor this for the same reason - there is nowhere to put |
| 1296 | /// deinitialization code. |
| 1297 | /// So, API users of the zig std lib have two options: |
| 1298 | /// * (recommended) The simple, cross-platform way: just call `WSASocketW` |
| 1299 | /// and don't worry about it. Zig will call WSAStartup() in a thread-safe |
| 1300 | /// manner and never deinitialize networking. This is ideal for an |
| 1301 | /// application which has the capability to do networking. |
| 1302 | /// * The getting-your-hands-dirty way: call `WSAStartup()` before doing |
| 1303 | /// networking, so that the error handling code for WSANOTINITIALISED never |
| 1304 | /// gets run, which then allows the application or library to call `WSACleanup()`. |
| 1305 | /// This could make sense for a library, which has init and deinit |
| 1306 | /// functions for the whole library's lifetime. |
| 1283 | 1307 | pub fn WSASocketW( |
| 1284 | 1308 | af: i32, |
| 1285 | 1309 | socket_type: i32, |
| ... | ... | @@ -1288,17 +1312,40 @@ pub fn WSASocketW( |
| 1288 | 1312 | g: ws2_32.GROUP, |
| 1289 | 1313 | dwFlags: DWORD, |
| 1290 | 1314 | ) !ws2_32.SOCKET { |
| 1291 | | const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags); |
| 1292 | | if (rc == ws2_32.INVALID_SOCKET) { |
| 1293 | | switch (ws2_32.WSAGetLastError()) { |
| 1294 | | .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| 1295 | | .WSAEMFILE => return error.ProcessFdQuotaExceeded, |
| 1296 | | .WSAENOBUFS => return error.SystemResources, |
| 1297 | | .WSAEPROTONOSUPPORT => return error.ProtocolNotSupported, |
| 1298 | | else => |err| return unexpectedWSAError(err), |
| 1315 | var first = true; |
| 1316 | while (true) { |
| 1317 | const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags); |
| 1318 | if (rc == ws2_32.INVALID_SOCKET) { |
| 1319 | switch (ws2_32.WSAGetLastError()) { |
| 1320 | .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| 1321 | .WSAEMFILE => return error.ProcessFdQuotaExceeded, |
| 1322 | .WSAENOBUFS => return error.SystemResources, |
| 1323 | .WSAEPROTONOSUPPORT => return error.ProtocolNotSupported, |
| 1324 | .WSANOTINITIALISED => { |
| 1325 | if (!first) return error.Unexpected; |
| 1326 | first = false; |
| 1327 | |
| 1328 | var held = wsa_startup_mutex.acquire(); |
| 1329 | defer held.release(); |
| 1330 | |
| 1331 | // Here we could use a flag to prevent multiple threads to prevent |
| 1332 | // multiple calls to WSAStartup, but it doesn't matter. We're globally |
| 1333 | // leaking the resource intentionally, and the mutex already prevents |
| 1334 | // data races within the WSAStartup function. |
| 1335 | _ = WSAStartup(2, 2) catch |err| switch (err) { |
| 1336 | error.SystemNotAvailable => return error.SystemResources, |
| 1337 | error.VersionNotSupported => return error.Unexpected, |
| 1338 | error.BlockingOperationInProgress => return error.Unexpected, |
| 1339 | error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, |
| 1340 | error.Unexpected => return error.Unexpected, |
| 1341 | }; |
| 1342 | continue; |
| 1343 | }, |
| 1344 | else => |err| return unexpectedWSAError(err), |
| 1345 | } |
| 1299 | 1346 | } |
| 1347 | return rc; |
| 1300 | 1348 | } |
| 1301 | | return rc; |
| 1302 | 1349 | } |
| 1303 | 1350 | |
| 1304 | 1351 | pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 { |