authorgravatar for sachabarsayuracko@gmail.comglowsquid <sachabarsayuracko@gmail.com> 2026-04-16 19:47:46+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-16 19:47:46+02:00
log0177cb57c03d0e10b4d0ef6a11ebf16de3dfd2b5
treec599aa3d7c8b8dea2ae82d8b5c176c98f6154e55
parentffcfeb919fc08724eea0346af8ea5531d62e2734

std.http.Client: make mutexes cancelable (#31880)

Makes most of the mutexes cancelable with the exception of the ones in deinit functions. I also fixed the compilation errors with `ConnectionPool.resize`. Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31880 Reviewed-by: Andrew Kelley <andrew@ziglang.org> Co-authored-by: glowsquid <sachabarsayuracko@gmail.com> Co-committed-by: glowsquid <sachabarsayuracko@gmail.com>

1 files changed, 21 insertions(+), 24 deletions(-)

lib/std/http/Client.zig+21-24
......@@ -4,8 +4,6 @@
44//!
55//! TLS support may be disabled via `std.options.http_disable_tls`.
66//!
7//! TODO all the lockUncancelable in this file should be changed to regular lock and
8//! `error.Canceled` added to more error sets.
97const Client = @This();
108
119const builtin = @import("builtin");
......@@ -84,8 +82,8 @@ pub const ConnectionPool = struct {
8482 /// If no connection is found, null is returned.
8583 ///
8684 /// Threadsafe.
87 pub fn findConnection(pool: *ConnectionPool, io: Io, criteria: Criteria) ?*Connection {
88 pool.mutex.lockUncancelable(io);
85 pub fn findConnection(pool: *ConnectionPool, io: Io, criteria: Criteria) Io.Cancelable!?*Connection {
86 try pool.mutex.lock(io);
8987 defer pool.mutex.unlock(io);
9088
9189 var next = pool.free.last;
......@@ -113,8 +111,8 @@ pub const ConnectionPool = struct {
113111 }
114112
115113 /// Acquires an existing connection from the connection pool. This function is threadsafe.
116 pub fn acquire(pool: *ConnectionPool, io: Io, connection: *Connection) void {
117 pool.mutex.lockUncancelable(io);
114 pub fn acquire(pool: *ConnectionPool, io: Io, connection: *Connection) Io.Cancelable!void {
115 try pool.mutex.lock(io);
118116 defer pool.mutex.unlock(io);
119117
120118 return pool.acquireUnsafe(connection);
......@@ -150,8 +148,8 @@ pub const ConnectionPool = struct {
150148 }
151149
152150 /// Adds a newly created node to the pool of used connections. This function is threadsafe.
153 pub fn addUsed(pool: *ConnectionPool, io: Io, connection: *Connection) void {
154 pool.mutex.lockUncancelable(io);
151 pub fn addUsed(pool: *ConnectionPool, io: Io, connection: *Connection) Io.Cancelable!void {
152 try pool.mutex.lock(io);
155153 defer pool.mutex.unlock(io);
156154
157155 pool.used.append(&connection.pool_node);
......@@ -162,18 +160,15 @@ pub const ConnectionPool = struct {
162160 /// If the new size is smaller than the current size, then idle connections will be closed until the pool is the new size.
163161 ///
164162 /// Threadsafe.
165 pub fn resize(pool: *ConnectionPool, io: Io, allocator: Allocator, new_size: usize) void {
166 pool.mutex.lockUncancelable(io);
163 pub fn resize(pool: *ConnectionPool, io: Io, new_size: usize) Io.Cancelable!void {
164 try pool.mutex.lock(io);
167165 defer pool.mutex.unlock(io);
168166
169 const next = pool.free.first;
170 _ = next;
171167 while (pool.free_len > new_size) {
172 const popped = pool.free.popFirst() orelse unreachable;
168 const popped: *Connection = @alignCast(@fieldParentPtr("pool_node", pool.free.popFirst().?));
173169 pool.free_len -= 1;
174170
175 popped.data.close(allocator);
176 allocator.destroy(popped);
171 popped.destroy(io);
177172 }
178173
179174 pool.free_size = new_size;
......@@ -1323,7 +1318,7 @@ pub fn initDefaultProxies(client: *Client, arena: Allocator, environ_map: *const
13231318 const io = client.io;
13241319
13251320 // Prevent any new connections from being created.
1326 client.connection_pool.mutex.lockUncancelable(io);
1321 try client.connection_pool.mutex.lock(io);
13271322 defer client.connection_pool.mutex.unlock(io);
13281323
13291324 assert(client.connection_pool.used.first == null); // There are active requests.
......@@ -1418,7 +1413,7 @@ pub const basic_authorization = struct {
14181413
14191414pub const ConnectTcpError = error{
14201415 TlsInitializationFailed,
1421} || Allocator.Error || HostName.ConnectError;
1416} || Allocator.Error || HostName.ConnectError || Io.Cancelable;
14221417
14231418/// Reuses a `Connection` if one matching `host` and `port` is already open.
14241419///
......@@ -1451,7 +1446,7 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp
14511446 const proxied_host = options.proxied_host orelse host;
14521447 const proxied_port = options.proxied_port orelse port;
14531448
1454 if (client.connection_pool.findConnection(io, .{
1449 if (try client.connection_pool.findConnection(io, .{
14551450 .host = proxied_host,
14561451 .port = proxied_port,
14571452 .protocol = protocol,
......@@ -1469,18 +1464,20 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp
14691464 error.Canceled => |e| return e,
14701465 else => return error.TlsInitializationFailed,
14711466 };
1472 client.connection_pool.addUsed(io, &tc.connection);
1467 errdefer tc.destroy();
1468 try client.connection_pool.addUsed(io, &tc.connection);
14731469 return &tc.connection;
14741470 },
14751471 .plain => {
14761472 const pc = try Connection.Plain.create(client, proxied_host, proxied_port, stream);
1477 client.connection_pool.addUsed(io, &pc.connection);
1473 errdefer pc.destroy();
1474 try client.connection_pool.addUsed(io, &pc.connection);
14781475 return &pc.connection;
14791476 },
14801477 }
14811478}
14821479
1483pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError;
1480pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError || Io.Cancelable;
14841481
14851482/// Connect to `path` as a unix domain socket. This will reuse a connection if one is already open.
14861483///
......@@ -1488,7 +1485,7 @@ pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{N
14881485pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connection {
14891486 const io = client.io;
14901487
1491 if (client.connection_pool.findConnection(io, .{
1488 if (try client.connection_pool.findConnection(io, .{
14921489 .host = path,
14931490 .port = 0,
14941491 .protocol = .plain,
......@@ -1512,7 +1509,7 @@ pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connecti
15121509 };
15131510 errdefer client.allocator.free(conn.data.host);
15141511
1515 client.connection_pool.addUsed(conn);
1512 try client.connection_pool.addUsed(conn);
15161513
15171514 return &conn.data;
15181515}
......@@ -1530,7 +1527,7 @@ pub fn connectProxied(
15301527 const io = client.io;
15311528 if (!proxy.supports_connect) return error.TunnelNotSupported;
15321529
1533 if (client.connection_pool.findConnection(io, .{
1530 if (try client.connection_pool.findConnection(io, .{
15341531 .host = proxied_host,
15351532 .port = proxied_port,
15361533 .protocol = proxy.protocol,