mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
Merge pull request #8695 from Bxil/socket
std.os: WSAStartup is now called upon socket creation when needed
This commit is contained in:
commit
34d0542a53
@ -2728,10 +2728,21 @@ pub const SocketError = error{
|
|||||||
|
|
||||||
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
|
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
// NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into windows-analagous operations
|
// NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into
|
||||||
|
// windows-analagous operations
|
||||||
const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||||
const flags: u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0;
|
const flags: u32 = if ((socket_type & SOCK_CLOEXEC) != 0)
|
||||||
const rc = try windows.WSASocketW(@bitCast(i32, domain), @bitCast(i32, filtered_sock_type), @bitCast(i32, protocol), null, 0, flags);
|
windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT
|
||||||
|
else
|
||||||
|
0;
|
||||||
|
const rc = try windows.WSASocketW(
|
||||||
|
@bitCast(i32, domain),
|
||||||
|
@bitCast(i32, filtered_sock_type),
|
||||||
|
@bitCast(i32, protocol),
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
flags,
|
||||||
|
);
|
||||||
errdefer windows.closesocket(rc) catch unreachable;
|
errdefer windows.closesocket(rc) catch unreachable;
|
||||||
if ((socket_type & SOCK_NONBLOCK) != 0) {
|
if ((socket_type & SOCK_NONBLOCK) != 0) {
|
||||||
var mode: c_ulong = 1; // nonblocking
|
var mode: c_ulong = 1; // nonblocking
|
||||||
|
|||||||
@ -1261,7 +1261,7 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
|
|||||||
.WSASYSNOTREADY => return error.SystemNotAvailable,
|
.WSASYSNOTREADY => return error.SystemNotAvailable,
|
||||||
.WSAVERNOTSUPPORTED => return error.VersionNotSupported,
|
.WSAVERNOTSUPPORTED => return error.VersionNotSupported,
|
||||||
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
|
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
|
||||||
.WSAEPROCLIM => return error.SystemResources,
|
.WSAEPROCLIM => return error.ProcessFdQuotaExceeded,
|
||||||
else => |err| return unexpectedWSAError(err),
|
else => |err| return unexpectedWSAError(err),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -1280,6 +1280,30 @@ pub fn WSACleanup() !void {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wsa_startup_mutex: std.Thread.Mutex = .{};
|
||||||
|
|
||||||
|
/// Microsoft requires WSAStartup to be called to initialize, or else
|
||||||
|
/// WSASocketW will return WSANOTINITIALISED.
|
||||||
|
/// Since this is a standard library, we do not have the luxury of
|
||||||
|
/// putting initialization code anywhere, because we would not want
|
||||||
|
/// to pay the cost of calling WSAStartup if there ended up being no
|
||||||
|
/// networking. Also, if Zig code is used as a library, Zig is not in
|
||||||
|
/// charge of the start code, and we couldn't put in any initialization
|
||||||
|
/// code even if we wanted to.
|
||||||
|
/// The documentation for WSAStartup mentions that there must be a
|
||||||
|
/// matching WSACleanup call. It is not possible for the Zig Standard
|
||||||
|
/// Library to honor this for the same reason - there is nowhere to put
|
||||||
|
/// deinitialization code.
|
||||||
|
/// So, API users of the zig std lib have two options:
|
||||||
|
/// * (recommended) The simple, cross-platform way: just call `WSASocketW`
|
||||||
|
/// and don't worry about it. Zig will call WSAStartup() in a thread-safe
|
||||||
|
/// manner and never deinitialize networking. This is ideal for an
|
||||||
|
/// application which has the capability to do networking.
|
||||||
|
/// * The getting-your-hands-dirty way: call `WSAStartup()` before doing
|
||||||
|
/// networking, so that the error handling code for WSANOTINITIALISED never
|
||||||
|
/// gets run, which then allows the application or library to call `WSACleanup()`.
|
||||||
|
/// This could make sense for a library, which has init and deinit
|
||||||
|
/// functions for the whole library's lifetime.
|
||||||
pub fn WSASocketW(
|
pub fn WSASocketW(
|
||||||
af: i32,
|
af: i32,
|
||||||
socket_type: i32,
|
socket_type: i32,
|
||||||
@ -1288,6 +1312,8 @@ pub fn WSASocketW(
|
|||||||
g: ws2_32.GROUP,
|
g: ws2_32.GROUP,
|
||||||
dwFlags: DWORD,
|
dwFlags: DWORD,
|
||||||
) !ws2_32.SOCKET {
|
) !ws2_32.SOCKET {
|
||||||
|
var first = true;
|
||||||
|
while (true) {
|
||||||
const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags);
|
const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags);
|
||||||
if (rc == ws2_32.INVALID_SOCKET) {
|
if (rc == ws2_32.INVALID_SOCKET) {
|
||||||
switch (ws2_32.WSAGetLastError()) {
|
switch (ws2_32.WSAGetLastError()) {
|
||||||
@ -1295,10 +1321,31 @@ pub fn WSASocketW(
|
|||||||
.WSAEMFILE => return error.ProcessFdQuotaExceeded,
|
.WSAEMFILE => return error.ProcessFdQuotaExceeded,
|
||||||
.WSAENOBUFS => return error.SystemResources,
|
.WSAENOBUFS => return error.SystemResources,
|
||||||
.WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
|
.WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
|
||||||
|
.WSANOTINITIALISED => {
|
||||||
|
if (!first) return error.Unexpected;
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
var held = wsa_startup_mutex.acquire();
|
||||||
|
defer held.release();
|
||||||
|
|
||||||
|
// Here we could use a flag to prevent multiple threads to prevent
|
||||||
|
// multiple calls to WSAStartup, but it doesn't matter. We're globally
|
||||||
|
// leaking the resource intentionally, and the mutex already prevents
|
||||||
|
// data races within the WSAStartup function.
|
||||||
|
_ = WSAStartup(2, 2) catch |err| switch (err) {
|
||||||
|
error.SystemNotAvailable => return error.SystemResources,
|
||||||
|
error.VersionNotSupported => return error.Unexpected,
|
||||||
|
error.BlockingOperationInProgress => return error.Unexpected,
|
||||||
|
error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
|
||||||
|
error.Unexpected => return error.Unexpected,
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
},
|
||||||
else => |err| return unexpectedWSAError(err),
|
else => |err| return unexpectedWSAError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 {
|
pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user