| author | |
| committer | |
| log | e25168d01be35bea2c8e811fb5fddab847ae6938 |
| tree | 2b085034a0cd8e46fb778dea134d48e55e7be418 |
| parent | 6eeceb4b14965a11d3f6b2969b82a696ba99bffc |
| parent | 95f57c3369181123aa7ebd4a9654fc489f8bb4fd |
| signature |
Fix `respondWebSocket`, use overlapped sockets on Windows, and re-enable --webui6 files changed, 75 insertions(+), 29 deletions(-)
lib/build-web/index.html+2| ... | @@ -105,6 +105,7 @@ | ... | @@ -105,6 +105,7 @@ |
| 105 | <th scope="col">Semantic Analysis</th> | 105 | <th scope="col">Semantic Analysis</th> |
| 106 | <th scope="col">Code Generation</th> | 106 | <th scope="col">Code Generation</th> |
| 107 | <th scope="col">Linking</th> | 107 | <th scope="col">Linking</th> |
| 108 | <th scope="col">Total</th> | ||
| 108 | </tr> | 109 | </tr> |
| 109 | </thead> | 110 | </thead> |
| 110 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility | 111 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility |
| ... | @@ -125,6 +126,7 @@ | ... | @@ -125,6 +126,7 @@ |
| 125 | <th scope="col">Semantic Analysis</th> | 126 | <th scope="col">Semantic Analysis</th> |
| 126 | <th scope="col">Code Generation</th> | 127 | <th scope="col">Code Generation</th> |
| 127 | <th scope="col">Linking</th> | 128 | <th scope="col">Linking</th> |
| 129 | <th scope="col">Total</th> | ||
| 128 | </tr> | 130 | </tr> |
| 129 | </thead> | 131 | </thead> |
| 130 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility | 132 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility |
lib/build-web/time_report.zig+4| ... | @@ -175,6 +175,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -175,6 +175,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 175 | \\ <td>{D}</td> | 175 | \\ <td>{D}</td> |
| 176 | \\ <td>{D}</td> | 176 | \\ <td>{D}</td> |
| 177 | \\ <td>{D}</td> | 177 | \\ <td>{D}</td> |
| 178 | \\ <td>{D}</td> | ||
| 178 | \\</tr> | 179 | \\</tr> |
| 179 | \\ | 180 | \\ |
| 180 | , .{ | 181 | , .{ |
| ... | @@ -182,6 +183,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -182,6 +183,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 182 | file.ns_sema, | 183 | file.ns_sema, |
| 183 | file.ns_codegen, | 184 | file.ns_codegen, |
| 184 | file.ns_link, | 185 | file.ns_link, |
| 186 | file.ns_sema + file.ns_codegen + file.ns_link, | ||
| 185 | }); | 187 | }); |
| 186 | } | 188 | } |
| 187 | if (slowest_files.len > max_table_rows) { | 189 | if (slowest_files.len > max_table_rows) { |
| ... | @@ -203,6 +205,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -203,6 +205,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 203 | \\ <td>{D}</td> | 205 | \\ <td>{D}</td> |
| 204 | \\ <td>{D}</td> | 206 | \\ <td>{D}</td> |
| 205 | \\ <td>{D}</td> | 207 | \\ <td>{D}</td> |
| 208 | \\ <td>{D}</td> | ||
| 206 | \\</tr> | 209 | \\</tr> |
| 207 | \\ | 210 | \\ |
| 208 | , .{ | 211 | , .{ |
| ... | @@ -212,6 +215,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -212,6 +215,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 212 | decl.ns_sema, | 215 | decl.ns_sema, |
| 213 | decl.ns_codegen, | 216 | decl.ns_codegen, |
| 214 | decl.ns_link, | 217 | decl.ns_link, |
| 218 | decl.ns_sema + decl.ns_codegen + decl.ns_link, | ||
| 215 | }); | 219 | }); |
| 216 | } | 220 | } |
| 217 | if (slowest_decls.len > max_table_rows) { | 221 | if (slowest_decls.len > max_table_rows) { |
lib/std/Build/WebServer.zig+2-11| ... | @@ -65,16 +65,6 @@ pub fn init(opts: Options) WebServer { | ... | @@ -65,16 +65,6 @@ pub fn init(opts: Options) WebServer { |
| 65 | std.process.fatal("--webui not yet implemented for single-threaded builds", .{}); | 65 | std.process.fatal("--webui not yet implemented for single-threaded builds", .{}); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | if (builtin.os.tag == .windows) { | ||
| 69 | // At the time of writing, there are two bugs in the standard library which break this feature on Windows: | ||
| 70 | // * Reading from a socket on one thread while writing to it on another seems to deadlock. | ||
| 71 | // * Vectored writes to sockets currently trigger an infinite loop when a buffer has length 0. | ||
| 72 | // | ||
| 73 | // Both of these bugs are expected to be solved by changes which are currently in the unmerged | ||
| 74 | // 'wrangle-writer-buffering' branch. Until that makes it in, this must remain disabled. | ||
| 75 | std.process.fatal("--webui is currently disabled on Windows due to bugs", .{}); | ||
| 76 | } | ||
| 77 | |||
| 78 | const all_steps = opts.all_steps; | 68 | const all_steps = opts.all_steps; |
| 79 | 69 | ||
| 80 | const step_names_trailing = opts.gpa.alloc(u8, len: { | 70 | const step_names_trailing = opts.gpa.alloc(u8, len: { |
| ... | @@ -297,7 +287,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { | ... | @@ -297,7 +287,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { |
| 297 | copy.* = @atomicLoad(u8, shared, .monotonic); | 287 | copy.* = @atomicLoad(u8, shared, .monotonic); |
| 298 | } | 288 | } |
| 299 | 289 | ||
| 300 | _ = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock }); | 290 | const recv_thread = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock }); |
| 291 | defer recv_thread.join(); | ||
| 301 | 292 | ||
| 302 | { | 293 | { |
| 303 | const hello_header: abi.Hello = .{ | 294 | const hello_header: abi.Hello = .{ |
lib/std/http/Server.zig-1| ... | @@ -546,7 +546,6 @@ pub const Request = struct { | ... | @@ -546,7 +546,6 @@ pub const Request = struct { |
| 546 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); | 546 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); |
| 547 | const base64_digest = try out.writableArray(28); | 547 | const base64_digest = try out.writableArray(28); |
| 548 | assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); | 548 | assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); |
| 549 | out.advance(base64_digest.len); | ||
| 550 | try out.writeAll("\r\n"); | 549 | try out.writeAll("\r\n"); |
| 551 | 550 | ||
| 552 | for (options.extra_headers) |header| { | 551 | for (options.extra_headers) |header| { |
lib/std/net.zig+63-11| ... | @@ -259,6 +259,7 @@ pub const Address = extern union { | ... | @@ -259,6 +259,7 @@ pub const Address = extern union { |
| 259 | /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX. | 259 | /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX. |
| 260 | /// Sets SO_REUSEADDR on Windows, which is roughly equivalent. | 260 | /// Sets SO_REUSEADDR on Windows, which is roughly equivalent. |
| 261 | reuse_address: bool = false, | 261 | reuse_address: bool = false, |
| 262 | /// Sets O_NONBLOCK. | ||
| 262 | force_nonblocking: bool = false, | 263 | force_nonblocking: bool = false, |
| 263 | }; | 264 | }; |
| 264 | 265 | ||
| ... | @@ -1998,11 +1999,8 @@ pub const Stream = struct { | ... | @@ -1998,11 +1999,8 @@ pub const Stream = struct { |
| 1998 | return n; | 1999 | return n; |
| 1999 | } | 2000 | } |
| 2000 | 2001 | ||
| 2001 | fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 { | 2002 | fn handleRecvError(winsock_error: windows.ws2_32.WinsockError) Error!void { |
| 2002 | var n: u32 = undefined; | 2003 | switch (winsock_error) { |
| 2003 | var flags: u32 = 0; | ||
| 2004 | const rc = windows.ws2_32.WSARecvFrom(r.net_stream.handle, bufs.ptr, @intCast(bufs.len), &n, &flags, null, null, null, null); | ||
| 2005 | if (rc != 0) switch (windows.ws2_32.WSAGetLastError()) { | ||
| 2006 | .WSAECONNRESET => return error.ConnectionResetByPeer, | 2004 | .WSAECONNRESET => return error.ConnectionResetByPeer, |
| 2007 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. | 2005 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. |
| 2008 | .WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2 | 2006 | .WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2 |
| ... | @@ -2013,10 +2011,39 @@ pub const Stream = struct { | ... | @@ -2013,10 +2011,39 @@ pub const Stream = struct { |
| 2013 | .WSAENOTCONN => return error.SocketNotConnected, | 2011 | .WSAENOTCONN => return error.SocketNotConnected, |
| 2014 | .WSAEWOULDBLOCK => return error.WouldBlock, | 2012 | .WSAEWOULDBLOCK => return error.WouldBlock, |
| 2015 | .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function | 2013 | .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function |
| 2016 | .WSA_IO_PENDING => unreachable, // not using overlapped I/O | 2014 | .WSA_IO_PENDING => unreachable, |
| 2017 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O | 2015 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O |
| 2018 | else => |err| return windows.unexpectedWSAError(err), | 2016 | else => |err| return windows.unexpectedWSAError(err), |
| 2017 | } | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 { | ||
| 2021 | var flags: u32 = 0; | ||
| 2022 | var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED); | ||
| 2023 | |||
| 2024 | var n: u32 = undefined; | ||
| 2025 | if (windows.ws2_32.WSARecv( | ||
| 2026 | r.net_stream.handle, | ||
| 2027 | bufs.ptr, | ||
| 2028 | @intCast(bufs.len), | ||
| 2029 | &n, | ||
| 2030 | &flags, | ||
| 2031 | &overlapped, | ||
| 2032 | null, | ||
| 2033 | ) == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) { | ||
| 2034 | .WSA_IO_PENDING => { | ||
| 2035 | var result_flags: u32 = undefined; | ||
| 2036 | if (windows.ws2_32.WSAGetOverlappedResult( | ||
| 2037 | r.net_stream.handle, | ||
| 2038 | &overlapped, | ||
| 2039 | &n, | ||
| 2040 | windows.TRUE, | ||
| 2041 | &result_flags, | ||
| 2042 | ) == windows.FALSE) try handleRecvError(windows.ws2_32.WSAGetLastError()); | ||
| 2043 | }, | ||
| 2044 | else => |winsock_error| try handleRecvError(winsock_error), | ||
| 2019 | }; | 2045 | }; |
| 2046 | |||
| 2020 | return n; | 2047 | return n; |
| 2021 | } | 2048 | } |
| 2022 | }, | 2049 | }, |
| ... | @@ -2136,10 +2163,8 @@ pub const Stream = struct { | ... | @@ -2136,10 +2163,8 @@ pub const Stream = struct { |
| 2136 | return io_w.consume(n); | 2163 | return io_w.consume(n); |
| 2137 | } | 2164 | } |
| 2138 | 2165 | ||
| 2139 | fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 { | 2166 | fn handleSendError(winsock_error: windows.ws2_32.WinsockError) Error!void { |
| 2140 | var n: u32 = undefined; | 2167 | switch (winsock_error) { |
| 2141 | const rc = windows.ws2_32.WSASend(handle, bufs.ptr, @intCast(bufs.len), &n, 0, null, null); | ||
| 2142 | if (rc == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) { | ||
| 2143 | .WSAECONNABORTED => return error.ConnectionResetByPeer, | 2168 | .WSAECONNABORTED => return error.ConnectionResetByPeer, |
| 2144 | .WSAECONNRESET => return error.ConnectionResetByPeer, | 2169 | .WSAECONNRESET => return error.ConnectionResetByPeer, |
| 2145 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. | 2170 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. |
| ... | @@ -2155,10 +2180,37 @@ pub const Stream = struct { | ... | @@ -2155,10 +2180,37 @@ pub const Stream = struct { |
| 2155 | .WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown | 2180 | .WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown |
| 2156 | .WSAEWOULDBLOCK => return error.WouldBlock, | 2181 | .WSAEWOULDBLOCK => return error.WouldBlock, |
| 2157 | .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function | 2182 | .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function |
| 2158 | .WSA_IO_PENDING => unreachable, // not using overlapped I/O | 2183 | .WSA_IO_PENDING => unreachable, |
| 2159 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O | 2184 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O |
| 2160 | else => |err| return windows.unexpectedWSAError(err), | 2185 | else => |err| return windows.unexpectedWSAError(err), |
| 2186 | } | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 { | ||
| 2190 | var n: u32 = undefined; | ||
| 2191 | var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED); | ||
| 2192 | if (windows.ws2_32.WSASend( | ||
| 2193 | handle, | ||
| 2194 | bufs.ptr, | ||
| 2195 | @intCast(bufs.len), | ||
| 2196 | &n, | ||
| 2197 | 0, | ||
| 2198 | &overlapped, | ||
| 2199 | null, | ||
| 2200 | ) == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) { | ||
| 2201 | .WSA_IO_PENDING => { | ||
| 2202 | var result_flags: u32 = undefined; | ||
| 2203 | if (windows.ws2_32.WSAGetOverlappedResult( | ||
| 2204 | handle, | ||
| 2205 | &overlapped, | ||
| 2206 | &n, | ||
| 2207 | windows.TRUE, | ||
| 2208 | &result_flags, | ||
| 2209 | ) == windows.FALSE) try handleSendError(windows.ws2_32.WSAGetLastError()); | ||
| 2210 | }, | ||
| 2211 | else => |winsock_error| try handleSendError(winsock_error), | ||
| 2161 | }; | 2212 | }; |
| 2213 | |||
| 2162 | return n; | 2214 | return n; |
| 2163 | } | 2215 | } |
| 2164 | }, | 2216 | }, |
lib/std/posix.zig+4-6| ... | @@ -3615,13 +3615,11 @@ pub const SocketError = error{ | ... | @@ -3615,13 +3615,11 @@ pub const SocketError = error{ |
| 3615 | 3615 | ||
| 3616 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t { | 3616 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t { |
| 3617 | if (native_os == .windows) { | 3617 | if (native_os == .windows) { |
| 3618 | // NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into | 3618 | // These flags are not actually part of the Windows API, instead they are converted here for compatibility |
| 3619 | // windows-analogous operations | ||
| 3620 | const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC); | 3619 | const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC); |
| 3621 | const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0) | 3620 | var flags: u32 = windows.ws2_32.WSA_FLAG_OVERLAPPED; |
| 3622 | windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT | 3621 | if ((socket_type & SOCK.CLOEXEC) != 0) flags |= windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; |
| 3623 | else | 3622 | |
| 3624 | 0; | ||
| 3625 | const rc = try windows.WSASocketW( | 3623 | const rc = try windows.WSASocketW( |
| 3626 | @bitCast(domain), | 3624 | @bitCast(domain), |
| 3627 | @bitCast(filtered_sock_type), | 3625 | @bitCast(filtered_sock_type), |