2021-02-11 21:25:35 +03:00
|
|
|
/*
|
2021-07-05 15:10:34 +03:00
|
|
|
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
|
|
|
* Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
|
2021-02-11 21:25:35 +03:00
|
|
|
*
|
2021-04-22 11:24:48 +03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2021-02-11 21:25:35 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <AK/Assertions.h>
|
2021-07-05 15:10:34 +03:00
|
|
|
#include <AK/Atomic.h>
|
|
|
|
#include <AK/Types.h>
|
2021-04-14 05:29:39 +03:00
|
|
|
#include <errno.h>
|
2021-02-11 21:25:35 +03:00
|
|
|
#include <semaphore.h>
|
2021-07-05 15:10:34 +03:00
|
|
|
#include <serenity.h>
|
2021-02-11 21:25:35 +03:00
|
|
|
|
2021-07-05 15:10:34 +03:00
|
|
|
// Whether sem_wait() or sem_post() is responsible for waking any sleeping
|
|
|
|
// threads.
|
|
|
|
static constexpr u32 POST_WAKES = 1 << 31;
|
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html
|
2022-04-01 20:58:27 +03:00
|
|
|
sem_t* sem_open(char const*, int, ...)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-04-14 05:29:39 +03:00
|
|
|
errno = ENOSYS;
|
2021-07-05 15:10:34 +03:00
|
|
|
return nullptr;
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_close.html
|
2021-07-05 15:10:34 +03:00
|
|
|
int sem_close(sem_t*)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-07-05 15:10:34 +03:00
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_unlink.html
|
2022-04-01 20:58:27 +03:00
|
|
|
int sem_unlink(char const*)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-07-05 15:10:34 +03:00
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html
|
2021-04-14 05:29:39 +03:00
|
|
|
int sem_init(sem_t* sem, int shared, unsigned int value)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-04-14 23:45:21 +03:00
|
|
|
if (shared) {
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value > SEM_VALUE_MAX) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
|
|
|
sem->value = value;
|
|
|
|
return 0;
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html
|
2021-07-05 15:10:34 +03:00
|
|
|
int sem_destroy(sem_t*)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-07-05 15:10:34 +03:00
|
|
|
return 0;
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html
|
2021-07-05 15:10:34 +03:00
|
|
|
int sem_getvalue(sem_t* sem, int* sval)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-07-05 15:10:34 +03:00
|
|
|
u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
|
|
|
|
*sval = value & ~POST_WAKES;
|
2021-04-14 05:29:39 +03:00
|
|
|
return 0;
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html
|
2021-07-05 15:10:34 +03:00
|
|
|
int sem_post(sem_t* sem)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-07-05 15:10:34 +03:00
|
|
|
u32 value = AK::atomic_fetch_add(&sem->value, 1u, AK::memory_order_release);
|
|
|
|
// Fast path: no need to wake.
|
|
|
|
if (!(value & POST_WAKES)) [[likely]]
|
|
|
|
return 0;
|
2021-04-14 23:52:33 +03:00
|
|
|
|
2021-07-05 15:10:34 +03:00
|
|
|
// Pass the responsibility for waking more threads if more slots become
|
|
|
|
// available later to sem_wait() in the thread we're about to wake, as
|
|
|
|
// opposed to further sem_post() calls that free up those slots.
|
|
|
|
value = AK::atomic_fetch_and(&sem->value, ~POST_WAKES, AK::memory_order_relaxed);
|
|
|
|
// Check if another sem_post() call has handled it already.
|
|
|
|
if (!(value & POST_WAKES)) [[likely]]
|
|
|
|
return 0;
|
2022-07-14 01:25:35 +03:00
|
|
|
int rc = futex_wake(&sem->value, 1, false);
|
2021-07-06 14:39:46 +03:00
|
|
|
VERIFY(rc >= 0);
|
2021-04-14 05:29:39 +03:00
|
|
|
return 0;
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html
|
2021-07-05 15:10:34 +03:00
|
|
|
int sem_trywait(sem_t* sem)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-07-05 15:10:34 +03:00
|
|
|
u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
|
|
|
|
u32 count = value & ~POST_WAKES;
|
2022-02-16 21:23:24 +03:00
|
|
|
if (count == 0) {
|
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-07-05 15:10:34 +03:00
|
|
|
// Decrement the count without touching the flag.
|
|
|
|
u32 desired = (count - 1) | (value & POST_WAKES);
|
|
|
|
bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire);
|
2022-02-16 21:23:24 +03:00
|
|
|
if (exchanged) [[likely]] {
|
2021-07-05 15:10:34 +03:00
|
|
|
return 0;
|
2022-02-16 21:23:24 +03:00
|
|
|
} else {
|
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_wait.html
|
2021-04-14 05:29:39 +03:00
|
|
|
int sem_wait(sem_t* sem)
|
2021-02-11 21:25:35 +03:00
|
|
|
{
|
2021-07-05 15:10:34 +03:00
|
|
|
return sem_timedwait(sem, nullptr);
|
|
|
|
}
|
2021-04-14 05:29:39 +03:00
|
|
|
|
2022-01-07 11:33:49 +03:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html
|
2021-07-05 15:10:34 +03:00
|
|
|
int sem_timedwait(sem_t* sem, const struct timespec* abstime)
|
|
|
|
{
|
|
|
|
u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
|
|
|
|
bool responsible_for_waking = false;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
u32 count = value & ~POST_WAKES;
|
|
|
|
if (count > 0) [[likely]] {
|
|
|
|
// It looks like there are some free slots.
|
|
|
|
u32 whether_post_wakes = value & POST_WAKES;
|
|
|
|
bool going_to_wake = false;
|
|
|
|
if (responsible_for_waking && !whether_post_wakes) {
|
|
|
|
// If we have ourselves been woken up previously, and the
|
|
|
|
// POST_WAKES flag is not set, that means some more slots might
|
|
|
|
// be available now, and it's us who has to wake up additional
|
|
|
|
// threads.
|
|
|
|
if (count > 1) [[unlikely]]
|
|
|
|
going_to_wake = true;
|
|
|
|
// Pass the responsibility for waking up further threads back to
|
|
|
|
// sem_post() calls. In particular, we don't want the threads
|
|
|
|
// we're about to wake to try to wake anyone else.
|
|
|
|
whether_post_wakes = POST_WAKES;
|
|
|
|
}
|
|
|
|
// Now, try to commit this.
|
|
|
|
u32 desired = (count - 1) | whether_post_wakes;
|
|
|
|
bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire);
|
|
|
|
if (!exchanged) [[unlikely]]
|
|
|
|
// Re-evaluate.
|
|
|
|
continue;
|
|
|
|
if (going_to_wake) [[unlikely]] {
|
2022-07-14 01:25:35 +03:00
|
|
|
int rc = futex_wake(&sem->value, count - 1, false);
|
2021-07-05 15:10:34 +03:00
|
|
|
VERIFY(rc >= 0);
|
|
|
|
}
|
|
|
|
return 0;
|
2021-04-14 05:29:39 +03:00
|
|
|
}
|
2021-07-05 15:10:34 +03:00
|
|
|
// We're probably going to sleep, so attempt to set the flag. We do not
|
|
|
|
// commit to sleeping yet, though, as setting the flag may fail and
|
|
|
|
// cause us to reevaluate what we're doing.
|
|
|
|
if (value == 0) {
|
|
|
|
bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, POST_WAKES, AK::memory_order_relaxed);
|
|
|
|
if (!exchanged) [[unlikely]]
|
|
|
|
// Re-evaluate.
|
|
|
|
continue;
|
|
|
|
value = POST_WAKES;
|
|
|
|
}
|
|
|
|
// At this point, we're committed to sleeping.
|
|
|
|
responsible_for_waking = true;
|
2022-07-14 01:25:35 +03:00
|
|
|
futex_wait(&sem->value, value, abstime, CLOCK_REALTIME, false);
|
2021-07-05 15:10:34 +03:00
|
|
|
// This is the state we will probably see upon being waked:
|
|
|
|
value = 1;
|
2021-04-14 05:29:39 +03:00
|
|
|
}
|
2021-02-11 21:25:35 +03:00
|
|
|
}
|