| 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 | 105 | <th scope="col">Semantic Analysis</th> |
| 106 | 106 | <th scope="col">Code Generation</th> |
| 107 | 107 | <th scope="col">Linking</th> |
| 108 | <th scope="col">Total</th> | |
| 108 | 109 | </tr> |
| 109 | 110 | </thead> |
| 110 | 111 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility |
| ... | ... | @@ -125,6 +126,7 @@ |
| 125 | 126 | <th scope="col">Semantic Analysis</th> |
| 126 | 127 | <th scope="col">Code Generation</th> |
| 127 | 128 | <th scope="col">Linking</th> |
| 129 | <th scope="col">Total</th> | |
| 128 | 130 | </tr> |
| 129 | 131 | </thead> |
| 130 | 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 | 175 | \\ <td>{D}</td> |
| 176 | 176 | \\ <td>{D}</td> |
| 177 | 177 | \\ <td>{D}</td> |
| 178 | \\ <td>{D}</td> | |
| 178 | 179 | \\</tr> |
| 179 | 180 | \\ |
| 180 | 181 | , .{ |
| ... | ... | @@ -182,6 +183,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 182 | 183 | file.ns_sema, |
| 183 | 184 | file.ns_codegen, |
| 184 | 185 | file.ns_link, |
| 186 | file.ns_sema + file.ns_codegen + file.ns_link, | |
| 185 | 187 | }); |
| 186 | 188 | } |
| 187 | 189 | if (slowest_files.len > max_table_rows) { |
| ... | ... | @@ -203,6 +205,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 203 | 205 | \\ <td>{D}</td> |
| 204 | 206 | \\ <td>{D}</td> |
| 205 | 207 | \\ <td>{D}</td> |
| 208 | \\ <td>{D}</td> | |
| 206 | 209 | \\</tr> |
| 207 | 210 | \\ |
| 208 | 211 | , .{ |
| ... | ... | @@ -212,6 +215,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 212 | 215 | decl.ns_sema, |
| 213 | 216 | decl.ns_codegen, |
| 214 | 217 | decl.ns_link, |
| 218 | decl.ns_sema + decl.ns_codegen + decl.ns_link, | |
| 215 | 219 | }); |
| 216 | 220 | } |
| 217 | 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 | 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 | 68 | const all_steps = opts.all_steps; |
| 79 | 69 | |
| 80 | 70 | const step_names_trailing = opts.gpa.alloc(u8, len: { |
| ... | ... | @@ -297,7 +287,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { |
| 297 | 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 | 294 | const hello_header: abi.Hello = .{ |
lib/std/http/Server.zig-1| ... | ... | @@ -546,7 +546,6 @@ pub const Request = struct { |
| 546 | 546 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); |
| 547 | 547 | const base64_digest = try out.writableArray(28); |
| 548 | 548 | assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); |
| 549 | out.advance(base64_digest.len); | |
| 550 | 549 | try out.writeAll("\r\n"); |
| 551 | 550 | |
| 552 | 551 | for (options.extra_headers) |header| { |
lib/std/net.zig+63-11| ... | ... | @@ -259,6 +259,7 @@ pub const Address = extern union { |
| 259 | 259 | /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX. |
| 260 | 260 | /// Sets SO_REUSEADDR on Windows, which is roughly equivalent. |
| 261 | 261 | reuse_address: bool = false, |
| 262 | /// Sets O_NONBLOCK. | |
| 262 | 263 | force_nonblocking: bool = false, |
| 263 | 264 | }; |
| 264 | 265 | |
| ... | ... | @@ -1998,11 +1999,8 @@ pub const Stream = struct { |
| 1998 | 1999 | return n; |
| 1999 | 2000 | } |
| 2000 | 2001 | |
| 2001 | fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 { | |
| 2002 | var n: u32 = undefined; | |
| 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()) { | |
| 2002 | fn handleRecvError(winsock_error: windows.ws2_32.WinsockError) Error!void { | |
| 2003 | switch (winsock_error) { | |
| 2006 | 2004 | .WSAECONNRESET => return error.ConnectionResetByPeer, |
| 2007 | 2005 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. |
| 2008 | 2006 | .WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2 |
| ... | ... | @@ -2013,10 +2011,39 @@ pub const Stream = struct { |
| 2013 | 2011 | .WSAENOTCONN => return error.SocketNotConnected, |
| 2014 | 2012 | .WSAEWOULDBLOCK => return error.WouldBlock, |
| 2015 | 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 | 2015 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O |
| 2018 | 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 | 2047 | return n; |
| 2021 | 2048 | } |
| 2022 | 2049 | }, |
| ... | ... | @@ -2136,10 +2163,8 @@ pub const Stream = struct { |
| 2136 | 2163 | return io_w.consume(n); |
| 2137 | 2164 | } |
| 2138 | 2165 | |
| 2139 | fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 { | |
| 2140 | var n: u32 = undefined; | |
| 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()) { | |
| 2166 | fn handleSendError(winsock_error: windows.ws2_32.WinsockError) Error!void { | |
| 2167 | switch (winsock_error) { | |
| 2143 | 2168 | .WSAECONNABORTED => return error.ConnectionResetByPeer, |
| 2144 | 2169 | .WSAECONNRESET => return error.ConnectionResetByPeer, |
| 2145 | 2170 | .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space. |
| ... | ... | @@ -2155,10 +2180,37 @@ pub const Stream = struct { |
| 2155 | 2180 | .WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown |
| 2156 | 2181 | .WSAEWOULDBLOCK => return error.WouldBlock, |
| 2157 | 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 | 2184 | .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O |
| 2160 | 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 | 2214 | return n; |
| 2163 | 2215 | } |
| 2164 | 2216 | }, |
lib/std/posix.zig+4-6| ... | ... | @@ -3615,13 +3615,11 @@ pub const SocketError = error{ |
| 3615 | 3615 | |
| 3616 | 3616 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t { |
| 3617 | 3617 | if (native_os == .windows) { |
| 3618 | // NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into | |
| 3619 | // windows-analogous operations | |
| 3618 | // These flags are not actually part of the Windows API, instead they are converted here for compatibility | |
| 3620 | 3619 | const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC); |
| 3621 | const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0) | |
| 3622 | windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT | |
| 3623 | else | |
| 3624 | 0; | |
| 3620 | var flags: u32 = windows.ws2_32.WSA_FLAG_OVERLAPPED; | |
| 3621 | if ((socket_type & SOCK.CLOEXEC) != 0) flags |= windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; | |
| 3622 | ||
| 3625 | 3623 | const rc = try windows.WSASocketW( |
| 3626 | 3624 | @bitCast(domain), |
| 3627 | 3625 | @bitCast(filtered_sock_type), |