Skip to content

libkernel: sceKernelWaitEqueue returns a level-triggered user event several times in one call, filling the caller's buffer #3115

Closed

Description

@husseinkad

What

An event added with sceKernelAddUserEvent has no EV_CLEAR, so it stays triggered until it is deleted. The collection loop in core/libs/prx/libkernel/Equeue/Equeue.cpp keeps re-emitting it until the caller's buffer is full: a sceKernelWaitEqueue call with num = 4 on one level event returns count 4, all the same event. kqueue examines a knote once per scan and re-queues a reported level knote behind the scan marker for the next call (kqueue_scan in sys/kern/kern_event.c; kevent(2): "Multiple events which trigger the filter do not result in multiple kevents being placed on the kqueue"), so a level event is reported at most once per call and again on the next one. A title that waits with a buffer larger than one therefore sees duplicates, and AMPR completions queued behind such an event are never delivered because every call is filled with the repeats.

Reproduce

Windows 11, WinLibs GCC 15.2.0 r7, main at the time of writing: create an equeue, add a user event with sceKernelAddUserEvent, trigger it, call sceKernelWaitEqueue(eq, ev, 4, &out, NULL): out is 4 and all four entries are the same event (FreeBSD semantics: 1).

Fix

A tested fix is on my fork: husseinkad:fix/kernel-equeue-level-event-once (two commits on main; PR #2703, closed by the intake check): the two copies of the collection loop (GetTriggeredEvents and WaitForEvents) become one CollectTriggered, which only stays on the same event when a distinct queued completion follows, which the AMPR and VideoOut events rely on. Timers, HR timers and EV_CLEAR events behave as before. The Orbis behaviour was not measured on a console; that, the remaining starvation of later events when num is smaller than the number of ready level events (FreeBSD rotates them) and AMPR/VideoOut completions being returned as separate entries are recorded in TechnicalDebt. Open PR #1091 (guest socket events) also edits Equeue.cpp and Equeue.hpp and conflicts textually; its PollEvents() call has to stay in the callers before CollectTriggered.

guest_equeue_events covers a level event reported once per call with num = 4 on the poll path and on a blocking wait (also when a helper thread triggers it about 10 ms later), an edge event reported once and again after a new trigger, and two AMPR completions of one event delivered in order; it fails without the change. A probe of eight scenarios against main's and the branch's Equeue.cpp (about 6,400 checks: N level, edge and mixed events with num below, equal to and above N, one event triggered 50 times between waits, a blocked waiter triggered later, periodic and one-shot timers, 20 AMPR completions on one ident, deleting an event and the equeue while 4 threads wait, a 4-waiter/4-trigger stress of 3 seconds) passes on the branch with no event dropped or duplicated within a call, while main fails five of the eight. Linux not run.

AI-assisted: yes.

Activity

  1. MarcZX10 commented on Oct 11, 2026

    @MarcZX10
    Contributor

    I carried the two commits from your fix/kernel-equeue-level-event-once branch into #3489, preserving their original authorship. I resolved the overlap with merged #1091 while retaining its socket polling path. Linux Release validation passes the equeue, socket-event, AMPR, UDP and TCP tests three consecutive times each; conventions and diff checks pass.

  2. MarcZX10 commented on Oct 11, 2026

    @MarcZX10
    Contributor

    Update: the carried fix is now available as #3489, based on current main. The integration preserves the existing socket event handling from merged #1091 and adds once-per-wait collection for level-triggered events. Local Linux Release checks passed three consecutive runs for guest_equeue_events, guest_socket_events, guest_ampr_markers, guest_ampr_threads, guest_udp, and guest_tcp. Applicable GitHub CI has also completed successfully, including the Windows relinker; full Linux/Windows builds were skipped by the contributor policy. The PR is open and clean.

  3. adriadam10 commented on Oct 11, 2026

    @adriadam10
    Collaborator

    Fixed by #3489: a level-triggered user event is reported at most once per sceKernelWaitEqueue call, covered by guest_equeue_events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions