lib/wayland/src/runtime/event.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const sys = @import("sys");
3 const wayland = @import("../root.zig");
4 const message = @import("message.zig");
5
6 pub const maximum_descriptor_count = maximumDescriptorCount(
7 &wayland.protocol.interfaces,
8 );
9
10 pub const Limits = struct {
11 payload_byte_count: usize = wayland.protocol.value.maximum_payload_size,
12 descriptor_count: usize = maximum_descriptor_count,
13 };
14
15 pub const CapacityError = error{
16 EventPayloadStorageTooLarge,
17 EventDescriptorStorageTooLarge,
18 CapacityOverflow,
19 };
20
21 pub const Capacity = struct {
22 payload_byte_count: usize,
23 descriptor_count: usize,
24 descriptor_bytes: usize,
25 total_requested_bytes: usize,
26
27 pub fn derive(limits: Limits) CapacityError!Capacity {
28 if (limits.payload_byte_count > wayland.protocol.value.maximum_payload_size) {
29 return error.EventPayloadStorageTooLarge;
30 }
31 if (limits.descriptor_count > maximum_descriptor_count) {
32 return error.EventDescriptorStorageTooLarge;
33 }
34 const descriptor_bytes = std.math.mul(
35 usize,
36 limits.descriptor_count,
37 @sizeOf(sys.fd.Descriptor),
38 ) catch return error.CapacityOverflow;
39 const total_requested_bytes = std.math.add(
40 usize,
41 limits.payload_byte_count,
42 descriptor_bytes,
43 ) catch return error.CapacityOverflow;
44 return .{
45 .payload_byte_count = limits.payload_byte_count,
46 .descriptor_count = limits.descriptor_count,
47 .descriptor_bytes = descriptor_bytes,
48 .total_requested_bytes = total_requested_bytes,
49 };
50 }
51 };
52
53 pub const StorageError = error{
54 EventPayloadCapacityExceeded,
55 EventDescriptorCapacityExceeded,
56 };
57
58 pub const Status = struct {
59 event_payload_capacity_rejection_count: u64 = 0,
60 event_descriptor_capacity_rejection_count: u64 = 0,
61 };
62
63 pub const default_capacity = Capacity.derive(.{}) catch unreachable;
64
65 pub const FatalView = struct {
66 object_id: u32,
67 code: u32,
68 message: []const u8,
69 };
70
71 pub const GlobalView = struct {
72 registry_id: u32,
73 name: u32,
74 interface: []const u8,
75 version: u32,
76 };
77
78 pub const CallbackDone = struct {
79 callback_id: u32,
80 data: u32,
81 };
82
83 pub const GlobalRemoved = struct {
84 registry_id: u32,
85 name: u32,
86 };
87
88 pub const EventView = union(enum) {
89 fatal: FatalView,
90 delete_id: u32,
91 callback_done: CallbackDone,
92 global: GlobalView,
93 global_remove: GlobalRemoved,
94 routed: message.RoutedView,
95 };
96
97 pub const Step = union(enum) {
98 event: EventView,
99 pending,
100 closed,
101 };
102
103 pub const StoredRouted = struct {
104 payload: []const u8,
105 descriptors: []sys.fd.Descriptor,
106 };
107
108 pub const Storage = struct {
109 session_allocator: std.mem.Allocator,
110 payload: []u8,
111 descriptors: []sys.fd.Descriptor,
112 payload_count: usize = 0,
113 descriptor_count: usize = 0,
114 payload_capacity_rejection_count: u64 = 0,
115 descriptor_capacity_rejection_count: u64 = 0,
116
117 pub fn init(
118 session_allocator: std.mem.Allocator,
119 capacity: Capacity,
120 ) std.mem.Allocator.Error!Storage {
121 const payload = try session_allocator.alloc(u8, capacity.payload_byte_count);
122 errdefer if (payload.len != 0) session_allocator.free(payload);
123 return .{
124 .session_allocator = session_allocator,
125 .payload = payload,
126 .descriptors = try session_allocator.alloc(
127 sys.fd.Descriptor,
128 capacity.descriptor_count,
129 ),
130 };
131 }
132
133 pub fn deinit(self: *Storage) void {
134 self.reset();
135 if (self.payload.len != 0) self.session_allocator.free(self.payload);
136 if (self.descriptors.len != 0) self.session_allocator.free(self.descriptors);
137 self.* = undefined;
138 }
139
140 pub fn reset(self: *Storage) void {
141 self.assertValid();
142 for (self.descriptors[0..self.descriptor_count]) |descriptor| {
143 if (descriptor >= 0) sys.fd.close(descriptor);
144 }
145 self.payload_count = 0;
146 self.descriptor_count = 0;
147 self.assertValid();
148 }
149
150 pub fn admit(
151 self: *Storage,
152 payload_byte_count: usize,
153 descriptor_count: usize,
154 ) StorageError!void {
155 self.assertValid();
156 std.debug.assert(self.payload_count == 0);
157 std.debug.assert(self.descriptor_count == 0);
158 var failure: ?StorageError = null;
159 if (payload_byte_count > self.payload.len) {
160 self.payload_capacity_rejection_count +|= 1;
161 failure = error.EventPayloadCapacityExceeded;
162 }
163 if (descriptor_count > self.descriptors.len) {
164 self.descriptor_capacity_rejection_count +|= 1;
165 if (failure == null) failure = error.EventDescriptorCapacityExceeded;
166 }
167 if (failure) |err| return err;
168 }
169
170 pub fn storePayloadAssumeCapacity(
171 self: *Storage,
172 payload: []const u8,
173 ) []const u8 {
174 self.assertValid();
175 std.debug.assert(self.payload_count == 0);
176 std.debug.assert(self.descriptor_count == 0);
177 std.debug.assert(payload.len <= self.payload.len);
178 @memcpy(self.payload[0..payload.len], payload);
179 self.payload_count = payload.len;
180 self.assertValid();
181 return self.payload[0..self.payload_count];
182 }
183
184 pub fn storeRoutedAssumeCapacity(
185 self: *Storage,
186 payload: []const u8,
187 owned_descriptors: []const sys.fd.Descriptor,
188 ) StoredRouted {
189 const stored_payload = self.storePayloadAssumeCapacity(payload);
190 std.debug.assert(owned_descriptors.len <= self.descriptors.len);
191 @memcpy(self.descriptors[0..owned_descriptors.len], owned_descriptors);
192 self.descriptor_count = owned_descriptors.len;
193 self.assertValid();
194 return .{
195 .payload = stored_payload,
196 .descriptors = self.descriptors[0..self.descriptor_count],
197 };
198 }
199
200 pub fn status(self: *const Storage) Status {
201 self.assertValid();
202 return .{
203 .event_payload_capacity_rejection_count = self.payload_capacity_rejection_count,
204 .event_descriptor_capacity_rejection_count = self.descriptor_capacity_rejection_count,
205 };
206 }
207
208 fn assertValid(self: *const Storage) void {
209 std.debug.assert(self.payload_count <= self.payload.len);
210 std.debug.assert(self.descriptor_count <= self.descriptors.len);
211 }
212 };
213
214 fn maximumDescriptorCount(
215 interfaces: []const wayland.protocol.schema.Interface,
216 ) usize {
217 var maximum: usize = 0;
218 for (interfaces) |interface| {
219 for (interface.events) |item| {
220 maximum = @max(maximum, item.descriptor_count);
221 }
222 }
223 return maximum;
224 }
225
226 test "runtime event capacity derives exact reusable storage" {
227 const capacity = try Capacity.derive(.{
228 .payload_byte_count = 13,
229 .descriptor_count = 1,
230 });
231 try std.testing.expectEqual(@as(usize, 13), capacity.payload_byte_count);
232 try std.testing.expectEqual(@as(usize, 1), capacity.descriptor_count);
233 try std.testing.expectEqual(
234 @sizeOf(sys.fd.Descriptor),
235 capacity.descriptor_bytes,
236 );
237 try std.testing.expectEqual(
238 13 + @sizeOf(sys.fd.Descriptor),
239 capacity.total_requested_bytes,
240 );
241 try std.testing.expectEqual(@as(usize, 1), maximum_descriptor_count);
242 try std.testing.expectEqual(
243 @as(usize, wayland.protocol.value.maximum_payload_size),
244 default_capacity.payload_byte_count,
245 );
246 try std.testing.expectEqual(
247 maximum_descriptor_count,
248 default_capacity.descriptor_count,
249 );
250 try std.testing.expectError(
251 error.EventPayloadStorageTooLarge,
252 Capacity.derive(.{
253 .payload_byte_count = wayland.protocol.value.maximum_payload_size + 1,
254 }),
255 );
256 try std.testing.expectError(
257 error.EventDescriptorStorageTooLarge,
258 Capacity.derive(.{ .descriptor_count = maximum_descriptor_count + 1 }),
259 );
260 for (wayland.protocol.interfaces) |interface| {
261 for (interface.events) |item| {
262 try std.testing.expect(item.descriptor_count <= maximum_descriptor_count);
263 }
264 }
265 }
266
267 test "runtime event storage acquires both regions before use" {
268 const capacity = try Capacity.derive(.{});
269 for (0..2) |fail_index| {
270 var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{
271 .fail_index = fail_index,
272 });
273 try std.testing.expectError(
274 error.OutOfMemory,
275 Storage.init(failing.allocator(), capacity),
276 );
277 }
278 var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{
279 .fail_index = 2,
280 });
281 var storage = try Storage.init(failing.allocator(), capacity);
282 defer storage.deinit();
283 try std.testing.expectEqual(@as(usize, 2), failing.allocations);
284 }
285
286 test "runtime event storage rejects max plus one without mutation" {
287 var storage = try Storage.init(
288 std.testing.allocator,
289 try Capacity.derive(.{ .payload_byte_count = 3, .descriptor_count = 1 }),
290 );
291 defer storage.deinit();
292 const payload_pointer = storage.payload.ptr;
293 const descriptor_pointer = storage.descriptors.ptr;
294
295 try std.testing.expectError(
296 error.EventPayloadCapacityExceeded,
297 storage.admit(4, 2),
298 );
299 try std.testing.expectEqual(@as(usize, 0), storage.payload_count);
300 try std.testing.expectEqual(@as(usize, 0), storage.descriptor_count);
301 try std.testing.expectEqual(payload_pointer, storage.payload.ptr);
302 try std.testing.expectEqual(descriptor_pointer, storage.descriptors.ptr);
303 try std.testing.expectEqual(
304 @as(u64, 1),
305 storage.status().event_payload_capacity_rejection_count,
306 );
307 try std.testing.expectEqual(
308 @as(u64, 1),
309 storage.status().event_descriptor_capacity_rejection_count,
310 );
311
312 storage.payload_capacity_rejection_count = std.math.maxInt(u64);
313 storage.descriptor_capacity_rejection_count = std.math.maxInt(u64);
314 try std.testing.expectError(
315 error.EventPayloadCapacityExceeded,
316 storage.admit(4, 2),
317 );
318 try std.testing.expectEqual(
319 std.math.maxInt(u64),
320 storage.status().event_payload_capacity_rejection_count,
321 );
322 try std.testing.expectEqual(
323 std.math.maxInt(u64),
324 storage.status().event_descriptor_capacity_rejection_count,
325 );
326 }
327
328 test "runtime event storage closes only untaken descriptors on reset" {
329 var storage = try Storage.init(std.testing.allocator, try Capacity.derive(.{}));
330 defer storage.deinit();
331 const pipe = try sys.fd.pipeWithOptions(.{ .close_on_exec = true });
332 defer sys.fd.close(pipe[0]);
333
334 try storage.admit(4, 1);
335 const stored = storage.storeRoutedAssumeCapacity("data", &.{pipe[1]});
336 var routed: message.RoutedView = .{
337 .object_id = 2,
338 .interface = &wayland.protocol.core.wl_keyboard.metadata,
339 .version = 1,
340 .metadata = &wayland.protocol.core.wl_keyboard.events.keymap,
341 .payload = stored.payload,
342 .descriptors = stored.descriptors,
343 };
344 const taken = try routed.takeDescriptor(0);
345 defer sys.fd.close(taken);
346 storage.reset();
347 try std.testing.expectEqual(@as(usize, 1), try sys.fd.write(taken, "x"));
348 var byte: [1]u8 = undefined;
349 try std.testing.expectEqual(@as(usize, 1), try sys.fd.read(pipe[0], &byte));
350 try std.testing.expectEqual(@as(u8, 'x'), byte[0]);
351
352 const untaken_pipe = try sys.fd.pipeWithOptions(.{ .close_on_exec = true });
353 defer sys.fd.close(untaken_pipe[0]);
354 try storage.admit(4, 1);
355 _ = storage.storeRoutedAssumeCapacity("next", &.{untaken_pipe[1]});
356 storage.reset();
357 try std.testing.expectEqual(
358 @as(usize, 0),
359 try sys.fd.read(untaken_pipe[0], &byte),
360 );
361 }