Skip to content

feat(libkernel): implement the unnamed ksem semaphores - #9284

Open
DavidSamir wants to merge 7 commits into
boykopovar:mainfrom
DavidSamir:feat/libkernel-sys-ksem-unnamed
Open

DavidSamir wants to merge 7 commits into
boykopovar:mainfrom
DavidSamir:feat/libkernel-sys-ksem-unnamed

Conversation

@DavidSamir

@DavidSamir DavidSamir commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

Implements the unnamed ksem_* exports in libkernel, in a new libkernel/Semaphore/src/Ksem.cpp, following FreeBSD 11 sys/kern/uipc_sem.c. They were discovery stubs in libkernel_sys that threw NotImplemented.

Moved to libkernel so libkernel imports reach it on Windows. The PS5 3.20 export lists have ksem_* in libkernel as well as in libkernel_sys and libkernel_web. On Windows a libkernel import never searches libkernel_sys: WindowsEntryStubBuilder.cpp:175 only falls back the other way, from libkernel_sys and libkernel_web to libkernel. With the implementation in libkernel and the libkernel_sys stubs removed, both imports reach it, and the duplicate-export rule holds. On Linux and macOS, ELF lookup is global, so either import already worked. (Review by @pipe1os.)

Export Behaviour
ksem_init(semid_t* idp, u_int value) EINVAL for value > SEM_VALUE_MAX (0x7fffffff). ENOSPC when no descriptor is left, whatever the cause (uipc_sem.c:475-479). EFAULT for a NULL idp. ENOSPC once 30 semaphores (SEM_MAX, p1003_1b.sem_nsems_max) exist (:300-304, :496-498); that check comes after the id is written, as in FreeBSD.
ksem_post(semid_t) EOVERFLOW at SEM_VALUE_MAX
ksem_wait(semid_t) waits until the count is positive
ksem_trywait(semid_t) EAGAIN at 0
ksem_timedwait(semid_t, const timespec*) an absolute CLOCK_REALTIME deadline, giving ETIMEDOUT. A tv_nsec outside 0..999999999 gives EINVAL before any wait, as sys_ksem_timedwait does.
ksem_getvalue(semid_t, int*) EFAULT for a NULL out pointer
ksem_destroy(semid_t) EBUSY while a thread waits

An id that isn't a live ksem gives EINVAL, as ksem_get maps fget's failure. semid_t is intptr_t (sys/_semaphore.h).

The id is a descriptor, as in FreeBSD:

  • Each id is a descriptor reserved on the host null device, the same way libkernel's kqueue reserves one, and the semaphore lives in a table keyed by that descriptor.
  • So that a closed id can never reach a stale semaphore, libkernel's central close notification (EqueueDescriptorClosed) gains a small observer list. It is already called from close, from dup2 replacing a descriptor, and from sockets. Ksem.cpp registers through RegisterDescriptorCloseObserver_nid_no_patch to drop the entry when its descriptor is closed or replaced.
  • ksem_destroy releases the id through the guest close.

Duplicating an id throws: dup of a ksem id, or dup2 from one, throws, as the same spot does for a kqueue (Stdio.cpp). This avoids handing back a descriptor that isn't a semaphore, where FreeBSD would share it. dup2 onto an id is exact: it closes the semaphore.

The named forms (ksem_open, ksem_close, ksem_unlink) follow in #9293, which depends on this one. A debt line in libkernel.md records the dup throw and that a guest signal doesn't interrupt a wait with EINTR.

Tested

Windows 11 Pro 26200, WinLibs GCC 15.2.0 r7, Release. These are CPU-side exports, so no GPU is involved.

