| author | |
| committer | |
| log | 46e5068e48fdc7ecdc6121b978c87fad680cbe40 |
| tree | 672e811bd1a3c6dd2ecb5c678d0bcf2702999dc8 |
| parent | a37c0bca2248e5d2e18c4855ee8d8d17bf26aa26 |
4 files changed, 237 insertions(+), 148 deletions(-)
lib/std/Io.zig+34-18| ... | @@ -641,8 +641,8 @@ pub const VTable = struct { | ... | @@ -641,8 +641,8 @@ pub const VTable = struct { |
| 641 | context_alignment: std.mem.Alignment, | 641 | context_alignment: std.mem.Alignment, |
| 642 | start: *const fn (context: *const anyopaque) void, | 642 | start: *const fn (context: *const anyopaque) void, |
| 643 | ) void, | 643 | ) void, |
| 644 | groupWait: *const fn (?*anyopaque, *Group) void, | 644 | groupWait: *const fn (?*anyopaque, *Group, token: *anyopaque) void, |
| 645 | groupCancel: *const fn (?*anyopaque, *Group) void, | 645 | groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void, |
| 646 | 646 | ||
| 647 | /// Blocks until one of the futures from the list has a result ready, such | 647 | /// Blocks until one of the futures from the list has a result ready, such |
| 648 | /// that awaiting it will not block. Returns that index. | 648 | /// that awaiting it will not block. Returns that index. |
| ... | @@ -665,14 +665,14 @@ pub const VTable = struct { | ... | @@ -665,14 +665,14 @@ pub const VTable = struct { |
| 665 | fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void, | 665 | fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void, |
| 666 | fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void, | 666 | fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void, |
| 667 | 667 | ||
| 668 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, | 668 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) NowError!Timestamp, |
| 669 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, | 669 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, timeout: Timeout) SleepError!void, |
| 670 | 670 | ||
| 671 | listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, | 671 | listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, |
| 672 | accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Stream, | 672 | accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Stream, |
| 673 | ipBind: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket, | 673 | ipBind: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket, |
| 674 | netSend: *const fn (?*anyopaque, handle: net.Socket.Handle, address: net.IpAddress, data: []const u8) net.Socket.SendError!void, | 674 | netSend: *const fn (?*anyopaque, handle: net.Socket.Handle, address: *const net.IpAddress, data: []const u8) net.Socket.SendError!void, |
| 675 | netReceive: *const fn (?*anyopaque, handle: net.Socket.Handle, address: net.IpAddress, buffer: []u8) net.Socket.ReceiveError!void, | 675 | netReceive: *const fn (?*anyopaque, handle: net.Socket.Handle, buffer: []u8, timeout: Timeout) net.Socket.ReceiveTimeoutError!net.ReceivedMessage, |
| 676 | netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize, | 676 | netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize, |
| 677 | netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize, | 677 | netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize, |
| 678 | netClose: *const fn (?*anyopaque, handle: net.Socket.Handle) void, | 678 | netClose: *const fn (?*anyopaque, handle: net.Socket.Handle) void, |
| ... | @@ -710,6 +710,15 @@ pub const Timestamp = enum(i96) { | ... | @@ -710,6 +710,15 @@ pub const Timestamp = enum(i96) { |
| 710 | pub fn addDuration(from: Timestamp, duration: Duration) Timestamp { | 710 | pub fn addDuration(from: Timestamp, duration: Duration) Timestamp { |
| 711 | return @enumFromInt(@intFromEnum(from) + duration.nanoseconds); | 711 | return @enumFromInt(@intFromEnum(from) + duration.nanoseconds); |
| 712 | } | 712 | } |
| 713 | |||
| 714 | pub fn fromNow(io: Io, clockid: std.posix.clockid_t, duration: Duration) NowError!Timestamp { | ||
| 715 | const now_ts = try now(io, clockid); | ||
| 716 | return addDuration(now_ts, duration); | ||
| 717 | } | ||
| 718 | |||
| 719 | pub fn compare(lhs: Timestamp, op: std.math.CompareOperator, rhs: Timestamp) bool { | ||
| 720 | return std.math.compare(@intFromEnum(lhs), op, @intFromEnum(rhs)); | ||
| 721 | } | ||
| 713 | }; | 722 | }; |
| 714 | pub const Duration = struct { | 723 | pub const Duration = struct { |
| 715 | nanoseconds: i96, | 724 | nanoseconds: i96, |
| ... | @@ -722,11 +731,14 @@ pub const Duration = struct { | ... | @@ -722,11 +731,14 @@ pub const Duration = struct { |
| 722 | return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_s }; | 731 | return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_s }; |
| 723 | } | 732 | } |
| 724 | }; | 733 | }; |
| 725 | pub const Deadline = union(enum) { | 734 | pub const Timeout = union(enum) { |
| 735 | none, | ||
| 726 | duration: Duration, | 736 | duration: Duration, |
| 727 | timestamp: Timestamp, | 737 | deadline: Timestamp, |
| 738 | |||
| 739 | pub const Error = error{Timeout}; | ||
| 728 | }; | 740 | }; |
| 729 | pub const ClockGetTimeError = std.posix.ClockGetTimeError || Cancelable; | 741 | pub const NowError = std.posix.ClockGetTimeError || Cancelable; |
| 730 | pub const SleepError = error{ UnsupportedClock, Unexpected, Canceled }; | 742 | pub const SleepError = error{ UnsupportedClock, Unexpected, Canceled }; |
| 731 | 743 | ||
| 732 | pub const AnyFuture = opaque {}; | 744 | pub const AnyFuture = opaque {}; |
| ... | @@ -768,8 +780,10 @@ pub const Group = struct { | ... | @@ -768,8 +780,10 @@ pub const Group = struct { |
| 768 | /// | 780 | /// |
| 769 | /// `function` *may* be called immediately, before `async` returns. | 781 | /// `function` *may* be called immediately, before `async` returns. |
| 770 | /// | 782 | /// |
| 771 | /// After this is called, `wait` must be called before the group is | 783 | /// After this is called, `wait` or `cancel` must be called before the |
| 772 | /// deinitialized. | 784 | /// group is deinitialized. |
| 785 | /// | ||
| 786 | /// Threadsafe. | ||
| 773 | /// | 787 | /// |
| 774 | /// See also: | 788 | /// See also: |
| 775 | /// * `Io.async` | 789 | /// * `Io.async` |
| ... | @@ -789,7 +803,9 @@ pub const Group = struct { | ... | @@ -789,7 +803,9 @@ pub const Group = struct { |
| 789 | /// | 803 | /// |
| 790 | /// Idempotent. Not threadsafe. | 804 | /// Idempotent. Not threadsafe. |
| 791 | pub fn wait(g: *Group, io: Io) void { | 805 | pub fn wait(g: *Group, io: Io) void { |
| 792 | io.vtable.groupWait(io.userdata, g); | 806 | const token = g.token orelse return; |
| 807 | g.token = null; | ||
| 808 | io.vtable.groupWait(io.userdata, g, token); | ||
| 793 | } | 809 | } |
| 794 | 810 | ||
| 795 | /// Equivalent to `wait` but requests cancellation on all tasks owned by | 811 | /// Equivalent to `wait` but requests cancellation on all tasks owned by |
| ... | @@ -797,9 +813,9 @@ pub const Group = struct { | ... | @@ -797,9 +813,9 @@ pub const Group = struct { |
| 797 | /// | 813 | /// |
| 798 | /// Idempotent. Not threadsafe. | 814 | /// Idempotent. Not threadsafe. |
| 799 | pub fn cancel(g: *Group, io: Io) void { | 815 | pub fn cancel(g: *Group, io: Io) void { |
| 800 | if (g.token == null) return; | 816 | const token = g.token orelse return; |
| 801 | io.vtable.groupCancel(io.userdata, g); | 817 | g.token = null; |
| 802 | assert(g.token == null); | 818 | io.vtable.groupCancel(io.userdata, g, token); |
| 803 | } | 819 | } |
| 804 | }; | 820 | }; |
| 805 | 821 | ||
| ... | @@ -1215,12 +1231,12 @@ pub fn cancelRequested(io: Io) bool { | ... | @@ -1215,12 +1231,12 @@ pub fn cancelRequested(io: Io) bool { |
| 1215 | return io.vtable.cancelRequested(io.userdata); | 1231 | return io.vtable.cancelRequested(io.userdata); |
| 1216 | } | 1232 | } |
| 1217 | 1233 | ||
| 1218 | pub fn now(io: Io, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp { | 1234 | pub fn now(io: Io, clockid: std.posix.clockid_t) NowError!Timestamp { |
| 1219 | return io.vtable.now(io.userdata, clockid); | 1235 | return io.vtable.now(io.userdata, clockid); |
| 1220 | } | 1236 | } |
| 1221 | 1237 | ||
| 1222 | pub fn sleep(io: Io, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void { | 1238 | pub fn sleep(io: Io, clockid: std.posix.clockid_t, timeout: Timeout) SleepError!void { |
| 1223 | return io.vtable.sleep(io.userdata, clockid, deadline); | 1239 | return io.vtable.sleep(io.userdata, clockid, timeout); |
| 1224 | } | 1240 | } |
| 1225 | 1241 | ||
| 1226 | pub fn sleepDuration(io: Io, duration: Duration) SleepError!void { | 1242 | pub fn sleepDuration(io: Io, duration: Duration) SleepError!void { |
lib/std/Io/Threaded.zig+58-27| ... | @@ -463,16 +463,19 @@ fn groupAsync( | ... | @@ -463,16 +463,19 @@ fn groupAsync( |
| 463 | }, | 463 | }, |
| 464 | .pool = pool, | 464 | .pool = pool, |
| 465 | .group = group, | 465 | .group = group, |
| 466 | .node = .{ .next = @ptrCast(@alignCast(group.token)) }, | 466 | .node = undefined, |
| 467 | .func = start, | 467 | .func = start, |
| 468 | .context_alignment = context_alignment, | 468 | .context_alignment = context_alignment, |
| 469 | .context_len = context.len, | 469 | .context_len = context.len, |
| 470 | }; | 470 | }; |
| 471 | group.token = &gc.node; | ||
| 472 | @memcpy(gc.contextPointer()[0..context.len], context); | 471 | @memcpy(gc.contextPointer()[0..context.len], context); |
| 473 | 472 | ||
| 474 | pool.mutex.lock(); | 473 | pool.mutex.lock(); |
| 475 | 474 | ||
| 475 | // Append to the group linked list inside the mutex to make `Io.Group.async` thread-safe. | ||
| 476 | gc.node = .{ .next = @ptrCast(@alignCast(group.token)) }; | ||
| 477 | group.token = &gc.node; | ||
| 478 | |||
| 476 | const thread_capacity = cpu_count - 1 + pool.concurrent_count; | 479 | const thread_capacity = cpu_count - 1 + pool.concurrent_count; |
| 477 | 480 | ||
| 478 | pool.threads.ensureTotalCapacityPrecise(gpa, thread_capacity) catch { | 481 | pool.threads.ensureTotalCapacityPrecise(gpa, thread_capacity) catch { |
| ... | @@ -493,6 +496,8 @@ fn groupAsync( | ... | @@ -493,6 +496,8 @@ fn groupAsync( |
| 493 | pool.threads.appendAssumeCapacity(thread); | 496 | pool.threads.appendAssumeCapacity(thread); |
| 494 | } | 497 | } |
| 495 | 498 | ||
| 499 | // This needs to be done before unlocking the mutex to avoid a race with | ||
| 500 | // the associated task finishing. | ||
| 496 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); | 501 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); |
| 497 | std.Thread.WaitGroup.startStateless(group_state); | 502 | std.Thread.WaitGroup.startStateless(group_state); |
| 498 | 503 | ||
| ... | @@ -500,21 +505,16 @@ fn groupAsync( | ... | @@ -500,21 +505,16 @@ fn groupAsync( |
| 500 | pool.cond.signal(); | 505 | pool.cond.signal(); |
| 501 | } | 506 | } |
| 502 | 507 | ||
| 503 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group) void { | 508 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { |
| 504 | if (builtin.single_threaded) return; | ||
| 505 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 509 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 506 | _ = pool; | 510 | _ = pool; |
| 511 | |||
| 512 | if (builtin.single_threaded) return; | ||
| 513 | |||
| 507 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); | 514 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); |
| 508 | const reset_event: *ResetEvent = @ptrCast(&group.context); | 515 | const reset_event: *ResetEvent = @ptrCast(&group.context); |
| 509 | std.Thread.WaitGroup.waitStateless(group_state, reset_event); | 516 | std.Thread.WaitGroup.waitStateless(group_state, reset_event); |
| 510 | } | ||
| 511 | 517 | ||
| 512 | fn groupCancel(userdata: ?*anyopaque, group: *Io.Group) void { | ||
| 513 | if (builtin.single_threaded) return; | ||
| 514 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | ||
| 515 | _ = pool; | ||
| 516 | const token = group.token.?; | ||
| 517 | group.token = null; | ||
| 518 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); | 518 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); |
| 519 | while (true) { | 519 | while (true) { |
| 520 | const gc: *GroupClosure = @fieldParentPtr("node", node); | 520 | const gc: *GroupClosure = @fieldParentPtr("node", node); |
| ... | @@ -523,6 +523,36 @@ fn groupCancel(userdata: ?*anyopaque, group: *Io.Group) void { | ... | @@ -523,6 +523,36 @@ fn groupCancel(userdata: ?*anyopaque, group: *Io.Group) void { |
| 523 | } | 523 | } |
| 524 | } | 524 | } |
| 525 | 525 | ||
| 526 | fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { | ||
| 527 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | ||
| 528 | const gpa = pool.allocator; | ||
| 529 | |||
| 530 | if (builtin.single_threaded) return; | ||
| 531 | |||
| 532 | { | ||
| 533 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); | ||
| 534 | while (true) { | ||
| 535 | const gc: *GroupClosure = @fieldParentPtr("node", node); | ||
| 536 | gc.closure.requestCancel(); | ||
| 537 | node = node.next orelse break; | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); | ||
| 542 | const reset_event: *ResetEvent = @ptrCast(&group.context); | ||
| 543 | std.Thread.WaitGroup.waitStateless(group_state, reset_event); | ||
| 544 | |||
| 545 | { | ||
| 546 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); | ||
| 547 | while (true) { | ||
| 548 | const gc: *GroupClosure = @fieldParentPtr("node", node); | ||
| 549 | const node_next = node.next; | ||
| 550 | gc.free(gpa); | ||
| 551 | node = node_next orelse break; | ||
| 552 | } | ||
| 553 | } | ||
| 554 | } | ||
| 555 | |||
| 526 | fn await( | 556 | fn await( |
| 527 | userdata: ?*anyopaque, | 557 | userdata: ?*anyopaque, |
| 528 | any_future: *Io.AnyFuture, | 558 | any_future: *Io.AnyFuture, |
| ... | @@ -774,7 +804,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File | ... | @@ -774,7 +804,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File |
| 774 | .SUCCESS => return nread, | 804 | .SUCCESS => return nread, |
| 775 | .INTR => unreachable, | 805 | .INTR => unreachable, |
| 776 | .INVAL => unreachable, | 806 | .INVAL => unreachable, |
| 777 | .FAULT => unreachable, | 807 | .FAULT => |err| return errnoBug(err), |
| 778 | .AGAIN => unreachable, // currently not support in WASI | 808 | .AGAIN => unreachable, // currently not support in WASI |
| 779 | .BADF => return error.NotOpenForReading, // can be a race condition | 809 | .BADF => return error.NotOpenForReading, // can be a race condition |
| 780 | .IO => return error.InputOutput, | 810 | .IO => return error.InputOutput, |
| ... | @@ -796,7 +826,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File | ... | @@ -796,7 +826,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File |
| 796 | .SUCCESS => return @intCast(rc), | 826 | .SUCCESS => return @intCast(rc), |
| 797 | .INTR => continue, | 827 | .INTR => continue, |
| 798 | .INVAL => unreachable, | 828 | .INVAL => unreachable, |
| 799 | .FAULT => unreachable, | 829 | .FAULT => |err| return errnoBug(err), |
| 800 | .SRCH => return error.ProcessNotFound, | 830 | .SRCH => return error.ProcessNotFound, |
| 801 | .AGAIN => return error.WouldBlock, | 831 | .AGAIN => return error.WouldBlock, |
| 802 | .BADF => return error.NotOpenForReading, // can be a race condition | 832 | .BADF => return error.NotOpenForReading, // can be a race condition |
| ... | @@ -896,7 +926,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset | ... | @@ -896,7 +926,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset |
| 896 | .SUCCESS => return nread, | 926 | .SUCCESS => return nread, |
| 897 | .INTR => unreachable, | 927 | .INTR => unreachable, |
| 898 | .INVAL => unreachable, | 928 | .INVAL => unreachable, |
| 899 | .FAULT => unreachable, | 929 | .FAULT => |err| return errnoBug(err), |
| 900 | .AGAIN => unreachable, | 930 | .AGAIN => unreachable, |
| 901 | .BADF => return error.NotOpenForReading, // can be a race condition | 931 | .BADF => return error.NotOpenForReading, // can be a race condition |
| 902 | .IO => return error.InputOutput, | 932 | .IO => return error.InputOutput, |
| ... | @@ -922,7 +952,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset | ... | @@ -922,7 +952,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset |
| 922 | .SUCCESS => return @bitCast(rc), | 952 | .SUCCESS => return @bitCast(rc), |
| 923 | .INTR => continue, | 953 | .INTR => continue, |
| 924 | .INVAL => unreachable, | 954 | .INVAL => unreachable, |
| 925 | .FAULT => unreachable, | 955 | .FAULT => |err| return errnoBug(err), |
| 926 | .SRCH => return error.ProcessNotFound, | 956 | .SRCH => return error.ProcessNotFound, |
| 927 | .AGAIN => return error.WouldBlock, | 957 | .AGAIN => return error.WouldBlock, |
| 928 | .BADF => return error.NotOpenForReading, // can be a race condition | 958 | .BADF => return error.NotOpenForReading, // can be a race condition |
| ... | @@ -969,18 +999,19 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi | ... | @@ -969,18 +999,19 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi |
| 969 | }; | 999 | }; |
| 970 | } | 1000 | } |
| 971 | 1001 | ||
| 972 | fn now(userdata: ?*anyopaque, clockid: posix.clockid_t) Io.ClockGetTimeError!Io.Timestamp { | 1002 | fn now(userdata: ?*anyopaque, clockid: posix.clockid_t) Io.NowError!Io.Timestamp { |
| 973 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 1003 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 974 | try pool.checkCancel(); | 1004 | try pool.checkCancel(); |
| 975 | const timespec = try posix.clock_gettime(clockid); | 1005 | const timespec = try posix.clock_gettime(clockid); |
| 976 | return @enumFromInt(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec); | 1006 | return @enumFromInt(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec); |
| 977 | } | 1007 | } |
| 978 | 1008 | ||
| 979 | fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void { | 1009 | fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, timeout: Io.Timeout) Io.SleepError!void { |
| 980 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 1010 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 981 | const deadline_nanoseconds: i96 = switch (deadline) { | 1011 | const deadline_nanoseconds: i96 = switch (timeout) { |
| 1012 | .none => std.math.maxInt(i96), | ||
| 982 | .duration => |duration| duration.nanoseconds, | 1013 | .duration => |duration| duration.nanoseconds, |
| 983 | .timestamp => |timestamp| @intFromEnum(timestamp), | 1014 | .deadline => |deadline| @intFromEnum(deadline), |
| 984 | }; | 1015 | }; |
| 985 | var timespec: posix.timespec = .{ | 1016 | var timespec: posix.timespec = .{ |
| 986 | .sec = @intCast(@divFloor(deadline_nanoseconds, std.time.ns_per_s)), | 1017 | .sec = @intCast(@divFloor(deadline_nanoseconds, std.time.ns_per_s)), |
| ... | @@ -988,12 +1019,12 @@ fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) | ... | @@ -988,12 +1019,12 @@ fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) |
| 988 | }; | 1019 | }; |
| 989 | while (true) { | 1020 | while (true) { |
| 990 | try pool.checkCancel(); | 1021 | try pool.checkCancel(); |
| 991 | switch (std.os.linux.E.init(std.os.linux.clock_nanosleep(clockid, .{ .ABSTIME = switch (deadline) { | 1022 | switch (std.os.linux.E.init(std.os.linux.clock_nanosleep(clockid, .{ .ABSTIME = switch (timeout) { |
| 992 | .duration => false, | 1023 | .none, .duration => false, |
| 993 | .timestamp => true, | 1024 | .deadline => true, |
| 994 | } }, &timespec, &timespec))) { | 1025 | } }, &timespec, &timespec))) { |
| 995 | .SUCCESS => return, | 1026 | .SUCCESS => return, |
| 996 | .FAULT => unreachable, | 1027 | .FAULT => |err| return errnoBug(err), |
| 997 | .INTR => {}, | 1028 | .INTR => {}, |
| 998 | .INVAL => return error.UnsupportedClock, | 1029 | .INVAL => return error.UnsupportedClock, |
| 999 | else => |err| return posix.unexpectedErrno(err), | 1030 | else => |err| return posix.unexpectedErrno(err), |
| ... | @@ -1278,7 +1309,7 @@ fn netReadPosix(userdata: ?*anyopaque, stream: Io.net.Stream, data: [][]u8) Io.n | ... | @@ -1278,7 +1309,7 @@ fn netReadPosix(userdata: ?*anyopaque, stream: Io.net.Stream, data: [][]u8) Io.n |
| 1278 | fn netSend( | 1309 | fn netSend( |
| 1279 | userdata: ?*anyopaque, | 1310 | userdata: ?*anyopaque, |
| 1280 | handle: Io.net.Socket.Handle, | 1311 | handle: Io.net.Socket.Handle, |
| 1281 | address: Io.net.IpAddress, | 1312 | address: *const Io.net.IpAddress, |
| 1282 | data: []const u8, | 1313 | data: []const u8, |
| 1283 | ) Io.net.Socket.SendError!void { | 1314 | ) Io.net.Socket.SendError!void { |
| 1284 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 1315 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| ... | @@ -1293,15 +1324,15 @@ fn netSend( | ... | @@ -1293,15 +1324,15 @@ fn netSend( |
| 1293 | fn netReceive( | 1324 | fn netReceive( |
| 1294 | userdata: ?*anyopaque, | 1325 | userdata: ?*anyopaque, |
| 1295 | handle: Io.net.Socket.Handle, | 1326 | handle: Io.net.Socket.Handle, |
| 1296 | address: Io.net.IpAddress, | ||
| 1297 | buffer: []u8, | 1327 | buffer: []u8, |
| 1298 | ) Io.net.Socket.ReceiveError!void { | 1328 | timeout: Io.Timeout, |
| 1329 | ) Io.net.Socket.ReceiveTimeoutError!Io.net.ReceivedMessage { | ||
| 1299 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 1330 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1300 | try pool.checkCancel(); | 1331 | try pool.checkCancel(); |
| 1301 | 1332 | ||
| 1302 | _ = handle; | 1333 | _ = handle; |
| 1303 | _ = address; | ||
| 1304 | _ = buffer; | 1334 | _ = buffer; |
| 1335 | _ = timeout; | ||
| 1305 | @panic("TODO"); | 1336 | @panic("TODO"); |
| 1306 | } | 1337 | } |
| 1307 | 1338 |
lib/std/Io/net.zig+32-10| ... | @@ -134,13 +134,13 @@ pub const IpAddress = union(enum) { | ... | @@ -134,13 +134,13 @@ pub const IpAddress = union(enum) { |
| 134 | } | 134 | } |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | pub fn eql(a: IpAddress, b: IpAddress) bool { | 137 | pub fn eql(a: *const IpAddress, b: *const IpAddress) bool { |
| 138 | return switch (a) { | 138 | return switch (a.*) { |
| 139 | .ip4 => |a_ip4| switch (b) { | 139 | .ip4 => |a_ip4| switch (b.*) { |
| 140 | .ip4 => |b_ip4| a_ip4.eql(b_ip4), | 140 | .ip4 => |b_ip4| a_ip4.eql(b_ip4), |
| 141 | else => false, | 141 | else => false, |
| 142 | }, | 142 | }, |
| 143 | .ip6 => |a_ip6| switch (b) { | 143 | .ip6 => |a_ip6| switch (b.*) { |
| 144 | .ip6 => |b_ip6| a_ip6.eql(b_ip6), | 144 | .ip6 => |b_ip6| a_ip6.eql(b_ip6), |
| 145 | else => false, | 145 | else => false, |
| 146 | }, | 146 | }, |
| ... | @@ -695,6 +695,11 @@ pub const Ip6Address = struct { | ... | @@ -695,6 +695,11 @@ pub const Ip6Address = struct { |
| 695 | }; | 695 | }; |
| 696 | }; | 696 | }; |
| 697 | 697 | ||
| 698 | pub const ReceivedMessage = struct { | ||
| 699 | from: IpAddress, | ||
| 700 | len: usize, | ||
| 701 | }; | ||
| 702 | |||
| 698 | pub const Interface = struct { | 703 | pub const Interface = struct { |
| 699 | /// Value 0 indicates `none`. | 704 | /// Value 0 indicates `none`. |
| 700 | index: u32, | 705 | index: u32, |
| ... | @@ -816,14 +821,31 @@ pub const Socket = struct { | ... | @@ -816,14 +821,31 @@ pub const Socket = struct { |
| 816 | return io.vtable.netSend(io.userdata, s.handle, dest, data); | 821 | return io.vtable.netSend(io.userdata, s.handle, dest, data); |
| 817 | } | 822 | } |
| 818 | 823 | ||
| 819 | pub const ReceiveError = error{} || Io.Cancelable; | 824 | pub const ReceiveError = error{} || Io.UnexpectedError || Io.Cancelable; |
| 820 | 825 | ||
| 821 | /// Transfers `data` from `source`, connectionless. | 826 | /// Waits for data. Connectionless. |
| 822 | /// | 827 | /// |
| 823 | /// Returned slice has same pointer as `buffer` with possibly shorter length. | 828 | /// See also: |
| 824 | pub fn receive(s: *const Socket, io: Io, source: *const IpAddress, buffer: []u8) ReceiveError![]u8 { | 829 | /// * `receiveTimeout` |
| 825 | const n = try io.vtable.netReceive(io.userdata, s.handle, source, buffer); | 830 | pub fn receive(s: *const Socket, io: Io, source: *const IpAddress, buffer: []u8) ReceiveError!ReceivedMessage { |
| 826 | return buffer[0..n]; | 831 | return io.vtable.netReceive(io.userdata, s.handle, source, buffer, .none); |
| 832 | } | ||
| 833 | |||
| 834 | pub const ReceiveTimeoutError = ReceiveError || Io.Timeout.Error; | ||
| 835 | |||
| 836 | /// Waits for data. Connectionless. | ||
| 837 | /// | ||
| 838 | /// Returns `error.Timeout` if no message arrives early enough. | ||
| 839 | /// | ||
| 840 | /// See also: | ||
| 841 | /// * `receive` | ||
| 842 | pub fn receiveTimeout( | ||
| 843 | s: *const Socket, | ||
| 844 | io: Io, | ||
| 845 | buffer: []u8, | ||
| 846 | timeout: Io.Timeout, | ||
| 847 | ) ReceiveTimeoutError!ReceivedMessage { | ||
| 848 | return io.vtable.netReceive(io.userdata, s.handle, buffer, timeout); | ||
| 827 | } | 849 | } |
| 828 | }; | 850 | }; |
| 829 | 851 |
lib/std/Io/net/HostName.zig+113-93| ... | @@ -46,15 +46,13 @@ pub const LookupOptions = struct { | ... | @@ -46,15 +46,13 @@ pub const LookupOptions = struct { |
| 46 | family: ?IpAddress.Family = null, | 46 | family: ?IpAddress.Family = null, |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | pub const LookupError = Io.Cancelable || Io.File.OpenError || Io.File.Reader.Error || error{ | 49 | pub const LookupError = error{ |
| 50 | UnknownHostName, | 50 | UnknownHostName, |
| 51 | ResolvConfParseFailed, | 51 | ResolvConfParseFailed, |
| 52 | // TODO remove from error set; retry a few times then report a different error | ||
| 53 | TemporaryNameServerFailure, | ||
| 54 | InvalidDnsARecord, | 52 | InvalidDnsARecord, |
| 55 | InvalidDnsAAAARecord, | 53 | InvalidDnsAAAARecord, |
| 56 | NameServerFailure, | 54 | NameServerFailure, |
| 57 | }; | 55 | } || Io.NowError || IpAddress.BindError || Io.File.OpenError || Io.File.Reader.Error || Io.Cancelable; |
| 58 | 56 | ||
| 59 | pub const LookupResult = struct { | 57 | pub const LookupResult = struct { |
| 60 | /// How many `LookupOptions.addresses_buffer` elements are populated. | 58 | /// How many `LookupOptions.addresses_buffer` elements are populated. |
| ... | @@ -185,7 +183,7 @@ fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult | ... | @@ -185,7 +183,7 @@ fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult |
| 185 | return result; | 183 | return result; |
| 186 | } | 184 | } |
| 187 | 185 | ||
| 188 | fn lookupDnsSearch(host_name: HostName, io: Io, options: LookupOptions) !LookupResult { | 186 | fn lookupDnsSearch(host_name: HostName, io: Io, options: LookupOptions) LookupError!LookupResult { |
| 189 | const rc = ResolvConf.init(io) catch return error.ResolvConfParseFailed; | 187 | const rc = ResolvConf.init(io) catch return error.ResolvConfParseFailed; |
| 190 | 188 | ||
| 191 | // Count dots, suppress search when >=ndots or name ends in | 189 | // Count dots, suppress search when >=ndots or name ends in |
| ... | @@ -218,19 +216,17 @@ fn lookupDnsSearch(host_name: HostName, io: Io, options: LookupOptions) !LookupR | ... | @@ -218,19 +216,17 @@ fn lookupDnsSearch(host_name: HostName, io: Io, options: LookupOptions) !LookupR |
| 218 | return lookupDns(io, lookup_canon_name, &rc, options); | 216 | return lookupDns(io, lookup_canon_name, &rc, options); |
| 219 | } | 217 | } |
| 220 | 218 | ||
| 221 | const DnsReply = struct { | 219 | fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, options: LookupOptions) LookupError!LookupResult { |
| 222 | buf: [512]u8, | ||
| 223 | len: usize, | ||
| 224 | }; | ||
| 225 | |||
| 226 | fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, options: LookupOptions) !LookupResult { | ||
| 227 | const family_records: [2]struct { af: IpAddress.Family, rr: u8 } = .{ | 220 | const family_records: [2]struct { af: IpAddress.Family, rr: u8 } = .{ |
| 228 | .{ .af = .ip6, .rr = std.posix.RR.A }, | 221 | .{ .af = .ip6, .rr = std.posix.RR.A }, |
| 229 | .{ .af = .ip4, .rr = std.posix.RR.AAAA }, | 222 | .{ .af = .ip4, .rr = std.posix.RR.AAAA }, |
| 230 | }; | 223 | }; |
| 231 | var query_buffers: [2][280]u8 = undefined; | 224 | var query_buffers: [2][280]u8 = undefined; |
| 225 | var answer_buffers: [2][512]u8 = undefined; | ||
| 232 | var queries_buffer: [2][]const u8 = undefined; | 226 | var queries_buffer: [2][]const u8 = undefined; |
| 227 | var answers_buffer: [2][]const u8 = undefined; | ||
| 233 | var nq: usize = 0; | 228 | var nq: usize = 0; |
| 229 | var next_answer_buffer: usize = 0; | ||
| 234 | 230 | ||
| 235 | for (family_records) |fr| { | 231 | for (family_records) |fr| { |
| 236 | if (options.family != fr.af) { | 232 | if (options.family != fr.af) { |
| ... | @@ -241,41 +237,123 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio | ... | @@ -241,41 +237,123 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio |
| 241 | } | 237 | } |
| 242 | } | 238 | } |
| 243 | 239 | ||
| 240 | var ip4_mapped: [ResolvConf.max_nameservers]IpAddress = undefined; | ||
| 241 | var any_ip6 = false; | ||
| 242 | for (rc.nameservers(), &ip4_mapped) |*ns, *m| { | ||
| 243 | m.* = .{ .ip6 = .fromAny(ns.*) }; | ||
| 244 | any_ip6 = any_ip6 or ns.* == .ip6; | ||
| 245 | } | ||
| 246 | var socket = s: { | ||
| 247 | if (any_ip6) ip6: { | ||
| 248 | const ip6_addr: IpAddress = .{ .ip6 = .unspecified(0) }; | ||
| 249 | const socket = ip6_addr.bind(io, .{ .ip6_only = true, .mode = .dgram }) catch |err| switch (err) { | ||
| 250 | error.AddressFamilyUnsupported => break :ip6, | ||
| 251 | else => |e| return e, | ||
| 252 | }; | ||
| 253 | break :s socket; | ||
| 254 | } | ||
| 255 | any_ip6 = false; | ||
| 256 | const ip4_addr: IpAddress = .{ .ip4 = .unspecified(0) }; | ||
| 257 | const socket = try ip4_addr.bind(io, .{ .mode = .dgram }); | ||
| 258 | break :s socket; | ||
| 259 | }; | ||
| 260 | defer socket.close(io); | ||
| 261 | |||
| 262 | const mapped_nameservers = if (any_ip6) ip4_mapped[0..rc.nameservers_len] else rc.nameservers(); | ||
| 244 | const queries = queries_buffer[0..nq]; | 263 | const queries = queries_buffer[0..nq]; |
| 245 | var replies_buffer: [2]DnsReply = undefined; | 264 | const answers = answers_buffer[0..queries.len]; |
| 246 | var replies: Io.Queue(DnsReply) = .init(&replies_buffer); | 265 | for (answers) |*answer| answer.len = 0; |
| 247 | try rc.sendMessage(io, queries, &replies); | 266 | |
| 248 | 267 | var now_ts = try io.now(.MONOTONIC); | |
| 249 | for (replies) |reply| { | 268 | const final_ts = now_ts.addDuration(.seconds(rc.timeout_seconds)); |
| 250 | if (reply.len < 4 or (reply[3] & 15) == 2) return error.TemporaryNameServerFailure; | 269 | const attempt_duration: Io.Duration = .{ |
| 251 | if ((reply[3] & 15) == 3) return .empty; | 270 | .nanoseconds = std.time.ns_per_s * @as(usize, rc.timeout_seconds) / rc.attempts, |
| 252 | if ((reply[3] & 15) != 0) return error.UnknownHostName; | 271 | }; |
| 272 | |||
| 273 | send: while (now_ts.compare(.lt, final_ts)) : (now_ts = try io.now(.MONOTONIC)) { | ||
| 274 | var group: Io.Group = .init; | ||
| 275 | defer group.cancel(io); | ||
| 276 | |||
| 277 | for (queries, answers) |query, *answer| { | ||
| 278 | if (answer.len != 0) continue; | ||
| 279 | for (mapped_nameservers) |*ns| { | ||
| 280 | group.async(io, sendIgnoringResult, .{ io, socket.handle, ns, query }); | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | const timeout: Io.Timeout = .{ .deadline = now_ts.addDuration(attempt_duration) }; | ||
| 285 | |||
| 286 | while (true) { | ||
| 287 | const buf = &answer_buffers[next_answer_buffer]; | ||
| 288 | const reply = socket.receiveTimeout(io, buf, timeout) catch |err| switch (err) { | ||
| 289 | error.Canceled => return error.Canceled, | ||
| 290 | error.Timeout => continue :send, | ||
| 291 | else => continue, | ||
| 292 | }; | ||
| 293 | |||
| 294 | // Ignore non-identifiable packets. | ||
| 295 | if (reply.len < 4) continue; | ||
| 296 | |||
| 297 | // Ignore replies from addresses we didn't send to. | ||
| 298 | const ns = for (mapped_nameservers) |*ns| { | ||
| 299 | if (reply.from.eql(ns)) break ns; | ||
| 300 | } else { | ||
| 301 | continue; | ||
| 302 | }; | ||
| 303 | |||
| 304 | const reply_msg = buf[0..reply.len]; | ||
| 305 | |||
| 306 | // Find which query this answer goes with, if any. | ||
| 307 | const query, const answer = for (queries, answers) |query, *answer| { | ||
| 308 | if (reply_msg[0] == query[0] and reply_msg[1] == query[1]) break .{ query, answer }; | ||
| 309 | } else { | ||
| 310 | continue; | ||
| 311 | }; | ||
| 312 | if (answer.len != 0) continue; | ||
| 313 | |||
| 314 | // Only accept positive or negative responses; retry immediately on | ||
| 315 | // server failure, and ignore all other codes such as refusal. | ||
| 316 | switch (reply_msg[3] & 15) { | ||
| 317 | 0, 3 => { | ||
| 318 | answer.* = reply_msg; | ||
| 319 | next_answer_buffer += 1; | ||
| 320 | if (next_answer_buffer == answers.len) break :send; | ||
| 321 | }, | ||
| 322 | 2 => { | ||
| 323 | group.async(io, sendIgnoringResult, .{ io, socket.handle, ns, query }); | ||
| 324 | continue; | ||
| 325 | }, | ||
| 326 | else => continue, | ||
| 327 | } | ||
| 328 | } | ||
| 329 | } else { | ||
| 330 | return error.NameServerFailure; | ||
| 253 | } | 331 | } |
| 254 | 332 | ||
| 255 | var addresses_len: usize = 0; | 333 | var addresses_len: usize = 0; |
| 256 | var canonical_name: ?HostName = null; | 334 | var canonical_name: ?HostName = null; |
| 257 | 335 | ||
| 258 | for (replies) |reply| { | 336 | for (answers) |answer| { |
| 259 | var it = DnsResponse.init(reply) catch { | 337 | var it = DnsResponse.init(answer) catch { |
| 260 | // TODO accept a diagnostics struct and append warnings | 338 | // TODO accept a diagnostics struct and append warnings |
| 261 | continue; | 339 | continue; |
| 262 | }; | 340 | }; |
| 263 | while (it.next() catch { | 341 | while (it.next() catch { |
| 264 | // TODO accept a diagnostics struct and append warnings | 342 | // TODO accept a diagnostics struct and append warnings |
| 265 | continue; | 343 | continue; |
| 266 | }) |answer| switch (answer.rr) { | 344 | }) |record| switch (record.rr) { |
| 267 | std.posix.RR.A => { | 345 | std.posix.RR.A => { |
| 268 | if (answer.data.len != 4) return error.InvalidDnsARecord; | 346 | if (record.data.len != 4) return error.InvalidDnsARecord; |
| 269 | options.addresses_buffer[addresses_len] = .{ .ip4 = .{ | 347 | options.addresses_buffer[addresses_len] = .{ .ip4 = .{ |
| 270 | .bytes = answer.data[0..4].*, | 348 | .bytes = record.data[0..4].*, |
| 271 | .port = options.port, | 349 | .port = options.port, |
| 272 | } }; | 350 | } }; |
| 273 | addresses_len += 1; | 351 | addresses_len += 1; |
| 274 | }, | 352 | }, |
| 275 | std.posix.RR.AAAA => { | 353 | std.posix.RR.AAAA => { |
| 276 | if (answer.data.len != 16) return error.InvalidDnsAAAARecord; | 354 | if (record.data.len != 16) return error.InvalidDnsAAAARecord; |
| 277 | options.addresses_buffer[addresses_len] = .{ .ip6 = .{ | 355 | options.addresses_buffer[addresses_len] = .{ .ip6 = .{ |
| 278 | .bytes = answer.data[0..16].*, | 356 | .bytes = record.data[0..16].*, |
| 279 | .port = options.port, | 357 | .port = options.port, |
| 280 | } }; | 358 | } }; |
| 281 | addresses_len += 1; | 359 | addresses_len += 1; |
| ... | @@ -285,7 +363,7 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio | ... | @@ -285,7 +363,7 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio |
| 285 | @panic("TODO"); | 363 | @panic("TODO"); |
| 286 | //var tmp: [256]u8 = undefined; | 364 | //var tmp: [256]u8 = undefined; |
| 287 | //// Returns len of compressed name. strlen to get canon name. | 365 | //// Returns len of compressed name. strlen to get canon name. |
| 288 | //_ = try posix.dn_expand(packet, answer.data, &tmp); | 366 | //_ = try posix.dn_expand(packet, record.data, &tmp); |
| 289 | //const canon_name = mem.sliceTo(&tmp, 0); | 367 | //const canon_name = mem.sliceTo(&tmp, 0); |
| 290 | //if (isValidHostName(canon_name)) { | 368 | //if (isValidHostName(canon_name)) { |
| 291 | // ctx.canon.items.len = 0; | 369 | // ctx.canon.items.len = 0; |
| ... | @@ -304,6 +382,10 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio | ... | @@ -304,6 +382,10 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio |
| 304 | return error.NameServerFailure; | 382 | return error.NameServerFailure; |
| 305 | } | 383 | } |
| 306 | 384 | ||
| 385 | fn sendIgnoringResult(io: Io, socket_handle: Io.net.Socket.Handle, dest: *const IpAddress, msg: []const u8) void { | ||
| 386 | _ = io.vtable.netSend(io.userdata, socket_handle, dest, msg) catch {}; | ||
| 387 | } | ||
| 388 | |||
| 307 | fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResult { | 389 | fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResult { |
| 308 | const file = Io.File.openAbsolute(io, "/etc/hosts", .{}) catch |err| switch (err) { | 390 | const file = Io.File.openAbsolute(io, "/etc/hosts", .{}) catch |err| switch (err) { |
| 309 | error.FileNotFound, | 391 | error.FileNotFound, |
| ... | @@ -523,7 +605,7 @@ pub fn connectTcp(host_name: HostName, io: Io, port: u16) ConnectTcpError!Stream | ... | @@ -523,7 +605,7 @@ pub fn connectTcp(host_name: HostName, io: Io, port: u16) ConnectTcpError!Stream |
| 523 | pub const ResolvConf = struct { | 605 | pub const ResolvConf = struct { |
| 524 | attempts: u32, | 606 | attempts: u32, |
| 525 | ndots: u32, | 607 | ndots: u32, |
| 526 | timeout: Io.Duration, | 608 | timeout_seconds: u32, |
| 527 | nameservers_buffer: [max_nameservers]IpAddress, | 609 | nameservers_buffer: [max_nameservers]IpAddress, |
| 528 | nameservers_len: usize, | 610 | nameservers_len: usize, |
| 529 | search_buffer: [max_len]u8, | 611 | search_buffer: [max_len]u8, |
| ... | @@ -539,7 +621,7 @@ pub const ResolvConf = struct { | ... | @@ -539,7 +621,7 @@ pub const ResolvConf = struct { |
| 539 | .search_buffer = undefined, | 621 | .search_buffer = undefined, |
| 540 | .search_len = 0, | 622 | .search_len = 0, |
| 541 | .ndots = 1, | 623 | .ndots = 1, |
| 542 | .timeout = .seconds(5), | 624 | .timeout_seconds = 5, |
| 543 | .attempts = 2, | 625 | .attempts = 2, |
| 544 | }; | 626 | }; |
| 545 | 627 | ||
| ... | @@ -589,7 +671,7 @@ pub const ResolvConf = struct { | ... | @@ -589,7 +671,7 @@ pub const ResolvConf = struct { |
| 589 | switch (std.meta.stringToEnum(Option, name) orelse continue) { | 671 | switch (std.meta.stringToEnum(Option, name) orelse continue) { |
| 590 | .ndots => rc.ndots = @min(value, 15), | 672 | .ndots => rc.ndots = @min(value, 15), |
| 591 | .attempts => rc.attempts = @min(value, 10), | 673 | .attempts => rc.attempts = @min(value, 10), |
| 592 | .timeout => rc.timeout = .seconds(@min(value, 60)), | 674 | .timeout => rc.timeout_seconds = @min(value, 60), |
| 593 | } | 675 | } |
| 594 | }, | 676 | }, |
| 595 | .nameserver => { | 677 | .nameserver => { |
| ... | @@ -621,68 +703,6 @@ pub const ResolvConf = struct { | ... | @@ -621,68 +703,6 @@ pub const ResolvConf = struct { |
| 621 | fn nameservers(rc: *const ResolvConf) []const IpAddress { | 703 | fn nameservers(rc: *const ResolvConf) []const IpAddress { |
| 622 | return rc.nameservers_buffer[0..rc.nameservers_len]; | 704 | return rc.nameservers_buffer[0..rc.nameservers_len]; |
| 623 | } | 705 | } |
| 624 | |||
| 625 | fn sendMessage( | ||
| 626 | rc: *const ResolvConf, | ||
| 627 | io: Io, | ||
| 628 | queries: []const []const u8, | ||
| 629 | replies: *Io.Queue(DnsReply), | ||
| 630 | ) !void { | ||
| 631 | var ip4_mapped: [ResolvConf.max_nameservers]IpAddress = undefined; | ||
| 632 | var any_ip6 = false; | ||
| 633 | for (rc.nameservers(), &ip4_mapped) |*ns, *m| { | ||
| 634 | m.* = .{ .ip6 = .fromAny(ns.*) }; | ||
| 635 | any_ip6 = any_ip6 or ns.* == .ip6; | ||
| 636 | } | ||
| 637 | |||
| 638 | const socket = s: { | ||
| 639 | if (any_ip6) ip6: { | ||
| 640 | const ip6_addr: IpAddress = .{ .ip6 = .unspecified(0) }; | ||
| 641 | const socket = ip6_addr.bind(io, .{ .ip6_only = true, .mode = .dgram }) catch |err| switch (err) { | ||
| 642 | error.AddressFamilyUnsupported => break :ip6, | ||
| 643 | else => |e| return e, | ||
| 644 | }; | ||
| 645 | break :s socket; | ||
| 646 | } | ||
| 647 | any_ip6 = false; | ||
| 648 | const ip4_addr: IpAddress = .{ .ip4 = .unspecified(0) }; | ||
| 649 | const socket = try ip4_addr.bind(io, .{ .mode = .dgram }); | ||
| 650 | break :s socket; | ||
| 651 | }; | ||
| 652 | defer socket.close(); | ||
| 653 | |||
| 654 | const mapped_nameservers = if (any_ip6) ip4_mapped[0..rc.nameservers_len] else rc.nameservers(); | ||
| 655 | |||
| 656 | var group: Io.Group = .init; | ||
| 657 | defer group.cancel(); | ||
| 658 | |||
| 659 | for (queries) |query| { | ||
| 660 | for (mapped_nameservers) |*ns| { | ||
| 661 | group.async(sendOneMessage, .{ io, query, ns }); | ||
| 662 | } | ||
| 663 | } | ||
| 664 | |||
| 665 | const deadline: Io.Deadline = .fromDuration(rc.timeout); | ||
| 666 | |||
| 667 | for (0..queries.len) |_| { | ||
| 668 | const msg = socket.receiveDeadline(deadline) catch |err| switch (err) { | ||
| 669 | error.Timeout => return error.Timeout, | ||
| 670 | error.Canceled => return error.Canceled, | ||
| 671 | else => continue, | ||
| 672 | }; | ||
| 673 | _ = msg; | ||
| 674 | _ = replies; | ||
| 675 | @panic("TODO check msg for dns reply and put into replies queue"); | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | fn sendOneMessage( | ||
| 680 | io: Io, | ||
| 681 | query: []const u8, | ||
| 682 | ns: *const IpAddress, | ||
| 683 | ) void { | ||
| 684 | io.vtable.netSend(io.userdata, ns.*, &.{query}) catch |err| switch (err) {}; | ||
| 685 | } | ||
| 686 | }; | 706 | }; |
| 687 | 707 | ||
| 688 | test ResolvConf { | 708 | test ResolvConf { |
| ... | @@ -702,7 +722,7 @@ test ResolvConf { | ... | @@ -702,7 +722,7 @@ test ResolvConf { |
| 702 | .search_buffer = undefined, | 722 | .search_buffer = undefined, |
| 703 | .search_len = 0, | 723 | .search_len = 0, |
| 704 | .ndots = 1, | 724 | .ndots = 1, |
| 705 | .timeout = .seconds(5), | 725 | .timeout_seconds = 5, |
| 706 | .attempts = 2, | 726 | .attempts = 2, |
| 707 | }; | 727 | }; |
| 708 | 728 |