| ... | @@ -1261,7 +1261,7 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA { | ... | @@ -1261,7 +1261,7 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA { |
| 1261 | .WSASYSNOTREADY => return error.SystemNotAvailable, | 1261 | .WSASYSNOTREADY => return error.SystemNotAvailable, |
| 1262 | .WSAVERNOTSUPPORTED => return error.VersionNotSupported, | 1262 | .WSAVERNOTSUPPORTED => return error.VersionNotSupported, |
| 1263 | .WSAEINPROGRESS => return error.BlockingOperationInProgress, | 1263 | .WSAEINPROGRESS => return error.BlockingOperationInProgress, |
| 1264 | .WSAEPROCLIM => return error.SystemResources, | 1264 | .WSAEPROCLIM => return error.ProcessFdQuotaExceeded, |
| 1265 | else => |err| return unexpectedWSAError(err), | 1265 | else => |err| return unexpectedWSAError(err), |
| 1266 | }, | 1266 | }, |
| 1267 | }; | 1267 | }; |
| ... | @@ -1280,6 +1280,30 @@ pub fn WSACleanup() !void { | ... | @@ -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 | pub fn WSASocketW( | 1307 | pub fn WSASocketW( |
| 1284 | af: i32, | 1308 | af: i32, |
| 1285 | socket_type: i32, | 1309 | socket_type: i32, |
| ... | @@ -1288,18 +1312,40 @@ pub fn WSASocketW( | ... | @@ -1288,18 +1312,40 @@ pub fn WSASocketW( |
| 1288 | g: ws2_32.GROUP, | 1312 | g: ws2_32.GROUP, |
| 1289 | dwFlags: DWORD, | 1313 | dwFlags: DWORD, |
| 1290 | ) !ws2_32.SOCKET { | 1314 | ) !ws2_32.SOCKET { |
| 1291 | const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags); | 1315 | var first = true; |
| 1292 | if (rc == ws2_32.INVALID_SOCKET) { | 1316 | while (true) { |
| 1293 | switch (ws2_32.WSAGetLastError()) { | 1317 | const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags); |
| 1294 | .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, | 1318 | if (rc == ws2_32.INVALID_SOCKET) { |
| 1295 | .WSAEMFILE => return error.ProcessFdQuotaExceeded, | 1319 | switch (ws2_32.WSAGetLastError()) { |
| 1296 | .WSAENOBUFS => return error.SystemResources, | 1320 | .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| 1297 | .WSAEPROTONOSUPPORT => return error.ProtocolNotSupported, | 1321 | .WSAEMFILE => return error.ProcessFdQuotaExceeded, |
| 1298 | .WSANOTINITIALISED => return error.NotInitialised, | 1322 | .WSAENOBUFS => return error.SystemResources, |
| 1299 | else => |err| return unexpectedWSAError(err), | 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 | } |
| 1300 | } | 1346 | } |
| | 1347 | return rc; |
| 1301 | } | 1348 | } |
| 1302 | return rc; | | |
| 1303 | } | 1349 | } |
| 1304 | | 1350 | |
| 1305 | pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 { | 1351 | pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 { |