Is there a way to concurrently, i.e. through std.Io.concurrent, read from and write to std.crypto.tls.Client’s writer and reader? No particular requirements, just that blocked reads don’t block writes and vice versa.
“solution” to my particular use case
// this might be the worst concurrency code i've ever written
var buf: [2]Result = undefined;
var first = std.Io.Select(Result).init(io, &buf);
defer while (first.cancel()) |x| switch (x) {
inline else => |m| gpa.free(m catch continue),
};
try first.concurrent(.received_message, std.Io.Queue([]u8).getOne, .{ client.outbound_packets, io });
try first.concurrent(.message_to_send, receiveMessage, .{ reader, io, gpa, client.outbound_packets });
const result = try first.await();
Keep in mind that std.crypto.tls.Client is not safe to be used concurrently from two threads. If you use reader and writer concurrently, you will have them overriding internal fields without any synchronization.
I have a fork of tls.zig that allows me to do this concurrently, but in general it’s not a supported operation in TLS libraries. Even OpenSSL/BoringSSL don’t allow you to use the TLS context concurrently.
If you have an app that needs concurrent network I/O and still do TLS using the std client, you would need two tasks for the reads and writes, and build std.Io.Reader/Writer based on std.Io.Queue(u8) and feed the TLS client from those.
Thanks for the response.
Keep in mind that
std.crypto.tls.Clientis not safe to be used concurrently from two threads. If you usereaderandwriterconcurrently, you will have them overriding internal fields without any synchronization.
Yes, that is precisely the problem.
I have a fork of tls.zig that allows me to do this concurrently, but in general it’s not a supported operation in TLS libraries. Even OpenSSL/BoringSSL don’t allow you to use the TLS context concurrently.
The intended usage of std.tls.Client is to use their std.Io.Reader and std.Io.Writer, which block on network I/O if backed by readers and writers that do network I/O, so the only way to serialize the access to std.tls.Client without reaching into implementation details is to hold a mutex while blocked on network I/O meaning that reading blocks writing and vice versa.
If you have an app that needs concurrent network I/O and still do TLS using the std client, you would need two tasks for the reads and writes, and build
std.Io.Reader/Writerbased onstd.Io.Queue(u8)and feed the TLS client from those.
Sorry, I don’t understand how that would allow for this use case?
Yeah, it’s actually more complex. I was actually guiding someone else how to deal with a similar problem, but they ended up using native zio primitives to make this efficient.
The easiest option might be a mutex around the TLS client access, but because it’s blocking API, you would still need custom reader/writer to unlock and relock the mutex around network calls.
So the operation would be like this:
- lock TLS mutex
- call TLS reader
- TLS does its thing
- it calls the source reader
- inside the custom source reader, you unlock the mutex
- then you call the TCP reader
- you relock the mutex
- TLS does it’s thing
- unlock the mutex
The same for writes.
Or, use tls.zig ![]()
Wow, that’s disgusting. Thank you though!
I think I’ll just use the sans-I/O API from tls.zig. It’s unfortunate that the std client isn’t suitable for this.
After(/if) this change lands, you will be able to use even the blocking API from tls.zig concurrently:
Thanks a lot!
I did move to its non-blocking API and std.Io.Batch in the meantime, I’ll see how it goes!