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 }