| ... | @@ -2830,6 +2830,390 @@ test "linkat" { | ... | @@ -2830,6 +2830,390 @@ test "linkat" { |
| 2830 | try testing.expectEqualStrings("hello", second_file_data[0..read]); | 2830 | try testing.expectEqualStrings("hello", second_file_data[0..read]); |
| 2831 | } | 2831 | } |
| 2832 | | 2832 | |
| | 2833 | test "provide_buffers: read" { |
| | 2834 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| | 2835 | |
| | 2836 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| | 2837 | error.SystemOutdated => return error.SkipZigTest, |
| | 2838 | error.PermissionDenied => return error.SkipZigTest, |
| | 2839 | else => return err, |
| | 2840 | }; |
| | 2841 | defer ring.deinit(); |
| | 2842 | |
| | 2843 | const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); |
| | 2844 | defer os.close(fd); |
| | 2845 | |
| | 2846 | const group_id = 1337; |
| | 2847 | const buffer_id = 0; |
| | 2848 | |
| | 2849 | const buffer_len = 128; |
| | 2850 | |
| | 2851 | var buffers: [4][buffer_len]u8 = undefined; |
| | 2852 | |
| | 2853 | // Provide 4 buffers |
| | 2854 | |
| | 2855 | { |
| | 2856 | const sqe = try ring.provide_buffers(0xcccccccc, @ptrCast([*]u8, &buffers), buffers.len, buffer_len, group_id, buffer_id); |
| | 2857 | try testing.expectEqual(linux.IORING_OP.PROVIDE_BUFFERS, sqe.opcode); |
| | 2858 | try testing.expectEqual(@as(i32, buffers.len), sqe.fd); |
| | 2859 | try testing.expectEqual(@as(u32, buffers[0].len), sqe.len); |
| | 2860 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 2861 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2862 | |
| | 2863 | const cqe = try ring.copy_cqe(); |
| | 2864 | switch (cqe.err()) { |
| | 2865 | // Happens when the kernel is < 5.7 |
| | 2866 | .INVAL => return error.SkipZigTest, |
| | 2867 | .SUCCESS => {}, |
| | 2868 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2869 | } |
| | 2870 | try testing.expectEqual(@as(u64, 0xcccccccc), cqe.user_data); |
| | 2871 | } |
| | 2872 | |
| | 2873 | // Do 4 reads which should consume all buffers |
| | 2874 | |
| | 2875 | var i: usize = 0; |
| | 2876 | while (i < buffers.len) : (i += 1) { |
| | 2877 | var sqe = try ring.read(0xdededede, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 2878 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); |
| | 2879 | try testing.expectEqual(@as(i32, fd), sqe.fd); |
| | 2880 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| | 2881 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| | 2882 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 2883 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2884 | |
| | 2885 | const cqe = try ring.copy_cqe(); |
| | 2886 | switch (cqe.err()) { |
| | 2887 | .SUCCESS => {}, |
| | 2888 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2889 | } |
| | 2890 | |
| | 2891 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); |
| | 2892 | const used_buffer_id = cqe.flags >> 16; |
| | 2893 | try testing.expect(used_buffer_id >= 0 and used_buffer_id <= 3); |
| | 2894 | try testing.expectEqual(@as(i32, buffer_len), cqe.res); |
| | 2895 | |
| | 2896 | try testing.expectEqual(@as(u64, 0xdededede), cqe.user_data); |
| | 2897 | try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer_len), buffers[used_buffer_id][0..@intCast(usize, cqe.res)]); |
| | 2898 | } |
| | 2899 | |
| | 2900 | // This read should fail |
| | 2901 | |
| | 2902 | { |
| | 2903 | var sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 2904 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); |
| | 2905 | try testing.expectEqual(@as(i32, fd), sqe.fd); |
| | 2906 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| | 2907 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| | 2908 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 2909 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2910 | |
| | 2911 | const cqe = try ring.copy_cqe(); |
| | 2912 | switch (cqe.err()) { |
| | 2913 | // Expected |
| | 2914 | .NOBUFS => {}, |
| | 2915 | .SUCCESS => std.debug.panic("unexpected success", .{}), |
| | 2916 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2917 | } |
| | 2918 | try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); |
| | 2919 | } |
| | 2920 | |
| | 2921 | // Provide 1 buffer again |
| | 2922 | |
| | 2923 | // Deliberately put something we don't expect in the buffers |
| | 2924 | mem.set(u8, mem.sliceAsBytes(&buffers), 42); |
| | 2925 | |
| | 2926 | const reprovided_buffer_id = 2; |
| | 2927 | |
| | 2928 | { |
| | 2929 | _ = try ring.provide_buffers(0xabababab, @ptrCast([*]u8, &buffers[reprovided_buffer_id]), 1, buffer_len, group_id, reprovided_buffer_id); |
| | 2930 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2931 | |
| | 2932 | const cqe = try ring.copy_cqe(); |
| | 2933 | switch (cqe.err()) { |
| | 2934 | .SUCCESS => {}, |
| | 2935 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2936 | } |
| | 2937 | } |
| | 2938 | |
| | 2939 | // Final read which should work |
| | 2940 | |
| | 2941 | { |
| | 2942 | var sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 2943 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); |
| | 2944 | try testing.expectEqual(@as(i32, fd), sqe.fd); |
| | 2945 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| | 2946 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| | 2947 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 2948 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2949 | |
| | 2950 | const cqe = try ring.copy_cqe(); |
| | 2951 | switch (cqe.err()) { |
| | 2952 | .SUCCESS => {}, |
| | 2953 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2954 | } |
| | 2955 | |
| | 2956 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); |
| | 2957 | const used_buffer_id = cqe.flags >> 16; |
| | 2958 | try testing.expectEqual(used_buffer_id, reprovided_buffer_id); |
| | 2959 | try testing.expectEqual(@as(i32, buffer_len), cqe.res); |
| | 2960 | try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); |
| | 2961 | try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer_len), buffers[used_buffer_id][0..@intCast(usize, cqe.res)]); |
| | 2962 | } |
| | 2963 | } |
| | 2964 | |
| | 2965 | test "remove_buffers" { |
| | 2966 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| | 2967 | |
| | 2968 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| | 2969 | error.SystemOutdated => return error.SkipZigTest, |
| | 2970 | error.PermissionDenied => return error.SkipZigTest, |
| | 2971 | else => return err, |
| | 2972 | }; |
| | 2973 | defer ring.deinit(); |
| | 2974 | |
| | 2975 | const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); |
| | 2976 | defer os.close(fd); |
| | 2977 | |
| | 2978 | const group_id = 1337; |
| | 2979 | const buffer_id = 0; |
| | 2980 | |
| | 2981 | const buffer_len = 128; |
| | 2982 | |
| | 2983 | var buffers: [4][buffer_len]u8 = undefined; |
| | 2984 | |
| | 2985 | // Provide 4 buffers |
| | 2986 | |
| | 2987 | { |
| | 2988 | _ = try ring.provide_buffers(0xcccccccc, @ptrCast([*]u8, &buffers), buffers.len, buffer_len, group_id, buffer_id); |
| | 2989 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2990 | |
| | 2991 | const cqe = try ring.copy_cqe(); |
| | 2992 | switch (cqe.err()) { |
| | 2993 | .SUCCESS => {}, |
| | 2994 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2995 | } |
| | 2996 | try testing.expectEqual(@as(u64, 0xcccccccc), cqe.user_data); |
| | 2997 | } |
| | 2998 | |
| | 2999 | // Remove the first 3 buffers |
| | 3000 | |
| | 3001 | { |
| | 3002 | var sqe = try ring.remove_buffers(0xbababababa, 3, group_id); |
| | 3003 | try testing.expectEqual(linux.IORING_OP.REMOVE_BUFFERS, sqe.opcode); |
| | 3004 | try testing.expectEqual(@as(i32, 3), sqe.fd); |
| | 3005 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| | 3006 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 3007 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3008 | |
| | 3009 | const cqe = try ring.copy_cqe(); |
| | 3010 | switch (cqe.err()) { |
| | 3011 | .SUCCESS => {}, |
| | 3012 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3013 | } |
| | 3014 | try testing.expectEqual(@as(u64, 0xbababababa), cqe.user_data); |
| | 3015 | } |
| | 3016 | |
| | 3017 | // This read should work |
| | 3018 | |
| | 3019 | { |
| | 3020 | _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 3021 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3022 | |
| | 3023 | const cqe = try ring.copy_cqe(); |
| | 3024 | switch (cqe.err()) { |
| | 3025 | .SUCCESS => {}, |
| | 3026 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3027 | } |
| | 3028 | |
| | 3029 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); |
| | 3030 | const used_buffer_id = cqe.flags >> 16; |
| | 3031 | try testing.expectEqual(used_buffer_id, 0); |
| | 3032 | try testing.expectEqual(@as(i32, buffer_len), cqe.res); |
| | 3033 | try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); |
| | 3034 | try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer_len), buffers[used_buffer_id][0..@intCast(usize, cqe.res)]); |
| | 3035 | } |
| | 3036 | |
| | 3037 | // Final read should _not_ work |
| | 3038 | |
| | 3039 | { |
| | 3040 | _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 3041 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3042 | |
| | 3043 | const cqe = try ring.copy_cqe(); |
| | 3044 | switch (cqe.err()) { |
| | 3045 | // Expected |
| | 3046 | .NOBUFS => {}, |
| | 3047 | .SUCCESS => std.debug.panic("unexpected success", .{}), |
| | 3048 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3049 | } |
| | 3050 | } |
| | 3051 | } |
| | 3052 | |
| | 3053 | test "provide_buffers: accept/connect/send/recv" { |
| | 3054 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| | 3055 | |
| | 3056 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| | 3057 | error.SystemOutdated => return error.SkipZigTest, |
| | 3058 | error.PermissionDenied => return error.SkipZigTest, |
| | 3059 | else => return err, |
| | 3060 | }; |
| | 3061 | defer ring.deinit(); |
| | 3062 | |
| | 3063 | const group_id = 1337; |
| | 3064 | const buffer_id = 0; |
| | 3065 | |
| | 3066 | const buffer_len = 128; |
| | 3067 | var buffers: [4][buffer_len]u8 = undefined; |
| | 3068 | |
| | 3069 | // Provide 4 buffers |
| | 3070 | |
| | 3071 | { |
| | 3072 | const sqe = try ring.provide_buffers(0xcccccccc, @ptrCast([*]u8, &buffers), buffers.len, buffer_len, group_id, buffer_id); |
| | 3073 | try testing.expectEqual(linux.IORING_OP.PROVIDE_BUFFERS, sqe.opcode); |
| | 3074 | try testing.expectEqual(@as(i32, buffers.len), sqe.fd); |
| | 3075 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| | 3076 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 3077 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3078 | |
| | 3079 | const cqe = try ring.copy_cqe(); |
| | 3080 | switch (cqe.err()) { |
| | 3081 | // Happens when the kernel is < 5.7 |
| | 3082 | .INVAL => return error.SkipZigTest, |
| | 3083 | .SUCCESS => {}, |
| | 3084 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3085 | } |
| | 3086 | try testing.expectEqual(@as(u64, 0xcccccccc), cqe.user_data); |
| | 3087 | } |
| | 3088 | |
| | 3089 | const socket_test_harness = try createSocketTestHarness(&ring); |
| | 3090 | defer socket_test_harness.close(); |
| | 3091 | |
| | 3092 | // Do 4 send on the socket |
| | 3093 | |
| | 3094 | { |
| | 3095 | var i: usize = 0; |
| | 3096 | while (i < buffers.len) : (i += 1) { |
| | 3097 | _ = try ring.send(0xdeaddead, socket_test_harness.server, &([_]u8{'z'} ** buffer_len), 0); |
| | 3098 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3099 | } |
| | 3100 | |
| | 3101 | var cqes: [4]linux.io_uring_cqe = undefined; |
| | 3102 | try testing.expectEqual(@as(u32, 4), try ring.copy_cqes(&cqes, 4)); |
| | 3103 | } |
| | 3104 | |
| | 3105 | // Do 4 recv which should consume all buffers |
| | 3106 | |
| | 3107 | // Deliberately put something we don't expect in the buffers |
| | 3108 | mem.set(u8, mem.sliceAsBytes(&buffers), 1); |
| | 3109 | |
| | 3110 | var i: usize = 0; |
| | 3111 | while (i < buffers.len) : (i += 1) { |
| | 3112 | var sqe = try ring.recv(0xdededede, socket_test_harness.client, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 3113 | try testing.expectEqual(linux.IORING_OP.RECV, sqe.opcode); |
| | 3114 | try testing.expectEqual(@as(i32, socket_test_harness.client), sqe.fd); |
| | 3115 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| | 3116 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| | 3117 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 3118 | try testing.expectEqual(@as(u32, 0), sqe.rw_flags); |
| | 3119 | try testing.expectEqual(@as(u32, linux.IOSQE_BUFFER_SELECT), sqe.flags); |
| | 3120 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3121 | |
| | 3122 | const cqe = try ring.copy_cqe(); |
| | 3123 | switch (cqe.err()) { |
| | 3124 | .SUCCESS => {}, |
| | 3125 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3126 | } |
| | 3127 | |
| | 3128 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); |
| | 3129 | const used_buffer_id = cqe.flags >> 16; |
| | 3130 | try testing.expect(used_buffer_id >= 0 and used_buffer_id <= 3); |
| | 3131 | try testing.expectEqual(@as(i32, buffer_len), cqe.res); |
| | 3132 | |
| | 3133 | try testing.expectEqual(@as(u64, 0xdededede), cqe.user_data); |
| | 3134 | const buffer = buffers[used_buffer_id][0..@intCast(usize, cqe.res)]; |
| | 3135 | try testing.expectEqualSlices(u8, &([_]u8{'z'} ** buffer_len), buffer); |
| | 3136 | } |
| | 3137 | |
| | 3138 | // This recv should fail |
| | 3139 | |
| | 3140 | { |
| | 3141 | var sqe = try ring.recv(0xdfdfdfdf, socket_test_harness.client, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 3142 | try testing.expectEqual(linux.IORING_OP.RECV, sqe.opcode); |
| | 3143 | try testing.expectEqual(@as(i32, socket_test_harness.client), sqe.fd); |
| | 3144 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| | 3145 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| | 3146 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 3147 | try testing.expectEqual(@as(u32, 0), sqe.rw_flags); |
| | 3148 | try testing.expectEqual(@as(u32, linux.IOSQE_BUFFER_SELECT), sqe.flags); |
| | 3149 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3150 | |
| | 3151 | const cqe = try ring.copy_cqe(); |
| | 3152 | switch (cqe.err()) { |
| | 3153 | // Expected |
| | 3154 | .NOBUFS => {}, |
| | 3155 | .SUCCESS => std.debug.panic("unexpected success", .{}), |
| | 3156 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3157 | } |
| | 3158 | try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); |
| | 3159 | } |
| | 3160 | |
| | 3161 | // Provide 1 buffer again |
| | 3162 | |
| | 3163 | const reprovided_buffer_id = 2; |
| | 3164 | |
| | 3165 | { |
| | 3166 | _ = try ring.provide_buffers(0xabababab, @ptrCast([*]u8, &buffers[reprovided_buffer_id]), 1, buffer_len, group_id, reprovided_buffer_id); |
| | 3167 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3168 | |
| | 3169 | const cqe = try ring.copy_cqe(); |
| | 3170 | switch (cqe.err()) { |
| | 3171 | .SUCCESS => {}, |
| | 3172 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3173 | } |
| | 3174 | } |
| | 3175 | |
| | 3176 | // Redo 1 send on the server socket |
| | 3177 | |
| | 3178 | { |
| | 3179 | _ = try ring.send(0xdeaddead, socket_test_harness.server, &([_]u8{'w'} ** buffer_len), 0); |
| | 3180 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3181 | |
| | 3182 | _ = try ring.copy_cqe(); |
| | 3183 | } |
| | 3184 | |
| | 3185 | // Final recv which should work |
| | 3186 | |
| | 3187 | // Deliberately put something we don't expect in the buffers |
| | 3188 | mem.set(u8, mem.sliceAsBytes(&buffers), 1); |
| | 3189 | |
| | 3190 | { |
| | 3191 | var sqe = try ring.recv(0xdfdfdfdf, socket_test_harness.client, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| | 3192 | try testing.expectEqual(linux.IORING_OP.RECV, sqe.opcode); |
| | 3193 | try testing.expectEqual(@as(i32, socket_test_harness.client), sqe.fd); |
| | 3194 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| | 3195 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| | 3196 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| | 3197 | try testing.expectEqual(@as(u32, 0), sqe.rw_flags); |
| | 3198 | try testing.expectEqual(@as(u32, linux.IOSQE_BUFFER_SELECT), sqe.flags); |
| | 3199 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 3200 | |
| | 3201 | const cqe = try ring.copy_cqe(); |
| | 3202 | switch (cqe.err()) { |
| | 3203 | .SUCCESS => {}, |
| | 3204 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 3205 | } |
| | 3206 | |
| | 3207 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); |
| | 3208 | const used_buffer_id = cqe.flags >> 16; |
| | 3209 | try testing.expectEqual(used_buffer_id, reprovided_buffer_id); |
| | 3210 | try testing.expectEqual(@as(i32, buffer_len), cqe.res); |
| | 3211 | try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); |
| | 3212 | const buffer = buffers[used_buffer_id][0..@intCast(usize, cqe.res)]; |
| | 3213 | try testing.expectEqualSlices(u8, &([_]u8{'w'} ** buffer_len), buffer); |
| | 3214 | } |
| | 3215 | } |
| | 3216 | |
| 2833 | /// Used for testing server/client interactions. | 3217 | /// Used for testing server/client interactions. |
| 2834 | const SocketTestHarness = struct { | 3218 | const SocketTestHarness = struct { |
| 2835 | listener: os.socket_t, | 3219 | listener: os.socket_t, |