guest_kernel_sys_ksem (core/libs/tests/GuestKernelSysKsem.cpp) covers:

  • init bounds and counting, EAGAIN, EOVERFLOW at the maximum, and EFAULT for NULL pointers;
  • timedwait with a bad tv_nsec (EINVAL), a 150 ms real-time deadline (ETIMEDOUT), and a later post that satisfies a wait;
  • a blocked waiter released by ksem_post, with ksem_destroy giving EBUSY while it waits;
  • -1, a regular file descriptor and a destroyed id all giving EINVAL;
  • closed and replaced ids: after close of an id, a regular file that reuses the same number gets EINVAL from ksem_post and ksem_getvalue; after dup2 of a file over an id, ksem_post and ksem_trywait give EINVAL;
  • new: dup of an id and dup2 from an id both throw, while dup2(id, id) keeps the semaphore;
  • new: 30 semaphores succeed and the 31st ksem_init gives ENOSPC with the id written but dead; destroying one makes room again.

The stale-descriptor file is opened by a guest-relative name, not a host temp path, so the test also runs on Linux (found by @xXPBSXx on #9097).

Results:

  • It passes, 10 of 10 runs.
  • Negative controls:
    • with the NotifyDescriptorCloseObservers call removed from EqueueDescriptorClosed, the test fails, because a destroyed id still answers;
    • with the SEM_MAX check removed, it fails at line 166;
    • with the dup check removed, it fails at line 153.
  • 32 tests matching guest_kernel_sys, semaphore, kqueue, equeue, dup, descriptor, stdio and guest_file pass, 4 runs out of 5. The other run had one failure that didn't repeat in three more runs of the set or ten runs of the ksem test. tools/check_conventions.py passes, including the duplicate-export rule.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Progress report (0c6a5bd → b0d85e3)

📈 System libraries: 42.17% (+0.07%, +7 implemented, -7 removed functions)

✅ 7 implemented
Library Name
libkernel ksem_destroy_nid_postfix
libkernel ksem_getvalue_nid_postfix
libkernel ksem_init_nid_postfix
libkernel ksem_post_nid_postfix
libkernel ksem_timedwait_nid_postfix
libkernel ksem_trywait_nid_postfix
libkernel ksem_wait_nid_postfix
🗑️ 7 removed
Library Name
libkernel_sys ksem_destroy_nid_postfix
libkernel_sys ksem_getvalue_nid_postfix
libkernel_sys ksem_init_nid_postfix
libkernel_sys ksem_post_nid_postfix
libkernel_sys ksem_timedwait_nid_postfix
libkernel_sys ksem_trywait_nid_postfix
libkernel_sys ksem_wait_nid_postfix

@pipe1os

pipe1os commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

I had ksem_* written on a local branch when this was opened, so I compared your diff with FreeBSD 11.0 uipc_sem.c. All of this is from reading the code; I haven't built this branch.

  1. Titles won't reach these exports. The PS5 3.20 export lists (DNNDHH/PS5-3.20_Libs) have ksem_* in libkernel.c as well as in libkernel_sys.c and libkernel_web.c. A game imports them from the libkernel module, and on Windows the import search only looks at the declared module plus libc.prx (WindowsEntryStubBuilder.cpp:169 and :176), so a title never finds an implementation in libkernel_sys.prx. Putting them in libkernel, and dropping the libkernel_sys placeholders for the duplicate-export rule, makes them reachable.
  2. ksem_init with no descriptor left: FreeBSD returns ENOSPC for an unnamed semaphore whatever falloc failed with (uipc_sem.c:475-479). Here it's EMFILE/ENFILE (Ksem.cpp:110).
  3. No semaphore limit: ksem_alloc fails once nsems reaches p1003_1b.sem_nsems_max (uipc_sem.c:300-304), which defaults to SEM_MAX = 30 (:80-82, set at :1022), and ksem_create turns that into ENOSPC for an unnamed one (:496-498). Here a 31st ksem_init succeeds.
  4. dup of an id: the debt entry says it gives a descriptor that isn't a semaphore. kqueue throws in the same spot (Stdio.cpp:763 and :793), which keeps exact-or-throw instead of a silent difference.

Everything else I checked matches: the tv_nsec check before the id lookup, EBUSY with a waiter, EOVERFLOW, and EINVAL for a dead or foreign id.

I also have ksem_open, ksem_close and ksem_unlink with tests (ENAMETOOLONG at 1024 bytes, the id written before the name is checked, O_CREAT | O_EXCL, unlink while open, a linked name counting toward the limit). If it helps with your named follow-up, I can share the branch and you take what's useful. No point in me opening a second PR.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 4563 core/libs/prx/libkernel/CMakeLists.txt
PR 4630 docs/dev/debt/libkernel.md
PR 5021 core/libs/CMakeLists.txt
core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 6044 core/libs/CMakeLists.txt
core/libs/prx/libkernel/CMakeLists.txt
PR 6149 docs/dev/debt/libkernel.md
PR 6740 docs/dev/debt/libkernel.md
PR 6830 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 6936 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7049 core/libs/prx/libkernel/Equeue/Equeue.cpp
docs/dev/debt/libkernel.md
PR 7088 docs/dev/debt/libkernel.md
PR 7090 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7121 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7135 docs/dev/debt/libkernel.md
PR 7149 docs/dev/debt/libkernel.md
PR 7156 core/libs/prx/libkernel/CMakeLists.txt
PR 7192 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 7201 core/libs/CMakeLists.txt
core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 7275 core/libs/CMakeLists.txt
PR 7299 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7447 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7464 docs/dev/debt/libkernel.md
PR 7492 core/libs/prx/libkernel/Equeue/Equeue.cpp
PR 7509 core/libs/prx/libkernel/Equeue/Equeue.cpp
docs/dev/debt/libkernel.md
PR 7511 docs/dev/debt/libkernel.md
PR 7530 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7532 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 7565 docs/dev/debt/libkernel.md
PR 7605 docs/dev/debt/libkernel.md
PR 7622 core/libs/CMakeLists.txt
PR 7653 core/libs/prx/libkernel/Equeue/Equeue.cpp
docs/dev/debt/libkernel.md
PR 7780 core/libs/CMakeLists.txt
PR 7804 docs/dev/debt/libkernel.md
PR 7853 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 8010 docs/dev/debt/libkernel.md
PR 8041 core/libs/prx/libkernel/CMakeLists.txt
PR 8264 core/libs/CMakeLists.txt
PR 8285 core/libs/CMakeLists.txt
PR 8311 core/libs/CMakeLists.txt
PR 8344 docs/dev/debt/libkernel.md
PR 8373 docs/dev/debt/libkernel.md
PR 8408 docs/dev/debt/libkernel.md
PR 8463 core/libs/prx/libkernel/CMakeLists.txt
PR 8477 core/libs/prx/libkernel/CMakeLists.txt
PR 8520 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 8533 docs/dev/debt/libkernel.md
PR 8553 docs/dev/debt/libkernel.md
PR 8605 docs/dev/debt/libkernel.md
PR 8629 core/libs/CMakeLists.txt
PR 8650 core/libs/CMakeLists.txt
PR 8717 core/libs/prx/libkernel/CMakeLists.txt
PR 8725 docs/dev/debt/libkernel.md
PR 8726 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 8728 docs/dev/debt/libkernel.md
PR 8742 docs/dev/debt/libkernel.md
PR 8872 docs/dev/debt/libkernel.md
PR 8916 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 8921 docs/dev/debt/libkernel.md
PR 9001 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9019 core/libs/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 9059 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9074 docs/dev/debt/libkernel.md
PR 9095 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9098 core/libs/CMakeLists.txt
PR 9099 core/libs/prx/libkernel/Equeue/Equeue.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9123 docs/dev/debt/libkernel.md
PR 9127 core/libs/prx/libkernel/Equeue/Equeue.cpp
docs/dev/debt/libkernel.md
PR 9133 docs/dev/debt/libkernel.md
PR 9153 docs/dev/debt/libkernel.md
PR 9160 core/libs/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9161 core/libs/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 9162 core/libs/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 9165 docs/dev/debt/libkernel.md
PR 9167 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9172 core/libs/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 9173 core/libs/CMakeLists.txt
PR 9187 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9195 core/libs/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 9199 docs/dev/debt/libkernel.md
PR 9212 core/libs/CMakeLists.txt
PR 9221 core/libs/CMakeLists.txt
PR 9239 docs/dev/debt/libkernel.md
PR 9263 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9298 docs/dev/debt/libkernel.md
PR 9326 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9359 docs/dev/debt/libkernel.md
PR 9363 docs/dev/debt/libkernel.md
PR 9365 docs/dev/debt/libkernel.md
PR 9378 docs/dev/debt/libkernel.md
PR 9389 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9423 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9442 core/libs/prx/libkernel/File/src/Stdio.cpp
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9461 docs/dev/debt/libkernel.md
PR 9469 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9473 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9477 docs/dev/debt/libkernel.md
PR 9478 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9482 docs/dev/debt/libkernel.md
PR 9487 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9491 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9499 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9502 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9505 core/libs/CMakeLists.txt
PR 9509 docs/dev/debt/libkernel.md
PR 9512 docs/dev/debt/libkernel.md
PR 9515 docs/dev/debt/libkernel.md
PR 9535 core/libs/CMakeLists.txt
PR 9536 core/libs/CMakeLists.txt
PR 9555 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9571 core/libs/CMakeLists.txt
PR 9573 core/libs/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9578 docs/dev/debt/libkernel.md
PR 9582 core/libs/prx/libkernel/CMakeLists.txt
PR 9618 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9631 core/libs/CMakeLists.txt
PR 9632 core/libs/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9669 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9682 core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9684 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9693 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9713 core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 9732 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9772 core/libs/CMakeLists.txt
PR 9774 core/libs/CMakeLists.txt
PR 9780 core/libs/prx/libkernel/Equeue/Equeue.cpp
PR 9785 core/libs/CMakeLists.txt
PR 9792 core/libs/CMakeLists.txt
PR 9794 docs/dev/debt/libkernel.md
PR 9802 core/libs/CMakeLists.txt
PR 9823 core/libs/prx/libkernel/Equeue/Equeue.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9843 docs/dev/debt/libkernel.md
PR 9876 docs/dev/debt/libkernel.md
PR 9882 docs/dev/debt/libkernel.md
PR 9902 core/libs/CMakeLists.txt
PR 9924 core/libs/CMakeLists.txt
PR 9933 core/libs/CMakeLists.txt
PR 9938 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9945 core/libs/prx/libkernel/CMakeLists.txt
PR 9947 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9962 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 9972 core/libs/prx/libkernel/CMakeLists.txt
PR 10014 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 10016 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 10018 docs/dev/debt/libkernel.md
PR 10025 docs/dev/debt/libkernel.md
PR 10042 core/libs/CMakeLists.txt
PR 10061 docs/dev/debt/libkernel.md
PR 10089 docs/dev/debt/libkernel.md
PR 10101 core/libs/CMakeLists.txt
PR 10105 docs/dev/debt/libkernel.md
PR 10107 docs/dev/debt/libkernel.md
PR 10110 core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 10121 docs/dev/debt/libkernel.md
PR 10130 docs/dev/debt/libkernel.md
PR 10132 docs/dev/debt/libkernel.md
PR 10133 docs/dev/debt/libkernel.md
PR 10137 docs/dev/debt/libkernel.md
PR 10162 docs/dev/debt/libkernel.md
PR 10177 docs/dev/debt/libkernel.md
PR 10179 docs/dev/debt/libkernel.md
PR 10192 docs/dev/debt/libkernel.md
PR 10198 docs/dev/debt/libkernel.md
PR 10230 docs/dev/debt/libkernel.md
PR 10243 docs/dev/debt/libkernel.md
PR 10255 core/libs/CMakeLists.txt
PR 10299 docs/dev/debt/libkernel.md
PR 10305 docs/dev/debt/libkernel.md
PR 10308 docs/dev/debt/libkernel.md
PR 10310 docs/dev/debt/libkernel.md
PR 10319 docs/dev/debt/libkernel.md
PR 10320 docs/dev/debt/libkernel.md
PR 10327 docs/dev/debt/libkernel.md
PR 10330 docs/dev/debt/libkernel.md
PR 10331 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 10335 core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 10350 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 10385 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel_sys/Export.Discovery.cpp
docs/dev/debt/libkernel.md
PR 10408 core/libs/CMakeLists.txt
PR 10421 core/libs/prx/libkernel_sys/Export.Discovery.cpp
PR 10425 docs/dev/debt/libkernel.md
PR 10442 docs/dev/debt/libkernel.md
PR 10456 docs/dev/debt/libkernel.md
PR 10499 docs/dev/debt/libkernel.md
PR 10501 docs/dev/debt/libkernel.md

@DavidSamir

Copy link
Copy Markdown
Contributor Author

@pipe1os thank you, all four points check out against the source, and point 1 matters most.

  1. Reachability: confirmed. The 3.20 lists export ksem_* from libkernel as well, and on Windows WindowsEntryStubBuilder.cpp:175 sends libkernel_sys/libkernel_web imports on to libkernel but not the reverse. I'll move the implementation into libkernel and drop the libkernel_sys placeholders, so both imports reach it (and the duplicate-export rule holds). The same applies to our other libkernel_sys PRs whose exports the 3.20 libkernel list also has, and they'll get the same move.
  2. ENOSPC for an unnamed ksem_init whatever falloc failed with: will follow uipc_sem.c:475-479.
  3. The p1003_1b.sem_nsems_max limit (SEM_MAX 30) with ENOSPC: will add it, with a test for the 31st.
  4. dup/dup2 of an id: will throw as kqueue does (Stdio.cpp:763/:793) instead of the silent difference, and drop that debt wording.

On the named part: #9293 already has ksem_open/ksem_close/ksem_unlink. I'd gladly compare your tests with it (the id written before the name check, and linked names counting toward the limit), so if you can share the branch, I'll credit what I take.

…elative name

An absolute host temp path is not mapped by the Linux guest resolver.
Found by xXPBSXx on boykopovar#9097.
The PS5 3.20 export lists have ksem_* in libkernel as well as
libkernel_sys, and on Windows a libkernel import never reaches
libkernel_sys, so titles importing them from libkernel did not find
them. Following pipe1os's review on boykopovar#9284, also: ENOSPC from
ksem_init when no descriptor is left, the SEM_MAX limit of 30 with
ENOSPC, and dup/dup2 from a ksem id throwing as for a kqueue.
@pipe1os

pipe1os commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

I'd gladly compare your tests with it (the id written before the name check, and linked names counting toward the limit), so if you can share the branch, I'll credit what I take.

Here it is: https://github.com/pipe1os/AnyPS5/tree/feat/libkernel-ksem. One commit, all ten in libkernel; the tests are in core/libs/tests/GuestKsem.cpp (cases anonymous, wait, named, limit). Take whatever's useful.

Comparing #9293's ksem_open with uipc_sem.c:

  • Order: sys_ksem_open checks oflag first (:633-634), then ksem_create checks the value (:470), allocates the descriptor and copies it out to idp (:475-487), and only then reads the name (:508, EFAULT/ENAMETOOLONG) and checks the leading / (:512). So idp already holds the descriptor number when the name, ENOENT or EEXIST fails. feat(libkernel): implement the named ksem semaphores #9293 checks the name before oflag and never writes idp on failure.
  • At the limit a named create is ENFILE (:527-529), not ENOSPC. A name that's still linked keeps its semaphore alive, so it counts until ksem_unlink.
  • Opening an existing name or unlinking one goes through ksem_access (:550, :412), which needs owner read and write in the mode and otherwise falls back to priv_check_cred. feat(libkernel): implement the named ksem semaphores #9293 ignores mode; I made that case throw, since the result depends on privilege.

And you're right about :175: main forwards libkernel_sys/libkernel_web imports to libkernel now. I was reading an older copy.

@DavidSamir DavidSamir changed the title feat(libkernel_sys): implement the unnamed ksem semaphores feat(libkernel): implement the unnamed ksem semaphores Oct 21, 2026
@DavidSamir

Copy link
Copy Markdown
Contributor Author

@pipe1os all four points are in 8db8924:

  1. The implementation is now in libkernel/Semaphore/src/Ksem.cpp and the libkernel_sys stubs are gone, so libkernel and libkernel_sys imports both reach it on Windows.
  2. ksem_init gives ENOSPC when no descriptor is left.
  3. SEM_MAX 30: the 31st ksem_init gives ENOSPC. Like FreeBSD, it does that after the id is written. Tested.
  4. dup of an id, and dup2 from one, throw as kqueue does. The debt wording is gone. dup2 onto an id still closes the semaphore exactly.

Negative controls for 3 and 4 are in Tested. I've retitled the PR feat(libkernel), and the move carries into #9293 next. I'd still like your named-ksem tests if you can share the branch.

AI-assisted: yes

@pipe1os

pipe1os commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

I'd still like your named-ksem tests if you can share the branch.

It's here: https://github.com/pipe1os/AnyPS5/tree/feat/libkernel-ksem (I linked it in my last comment too, easy to miss). The named cases are Named() and Limit() in core/libs/tests/GuestKsem.cpp.

I also ran 8db8924 on Windows: Windows 11 Pro 10.0.26300, WinLibs GCC 15.2.0 (ucrt-posix-seh r7), Ninja, Release, BUILD_TESTING=ON. Merged onto main 0c6a5bd without conflicts, no warnings.

  • guest_kernel_sys_ksem passes, five runs in a row. guest_kernel_sys, guest_dup, guest_kqueue, guest_equeue_event, guest_directory_close, guest_positioned_close, guest_kernel_fcntl and guest_socket_descriptors still pass with the close observer and the dup guards.
  • With core/libs/prx/libkernel, core/libs/prx/libkernel_sys and the debt file back to main, it aborts on ksem_init_nid_postfix not implemented.
  • The four points match uipc_sem.c now, including the 31st ksem_init writing the id before ENOSPC.

One thing for #9293: the limit here checks g_ksems.size(), which is one entry per descriptor. For unnamed semaphores that's the same as the semaphore count, but nsems counts semaphores (ksem_alloc/ksem_drop). So two opens of the same name count once, and a name that's closed but still linked keeps counting.

@jotitadev jotitadev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux run of b0d85e3c2, since the description only lists Windows.

Machine: Linux x86_64, i7-8700 (6 cores / 12 threads), Ubuntu 26.04, GCC 15.2, Release, BUILD_TESTING=ON, full build. Head b0d85e3c2 on its own base (main 4ec064d29).

  • Build ok. guest_kernel_sys_ksem passes.
  • Restoring libkernel_sys/Export.Discovery.cpp to that base and keeping the test aborts it with ksem_init_nid_postfix not implemented, so the test covers the change.

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@DavidSamir

Copy link
Copy Markdown
Contributor Author

Linux: Ubuntu 24.04.1 LTS (WSL2), GCC 13.3.0, Release; head b0d85e3c2 merged onto main 8c26eac3c.

  • guest_kernel_sys_ksem passes 3/3.
  • Negative control: with the SEM_MAX check removed (g_ksems.size() < SemMax replaced by true), it fails at line 166, as listed.
  • Negative control: with the dup check removed (both throws in Stdio.cpp), it fails at line 153, as listed.

Run from the PC (Windows host, Linux in WSL2), not from the PR author's machine.

This branch has not been deployed

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

Labels

build docs libkernel needs rebase Conflicts with main, rebase needed prx

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants