mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibPthread: Implement named semaphores
Note that as part of this commit semaphore.cpp is excluded from the DynamicLoader, as the dynamic loader does not build with pthread.cpp which semaphore.cpp uses.
This commit is contained in:
parent
23f3857cdd
commit
01f0ae20b6
Notes:
sideshowbarker
2024-07-17 11:34:34 +09:00
Author: https://github.com/IdanHo Commit: https://github.com/SerenityOS/serenity/commit/01f0ae20b6 Pull-request: https://github.com/SerenityOS/serenity/pull/14578
3 changed files with 179 additions and 10 deletions
|
@ -26,7 +26,7 @@ if (ENABLE_UNDEFINED_SANITIZER)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# pthread requires thread local storage, which DynamicLoader does not have.
|
# pthread requires thread local storage, which DynamicLoader does not have.
|
||||||
list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".*/LibC/pthread\\.cpp")
|
list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".*/LibC/(pthread|semaphore)\\.cpp")
|
||||||
|
|
||||||
add_definitions(-D_DYNAMIC_LOADER)
|
add_definitions(-D_DYNAMIC_LOADER)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
||||||
* Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
|
* Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Assertions.h>
|
#include <AK/Assertions.h>
|
||||||
#include <AK/Atomic.h>
|
#include <AK/Atomic.h>
|
||||||
|
#include <AK/HashMap.h>
|
||||||
|
#include <AK/ScopeGuard.h>
|
||||||
|
#include <AK/String.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <serenity.h>
|
#include <serenity.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
static constexpr u32 SEM_MAGIC = 0x78951230;
|
static constexpr u32 SEM_MAGIC = 0x78951230;
|
||||||
|
|
||||||
|
@ -18,25 +28,178 @@ static constexpr u32 SEM_MAGIC = 0x78951230;
|
||||||
// threads.
|
// threads.
|
||||||
static constexpr u32 POST_WAKES = 1 << 31;
|
static constexpr u32 POST_WAKES = 1 << 31;
|
||||||
|
|
||||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html
|
static constexpr auto sem_path_prefix = "/tmp/semaphore/"sv;
|
||||||
sem_t* sem_open(char const*, int, ...)
|
static constexpr auto SEM_NAME_MAX = PATH_MAX - sem_path_prefix.length();
|
||||||
|
static ErrorOr<String> sem_name_to_path(char const* name)
|
||||||
{
|
{
|
||||||
errno = ENOSYS;
|
if (name[0] != '/')
|
||||||
return nullptr;
|
return EINVAL;
|
||||||
|
++name;
|
||||||
|
|
||||||
|
auto name_length = strnlen(name, SEM_NAME_MAX);
|
||||||
|
if (name[name_length])
|
||||||
|
return ENAMETOOLONG;
|
||||||
|
|
||||||
|
auto name_view = StringView { name, name_length };
|
||||||
|
if (name_view.contains('/'))
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
StringBuilder builder;
|
||||||
|
TRY(builder.try_append(sem_path_prefix));
|
||||||
|
TRY(builder.try_append(name_view));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NamedSemaphore {
|
||||||
|
size_t times_opened { 0 };
|
||||||
|
dev_t dev { 0 };
|
||||||
|
ino_t ino { 0 };
|
||||||
|
sem_t* sem { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
static HashMap<String, NamedSemaphore> s_named_semaphores;
|
||||||
|
static pthread_mutex_t s_sem_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static pthread_once_t s_sem_once = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html
|
||||||
|
sem_t* sem_open(char const* name, int flags, ...)
|
||||||
|
{
|
||||||
|
auto path_or_error = sem_name_to_path(name);
|
||||||
|
if (path_or_error.is_error()) {
|
||||||
|
errno = path_or_error.error().code();
|
||||||
|
return SEM_FAILED;
|
||||||
|
}
|
||||||
|
auto path = path_or_error.release_value();
|
||||||
|
|
||||||
|
if (flags & ~(O_CREAT | O_EXCL)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return SEM_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_t mode = 0;
|
||||||
|
unsigned int value = 0;
|
||||||
|
if (flags & O_CREAT) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, flags);
|
||||||
|
mode = va_arg(ap, unsigned int);
|
||||||
|
value = va_arg(ap, unsigned int);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we are not in the middle of modifying this structure while a child is being forked, which will cause the child to end up with a partially-modified entry
|
||||||
|
pthread_once(&s_sem_once, []() {
|
||||||
|
pthread_atfork([]() { pthread_mutex_lock(&s_sem_mutex); }, []() { pthread_mutex_unlock(&s_sem_mutex); }, []() { pthread_mutex_unlock(&s_sem_mutex); });
|
||||||
|
});
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_sem_mutex);
|
||||||
|
ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_sem_mutex); };
|
||||||
|
|
||||||
|
int fd = open(path.characters(), O_RDWR | O_CLOEXEC | flags, mode);
|
||||||
|
if (fd == -1)
|
||||||
|
return SEM_FAILED;
|
||||||
|
|
||||||
|
ScopeGuard close_guard = [&fd] {
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (flock(fd, LOCK_EX) == -1)
|
||||||
|
return SEM_FAILED;
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
if (fstat(fd, &statbuf) == -1)
|
||||||
|
return SEM_FAILED;
|
||||||
|
|
||||||
|
auto existing_semaphore = s_named_semaphores.get(path);
|
||||||
|
if (existing_semaphore.has_value()) {
|
||||||
|
// If the file did not exist (aka if O_CREAT && O_EXCL but no EEXIST), or if the inode was replaced, remove the entry and start from scratch
|
||||||
|
if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL) || existing_semaphore->dev != statbuf.st_dev || existing_semaphore->ino != statbuf.st_ino) {
|
||||||
|
s_named_semaphores.remove(path);
|
||||||
|
} else { // otherwise, this is valid pre-existing named semaphore, so just increase the count and return it
|
||||||
|
existing_semaphore->times_opened++;
|
||||||
|
return existing_semaphore->sem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the file is smaller than the size, it's an uninitialized semaphore, so let's write an initial value
|
||||||
|
if (statbuf.st_size < (off_t)sizeof(sem_t)) {
|
||||||
|
sem_t init_sem;
|
||||||
|
init_sem.magic = SEM_MAGIC;
|
||||||
|
init_sem.value = value;
|
||||||
|
init_sem.flags = SEM_FLAG_PROCESS_SHARED | SEM_FLAG_NAMED;
|
||||||
|
if (write(fd, &init_sem, sizeof(sem_t)) != sizeof(sem_t))
|
||||||
|
return SEM_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flock(fd, LOCK_UN) == -1)
|
||||||
|
return SEM_FAILED;
|
||||||
|
|
||||||
|
auto* sem = (sem_t*)mmap(nullptr, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (sem == MAP_FAILED)
|
||||||
|
return SEM_FAILED;
|
||||||
|
|
||||||
|
ArmedScopeGuard munmap_guard = [&sem] {
|
||||||
|
munmap(sem, sizeof(sem_t));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sem->magic != SEM_MAGIC) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return SEM_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = s_named_semaphores.try_set(move(path), { .times_opened = 1, .dev = statbuf.st_dev, .ino = statbuf.st_ino, .sem = sem });
|
||||||
|
if (result.is_error()) {
|
||||||
|
errno = result.error().code();
|
||||||
|
return SEM_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
munmap_guard.disarm();
|
||||||
|
return sem;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_close.html
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_close.html
|
||||||
int sem_close(sem_t*)
|
int sem_close(sem_t* sem)
|
||||||
{
|
{
|
||||||
errno = ENOSYS;
|
if (sem->magic != SEM_MAGIC) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sem->flags & SEM_FLAG_NAMED) == 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_sem_mutex);
|
||||||
|
ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_sem_mutex); };
|
||||||
|
|
||||||
|
auto it = s_named_semaphores.begin();
|
||||||
|
for (; it != s_named_semaphores.end(); ++it) {
|
||||||
|
if (it->value.sem != sem)
|
||||||
|
continue;
|
||||||
|
auto is_last = --it->value.times_opened == 0;
|
||||||
|
if (is_last) {
|
||||||
|
munmap(it->value.sem, sizeof(sem_t));
|
||||||
|
s_named_semaphores.remove(it);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_unlink.html
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_unlink.html
|
||||||
int sem_unlink(char const*)
|
int sem_unlink(char const* name)
|
||||||
{
|
{
|
||||||
errno = ENOSYS;
|
auto path_or_error = sem_name_to_path(name);
|
||||||
return -1;
|
if (path_or_error.is_error()) {
|
||||||
|
errno = path_or_error.error().code();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto path = path_or_error.release_value();
|
||||||
|
|
||||||
|
return unlink(path.characters());
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html
|
||||||
|
@ -61,6 +224,11 @@ int sem_destroy(sem_t* sem)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sem->flags & SEM_FLAG_NAMED) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
sem->magic = 0;
|
sem->magic = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
#define SEM_FLAG_PROCESS_SHARED (1 << 0)
|
#define SEM_FLAG_PROCESS_SHARED (1 << 0)
|
||||||
|
#define SEM_FLAG_NAMED (1 << 1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
|
|
Loading…
Reference in a new issue