The Pedigree Project 0.1
epoll-syscalls.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 */
8
9#include "epoll-syscalls.h"
10#include "pedigree/kernel/Atomic.h"
11#include "pedigree/kernel/LockGuard.h"
12#include "pedigree/kernel/process/Mutex.h"
13#include "pedigree/kernel/process/Semaphore.h"
14#include "pedigree/kernel/process/Thread.h"
15#include "pedigree/kernel/syscallError.h"
16#include "pedigree/kernel/time/Time.h"
17#include "pedigree/kernel/utilities/List.h"
18#include "pedigree/kernel/utilities/SharedPointer.h"
19#include "pedigree/kernel/utilities/assert.h"
20
21#include <config.h>
22#include <fcntl.h>
23#include <limits.h>
24#include <signal.h>
25
26#include "modules/subsys/posix/FileDescriptor.h"
27#include "modules/subsys/posix/PosixSubsystem.h"
28#include "modules/subsys/posix/eventfd-syscalls.h"
29#include "modules/subsys/posix/fanotify-syscalls.h"
30#include "modules/subsys/posix/inotify-syscalls.h"
31#include "modules/subsys/posix/mqueue-syscalls.h"
32#include "modules/subsys/posix/net-syscalls.h"
33#include "modules/system/vfs/File.h"
34#include "signalfd-syscalls.h"
35#include "timerfd-syscalls.h"
36
37namespace {
38constexpr int MaximumEpollBatch = 16384;
39constexpr int LinuxMaximumEpollEvents = INT_MAX / static_cast<int>(sizeof(LinuxEpollEvent));
40constexpr size_t LinuxKernelSigsetSize = sizeof(uint64_t);
41constexpr uint64_t UnblockableSignals =
42 (static_cast<uint64_t>(1) << (SIGKILL - 1)) | (static_cast<uint64_t>(1) << (SIGSTOP - 1));
43
44constexpr uint32_t ReadEvents = LinuxEpoll::In | LinuxEpoll::ReadNormal | LinuxEpoll::ReadBand;
45constexpr uint32_t WriteEvents = LinuxEpoll::Out | LinuxEpoll::WriteNormal | LinuxEpoll::WriteBand;
46constexpr uint32_t RequestedEvents =
47 ReadEvents | WriteEvents | LinuxEpoll::Priority | LinuxEpoll::ReadHangup;
48constexpr uint32_t AlwaysReturnedEvents = LinuxEpoll::Error | LinuxEpoll::Hangup;
49constexpr uint32_t SupportedEvents =
50 RequestedEvents | AlwaysReturnedEvents | LinuxEpoll::OneShot | LinuxEpoll::EdgeTriggered;
51constexpr uint32_t UnsupportedModes =
52 LinuxEpoll::Exclusive | LinuxEpoll::Wakeup | LinuxEpoll::Message;
53
54struct EpollWatch {
55 EpollWatch(int watchedFd, const FileDescriptor::OpenFileDescriptionLease& openFile,
56 File* watchedFile, const SharedPointer<NetworkSyscalls>& watchedNetwork,
57 const SharedPointer<EventFd>& watchedEventFd,
58 const SharedPointer<InotifyInstance>& watchedInotify,
59 const SharedPointer<FanotifyInstance>& watchedFanotify,
60 const SharedPointer<PosixMessageQueue>& watchedMqueue,
61 const SharedPointer<TimerFd>& watchedTimerFd,
62 const SharedPointer<SignalFdView>& watchedSignalView, bool readable, bool writable,
63 const LinuxEpollEvent& event)
64 : fd(watchedFd),
65 description(openFile),
66 file(watchedFile),
67 network(watchedNetwork),
68 eventFd(watchedEventFd),
69 inotify(watchedInotify),
70 fanotify(watchedFanotify),
71 mqueue(watchedMqueue),
72 timerFd(watchedTimerFd),
73 signalView(watchedSignalView),
74 canRead(readable),
75 canWrite(writable),
76 events(event.events),
77 data(event.data),
78 armed(true),
79 observedEvents(0),
80 pendingEvents(0),
81 observedWriteGeneration(watchedEventFd ? watchedEventFd->writeGeneration() : 0),
82 observedGenerations(),
83 subscription() {}
84
85 int fd;
87 File* file;
95 bool canRead;
96 bool canWrite;
97 uint32_t events;
98 uint64_t data;
99 bool armed;
100 uint32_t observedEvents;
101 uint32_t pendingEvents;
102 uint64_t observedWriteGeneration;
103 ReadinessGenerations observedGenerations;
104 ReadinessSubscription subscription;
105};
106
107ReadinessSource* watchSource(const EpollWatch& watch) {
108 if (watch.timerFd) {
109 return watch.timerFd.get();
110 }
111 if (watch.signalView) {
112 return watch.signalView.get();
113 }
114 if (watch.file) {
115 return watch.file;
116 }
117 if (watch.network) {
118 return watch.network.get();
119 }
120 if (watch.inotify) {
121 return watch.inotify.get();
122 }
123 if (watch.fanotify) {
124 return watch.fanotify->readinessSource();
125 }
126 if (watch.mqueue) {
127 return watch.mqueue.get();
128 }
129 return watch.eventFd.get();
130}
131
132uint32_t eventsFor(ReadyMask ready, uint32_t requested) {
133 uint32_t result = 0;
134 if (ready & ReadyRead) {
135 result |= requested & (LinuxEpoll::In | LinuxEpoll::ReadNormal);
136 }
137 if (ready & ReadyPriority) {
138 result |= requested & (LinuxEpoll::Priority | LinuxEpoll::ReadBand);
139 }
140 if (ready & ReadyWrite) {
141 result |= requested & WriteEvents;
142 }
143 if (ready & ReadyError) {
144 result |= LinuxEpoll::Error;
145 }
146 if (ready & ReadyHangup) {
147 result |= LinuxEpoll::Hangup;
148 }
149 if ((ready & ReadyReadHangup) && (requested & LinuxEpoll::ReadHangup)) {
150 result |= LinuxEpoll::ReadHangup;
151 }
152 if (ready & ReadyInvalid) {
153 result |= LinuxEpoll::Error | LinuxEpoll::Hangup;
154 }
155 return result;
156}
157
158ReadyMask queryWatch(const EpollWatch& watch) {
159 if (watch.timerFd) {
160 return watch.timerFd->queryReady();
161 }
162 if (watch.signalView) {
163 return watch.signalView->queryCallerReady();
164 }
165 const bool reading = watch.canRead && (watch.events & (ReadEvents | LinuxEpoll::ReadHangup));
166 const bool writing = watch.canWrite && (watch.events & WriteEvents);
167 if (watch.file) {
168 return watch.description->queryFileReady(reading, writing);
169 }
170 if (watch.network) {
171 return watch.network->queryReady(reading, writing);
172 }
173 if (watch.eventFd) {
174 return watch.eventFd->queryReady();
175 }
176 if (watch.inotify) {
177 return watch.inotify->queryReady();
178 }
179 if (watch.fanotify) {
180 return watch.fanotify->queryReady();
181 }
182 if (watch.mqueue) {
183 return watch.mqueue->queryReady();
184 }
185 return ReadyInvalid;
186}
187
188uint32_t sampleWatch(EpollWatch& watch) {
189 const uint32_t readyEvents = eventsFor(queryWatch(watch), watch.events);
190 if (watch.events & LinuxEpoll::EdgeTriggered) {
191 ReadinessSource* source = watchSource(watch);
192 const ReadinessGenerations generations =
193 watch.signalView
194 ? watch.signalView->callerReadinessGenerations()
195 : (watch.file ? watch.description->fileReadinessGenerations()
196 : (source ? source->readinessGenerations() : ReadinessGenerations()));
197 const uint64_t writeGeneration = watch.eventFd ? watch.eventFd->writeGeneration() : 0;
198 // Keep edges which have not yet been consumed, but do not return a stale
199 // edge after another thread has made that predicate false. Source-owned
200 // generations preserve a false-to-true transition even when the producer's
201 // callback overtakes the delayed notification for the preceding drain.
202 watch.pendingEvents &= readyEvents;
203 watch.pendingEvents |= readyEvents & ~watch.observedEvents;
204 if (generations.read != watch.observedGenerations.read) {
205 watch.pendingEvents |= readyEvents & (LinuxEpoll::In | LinuxEpoll::ReadNormal);
206 }
207 if (generations.write != watch.observedGenerations.write) {
208 watch.pendingEvents |= readyEvents & WriteEvents;
209 }
210 if (generations.priority != watch.observedGenerations.priority) {
211 watch.pendingEvents |= readyEvents & (LinuxEpoll::Priority | LinuxEpoll::ReadBand);
212 }
213 if (generations.error != watch.observedGenerations.error) {
214 watch.pendingEvents |= readyEvents & LinuxEpoll::Error;
215 }
216 if (generations.hangup != watch.observedGenerations.hangup) {
217 watch.pendingEvents |= readyEvents & LinuxEpoll::Hangup;
218 }
219 if (generations.readHangup != watch.observedGenerations.readHangup) {
220 watch.pendingEvents |= readyEvents & LinuxEpoll::ReadHangup;
221 }
222 if (watch.eventFd && writeGeneration != watch.observedWriteGeneration) {
223 // eventfd is a counter rather than a byte stream. Linux's async-event
224 // users can deliberately leave it readable and treat every successful
225 // producer write as a new edge.
226 watch.pendingEvents |= readyEvents & ReadEvents;
227 }
228 watch.observedEvents = readyEvents;
229 watch.observedWriteGeneration = writeGeneration;
230 watch.observedGenerations = generations;
231 }
232 return readyEvents;
233}
234
235uint32_t reportableEvents(const EpollWatch& watch, uint32_t readyEvents) {
236 if (watch.events & LinuxEpoll::EdgeTriggered) {
237 return watch.pendingEvents;
238 }
239 return readyEvents;
240}
241
242void retireWatch(EpollWatch* watch) {
243 if (!watch) {
244 return;
245 }
246 watch->subscription.reset();
247 delete watch;
248}
249
250void retireWatches(List<EpollWatch*>& watches) {
251 while (watches.count()) {
252 retireWatch(watches.popFront());
253 }
254}
255
256bool acquireEpoll(int fd, SharedPointer<EpollInstance>& instance) {
257 DescriptorLease descriptor;
258 if (!acquireDescriptor(fd, descriptor)) {
259 SYSCALL_ERROR(BadFileDescriptor);
260 return false;
261 }
262
263 instance = descriptor->epollImpl;
264 if (!instance) {
265 SYSCALL_ERROR(InvalidArgument);
266 return false;
267 }
268 return true;
269}
270} // namespace
271
273 public:
274 explicit EpollReadinessObserver(EpollInstance* instance) : m_Instance(instance) {}
275
276 void readinessChanged(ReadyMask mask) override {
277 m_Instance->sourceReadinessChanged(mask);
278 }
279
280 private:
281 EpollInstance* m_Instance;
282};
283
285 public:
286 explicit EpollState(EpollInstance* instance)
287 : lock(),
288 watches(),
289 wakeup(0, true),
290 wakePending(false),
291 observer(new EpollReadinessObserver(instance)) {}
292
293 ~EpollState() {
294 // EpollInstance retires subscriptions before releasing their source and
295 // observer references.
296 assert(!watches.count());
297 }
298
299 Mutex lock;
300 List<EpollWatch*> watches;
301 Semaphore wakeup;
302 Atomic<bool> wakePending;
304};
305
306EpollInstance::EpollInstance() : ReadinessSource(), m_State(new EpollState(this)) {}
307
308EpollInstance::~EpollInstance() {
309 List<EpollWatch*> retiring;
310 {
311 LockGuard<Mutex> guard(m_State->lock);
312 while (m_State->watches.count()) {
313 retiring.pushBack(m_State->watches.popFront());
314 }
315 }
316
317 // ReadinessSubscription::reset closes admission and drains a callback which
318 // already captured this instance. Never wait for that callback while the
319 // instance lock is held.
320 retireWatches(retiring);
321 m_State->observer.reset();
322 closeReadiness(ReadyInvalid | ReadyHangup);
323 delete m_State;
324 m_State = nullptr;
325}
326
327void EpollInstance::wakeWaiter() {
328 // Treat the semaphore as a binary wakeup. Coalescing prevents repeated
329 // level-triggered waits from accumulating an unbounded stale count.
330 if (m_State->wakePending.compareAndSwap(false, true)) {
331 m_State->wakeup.release();
332 }
333}
334
335void EpollInstance::sourceReadinessChanged(ReadyMask) {
336 bool reportable = false;
337 {
338 LockGuard<Mutex> guard(m_State->lock);
339 for (EpollWatch* watch : m_State->watches) {
340 if (!watch->description->descriptorOwnerCount()) {
341 continue;
342 }
343
344 if (watch->signalView) {
345 // Signal callbacks run in the producer's context. The waiting thread
346 // must sample its own private queue, including after the registrar exits.
347 reportable |= watch->armed;
348 continue;
349 }
350
351 const uint32_t readyEvents = sampleWatch(*watch);
352 if (watch->armed && reportableEvents(*watch, readyEvents)) {
353 reportable = true;
354 }
355 }
356 }
357
358 // Notifications are change hints rather than payloads. Source generations
359 // make reordered callbacks safe; a waiter still performs an authoritative
360 // rescan before returning anything to userspace.
361 if (reportable) {
362 wakeWaiter();
363 notifyReadiness(ReadyRead);
364 }
365}
366
367int EpollInstance::control(int operation, int targetFd, const LinuxEpollEvent* event) {
368 if (operation != LinuxEpoll::ControlAdd && operation != LinuxEpoll::ControlDelete &&
369 operation != LinuxEpoll::ControlModify) {
370 SYSCALL_ERROR(InvalidArgument);
371 return -1;
372 }
373
374 if ((operation == LinuxEpoll::ControlAdd || operation == LinuxEpoll::ControlModify) && !event) {
375 SYSCALL_ERROR(BadAddress);
376 return -1;
377 }
378
379 if (event) {
380 if (event->events & UnsupportedModes) {
381 SYSCALL_ERROR(OperationNotSupported);
382 return -1;
383 }
384 if (event->events & ~SupportedEvents) {
385 SYSCALL_ERROR(InvalidArgument);
386 return -1;
387 }
388 }
389
390 DescriptorLease descriptor;
391 if (!acquireDescriptor(targetFd, descriptor)) {
392 SYSCALL_ERROR(BadFileDescriptor);
393 return -1;
394 }
395
396 // Nested epoll needs cycle detection and a ready-list propagation model.
397 // Until those semantics exist, reject every epoll target explicitly.
398 if (descriptor->epollImpl) {
399 SYSCALL_ERROR(InvalidArgument);
400 return -1;
401 }
402
404 File* file = description->getFile();
405 SharedPointer<NetworkSyscalls> network = description->getNetworkImpl();
406 SharedPointer<EventFd> eventFd = description->getEventFdImpl();
407 SharedPointer<InotifyInstance> inotify = description->getInotifyImpl();
408 auto fanotify = description->getFanotifyImpl();
409 SharedPointer<PosixMessageQueue> mqueue = description->getMqueueImpl();
410 auto timerFd = description->getTimerFdImpl();
411 auto signalFd = description->getSignalFdImpl();
412 if (!file && !network && !eventFd && !inotify && !fanotify && !mqueue && !timerFd && !signalFd) {
413 SYSCALL_ERROR(NotEnoughPermissions);
414 return -1;
415 }
416
417 if ((operation == LinuxEpoll::ControlAdd || operation == LinuxEpoll::ControlModify) && file &&
419 // Linux rejects regular files and directories with EPERM. Opt-in keeps a
420 // File subclass from appearing epollable merely because select() can be
421 // sampled once; it must also publish later readiness transitions.
422 SYSCALL_ERROR(NotEnoughPermissions);
423 return -1;
424 }
425
426 if (operation == LinuxEpoll::ControlDelete) {
427 EpollWatch* retiring = nullptr;
428 {
429 LockGuard<Mutex> guard(m_State->lock);
430 for (auto it = m_State->watches.begin(); it != m_State->watches.end(); ++it) {
431 EpollWatch* watch = *it;
432 if (watch->fd == targetFd && watch->description.get() == description.get()) {
433 retiring = watch;
434 m_State->watches.erase(it);
435 break;
436 }
437 }
438 }
439
440 if (!retiring) {
441 SYSCALL_ERROR(DoesNotExist);
442 return -1;
443 }
444 retireWatch(retiring);
445 return 0;
446 }
447
448 if (operation == LinuxEpoll::ControlModify) {
449 bool found = false;
450 {
451 LockGuard<Mutex> guard(m_State->lock);
452 for (EpollWatch* watch : m_State->watches) {
453 if (watch->fd == targetFd && watch->description.get() == description.get()) {
454 watch->events = event->events;
455 watch->data = event->data;
456 watch->armed = true;
457 // MOD both rearms EPOLLONESHOT and republishes an already-ready
458 // level for EPOLLET, matching Linux's re-poll-on-modify behavior.
459 watch->observedEvents = 0;
460 watch->pendingEvents = 0;
461 watch->observedWriteGeneration = watch->eventFd ? watch->eventFd->writeGeneration() : 0;
462 ReadinessSource* source = watchSource(*watch);
463 watch->observedGenerations =
464 watch->signalView
465 ? watch->signalView->callerReadinessGenerations()
466 : (watch->file
467 ? watch->description->fileReadinessGenerations()
468 : (source ? source->readinessGenerations() : ReadinessGenerations()));
469 found = true;
470 break;
471 }
472 }
473 }
474
475 if (!found) {
476 SYSCALL_ERROR(DoesNotExist);
477 return -1;
478 }
479
480 // MOD rearms EPOLLONESHOT and may make an already-true level relevant.
481 sourceReadinessChanged(ReadyAll);
482 return 0;
483 }
484
486 if (signalFd) {
487 signalView = signalFd->bindCaller();
488 if (!signalView) {
489 SYSCALL_ERROR(BadFileDescriptor);
490 return -1;
491 }
492 }
493
494 const int accessMode = descriptor->getStatusFlags() & O_ACCMODE;
495 const bool canRead =
496 network || eventFd || inotify || fanotify || mqueue || accessMode != O_WRONLY;
497 const bool canWrite = network || eventFd || mqueue || accessMode != O_RDONLY;
498 EpollWatch* watch =
499 new EpollWatch(targetFd, description, file, network, eventFd, inotify, fanotify, mqueue,
500 timerFd, signalView, canRead, canWrite, *event);
501
502 // Subscription precedes publication, so the first subsequent wait cannot
503 // miss a readiness transition between registration and its initial scan.
504 ReadinessSource* source = watchSource(*watch);
505 // Keep the subscription broad: EPOLL_CTL_MOD may change the interest mask
506 // without replacing the target registration.
507 if (!source->subscribeReadiness(ReadyAll, m_State->observer, watch->subscription)) {
508 delete watch;
509 SYSCALL_ERROR(NotEnoughPermissions);
510 return -1;
511 }
512
513 bool duplicate = false;
514 {
515 LockGuard<Mutex> guard(m_State->lock);
516 for (EpollWatch* existing : m_State->watches) {
517 if (existing->fd == targetFd && existing->description.get() == description.get()) {
518 duplicate = true;
519 break;
520 }
521 }
522 if (!duplicate) {
523 m_State->watches.pushBack(watch);
524 }
525 }
526
527 if (duplicate) {
528 retireWatch(watch);
529 SYSCALL_ERROR(FileExists);
530 return -1;
531 }
532
533 // An already-ready source need not generate a transition after ADD.
534 sourceReadinessChanged(ReadyAll);
535 return 0;
536}
537
538int EpollInstance::collectEvents(LinuxEpollEvent* events, int maxEvents, bool consumeOneShot) {
539 List<EpollWatch*> retiring;
540 int count = 0;
541 {
542 LockGuard<Mutex> guard(m_State->lock);
543 const size_t candidates = m_State->watches.count();
544 size_t inspected = 0;
545
546 // Moving each inspected watch to the tail gives bounded fairness when the
547 // caller's result array is smaller than the ready set.
548 while (inspected < candidates && count < maxEvents) {
549 EpollWatch* watch = m_State->watches.popFront();
550 ++inspected;
551
552 if (!watch->description->descriptorOwnerCount()) {
553 retiring.pushBack(watch);
554 continue;
555 }
556
557 if (!watch->armed) {
558 m_State->watches.pushBack(watch);
559 continue;
560 }
561
562 const uint32_t readyEvents = sampleWatch(*watch);
563 if (!watch->description->descriptorOwnerCount()) {
564 retiring.pushBack(watch);
565 continue;
566 }
567
568 const uint32_t returnedEvents = reportableEvents(*watch, readyEvents);
569 if (returnedEvents) {
570 if (events) {
571 events[count].events = returnedEvents;
572 events[count].data = watch->data;
573 }
574 ++count;
575 if (consumeOneShot) {
576 if (watch->events & LinuxEpoll::EdgeTriggered) {
577 watch->pendingEvents &= ~returnedEvents;
578 }
579 if (watch->events & LinuxEpoll::OneShot) {
580 watch->armed = false;
581 }
582 }
583 }
584
585 m_State->watches.pushBack(watch);
586 }
587 }
588
589 // A readiness callback admitted before removal may still be executing.
590 // Reset outside m_State->lock so its drain cannot deadlock with a waiter.
591 retireWatches(retiring);
592 return count;
593}
594
596 return collectEvents(nullptr, 1, false) ? ReadyRead : ReadyNone;
597}
598
599int EpollInstance::wait(LinuxEpollEvent* events, int maxEvents, int timeoutMilliseconds) {
600 const bool hasTimeout = timeoutMilliseconds >= 0;
601 const Time::Timestamp deadline =
602 timeoutMilliseconds > 0
603 ? Time::getTicks() +
604 static_cast<Time::Timestamp>(timeoutMilliseconds) * Time::Multiplier::Millisecond
605 : 0;
606
607 EMIT_IF(!THREADS) {
608 return collectEvents(events, maxEvents, true);
609 }
610 else {
611 while (true) {
612 // Always take the authoritative snapshot first. A level which changes
613 // after this scan leaves a semaphore count for the wait below.
614 int ready = collectEvents(events, maxEvents, true);
615 if (ready) {
616 // One source hint wakes one waiter. Hand the wake onward while a
617 // level may still be true so other threads already blocked on this
618 // epoll object are not stranded. An EPOLLONESHOT-only result causes
619 // at most one harmless extra rescan.
620 wakeWaiter();
621 return ready;
622 }
623 if (timeoutMilliseconds == 0) {
624 return 0;
625 }
626
627 // Consume an older hint without blocking, clear its binary admission,
628 // and rescan. A concurrent producer after the clear publishes a fresh
629 // semaphore count.
630 if (m_State->wakeup.tryAcquire()) {
631 m_State->wakePending = false;
632 continue;
633 }
634
635 size_t waitSeconds = 0;
636 size_t waitMicroseconds = 0;
637 if (hasTimeout) {
638 const Time::Timestamp now = Time::getTicks();
639 if (now >= deadline) {
640 return 0;
641 }
642
643 const Time::Timestamp remaining = deadline - now;
644 waitSeconds = remaining / Time::Multiplier::Second;
645 waitMicroseconds =
646 (remaining % Time::Multiplier::Second + Time::Multiplier::Microsecond - 1) /
647 Time::Multiplier::Microsecond;
648 if (waitMicroseconds >= 1000000) {
649 ++waitSeconds;
650 waitMicroseconds = 0;
651 }
652 }
653
654 Semaphore::SemaphoreError error = Semaphore::NoError;
655 const bool acquired =
656 m_State->wakeup.acquireWithError(1, waitSeconds, waitMicroseconds, error);
657 if (acquired) {
658 m_State->wakePending = false;
659 // The loop always rescans; callbacks carry hints, not event payloads.
660 continue;
661 }
662
663 // Readiness which became visible concurrently with timeout or a signal
664 // wins, matching the rest of the POSIX blocking-I/O boundary.
665 ready = collectEvents(events, maxEvents, true);
666 if (ready) {
667 return ready;
668 }
669 if (error == Semaphore::TimedOut) {
670 return 0;
671 }
672
673 SYSCALL_ERROR(Interrupted);
674 return -1;
675 }
676 }
677}
678
679int posix_epoll_create1(int flags) {
680 if (flags & ~LinuxEpoll::CloseOnExec) {
681 SYSCALL_ERROR(InvalidArgument);
682 return -1;
683 }
684
685 const size_t fd = getAvailableDescriptor();
686 const int descriptorFlags = flags & LinuxEpoll::CloseOnExec ? FD_CLOEXEC : 0;
687 FileDescriptor* descriptor = new FileDescriptor(nullptr, 0, fd, descriptorFlags, O_RDWR);
688 descriptor->epollImpl.reset(new EpollInstance);
689 addDescriptor(static_cast<int>(fd), descriptor);
690 return static_cast<int>(fd);
691}
692
693int posix_epoll_create(int size) {
694 if (size <= 0) {
695 SYSCALL_ERROR(InvalidArgument);
696 return -1;
697 }
698 return posix_epoll_create1(0);
699}
700
701int posix_epoll_ctl(int epollFd, int operation, int targetFd, const LinuxEpollEvent* event) {
703 if (!acquireEpoll(epollFd, instance)) {
704 return -1;
705 }
706
707 LinuxEpollEvent snapshot = {};
708 const LinuxEpollEvent* kernelEvent = nullptr;
709 if (operation == LinuxEpoll::ControlAdd || operation == LinuxEpoll::ControlModify) {
710 if (!PosixSubsystem::copyFromUser(&snapshot, event, 1, sizeof(snapshot))) {
711 SYSCALL_ERROR(BadAddress);
712 return -1;
713 }
714 kernelEvent = &snapshot;
715 }
716 return instance->control(operation, targetFd, kernelEvent);
717}
718
719namespace {
720int epollWait(int epollFd, LinuxEpollEvent* events, int maxEvents, int timeoutMilliseconds,
721 const uint64_t* temporarySignalMask) {
722 if (maxEvents <= 0 || maxEvents > LinuxMaximumEpollEvents) {
723 SYSCALL_ERROR(InvalidArgument);
724 return -1;
725 }
726
727 // Linux permits a much larger maxevents value than we want to allocate in
728 // one contiguous kernel buffer. Returning a smaller ready batch is valid;
729 // the rotating scan cursor exposes the remainder on subsequent waits.
730 const int eventCapacity = maxEvents < MaximumEpollBatch ? maxEvents : MaximumEpollBatch;
731
733 if (!acquireEpoll(epollFd, instance)) {
734 return -1;
735 }
736
737 size_t extent = 0;
738 if (!PosixSubsystem::checkUserBuffer(reinterpret_cast<uintptr_t>(events), eventCapacity,
739 sizeof(LinuxEpollEvent), PosixSubsystem::SafeWrite,
740 &extent)) {
741 SYSCALL_ERROR(BadAddress);
742 return -1;
743 }
744
745 LinuxEpollEvent* snapshot = new LinuxEpollEvent[eventCapacity];
746 int result = 0;
747 bool signalInterrupted = false;
748 if (temporarySignalMask) {
749 Thread* thread = Processor::information().getCurrentThread();
750 if (!thread) {
751 FATAL("epoll_pwait has no current Thread.");
752 }
753
754 Thread::TemporarySignalMask signalWait(*thread, *temporarySignalMask);
755 result = instance->wait(snapshot, eventCapacity, timeoutMilliseconds);
756 signalInterrupted = signalWait.finish();
757 } else {
758 result = instance->wait(snapshot, eventCapacity, timeoutMilliseconds);
759 }
760
761 if (!result && signalInterrupted) {
762 SYSCALL_ERROR(Interrupted);
763 result = -1;
764 }
765 if (result <= 0) {
766 delete[] snapshot;
767 return result;
768 }
769
770 const bool copied = PosixSubsystem::copyToUser(events, snapshot, result, sizeof(*snapshot));
771 delete[] snapshot;
772 if (!copied) {
773 SYSCALL_ERROR(BadAddress);
774 return -1;
775 }
776 return result;
777}
778} // namespace
779
780int posix_epoll_wait(int epollFd, LinuxEpollEvent* events, int maxEvents, int timeoutMilliseconds) {
781 return epollWait(epollFd, events, maxEvents, timeoutMilliseconds, nullptr);
782}
783
784int posix_epoll_pwait(int epollFd, LinuxEpollEvent* events, int maxEvents, int timeoutMilliseconds,
785 const void* signalMask, size_t signalMaskSize) {
786 if (!signalMask) {
787 return epollWait(epollFd, events, maxEvents, timeoutMilliseconds, nullptr);
788 }
789 if (signalMaskSize != LinuxKernelSigsetSize) {
790 SYSCALL_ERROR(InvalidArgument);
791 return -1;
792 }
793
794 uint64_t temporarySignalMask = 0;
795 if (!PosixSubsystem::copyFromUser(&temporarySignalMask, signalMask, LinuxKernelSigsetSize)) {
796 SYSCALL_ERROR(BadAddress);
797 return -1;
798 }
799 temporarySignalMask &= ~UnblockableSignals;
800 return epollWait(epollFd, events, maxEvents, timeoutMilliseconds, &temporarySignalMask);
801}
int control(int operation, int targetFd, const LinuxEpollEvent *event)
ReadyMask queryReady()
int wait(LinuxEpollEvent *events, int maxEvents, int timeoutMilliseconds)
void readinessChanged(ReadyMask mask) override
SharedPointer< EpollInstance > epollImpl
Epoll implementation for this descriptor (if it is an epoll object).
OpenFileDescriptionLease acquireOpenFileDescription() const
int getStatusFlags() const
Get current status flags.
Definition File.h:74
virtual bool supportsReadinessNotifications() const
Definition File.cc:937
Definition List.h:61
Iterator begin()
Definition List.h:122
Iterator end()
Definition List.h:132
Definition Mutex.h:56
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool checkUserBuffer(uintptr_t addr, size_t count, size_t elementSize, size_t flags, size_t *extent=nullptr)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
Definition Readiness.cc:208
MUST_USE_RESULT bool subscribeReadiness(ReadyMask interest, const SharedPointer< ReadinessObserver > &observer, ReadinessSubscription &subscription)
Definition Readiness.cc:181
virtual ReadinessGenerations readinessGenerations()
Definition Readiness.cc:177
void release(size_t n=1)
Definition Semaphore.cc:546
MUST_USE_RESULT bool acquireWithError(size_t n, size_t timeoutSecs, size_t timeoutUsecs, SemaphoreError &error)
Definition Semaphore.cc:357
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:481
T * get() const
Iterator erase(Iterator &Iter)
Definition List.h:352
T popFront()
Definition List.h:330
size_t count() const
Definition List.h:212
void pushBack(const T &value)
Definition List.h:216