tigerbeetle-node 0.11.13 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +5 -10
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +6 -16
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +9 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -55
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
  21. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  22. package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
  23. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  24. package/src/tigerbeetle/scripts/install.bat +0 -7
  25. package/src/tigerbeetle/scripts/install.sh +0 -21
  26. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  27. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  28. package/src/tigerbeetle/scripts/lint.zig +0 -199
  29. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  30. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
  31. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  32. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  33. package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
  34. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  35. package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
  36. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  37. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  38. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  39. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  40. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  41. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  42. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  43. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  44. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  45. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  46. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  47. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  48. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  49. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  50. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  51. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  52. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  53. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  54. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  55. package/src/tigerbeetle/src/benchmark.zig +0 -336
  56. package/src/tigerbeetle/src/config.zig +0 -233
  57. package/src/tigerbeetle/src/constants.zig +0 -428
  58. package/src/tigerbeetle/src/ewah.zig +0 -286
  59. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  60. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  61. package/src/tigerbeetle/src/fifo.zig +0 -120
  62. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  63. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  64. package/src/tigerbeetle/src/io/linux.zig +0 -1071
  65. package/src/tigerbeetle/src/io/test.zig +0 -643
  66. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  67. package/src/tigerbeetle/src/io.zig +0 -34
  68. package/src/tigerbeetle/src/iops.zig +0 -107
  69. package/src/tigerbeetle/src/lsm/README.md +0 -308
  70. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  71. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  72. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  73. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  74. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  75. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  76. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  77. package/src/tigerbeetle/src/lsm/forest.zig +0 -205
  78. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
  79. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  80. package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
  81. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  82. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  83. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  84. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
  85. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  86. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  87. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  88. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  89. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
  90. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
  91. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  92. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  93. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  94. package/src/tigerbeetle/src/lsm/table.zig +0 -1009
  95. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
  96. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  97. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
  98. package/src/tigerbeetle/src/lsm/test.zig +0 -439
  99. package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
  100. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
  101. package/src/tigerbeetle/src/message_bus.zig +0 -1013
  102. package/src/tigerbeetle/src/message_pool.zig +0 -156
  103. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  104. package/src/tigerbeetle/src/simulator.zig +0 -580
  105. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
  106. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  107. package/src/tigerbeetle/src/state_machine.zig +0 -2099
  108. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  109. package/src/tigerbeetle/src/stdx.zig +0 -171
  110. package/src/tigerbeetle/src/storage.zig +0 -393
  111. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  112. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  113. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  114. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  115. package/src/tigerbeetle/src/testing/cluster.zig +0 -444
  116. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  117. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  118. package/src/tigerbeetle/src/testing/id.zig +0 -99
  119. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
  120. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  121. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  122. package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
  123. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  124. package/src/tigerbeetle/src/testing/table.zig +0 -247
  125. package/src/tigerbeetle/src/testing/time.zig +0 -84
  126. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  127. package/src/tigerbeetle/src/time.zig +0 -112
  128. package/src/tigerbeetle/src/tracer.zig +0 -529
  129. package/src/tigerbeetle/src/unit_tests.zig +0 -40
  130. package/src/tigerbeetle/src/vopr.zig +0 -495
  131. package/src/tigerbeetle/src/vsr/README.md +0 -209
  132. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  133. package/src/tigerbeetle/src/vsr/clock.zig +0 -855
  134. package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
  135. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  136. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  137. package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
  138. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  139. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  140. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  141. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  142. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  143. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  144. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  145. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  146. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  147. package/src/tigerbeetle/src/vsr.zig +0 -1425
@@ -1,1183 +0,0 @@
1
- const std = @import("std");
2
- const os = std.os;
3
- const assert = std.debug.assert;
4
- const log = std.log.scoped(.io);
5
- const constants = @import("../constants.zig");
6
-
7
- const FIFO = @import("../fifo.zig").FIFO;
8
- const Time = @import("../time.zig").Time;
9
- const buffer_limit = @import("../io.zig").buffer_limit;
10
-
11
- pub const IO = struct {
12
- iocp: os.windows.HANDLE,
13
- timer: Time = .{},
14
- io_pending: usize = 0,
15
- timeouts: FIFO(Completion) = .{},
16
- completed: FIFO(Completion) = .{},
17
-
18
- pub fn init(entries: u12, flags: u32) !IO {
19
- _ = entries;
20
- _ = flags;
21
-
22
- _ = try os.windows.WSAStartup(2, 2);
23
- errdefer os.windows.WSACleanup() catch unreachable;
24
-
25
- const iocp = try os.windows.CreateIoCompletionPort(os.windows.INVALID_HANDLE_VALUE, null, 0, 0);
26
- return IO{ .iocp = iocp };
27
- }
28
-
29
- pub fn deinit(self: *IO) void {
30
- assert(self.iocp != os.windows.INVALID_HANDLE_VALUE);
31
- os.windows.CloseHandle(self.iocp);
32
- self.iocp = os.windows.INVALID_HANDLE_VALUE;
33
-
34
- os.windows.WSACleanup() catch unreachable;
35
- }
36
-
37
- pub fn tick(self: *IO) !void {
38
- return self.flush(.non_blocking);
39
- }
40
-
41
- pub fn run_for_ns(self: *IO, nanoseconds: u63) !void {
42
- const Callback = struct {
43
- fn on_timeout(timed_out: *bool, completion: *Completion, result: TimeoutError!void) void {
44
- _ = result catch unreachable;
45
- _ = completion;
46
- timed_out.* = true;
47
- }
48
- };
49
-
50
- var timed_out = false;
51
- var completion: Completion = undefined;
52
- self.timeout(*bool, &timed_out, Callback.on_timeout, &completion, nanoseconds);
53
-
54
- while (!timed_out) {
55
- try self.flush(.blocking);
56
- }
57
- }
58
-
59
- const FlushMode = enum {
60
- blocking,
61
- non_blocking,
62
- };
63
-
64
- fn flush(self: *IO, mode: FlushMode) !void {
65
- if (self.completed.empty()) {
66
- // Compute how long to poll by flushing timeout completions.
67
- // NOTE: this may push to completed queue
68
- var timeout_ms: ?os.windows.DWORD = null;
69
- if (self.flush_timeouts()) |expires_ns| {
70
- // 0ns expires should have been completed not returned
71
- assert(expires_ns != 0);
72
- // Round up sub-millisecond expire times to the next millisecond
73
- const expires_ms = (expires_ns + (std.time.ns_per_ms / 2)) / std.time.ns_per_ms;
74
- // Saturating cast to DWORD milliseconds
75
- const expires = std.math.cast(os.windows.DWORD, expires_ms) catch std.math.maxInt(os.windows.DWORD);
76
- // max DWORD is reserved for INFINITE so cap the cast at max - 1
77
- timeout_ms = if (expires == os.windows.INFINITE) expires - 1 else expires;
78
- }
79
-
80
- // Poll for IO iff theres IO pending and flush_timeouts() found no ready completions
81
- if (self.io_pending > 0 and self.completed.empty()) {
82
- // In blocking mode, we're always waiting at least until the timeout by run_for_ns.
83
- // In non-blocking mode, we shouldn't wait at all.
84
- const io_timeout = switch (mode) {
85
- .blocking => timeout_ms orelse @panic("IO.flush blocking unbounded"),
86
- .non_blocking => 0,
87
- };
88
-
89
- var events: [64]os.windows.OVERLAPPED_ENTRY = undefined;
90
- const num_events = os.windows.GetQueuedCompletionStatusEx(
91
- self.iocp,
92
- &events,
93
- io_timeout,
94
- false, // non-alertable wait
95
- ) catch |err| switch (err) {
96
- error.Timeout => 0,
97
- error.Aborted => unreachable,
98
- else => |e| return e,
99
- };
100
-
101
- assert(self.io_pending >= num_events);
102
- self.io_pending -= num_events;
103
-
104
- for (events[0..num_events]) |event| {
105
- const raw_overlapped = event.lpOverlapped;
106
- const overlapped = @fieldParentPtr(Completion.Overlapped, "raw", raw_overlapped);
107
- const completion = overlapped.completion;
108
- completion.next = null;
109
- self.completed.push(completion);
110
- }
111
- }
112
- }
113
-
114
- // Dequeue and invoke all the completions currently ready.
115
- // Must read all `completions` before invoking the callbacks
116
- // as the callbacks could potentially submit more completions.
117
- var completed = self.completed;
118
- self.completed = .{};
119
- while (completed.pop()) |completion| {
120
- (completion.callback)(Completion.Context{
121
- .io = self,
122
- .completion = completion,
123
- });
124
- }
125
- }
126
-
127
- fn flush_timeouts(self: *IO) ?u64 {
128
- var min_expires: ?u64 = null;
129
- var current_time: ?u64 = null;
130
- var timeouts: ?*Completion = self.timeouts.peek();
131
-
132
- // iterate through the timeouts, returning min_expires at the end
133
- while (timeouts) |completion| {
134
- timeouts = completion.next;
135
-
136
- // lazily get the current time
137
- const now = current_time orelse self.timer.monotonic();
138
- current_time = now;
139
-
140
- // move the completion to completed if it expired
141
- if (now >= completion.operation.timeout.deadline) {
142
- self.timeouts.remove(completion);
143
- self.completed.push(completion);
144
- continue;
145
- }
146
-
147
- // if it's still waiting, update min_timeout
148
- const expires = completion.operation.timeout.deadline - now;
149
- if (min_expires) |current_min_expires| {
150
- min_expires = std.math.min(expires, current_min_expires);
151
- } else {
152
- min_expires = expires;
153
- }
154
- }
155
-
156
- return min_expires;
157
- }
158
-
159
- /// This struct holds the data needed for a single IO operation
160
- pub const Completion = struct {
161
- next: ?*Completion,
162
- context: ?*anyopaque,
163
- callback: fn (Context) void,
164
- operation: Operation,
165
-
166
- const Context = struct {
167
- io: *IO,
168
- completion: *Completion,
169
- };
170
-
171
- const Overlapped = struct {
172
- raw: os.windows.OVERLAPPED,
173
- completion: *Completion,
174
- };
175
-
176
- const Transfer = struct {
177
- socket: os.socket_t,
178
- buf: os.windows.ws2_32.WSABUF,
179
- overlapped: Overlapped,
180
- pending: bool,
181
- };
182
-
183
- const Operation = union(enum) {
184
- accept: struct {
185
- overlapped: Overlapped,
186
- listen_socket: os.socket_t,
187
- client_socket: os.socket_t,
188
- addr_buffer: [(@sizeOf(std.net.Address) + 16) * 2]u8 align(4),
189
- },
190
- connect: struct {
191
- socket: os.socket_t,
192
- address: std.net.Address,
193
- overlapped: Overlapped,
194
- pending: bool,
195
- },
196
- send: Transfer,
197
- recv: Transfer,
198
- read: struct {
199
- fd: os.fd_t,
200
- buf: [*]u8,
201
- len: u32,
202
- offset: u64,
203
- },
204
- write: struct {
205
- fd: os.fd_t,
206
- buf: [*]const u8,
207
- len: u32,
208
- offset: u64,
209
- },
210
- close: struct {
211
- fd: os.fd_t,
212
- },
213
- timeout: struct {
214
- deadline: u64,
215
- },
216
- };
217
- };
218
-
219
- fn submit(
220
- self: *IO,
221
- context: anytype,
222
- comptime callback: anytype,
223
- completion: *Completion,
224
- comptime op_tag: std.meta.Tag(Completion.Operation),
225
- op_data: anytype,
226
- comptime OperationImpl: type,
227
- ) void {
228
- const Context = @TypeOf(context);
229
- const Callback = struct {
230
- fn onComplete(ctx: Completion.Context) void {
231
- // Perform the operation and get the result
232
- const data = &@field(ctx.completion.operation, @tagName(op_tag));
233
- const result = OperationImpl.do_operation(ctx, data);
234
-
235
- // For OVERLAPPED IO, error.WouldBlock assumes that it will be completed by IOCP.
236
- switch (op_tag) {
237
- .accept, .read, .recv, .connect, .write, .send => {
238
- _ = result catch |err| switch (err) {
239
- error.WouldBlock => {
240
- ctx.io.io_pending += 1;
241
- return;
242
- },
243
- else => {},
244
- };
245
- },
246
- else => {},
247
- }
248
-
249
- // The completion is finally ready to invoke the callback
250
- callback(
251
- @intToPtr(Context, @ptrToInt(ctx.completion.context)),
252
- ctx.completion,
253
- result,
254
- );
255
- }
256
- };
257
-
258
- // Setup the completion with the callback wrapper above
259
- completion.* = .{
260
- .next = null,
261
- .context = @ptrCast(?*anyopaque, context),
262
- .callback = Callback.onComplete,
263
- .operation = @unionInit(Completion.Operation, @tagName(op_tag), op_data),
264
- };
265
-
266
- // Submit the completion onto the right queue
267
- switch (op_tag) {
268
- .timeout => self.timeouts.push(completion),
269
- else => self.completed.push(completion),
270
- }
271
- }
272
-
273
- pub const AcceptError = os.AcceptError || os.SetSockOptError;
274
-
275
- pub fn accept(
276
- self: *IO,
277
- comptime Context: type,
278
- context: Context,
279
- comptime callback: fn (
280
- context: Context,
281
- completion: *Completion,
282
- result: AcceptError!os.socket_t,
283
- ) void,
284
- completion: *Completion,
285
- socket: os.socket_t,
286
- ) void {
287
- self.submit(
288
- context,
289
- callback,
290
- completion,
291
- .accept,
292
- .{
293
- .overlapped = undefined,
294
- .listen_socket = socket,
295
- .client_socket = INVALID_SOCKET,
296
- .addr_buffer = undefined,
297
- },
298
- struct {
299
- fn do_operation(ctx: Completion.Context, op: anytype) AcceptError!os.socket_t {
300
- var flags: os.windows.DWORD = undefined;
301
- var transferred: os.windows.DWORD = undefined;
302
-
303
- const rc = switch (op.client_socket) {
304
- // When first called, the client_socket is invalid so we start the op.
305
- INVALID_SOCKET => blk: {
306
- // Create the socket that will be used for accept.
307
- op.client_socket = ctx.io.open_socket(
308
- os.AF.INET,
309
- os.SOCK.STREAM,
310
- os.IPPROTO.TCP,
311
- ) catch |err| switch (err) {
312
- error.AddressFamilyNotSupported, error.ProtocolNotSupported => unreachable,
313
- else => |e| return e,
314
- };
315
-
316
- var sync_bytes_read: os.windows.DWORD = undefined;
317
- op.overlapped = .{
318
- .raw = std.mem.zeroes(os.windows.OVERLAPPED),
319
- .completion = ctx.completion,
320
- };
321
-
322
- // Start the asynchronous accept with the created socket.
323
- break :blk os.windows.ws2_32.AcceptEx(
324
- op.listen_socket,
325
- op.client_socket,
326
- &op.addr_buffer,
327
- 0,
328
- @sizeOf(std.net.Address) + 16,
329
- @sizeOf(std.net.Address) + 16,
330
- &sync_bytes_read,
331
- &op.overlapped.raw,
332
- );
333
- },
334
- // Called after accept was started, so get the result
335
- else => os.windows.ws2_32.WSAGetOverlappedResult(
336
- op.listen_socket,
337
- &op.overlapped.raw,
338
- &transferred,
339
- os.windows.FALSE, // dont wait
340
- &flags,
341
- ),
342
- };
343
-
344
- // return the socket if we succeed in accepting.
345
- if (rc != os.windows.FALSE) {
346
- // enables getsockopt, setsockopt, getsockname, getpeername
347
- _ = os.windows.ws2_32.setsockopt(
348
- op.client_socket,
349
- os.windows.ws2_32.SOL.SOCKET,
350
- os.windows.ws2_32.SO.UPDATE_ACCEPT_CONTEXT,
351
- null,
352
- 0,
353
- );
354
-
355
- return op.client_socket;
356
- }
357
-
358
- // destroy the client_socket we created if we get a non WouldBlock error
359
- errdefer |result| {
360
- _ = result catch |err| switch (err) {
361
- error.WouldBlock => {},
362
- else => {
363
- os.closeSocket(op.client_socket);
364
- op.client_socket = INVALID_SOCKET;
365
- },
366
- };
367
- }
368
-
369
- return switch (os.windows.ws2_32.WSAGetLastError()) {
370
- .WSA_IO_PENDING, .WSAEWOULDBLOCK, .WSA_IO_INCOMPLETE => error.WouldBlock,
371
- .WSANOTINITIALISED => unreachable, // WSAStartup() was called
372
- .WSAENETDOWN => unreachable, // WinSock error
373
- .WSAENOTSOCK => error.FileDescriptorNotASocket,
374
- .WSAEOPNOTSUPP => error.OperationNotSupported,
375
- .WSA_INVALID_HANDLE => unreachable, // we dont use hEvent in OVERLAPPED
376
- .WSAEFAULT, .WSA_INVALID_PARAMETER => unreachable, // params should be ok
377
- .WSAECONNRESET => error.ConnectionAborted,
378
- .WSAEMFILE => unreachable, // we create our own descriptor so its available
379
- .WSAENOBUFS => error.SystemResources,
380
- .WSAEINTR, .WSAEINPROGRESS => unreachable, // no blocking calls
381
- else => |err| os.windows.unexpectedWSAError(err),
382
- };
383
- }
384
- },
385
- );
386
- }
387
-
388
- pub const CloseError = error{
389
- FileDescriptorInvalid,
390
- DiskQuota,
391
- InputOutput,
392
- NoSpaceLeft,
393
- } || os.UnexpectedError;
394
-
395
- pub const ConnectError = os.ConnectError || error{FileDescriptorNotASocket};
396
-
397
- pub fn connect(
398
- self: *IO,
399
- comptime Context: type,
400
- context: Context,
401
- comptime callback: fn (
402
- context: Context,
403
- completion: *Completion,
404
- result: ConnectError!void,
405
- ) void,
406
- completion: *Completion,
407
- socket: os.socket_t,
408
- address: std.net.Address,
409
- ) void {
410
- self.submit(
411
- context,
412
- callback,
413
- completion,
414
- .connect,
415
- .{
416
- .socket = socket,
417
- .address = address,
418
- .overlapped = undefined,
419
- .pending = false,
420
- },
421
- struct {
422
- fn do_operation(ctx: Completion.Context, op: anytype) ConnectError!void {
423
- var flags: os.windows.DWORD = undefined;
424
- var transferred: os.windows.DWORD = undefined;
425
-
426
- const rc = blk: {
427
- // Poll for the result if we've already started the connect op.
428
- if (op.pending) {
429
- break :blk os.windows.ws2_32.WSAGetOverlappedResult(
430
- op.socket,
431
- &op.overlapped.raw,
432
- &transferred,
433
- os.windows.FALSE, // dont wait
434
- &flags,
435
- );
436
- }
437
-
438
- // ConnectEx requires the socket to be initially bound (INADDR_ANY)
439
- const inaddr_any = std.mem.zeroes([4]u8);
440
- const bind_addr = std.net.Address.initIp4(inaddr_any, 0);
441
- os.bind(
442
- op.socket,
443
- &bind_addr.any,
444
- bind_addr.getOsSockLen(),
445
- ) catch |err| switch (err) {
446
- error.AccessDenied => unreachable,
447
- error.SymLinkLoop => unreachable,
448
- error.NameTooLong => unreachable,
449
- error.NotDir => unreachable,
450
- error.ReadOnlyFileSystem => unreachable,
451
- error.NetworkSubsystemFailed => unreachable,
452
- error.AlreadyBound => unreachable,
453
- else => |e| return e,
454
- };
455
-
456
- const LPFN_CONNECTEX = fn (
457
- Socket: os.windows.ws2_32.SOCKET,
458
- SockAddr: *const os.windows.ws2_32.sockaddr,
459
- SockLen: os.socklen_t,
460
- SendBuf: ?*const anyopaque,
461
- SendBufLen: os.windows.DWORD,
462
- BytesSent: *os.windows.DWORD,
463
- Overlapped: *os.windows.OVERLAPPED,
464
- ) callconv(os.windows.WINAPI) os.windows.BOOL;
465
-
466
- // Find the ConnectEx function by dynamically looking it up on the socket.
467
- const connect_ex = os.windows.loadWinsockExtensionFunction(
468
- LPFN_CONNECTEX,
469
- op.socket,
470
- os.windows.ws2_32.WSAID_CONNECTEX,
471
- ) catch |err| switch (err) {
472
- error.OperationNotSupported => unreachable,
473
- error.ShortRead => unreachable,
474
- else => |e| return e,
475
- };
476
-
477
- op.pending = true;
478
- op.overlapped = .{
479
- .raw = std.mem.zeroes(os.windows.OVERLAPPED),
480
- .completion = ctx.completion,
481
- };
482
-
483
- // Start the connect operation.
484
- break :blk (connect_ex)(
485
- op.socket,
486
- &op.address.any,
487
- op.address.getOsSockLen(),
488
- null,
489
- 0,
490
- &transferred,
491
- &op.overlapped.raw,
492
- );
493
- };
494
-
495
- // return if we succeeded in connecting
496
- if (rc != os.windows.FALSE) {
497
- // enables getsockopt, setsockopt, getsockname, getpeername
498
- _ = os.windows.ws2_32.setsockopt(
499
- op.socket,
500
- os.windows.ws2_32.SOL.SOCKET,
501
- os.windows.ws2_32.SO.UPDATE_CONNECT_CONTEXT,
502
- null,
503
- 0,
504
- );
505
-
506
- return;
507
- }
508
-
509
- return switch (os.windows.ws2_32.WSAGetLastError()) {
510
- .WSA_IO_PENDING, .WSAEWOULDBLOCK, .WSA_IO_INCOMPLETE, .WSAEALREADY => error.WouldBlock,
511
- .WSANOTINITIALISED => unreachable, // WSAStartup() was called
512
- .WSAENETDOWN => unreachable, // network subsystem is down
513
- .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
514
- .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
515
- .WSAECONNREFUSED => error.ConnectionRefused,
516
- .WSAEFAULT => unreachable, // all addresses should be valid
517
- .WSAEINVAL => unreachable, // invalid socket type
518
- .WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable,
519
- .WSAENOBUFS => error.SystemResources,
520
- .WSAENOTSOCK => unreachable, // socket is not bound or is listening
521
- .WSAETIMEDOUT => error.ConnectionTimedOut,
522
- .WSA_INVALID_HANDLE => unreachable, // we dont use hEvent in OVERLAPPED
523
- else => |err| os.windows.unexpectedWSAError(err),
524
- };
525
- }
526
- },
527
- );
528
- }
529
-
530
- pub const SendError = os.SendError;
531
-
532
- pub fn send(
533
- self: *IO,
534
- comptime Context: type,
535
- context: Context,
536
- comptime callback: fn (
537
- context: Context,
538
- completion: *Completion,
539
- result: SendError!usize,
540
- ) void,
541
- completion: *Completion,
542
- socket: os.socket_t,
543
- buffer: []const u8,
544
- ) void {
545
- const transfer = Completion.Transfer{
546
- .socket = socket,
547
- .buf = os.windows.ws2_32.WSABUF{
548
- .len = @intCast(u32, buffer_limit(buffer.len)),
549
- .buf = @intToPtr([*]u8, @ptrToInt(buffer.ptr)),
550
- },
551
- .overlapped = undefined,
552
- .pending = false,
553
- };
554
-
555
- self.submit(
556
- context,
557
- callback,
558
- completion,
559
- .send,
560
- transfer,
561
- struct {
562
- fn do_operation(ctx: Completion.Context, op: anytype) SendError!usize {
563
- var flags: os.windows.DWORD = undefined;
564
- var transferred: os.windows.DWORD = undefined;
565
-
566
- const rc = blk: {
567
- // Poll for the result if we've already started the send op.
568
- if (op.pending) {
569
- break :blk os.windows.ws2_32.WSAGetOverlappedResult(
570
- op.socket,
571
- &op.overlapped.raw,
572
- &transferred,
573
- os.windows.FALSE, // dont wait
574
- &flags,
575
- );
576
- }
577
-
578
- op.pending = true;
579
- op.overlapped = .{
580
- .raw = std.mem.zeroes(os.windows.OVERLAPPED),
581
- .completion = ctx.completion,
582
- };
583
-
584
- // Start the send operation.
585
- break :blk switch (os.windows.ws2_32.WSASend(
586
- op.socket,
587
- @ptrCast([*]os.windows.ws2_32.WSABUF, &op.buf),
588
- 1, // one buffer
589
- &transferred,
590
- 0, // no flags
591
- &op.overlapped.raw,
592
- null,
593
- )) {
594
- os.windows.ws2_32.SOCKET_ERROR => @as(os.windows.BOOL, os.windows.FALSE),
595
- 0 => os.windows.TRUE,
596
- else => unreachable,
597
- };
598
- };
599
-
600
- // Return bytes transferred on success.
601
- if (rc != os.windows.FALSE)
602
- return transferred;
603
-
604
- return switch (os.windows.ws2_32.WSAGetLastError()) {
605
- .WSA_IO_PENDING, .WSAEWOULDBLOCK, .WSA_IO_INCOMPLETE => error.WouldBlock,
606
- .WSANOTINITIALISED => unreachable, // WSAStartup() was called
607
- .WSA_INVALID_HANDLE => unreachable, // we dont use OVERLAPPED.hEvent
608
- .WSA_INVALID_PARAMETER => unreachable, // parameters are fine
609
- .WSAECONNABORTED => error.ConnectionResetByPeer,
610
- .WSAECONNRESET => error.ConnectionResetByPeer,
611
- .WSAEFAULT => unreachable, // invalid buffer
612
- .WSAEINTR => unreachable, // this is non blocking
613
- .WSAEINPROGRESS => unreachable, // this is non blocking
614
- .WSAEINVAL => unreachable, // invalid socket type
615
- .WSAEMSGSIZE => error.MessageTooBig,
616
- .WSAENETDOWN => error.NetworkSubsystemFailed,
617
- .WSAENETRESET => error.ConnectionResetByPeer,
618
- .WSAENOBUFS => error.SystemResources,
619
- .WSAENOTCONN => error.FileDescriptorNotASocket,
620
- .WSAEOPNOTSUPP => unreachable, // we dont use MSG_OOB or MSG_PARTIAL
621
- .WSAESHUTDOWN => error.BrokenPipe,
622
- .WSA_OPERATION_ABORTED => unreachable, // operation was cancelled
623
- else => |err| os.windows.unexpectedWSAError(err),
624
- };
625
- }
626
- },
627
- );
628
- }
629
-
630
- pub const RecvError = os.RecvFromError;
631
-
632
- pub fn recv(
633
- self: *IO,
634
- comptime Context: type,
635
- context: Context,
636
- comptime callback: fn (
637
- context: Context,
638
- completion: *Completion,
639
- result: RecvError!usize,
640
- ) void,
641
- completion: *Completion,
642
- socket: os.socket_t,
643
- buffer: []u8,
644
- ) void {
645
- const transfer = Completion.Transfer{
646
- .socket = socket,
647
- .buf = os.windows.ws2_32.WSABUF{
648
- .len = @intCast(u32, buffer_limit(buffer.len)),
649
- .buf = buffer.ptr,
650
- },
651
- .overlapped = undefined,
652
- .pending = false,
653
- };
654
-
655
- self.submit(
656
- context,
657
- callback,
658
- completion,
659
- .recv,
660
- transfer,
661
- struct {
662
- fn do_operation(ctx: Completion.Context, op: anytype) RecvError!usize {
663
- var flags: os.windows.DWORD = 0; // used both as input and output
664
- var transferred: os.windows.DWORD = undefined;
665
-
666
- const rc = blk: {
667
- // Poll for the result if we've already started the recv op.
668
- if (op.pending) {
669
- break :blk os.windows.ws2_32.WSAGetOverlappedResult(
670
- op.socket,
671
- &op.overlapped.raw,
672
- &transferred,
673
- os.windows.FALSE, // dont wait
674
- &flags,
675
- );
676
- }
677
-
678
- op.pending = true;
679
- op.overlapped = .{
680
- .raw = std.mem.zeroes(os.windows.OVERLAPPED),
681
- .completion = ctx.completion,
682
- };
683
-
684
- // Start the recv operation.
685
- break :blk switch (os.windows.ws2_32.WSARecv(
686
- op.socket,
687
- @ptrCast([*]os.windows.ws2_32.WSABUF, &op.buf),
688
- 1, // one buffer
689
- &transferred,
690
- &flags,
691
- &op.overlapped.raw,
692
- null,
693
- )) {
694
- os.windows.ws2_32.SOCKET_ERROR => @as(os.windows.BOOL, os.windows.FALSE),
695
- 0 => os.windows.TRUE,
696
- else => unreachable,
697
- };
698
- };
699
-
700
- // Return bytes received on success.
701
- if (rc != os.windows.FALSE)
702
- return transferred;
703
-
704
- return switch (os.windows.ws2_32.WSAGetLastError()) {
705
- .WSA_IO_PENDING, .WSAEWOULDBLOCK, .WSA_IO_INCOMPLETE => error.WouldBlock,
706
- .WSANOTINITIALISED => unreachable, // WSAStartup() was called
707
- .WSA_INVALID_HANDLE => unreachable, // we dont use OVERLAPPED.hEvent
708
- .WSA_INVALID_PARAMETER => unreachable, // parameters are fine
709
- .WSAECONNABORTED => error.ConnectionRefused,
710
- .WSAECONNRESET => error.ConnectionResetByPeer,
711
- .WSAEDISCON => unreachable, // we only stream sockets
712
- .WSAEFAULT => unreachable, // invalid buffer
713
- .WSAEINTR => unreachable, // this is non blocking
714
- .WSAEINPROGRESS => unreachable, // this is non blocking
715
- .WSAEINVAL => unreachable, // invalid socket type
716
- .WSAEMSGSIZE => error.MessageTooBig,
717
- .WSAENETDOWN => error.NetworkSubsystemFailed,
718
- .WSAENETRESET => error.ConnectionResetByPeer,
719
- .WSAENOTCONN => error.SocketNotConnected,
720
- .WSAEOPNOTSUPP => unreachable, // we dont use MSG_OOB or MSG_PARTIAL
721
- .WSAESHUTDOWN => error.SocketNotConnected,
722
- .WSAETIMEDOUT => error.ConnectionRefused,
723
- .WSA_OPERATION_ABORTED => unreachable, // operation was cancelled
724
- else => |err| os.windows.unexpectedWSAError(err),
725
- };
726
- }
727
- },
728
- );
729
- }
730
-
731
- pub const ReadError = error{
732
- WouldBlock,
733
- NotOpenForReading,
734
- ConnectionResetByPeer,
735
- Alignment,
736
- InputOutput,
737
- IsDir,
738
- SystemResources,
739
- Unseekable,
740
- ConnectionTimedOut,
741
- } || os.UnexpectedError;
742
-
743
- pub fn read(
744
- self: *IO,
745
- comptime Context: type,
746
- context: Context,
747
- comptime callback: fn (
748
- context: Context,
749
- completion: *Completion,
750
- result: ReadError!usize,
751
- ) void,
752
- completion: *Completion,
753
- fd: os.fd_t,
754
- buffer: []u8,
755
- offset: u64,
756
- ) void {
757
- self.submit(
758
- context,
759
- callback,
760
- completion,
761
- .read,
762
- .{
763
- .fd = fd,
764
- .buf = buffer.ptr,
765
- .len = @intCast(u32, buffer_limit(buffer.len)),
766
- .offset = offset,
767
- },
768
- struct {
769
- fn do_operation(ctx: Completion.Context, op: anytype) ReadError!usize {
770
- // Do a synchronous read for now.
771
- _ = ctx;
772
- return os.pread(op.fd, op.buf[0..op.len], op.offset) catch |err| switch (err) {
773
- error.OperationAborted => unreachable,
774
- error.BrokenPipe => unreachable,
775
- error.ConnectionTimedOut => unreachable,
776
- error.AccessDenied => error.InputOutput,
777
- else => |e| e,
778
- };
779
- }
780
- },
781
- );
782
- }
783
-
784
- pub const WriteError = os.PWriteError;
785
-
786
- pub fn write(
787
- self: *IO,
788
- comptime Context: type,
789
- context: Context,
790
- comptime callback: fn (
791
- context: Context,
792
- completion: *Completion,
793
- result: WriteError!usize,
794
- ) void,
795
- completion: *Completion,
796
- fd: os.fd_t,
797
- buffer: []const u8,
798
- offset: u64,
799
- ) void {
800
- self.submit(
801
- context,
802
- callback,
803
- completion,
804
- .write,
805
- .{
806
- .fd = fd,
807
- .buf = buffer.ptr,
808
- .len = @intCast(u32, buffer_limit(buffer.len)),
809
- .offset = offset,
810
- },
811
- struct {
812
- fn do_operation(ctx: Completion.Context, op: anytype) WriteError!usize {
813
- // Do a synchronous write for now.
814
- _ = ctx;
815
- return os.pwrite(op.fd, op.buf[0..op.len], op.offset);
816
- }
817
- },
818
- );
819
- }
820
-
821
- pub fn close(
822
- self: *IO,
823
- comptime Context: type,
824
- context: Context,
825
- comptime callback: fn (
826
- context: Context,
827
- completion: *Completion,
828
- result: CloseError!void,
829
- ) void,
830
- completion: *Completion,
831
- fd: os.fd_t,
832
- ) void {
833
- self.submit(
834
- context,
835
- callback,
836
- completion,
837
- .close,
838
- .{ .fd = fd },
839
- struct {
840
- fn do_operation(ctx: Completion.Context, op: anytype) CloseError!void {
841
- _ = ctx;
842
-
843
- // Check if the fd is a SOCKET by seeing if getsockopt() returns ENOTSOCK
844
- // https://stackoverflow.com/a/50981652
845
- const socket = @ptrCast(os.socket_t, op.fd);
846
- getsockoptError(socket) catch |err| switch (err) {
847
- error.FileDescriptorNotASocket => return os.windows.CloseHandle(op.fd),
848
- else => {},
849
- };
850
-
851
- os.closeSocket(socket);
852
- }
853
- },
854
- );
855
- }
856
-
857
- pub const TimeoutError = error{Canceled} || os.UnexpectedError;
858
-
859
- pub fn timeout(
860
- self: *IO,
861
- comptime Context: type,
862
- context: Context,
863
- comptime callback: fn (
864
- context: Context,
865
- completion: *Completion,
866
- result: TimeoutError!void,
867
- ) void,
868
- completion: *Completion,
869
- nanoseconds: u63,
870
- ) void {
871
- // Special case a zero timeout as a yield.
872
- if (nanoseconds == 0) {
873
- completion.* = .{
874
- .next = null,
875
- .context = @ptrCast(?*anyopaque, context),
876
- .operation = undefined,
877
- .callback = struct {
878
- fn on_complete(ctx: Completion.Context) void {
879
- const _context = @intToPtr(Context, @ptrToInt(ctx.completion.context));
880
- callback(_context, ctx.completion, {});
881
- }
882
- }.on_complete,
883
- };
884
-
885
- self.completed.push(completion);
886
- return;
887
- }
888
-
889
- self.submit(
890
- context,
891
- callback,
892
- completion,
893
- .timeout,
894
- .{ .deadline = self.timer.monotonic() + nanoseconds },
895
- struct {
896
- fn do_operation(ctx: Completion.Context, op: anytype) TimeoutError!void {
897
- _ = ctx;
898
- _ = op;
899
- return;
900
- }
901
- },
902
- );
903
- }
904
-
905
- pub const INVALID_SOCKET = os.windows.ws2_32.INVALID_SOCKET;
906
-
907
- /// Creates a socket that can be used for async operations with the IO instance.
908
- pub fn open_socket(self: *IO, family: u32, sock_type: u32, protocol: u32) !os.socket_t {
909
- // SOCK_NONBLOCK | SOCK_CLOEXEC
910
- var flags: os.windows.DWORD = 0;
911
- flags |= os.windows.ws2_32.WSA_FLAG_OVERLAPPED;
912
- flags |= os.windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
913
-
914
- const socket = try os.windows.WSASocketW(
915
- @bitCast(i32, family),
916
- @bitCast(i32, sock_type),
917
- @bitCast(i32, protocol),
918
- null,
919
- 0,
920
- flags,
921
- );
922
- errdefer os.closeSocket(socket);
923
-
924
- const socket_iocp = try os.windows.CreateIoCompletionPort(socket, self.iocp, 0, 0);
925
- assert(socket_iocp == self.iocp);
926
-
927
- // Ensure that synchronous IO completion doesn't queue an unneeded overlapped
928
- // and that the event for the socket (WaitForSingleObject) doesn't need to be set.
929
- var mode: os.windows.BYTE = 0;
930
- mode |= os.windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
931
- mode |= os.windows.FILE_SKIP_SET_EVENT_ON_HANDLE;
932
-
933
- const handle = @ptrCast(os.windows.HANDLE, socket);
934
- try os.windows.SetFileCompletionNotificationModes(handle, mode);
935
-
936
- return socket;
937
- }
938
-
939
- /// Opens a directory with read only access.
940
- pub fn open_dir(dir_path: []const u8) !os.fd_t {
941
- const dir = try std.fs.cwd().openDir(dir_path, .{});
942
- return dir.fd;
943
- }
944
-
945
- pub const INVALID_FILE = os.windows.INVALID_HANDLE_VALUE;
946
-
947
- /// Opens or creates a journal file:
948
- /// - For reading and writing.
949
- /// - For Direct I/O (required on windows).
950
- /// - Obtains an advisory exclusive lock to the file descriptor.
951
- /// - Allocates the file contiguously on disk if this is supported by the file system.
952
- /// - Ensures that the file data is durable on disk.
953
- /// The caller is responsible for ensuring that the parent directory inode is durable.
954
- /// - Verifies that the file size matches the expected file size before returning.
955
- pub fn open_file(
956
- dir_handle: os.fd_t,
957
- relative_path: []const u8,
958
- size: u64,
959
- must_create: bool,
960
- ) !os.fd_t {
961
- assert(relative_path.len > 0);
962
- assert(size >= constants.sector_size);
963
- assert(size % constants.sector_size == 0);
964
-
965
- const path_w = try os.windows.sliceToPrefixedFileW(relative_path);
966
-
967
- // FILE_CREATE = O_CREAT | O_EXCL
968
- var creation_disposition: os.windows.DWORD = 0;
969
- if (must_create) {
970
- log.info("creating \"{s}\"...", .{relative_path});
971
- creation_disposition = os.windows.FILE_CREATE;
972
- } else {
973
- log.info("opening \"{s}\"...", .{relative_path});
974
- creation_disposition = os.windows.OPEN_EXISTING;
975
- }
976
-
977
- // O_EXCL
978
- var shared_mode: os.windows.DWORD = 0;
979
-
980
- // O_RDWR
981
- var access_mask: os.windows.DWORD = 0;
982
- access_mask |= os.windows.GENERIC_READ;
983
- access_mask |= os.windows.GENERIC_WRITE;
984
-
985
- // O_DIRECT | O_DSYNC
986
- var attributes: os.windows.DWORD = 0;
987
- attributes |= os.windows.FILE_FLAG_NO_BUFFERING;
988
- attributes |= os.windows.FILE_FLAG_WRITE_THROUGH;
989
-
990
- // This is critical as we rely on O_DSYNC for fsync() whenever we write to the file:
991
- assert((attributes & os.windows.FILE_FLAG_WRITE_THROUGH) > 0);
992
-
993
- // TODO: Add ReadFileEx/WriteFileEx support.
994
- // Not currently needed for O_DIRECT disk IO.
995
- // attributes |= os.windows.FILE_FLAG_OVERLAPPED;
996
-
997
- const handle = os.windows.kernel32.CreateFileW(
998
- path_w.span(),
999
- access_mask,
1000
- shared_mode,
1001
- null, // no security attributes required
1002
- creation_disposition,
1003
- attributes,
1004
- null, // no existing template file
1005
- );
1006
-
1007
- if (handle == os.windows.INVALID_HANDLE_VALUE) {
1008
- return switch (os.windows.kernel32.GetLastError()) {
1009
- .FILE_NOT_FOUND => error.FileNotFound,
1010
- .SHARING_VIOLATION, .ACCESS_DENIED => error.AccessDenied,
1011
- else => |err| {
1012
- log.warn("CreateFileW(): {}", .{err});
1013
- return os.windows.unexpectedError(err);
1014
- },
1015
- };
1016
- }
1017
-
1018
- errdefer os.windows.CloseHandle(handle);
1019
-
1020
- // Obtain an advisory exclusive lock
1021
- // even when we haven't given shared access to other processes.
1022
- fs_lock(handle, size) catch |err| switch (err) {
1023
- error.WouldBlock => @panic("another process holds the data file lock"),
1024
- else => return err,
1025
- };
1026
-
1027
- // Ask the file system to allocate contiguous sectors for the file (if possible):
1028
- if (must_create) {
1029
- log.info("allocating {}...", .{std.fmt.fmtIntSizeBin(size)});
1030
- fs_allocate(handle, size) catch {
1031
- log.warn("file system failed to preallocate the file memory", .{});
1032
- log.info("allocating by writing to the last sector of the file instead...", .{});
1033
-
1034
- const sector_size = constants.sector_size;
1035
- const sector: [sector_size]u8 align(sector_size) = [_]u8{0} ** sector_size;
1036
-
1037
- // Handle partial writes where the physical sector is less than a logical sector:
1038
- const write_offset = size - sector.len;
1039
- var written: usize = 0;
1040
- while (written < sector.len) {
1041
- written += try os.pwrite(handle, sector[written..], write_offset + written);
1042
- }
1043
- };
1044
- }
1045
-
1046
- // The best fsync strategy is always to fsync before reading because this prevents us from
1047
- // making decisions on data that was never durably written by a previously crashed process.
1048
- // We therefore always fsync when we open the path, also to wait for any pending O_DSYNC.
1049
- // Thanks to Alex Miller from FoundationDB for diving into our source and pointing this out.
1050
- try os.fsync(handle);
1051
-
1052
- // We cannot fsync the directory handle on Windows.
1053
- // We have no way to open a directory with write access.
1054
- //
1055
- // try os.fsync(dir_handle);
1056
- _ = dir_handle;
1057
-
1058
- const file_size = try os.windows.GetFileSizeEx(handle);
1059
- if (file_size < size) @panic("data file inode size was truncated or corrupted");
1060
-
1061
- return handle;
1062
- }
1063
-
1064
- fn fs_lock(handle: os.fd_t, size: u64) !void {
1065
- // TODO: Look into using SetFileIoOverlappedRange() for better unbuffered async IO perf
1066
- // NOTE: Requires SeLockMemoryPrivilege.
1067
-
1068
- const kernel32 = struct {
1069
- const LOCKFILE_EXCLUSIVE_LOCK = 0x2;
1070
- const LOCKFILE_FAIL_IMMEDIATELY = 01;
1071
-
1072
- extern "kernel32" fn LockFileEx(
1073
- hFile: os.windows.HANDLE,
1074
- dwFlags: os.windows.DWORD,
1075
- dwReserved: os.windows.DWORD,
1076
- nNumberOfBytesToLockLow: os.windows.DWORD,
1077
- nNumberOfBytesToLockHigh: os.windows.DWORD,
1078
- lpOverlapped: ?*os.windows.OVERLAPPED,
1079
- ) callconv(os.windows.WINAPI) os.windows.BOOL;
1080
- };
1081
-
1082
- // hEvent = null
1083
- // Offset & OffsetHigh = 0
1084
- var lock_overlapped = std.mem.zeroes(os.windows.OVERLAPPED);
1085
-
1086
- // LOCK_EX | LOCK_NB
1087
- var lock_flags: os.windows.DWORD = 0;
1088
- lock_flags |= kernel32.LOCKFILE_EXCLUSIVE_LOCK;
1089
- lock_flags |= kernel32.LOCKFILE_FAIL_IMMEDIATELY;
1090
-
1091
- const locked = kernel32.LockFileEx(
1092
- handle,
1093
- lock_flags,
1094
- 0, // reserved param is always zero
1095
- @truncate(u32, size), // low bits of size
1096
- @truncate(u32, size >> 32), // high bits of size
1097
- &lock_overlapped,
1098
- );
1099
-
1100
- if (locked == os.windows.FALSE) {
1101
- return switch (os.windows.kernel32.GetLastError()) {
1102
- .IO_PENDING => error.WouldBlock,
1103
- else => |err| os.windows.unexpectedError(err),
1104
- };
1105
- }
1106
- }
1107
-
1108
- fn fs_allocate(handle: os.fd_t, size: u64) !void {
1109
- // TODO: Look into using SetFileValidData() instead
1110
- // NOTE: Requires SE_MANAGE_VOLUME_NAME privilege
1111
-
1112
- // Move the file pointer to the start + size
1113
- const seeked = os.windows.kernel32.SetFilePointerEx(
1114
- handle,
1115
- @intCast(i64, size),
1116
- null, // no reference to new file pointer
1117
- os.windows.FILE_BEGIN,
1118
- );
1119
-
1120
- if (seeked == os.windows.FALSE) {
1121
- return switch (os.windows.kernel32.GetLastError()) {
1122
- .INVALID_HANDLE => unreachable,
1123
- .INVALID_PARAMETER => unreachable,
1124
- else => |err| os.windows.unexpectedError(err),
1125
- };
1126
- }
1127
-
1128
- // Mark the moved file pointer (start + size) as the physical EOF.
1129
- const allocated = os.windows.kernel32.SetEndOfFile(handle);
1130
- if (allocated == os.windows.FALSE) {
1131
- const err = os.windows.kernel32.GetLastError();
1132
- return os.windows.unexpectedError(err);
1133
- }
1134
- }
1135
- };
1136
-
1137
- // TODO: use os.getsockoptError when fixed for windows in stdlib
1138
- fn getsockoptError(socket: os.socket_t) IO.ConnectError!void {
1139
- var err_code: u32 = undefined;
1140
- var size: i32 = @sizeOf(u32);
1141
- const rc = os.windows.ws2_32.getsockopt(
1142
- socket,
1143
- os.SOL.SOCKET,
1144
- os.SO.ERROR,
1145
- std.mem.asBytes(&err_code),
1146
- &size,
1147
- );
1148
-
1149
- if (rc != 0) {
1150
- switch (os.windows.ws2_32.WSAGetLastError()) {
1151
- .WSAENETDOWN => return error.NetworkUnreachable,
1152
- .WSANOTINITIALISED => unreachable, // WSAStartup() was never called
1153
- .WSAEFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
1154
- .WSAEINVAL => unreachable, // The level parameter is unknown or invalid
1155
- .WSAENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
1156
- .WSAENOTSOCK => return error.FileDescriptorNotASocket,
1157
- else => |err| return os.windows.unexpectedWSAError(err),
1158
- }
1159
- }
1160
-
1161
- assert(size == 4);
1162
- if (err_code == 0)
1163
- return;
1164
-
1165
- const ws_err = @intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, err_code));
1166
- return switch (ws_err) {
1167
- .WSAEACCES => error.PermissionDenied,
1168
- .WSAEADDRINUSE => error.AddressInUse,
1169
- .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
1170
- .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
1171
- .WSAEALREADY => error.ConnectionPending,
1172
- .WSAEBADF => unreachable,
1173
- .WSAECONNREFUSED => error.ConnectionRefused,
1174
- .WSAEFAULT => unreachable,
1175
- .WSAEISCONN => unreachable, // error.AlreadyConnected,
1176
- .WSAENETUNREACH => error.NetworkUnreachable,
1177
- .WSAENOTSOCK => error.FileDescriptorNotASocket,
1178
- .WSAEPROTOTYPE => unreachable,
1179
- .WSAETIMEDOUT => error.ConnectionTimedOut,
1180
- .WSAECONNRESET => error.ConnectionResetByPeer,
1181
- else => |e| os.windows.unexpectedWSAError(e),
1182
- };
1183
- }