From e52802162c9badec51cb7862c5e1747757f37324 Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Wed, 19 Feb 2020 10:26:04 +0100 Subject: [PATCH] libc: reimplement synchronization primitives The new implementation relieves the main entrypoint from monitor jobs for contended lock primitives and is based on custom applicant data structures, per-lock resp. per-semaphore applicant lists, and a libc-internal blockade with timeouts based on libc kernel primitives. --- repos/libports/src/lib/libc/internal/env.h | 2 +- repos/libports/src/lib/libc/internal/init.h | 5 +- repos/libports/src/lib/libc/internal/kernel.h | 102 +++--- .../libports/src/lib/libc/internal/monitor.h | 70 ++-- .../libports/src/lib/libc/internal/pthread.h | 57 ++++ repos/libports/src/lib/libc/kernel.cc | 25 +- repos/libports/src/lib/libc/pthread.cc | 298 ++++++++++-------- repos/libports/src/lib/libc/semaphore.cc | 202 +++++++----- repos/libports/src/test/pthread/main.cc | 20 +- repos/ports/recipes/src/vbox5-nova/content.mk | 8 +- repos/ports/recipes/src/vbox5/content.mk | 8 +- 11 files changed, 485 insertions(+), 312 deletions(-) diff --git a/repos/libports/src/lib/libc/internal/env.h b/repos/libports/src/lib/libc/internal/env.h index 0cd249c9b..982775b23 100644 --- a/repos/libports/src/lib/libc/internal/env.h +++ b/repos/libports/src/lib/libc/internal/env.h @@ -64,7 +64,7 @@ class Libc::Env_implementation : public Libc::Env, public Config_accessor public: - Env_implementation(Genode::Env &env, Allocator &alloc) + Env_implementation(Genode::Env &env, Genode::Allocator &alloc) : _env(env), _vfs_env(_env, alloc, _vfs_config()) { } diff --git a/repos/libports/src/lib/libc/internal/init.h b/repos/libports/src/lib/libc/internal/init.h index a4ac81dc1..ee819a05b 100644 --- a/repos/libports/src/lib/libc/internal/init.h +++ b/repos/libports/src/lib/libc/internal/init.h @@ -38,6 +38,7 @@ namespace Libc { struct Watch; struct Signal; struct File_descriptor_allocator; + struct Timer_accessor; /** * Support for shared libraries @@ -107,8 +108,8 @@ namespace Libc { /** * Pthread/semaphore support */ - void init_pthread_support(Monitor &, Suspend &, Resume &); - void init_semaphore_support(Monitor &); + void init_pthread_support(Suspend &, Resume &, Timer_accessor &); + void init_semaphore_support(Timer_accessor &); struct Config_accessor : Interface { diff --git a/repos/libports/src/lib/libc/internal/kernel.h b/repos/libports/src/lib/libc/internal/kernel.h index 17aca1028..7fe5b0b7b 100644 --- a/repos/libports/src/lib/libc/internal/kernel.h +++ b/repos/libports/src/lib/libc/internal/kernel.h @@ -40,8 +40,52 @@ #include #include #include +#include -namespace Libc { class Kernel; } +namespace Libc { + class Kernel; + class Main_blockade; + class Main_job; +} + + +class Libc::Main_blockade : public Blockade +{ + private: + + uint64_t _timeout_ms; + bool const _timeout_valid { _timeout_ms != 0 }; + + struct Check : Suspend_functor + { + bool const &woken_up; + + Check(bool const &woken_up) : woken_up(woken_up) { } + + bool suspend() override { return !woken_up; } + }; + + public: + + Main_blockade(uint64_t timeout_ms) : _timeout_ms(timeout_ms) { } + + void block() override; + void wakeup() override; +}; + + +class Libc::Main_job : public Monitor::Job +{ + private: + + Main_blockade _blockade; + + public: + + Main_job(Monitor::Function &fn, uint64_t timeout_ms) + : Job(fn, _blockade), _blockade(timeout_ms) + { } +}; /** @@ -79,7 +123,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler, * * Not mirrored to forked processes. Preserved across 'execve' calls. */ - Allocator &_heap; + Genode::Allocator &_heap; /** * Name of the current binary's ROM module @@ -344,7 +388,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler, public: - Kernel(Genode::Env &env, Allocator &heap); + Kernel(Genode::Env &env, Genode::Allocator &heap); ~Kernel() { error(__PRETTY_FUNCTION__, " should not be executed!"); } @@ -484,53 +528,13 @@ struct Libc::Kernel final : Vfs::Io_response_handler, bool _monitor(Genode::Lock &mutex, Function &fn, uint64_t timeout_ms) override { if (_main_context()) { - - struct Job : Monitor::Job - { - Kernel &_kernel; - - uint64_t _timeout_ms; - bool _timeout_valid { _timeout_ms != 0 }; - - struct Check : Suspend_functor - { - bool const &completed; - - Check(bool const &completed) : completed(completed) { } - - bool suspend() override - { - return !completed; - } - } check { _completed }; - - Job(Monitor::Function &fn, Kernel &kernel, - Timer_accessor &timer_accessor, uint64_t timeout_ms) - : - Monitor::Job(fn, timer_accessor, 0 /* timeout handled by suspend */), - _kernel(kernel), _timeout_ms(timeout_ms) - { } - - void wait_for_completion() override - { - do { - _timeout_ms = _kernel._suspend_main(check, _timeout_ms); - _expired = _timeout_valid && !_timeout_ms; - } while (!completed() && !expired()); - } - - void complete() override - { - _completed = true; - _kernel._resume_main(); - } - } job { fn, *this, _timer_accessor, timeout_ms }; + Main_job job { fn, timeout_ms }; _monitors.monitor(mutex, job); return job.completed(); } else { - Monitor::Job job { fn, _timer_accessor, timeout_ms }; + Pthread_job job { fn, _timer_accessor, timeout_ms }; _monitors.monitor(mutex, job); return job.completed(); @@ -609,6 +613,14 @@ struct Libc::Kernel final : Vfs::Io_response_handler, */ bool main_context() const { return _main_context(); } + void resume_main() + { + if (_main_context()) + _resume_main(); + else + Signal_transmitter(*_resume_main_handler).submit(); + } + /** * Execute application code while already executing in run() */ diff --git a/repos/libports/src/lib/libc/internal/monitor.h b/repos/libports/src/lib/libc/internal/monitor.h index edafda7d9..7ded65830 100644 --- a/repos/libports/src/lib/libc/internal/monitor.h +++ b/repos/libports/src/lib/libc/internal/monitor.h @@ -19,9 +19,28 @@ #include /* libc-internal includes */ -#include #include +namespace Libc { class Blockade; }; + + +class Libc::Blockade +{ + protected: + + bool _woken_up { false }; + bool _expired { false }; + + public: + + bool woken_up() const { return _woken_up; } + bool expired() const { return _expired; } + + virtual void block() = 0; + virtual void wakeup() = 0; +}; + + namespace Libc { class Monitor; }; @@ -32,10 +51,10 @@ class Libc::Monitor : Interface struct Job; struct Pool; - protected: - struct Function : Interface { virtual bool execute() = 0; }; + protected: + virtual bool _monitor(Genode::Lock &, Function &, uint64_t) = 0; virtual void _charge_monitors() = 0; @@ -70,52 +89,27 @@ class Libc::Monitor : Interface }; -struct Libc::Monitor::Job : Timeout_handler +struct Libc::Monitor::Job { private: Monitor::Function &_fn; - - protected: - - bool _completed { false }; - bool _expired { false }; - - Lock _blockade { Lock::LOCKED }; - Constructible _timeout; + Blockade &_blockade; public: - Job(Monitor::Function &fn, - Timer_accessor &timer_accessor, uint64_t timeout_ms) - : _fn(fn) - { - if (timeout_ms) { - _timeout.construct(timer_accessor, *this); - _timeout->start(timeout_ms); - } - } + Job(Monitor::Function &fn, Blockade &blockade) + : _fn(fn), _blockade(blockade) { } - bool completed() const { return _completed; } - bool expired() const { return _expired; } - bool execute() { return _fn.execute(); } + virtual ~Job() { } - virtual void wait_for_completion() { _blockade.lock(); } + bool execute() { return _fn.execute(); } - virtual void complete() - { - _completed = true; - _blockade.unlock(); - } + bool completed() const { return _blockade.woken_up(); } + bool expired() const { return _blockade.expired(); } - /** - * Timeout_handler interface - */ - void handle_timeout() override - { - _expired = true; - _blockade.unlock(); - } + void wait_for_completion() { _blockade.block(); } + void complete() { _blockade.wakeup(); } }; diff --git a/repos/libports/src/lib/libc/internal/pthread.h b/repos/libports/src/lib/libc/internal/pthread.h index 23ef38d64..a0876a2be 100644 --- a/repos/libports/src/lib/libc/internal/pthread.h +++ b/repos/libports/src/lib/libc/internal/pthread.h @@ -26,11 +26,15 @@ /* libc-internal includes */ #include +#include +#include namespace Libc { struct Pthread; struct Pthread_registry; + struct Pthread_blockade; + struct Pthread_job; } @@ -293,4 +297,57 @@ struct pthread : Libc::Pthread }; +class Libc::Pthread_blockade : public Blockade, public Timeout_handler +{ + private: + + Lock _blockade { Lock::LOCKED }; + Constructible _timeout; + + public: + + Pthread_blockade(Timer_accessor &timer_accessor, uint64_t timeout_ms) + { + if (timeout_ms) { + _timeout.construct(timer_accessor, *this); + _timeout->start(timeout_ms); + } + } + + void block() override { _blockade.lock(); } + + void wakeup() override + { + _woken_up = true; + _blockade.unlock(); + } + + /** + * Timeout_handler interface + */ + void handle_timeout() override + { + _expired = true; + _blockade.unlock(); + } +}; + + +struct Libc::Pthread_job : Monitor::Job +{ + private: + + Pthread_blockade _blockade; + + public: + + Pthread_job(Monitor::Function &fn, + Timer_accessor &timer_accessor, uint64_t timeout_ms) + : + Job(fn, _blockade), + _blockade(timer_accessor, timeout_ms) + { } +}; + + #endif /* _LIBC__INTERNAL__PTHREAD_H_ */ diff --git a/repos/libports/src/lib/libc/kernel.cc b/repos/libports/src/lib/libc/kernel.cc index 7b8126a97..18e8ef34d 100644 --- a/repos/libports/src/lib/libc/kernel.cc +++ b/repos/libports/src/lib/libc/kernel.cc @@ -19,6 +19,27 @@ Libc::Kernel * Libc::Kernel::_kernel_ptr; +/** + * Blockade for main context + */ + +inline void Libc::Main_blockade::block() +{ + Check check { _woken_up }; + + do { + _timeout_ms = Kernel::kernel().suspend(check, _timeout_ms); + _expired = _timeout_valid && !_timeout_ms; + } while (!woken_up() && !expired()); +} + +inline void Libc::Main_blockade::wakeup() +{ + _woken_up = true; + Kernel::kernel().resume_main(); +} + + /** * Main context execution was suspended (on fork) * @@ -376,8 +397,8 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap) { atexit(close_file_descriptors_on_exit); - init_semaphore_support(*this); - init_pthread_support(*this, *this, *this); + init_semaphore_support(_timer_accessor); + init_pthread_support(*this, *this, _timer_accessor); _env.ep().register_io_progress_handler(*this); diff --git a/repos/libports/src/lib/libc/pthread.cc b/repos/libports/src/lib/libc/pthread.cc index a72bfb010..02161722c 100644 --- a/repos/libports/src/lib/libc/pthread.cc +++ b/repos/libports/src/lib/libc/pthread.cc @@ -3,7 +3,6 @@ * \author Christian Prochaska * \author Christian Helmuth * \date 2012-03-12 - * */ /* @@ -27,28 +26,31 @@ #include /* malloc, free */ /* libc-internal includes */ -#include #include -#include +#include +#include #include -#include +#include #include +#include + using namespace Libc; -static Thread *_main_thread_ptr; -static Resume *_resume_ptr; -static Suspend *_suspend_ptr; -static Monitor *_monitor_ptr; +static Thread *_main_thread_ptr; +static Resume *_resume_ptr; +static Suspend *_suspend_ptr; +static Timer_accessor *_timer_accessor_ptr; -void Libc::init_pthread_support(Monitor &monitor, Suspend &suspend, Resume &resume) +void Libc::init_pthread_support(Suspend &suspend, Resume &resume, + Timer_accessor &timer_accessor) { - _main_thread_ptr = Thread::myself(); - _monitor_ptr = &monitor; - _suspend_ptr = &suspend; - _resume_ptr = &resume; + _main_thread_ptr = Thread::myself(); + _suspend_ptr = &suspend; + _resume_ptr = &resume; + _timer_accessor_ptr = &timer_accessor; } @@ -186,61 +188,129 @@ struct pthread_mutex_attr { pthread_mutextype type; }; * This class is named 'struct pthread_mutex' because the 'pthread_mutex_t' * type is defined as 'struct pthread_mutex *' in '_pthreadtypes.h' */ -struct pthread_mutex +class pthread_mutex : Genode::Noncopyable { - pthread_t _owner { nullptr }; - unsigned _applicants { 0 }; - Lock _data_mutex; - Lock _monitor_mutex; + private: - struct Missing_call_of_init_pthread_support : Exception { }; - - struct Applicant + struct Applicant : Genode::Noncopyable { - pthread_mutex &m; + pthread_t const thread; - Applicant(pthread_mutex &m) : m(m) - { - Lock::Guard lock_guard(m._data_mutex); - ++m._applicants; - } + Applicant *next { nullptr }; - ~Applicant() - { - Lock::Guard lock_guard(m._data_mutex); - --m._applicants; - } + Libc::Blockade &blockade; + + Applicant(pthread_t thread, Libc::Blockade &blockade) + : thread(thread), blockade(blockade) + { } }; - Monitor & _monitor() - { - if (!_monitor_ptr) - throw Missing_call_of_init_pthread_support(); - return *_monitor_ptr; - } + Applicant *_applicants { nullptr }; - pthread_mutex() { } + protected: - virtual ~pthread_mutex() { } + pthread_t _owner { nullptr }; + Lock _data_mutex; - /* - * The behavior of the following function follows the "robust mutex" - * described IEEE Std 1003.1 POSIX.1-2017 - * https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html - */ - virtual int lock() = 0; - virtual int timedlock(timespec const &) = 0; - virtual int trylock() = 0; - virtual int unlock() = 0; + /* _data_mutex must be hold when calling the following methods */ + + void _append_applicant(Applicant *applicant) + { + Applicant **tail = &_applicants; + + for (; *tail; tail = &(*tail)->next) ; + + *tail = applicant; + } + + void _remove_applicant(Applicant *applicant) + { + Applicant **a = &_applicants; + + for (; *a && *a != applicant; a = &(*a)->next) ; + + *a = applicant->next; + } + + void _next_applicant_to_owner() + { + if (Applicant *next = _applicants) { + _remove_applicant(next); + _owner = next->thread; + next->blockade.wakeup(); + } else { + _owner = nullptr; + } + } + + bool _applicant_for_mutex(pthread_t thread, Libc::Blockade &blockade) + { + Applicant applicant { thread, blockade }; + + _append_applicant(&applicant); + + _data_mutex.unlock(); + + blockade.block(); + + _data_mutex.lock(); + + if (blockade.woken_up()) { + return true; + } else { + _remove_applicant(&applicant); + return false; + } + } + + struct Missing_call_of_init_pthread_support : Exception { }; + + Timer_accessor & _timer_accessor() + { + if (!_timer_accessor_ptr) + throw Missing_call_of_init_pthread_support(); + return *_timer_accessor_ptr; + } + + /** + * Enqueue current context as applicant for mutex + * + * Return true if mutex was aquired, false on timeout expiration. + */ + bool _apply_for_mutex(pthread_t thread, Libc::uint64_t timeout_ms) + { + if (Libc::Kernel::kernel().main_context()) { + Main_blockade blockade { timeout_ms }; + return _applicant_for_mutex(thread, blockade); + } else { + Pthread_blockade blockade { _timer_accessor(), timeout_ms }; + return _applicant_for_mutex(thread, blockade); + } + } + + public: + + pthread_mutex() { } + + virtual ~pthread_mutex() { } + + /* + * The behavior of the following function follows the "robust mutex" + * described IEEE Std 1003.1 POSIX.1-2017 + * https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html + */ + virtual int lock() = 0; + virtual int timedlock(timespec const &) = 0; + virtual int trylock() = 0; + virtual int unlock() = 0; }; struct Libc::Pthread_mutex_normal : pthread_mutex { + /* unsynchronized try */ int _try_lock(pthread_t thread) { - Lock::Guard lock_guard(_data_mutex); - if (!_owner) { _owner = thread; return 0; @@ -251,30 +321,25 @@ struct Libc::Pthread_mutex_normal : pthread_mutex int lock() override final { - Lock::Guard monitor_guard(_monitor_mutex); - pthread_t const myself = pthread_self(); + Lock::Guard lock_guard(_data_mutex); + /* fast path without lock contention */ if (_try_lock(myself) == 0) return 0; - { - Applicant guard { *this }; - - _monitor().monitor(_monitor_mutex, - [&] { return _try_lock(myself) == 0; }); - } + _apply_for_mutex(myself, 0); return 0; } int timedlock(timespec const &abs_timeout) override final { - Lock::Guard monitor_guard(_monitor_mutex); - pthread_t const myself = pthread_self(); + Lock::Guard lock_guard(_data_mutex); + /* fast path without lock contention - does not check abstimeout according to spec */ if (_try_lock(myself) == 0) return 0; @@ -286,37 +351,29 @@ struct Libc::Pthread_mutex_normal : pthread_mutex if (!timeout_ms) return ETIMEDOUT; - { - Applicant guard { *this }; - - auto fn = [&] { return _try_lock(myself) == 0; }; - - if (_monitor().monitor(_monitor_mutex, fn, timeout_ms)) - return 0; - else - return ETIMEDOUT; - } - - return 0; + if (_apply_for_mutex(myself, timeout_ms)) + return 0; + else + return ETIMEDOUT; } int trylock() override final { - return _try_lock(pthread_self()); + pthread_t const myself = pthread_self(); + + Lock::Guard lock_guard(_data_mutex); + + return _try_lock(myself); } int unlock() override final { - Lock::Guard monitor_guard(_monitor_mutex); Lock::Guard lock_guard(_data_mutex); if (_owner != pthread_self()) return EPERM; - _owner = nullptr; - - if (_applicants) - _monitor().charge_monitors(); + _next_applicant_to_owner(); return 0; } @@ -325,73 +382,56 @@ struct Libc::Pthread_mutex_normal : pthread_mutex struct Libc::Pthread_mutex_errorcheck : pthread_mutex { - enum Try_lock_result { SUCCESS, BUSY, DEADLOCK }; - - Try_lock_result _try_lock(pthread_t thread) + /* unsynchronized try */ + int _try_lock(pthread_t thread) { - Lock::Guard lock_guard(_data_mutex); - if (!_owner) { _owner = thread; - return SUCCESS; + return 0; } - return _owner == thread ? DEADLOCK : BUSY; + return _owner == thread ? EDEADLK : EBUSY; } int lock() override final { - Lock::Guard monitor_guard(_monitor_mutex); - pthread_t const myself = pthread_self(); - /* fast path without lock contention */ - switch (_try_lock(myself)) { - case SUCCESS: return 0; - case DEADLOCK: return EDEADLK; - case BUSY: [[fallthrough]]; - } + Lock::Guard lock_guard(_data_mutex); - { - Applicant guard { *this }; + /* fast path without lock contention (or deadlock) */ + int const result = _try_lock(myself); + if (!result || result == EDEADLK) + return result; - _monitor().monitor(_monitor_mutex, [&] { - /* DEADLOCK already handled above - just check for SUCCESS */ - return _try_lock(myself) == SUCCESS; - }); - } + _apply_for_mutex(myself, 0); return 0; } int timedlock(timespec const &) override final { + /* XXX not implemented yet */ return ENOSYS; } int trylock() override final { - switch (_try_lock(pthread_self())) { - case SUCCESS: return 0; - case DEADLOCK: return EDEADLK; - case BUSY: return EBUSY; - } + pthread_t const myself = pthread_self(); - return EBUSY; + Lock::Guard lock_guard(_data_mutex); + + return _try_lock(myself); } int unlock() override final { - Lock::Guard monitor_guard(_monitor_mutex); Lock::Guard lock_guard(_data_mutex); if (_owner != pthread_self()) return EPERM; - _owner = nullptr; - - if (_applicants) - _monitor().charge_monitors(); + _next_applicant_to_owner(); return 0; } @@ -402,13 +442,11 @@ struct Libc::Pthread_mutex_recursive : pthread_mutex { unsigned _nesting_level { 0 }; + /* unsynchronized try */ int _try_lock(pthread_t thread) { - Lock::Guard lock_guard(_data_mutex); - if (!_owner) { - _owner = thread; - _nesting_level = 1; + _owner = thread; return 0; } else if (_owner == thread) { ++_nesting_level; @@ -420,20 +458,15 @@ struct Libc::Pthread_mutex_recursive : pthread_mutex int lock() override final { - Lock::Guard monitor_guard(_monitor_mutex); - pthread_t const myself = pthread_self(); + Lock::Guard lock_guard(_data_mutex); + /* fast path without lock contention */ if (_try_lock(myself) == 0) return 0; - { - Applicant guard { *this }; - - _monitor().monitor(_monitor_mutex, - [&] { return _try_lock(myself) == 0; }); - } + _apply_for_mutex(myself, 0); return 0; } @@ -445,23 +478,24 @@ struct Libc::Pthread_mutex_recursive : pthread_mutex int trylock() override final { - return _try_lock(pthread_self()); + pthread_t const myself = pthread_self(); + + Lock::Guard lock_guard(_data_mutex); + + return _try_lock(myself); } int unlock() override final { - Lock::Guard monitor_guard(_monitor_mutex); Lock::Guard lock_guard(_data_mutex); if (_owner != pthread_self()) return EPERM; - --_nesting_level; - if (_nesting_level == 0) { - _owner = nullptr; - if (_applicants) - _monitor().charge_monitors(); - } + if (_nesting_level == 0) + _next_applicant_to_owner(); + else + --_nesting_level; return 0; } diff --git a/repos/libports/src/lib/libc/semaphore.cc b/repos/libports/src/lib/libc/semaphore.cc index 089768ff5..337d81498 100644 --- a/repos/libports/src/lib/libc/semaphore.cc +++ b/repos/libports/src/lib/libc/semaphore.cc @@ -24,142 +24,192 @@ #include /* libc-internal includes */ -#include #include -#include -#include #include +#include +#include +#include +#include using namespace Libc; -static Monitor *_monitor_ptr; +static Timer_accessor *_timer_accessor_ptr; -void Libc::init_semaphore_support(Monitor &monitor) +void Libc::init_semaphore_support(Timer_accessor &timer_accessor) { - _monitor_ptr = &monitor; + _timer_accessor_ptr = &timer_accessor; } -extern "C" { +/* + * This class is named 'struct sem' because the 'sem_t' type is + * defined as 'struct sem*' in 'semaphore.h' + */ +struct sem : Genode::Noncopyable +{ + private: - /* - * This class is named 'struct sem' because the 'sem_t' type is - * defined as 'struct sem*' in 'semaphore.h' - */ - struct sem - { - int _count; - unsigned _applicants { 0 }; - Lock _data_mutex; - Lock _monitor_mutex; - - struct Missing_call_of_init_pthread_support : Exception { }; - - struct Applicant + struct Applicant : Genode::Noncopyable { - sem &s; + Applicant *next { nullptr }; - Applicant(sem &s) : s(s) - { - Lock::Guard lock_guard(s._data_mutex); - ++s._applicants; - } + Libc::Blockade &blockade; - ~Applicant() - { - Lock::Guard lock_guard(s._data_mutex); - --s._applicants; - } + Applicant(Libc::Blockade &blockade) : blockade(blockade) { } }; - Monitor & _monitor() + Applicant *_applicants { nullptr }; + int _count; + Lock _data_mutex; + + /* _data_mutex must be hold when calling the following methods */ + + void _append_applicant(Applicant *applicant) { - if (!_monitor_ptr) - throw Missing_call_of_init_pthread_support(); - return *_monitor_ptr; + Applicant **tail = &_applicants; + + for (; *tail; tail = &(*tail)->next) ; + + *tail = applicant; } - sem(int value) : _count(value) { } - - int trydown() + void _remove_applicant(Applicant *applicant) + { + Applicant **a = &_applicants; + + for (; *a && *a != applicant; a = &(*a)->next) ; + + *a = applicant->next; + } + + void _count_up() + { + if (Applicant *next = _applicants) { + _remove_applicant(next); + next->blockade.wakeup(); + } else { + ++_count; + } + } + + bool _applicant_for_semaphore(Libc::Blockade &blockade) + { + Applicant applicant { blockade }; + + _append_applicant(&applicant); + + _data_mutex.unlock(); + + blockade.block(); + + _data_mutex.lock(); + + if (blockade.woken_up()) { + return true; + } else { + _remove_applicant(&applicant); + return false; + } + } + + struct Missing_call_of_init_semaphore_support : Exception { }; + + Timer_accessor & _timer_accessor() + { + if (!_timer_accessor_ptr) + throw Missing_call_of_init_semaphore_support(); + return *_timer_accessor_ptr; + } + + /** + * Enqueue current context as applicant for semaphore + * + * Return true if down was successful, false on timeout expiration. + */ + bool _apply_for_semaphore(Libc::uint64_t timeout_ms) + { + if (Libc::Kernel::kernel().main_context()) { + Main_blockade blockade { timeout_ms }; + return _applicant_for_semaphore(blockade); + } else { + Pthread_blockade blockade { _timer_accessor(), timeout_ms }; + return _applicant_for_semaphore(blockade); + } + } + + /* unsynchronized try */ + int _try_down() { - Lock::Guard lock_guard(_data_mutex); - if (_count > 0) { - _count--; + --_count; return 0; } return EBUSY; } + public: + + sem(int value) : _count(value) { } + + int count() const { return _count; } + + int trydown() + { + Lock::Guard lock_guard(_data_mutex); + + return _try_down(); + } + int down() { - Lock::Guard monitor_guard(_monitor_mutex); + Lock::Guard lock_guard(_data_mutex); - /* fast path without contention */ - if (trydown() == 0) + /* fast path */ + if (_try_down() == 0) return 0; - { - Applicant guard { *this }; - - auto fn = [&] { return trydown() == 0; }; - - (void)_monitor().monitor(_monitor_mutex, fn); - } + _apply_for_semaphore(0); return 0; } int down_timed(timespec const &abs_timeout) { - Lock::Guard monitor_guard(_monitor_mutex); + Lock::Guard lock_guard(_data_mutex); - /* fast path without wait - does not check abstimeout according to spec */ - if (trydown() == 0) + /* fast path */ + if (_try_down() == 0) return 0; timespec abs_now; clock_gettime(CLOCK_REALTIME, &abs_now); - uint64_t const timeout_ms = calculate_relative_timeout_ms(abs_now, abs_timeout); + Libc::uint64_t const timeout_ms = + calculate_relative_timeout_ms(abs_now, abs_timeout); if (!timeout_ms) return ETIMEDOUT; - { - Applicant guard { *this }; - - auto fn = [&] { return trydown() == 0; }; - - if (_monitor().monitor(_monitor_mutex, fn, timeout_ms)) - return 0; - else - return ETIMEDOUT; - } + if (_apply_for_semaphore(timeout_ms)) + return 0; + else + return ETIMEDOUT; } int up() { - Lock::Guard monitor_guard(_monitor_mutex); Lock::Guard lock_guard(_data_mutex); - _count++; - - if (_applicants) - _monitor().charge_monitors(); + _count_up(); return 0; } +}; - int count() - { - return _count; - } - }; +extern "C" { int sem_close(sem_t *) { diff --git a/repos/libports/src/test/pthread/main.cc b/repos/libports/src/test/pthread/main.cc index d3a2ed028..45b9807fc 100644 --- a/repos/libports/src/test/pthread/main.cc +++ b/repos/libports/src/test/pthread/main.cc @@ -325,7 +325,7 @@ static void *thread_mutex_func(void *arg) /* unlock normal mutex */ if (pthread_mutex_unlock(&test_mutex_data->normal_mutex) != 0) { - printf("Error: could not lock normal mutex\n"); + printf("Error: could not unlock normal mutex\n"); exit(-1); } @@ -568,15 +568,15 @@ struct Test_mutex_stress Test_mutex_stress() { printf("main thread: start %s stress test\n", mutex.type_string()); + pthread_mutex_lock(mutex.mutex()); for (Thread &t : threads) t.start(); + pthread_mutex_unlock(mutex.mutex()); for (Thread &t : threads) t.join(); printf("main thread: finished %s stress test\n", mutex.type_string()); } }; -extern "C" void wait_for_continue(); - static void test_mutex_stress() { printf("main thread: stressing mutexes\n"); @@ -689,7 +689,7 @@ struct Test_cond void signaller() { - printf("signaller: started\n"); + Genode::log("signaller: started"); unsigned num_events = 0; bool test_done = false; @@ -708,7 +708,7 @@ struct Test_cond pthread_cond_signal(_cond.cond()); break; case State::SHUTDOWN: - printf("signaller: shutting down\n"); + Genode::log("signaller: shutting down"); _shared_state = State::END; ++num_events; pthread_cond_broadcast(_cond.cond()); @@ -723,7 +723,7 @@ struct Test_cond usleep(1000); } - printf("signaller: finished after %u state changes\n", num_events); + Genode::log("signaller: finished after ", num_events, " state changes"); } static void *waiter_fn(void *arg) @@ -736,7 +736,7 @@ struct Test_cond { char const * const note = main_thread ? "(main thread)" : ""; - printf("waiter%s: started\n", note); + Genode::log("waiter", note, ": started"); unsigned pings = 0, pongs = 0; unsigned long iterations = 0; @@ -747,7 +747,7 @@ struct Test_cond auto handle_state = [&] { unsigned const num_events = pings + pongs; if (num_events == 2000) { - printf("waiter%s: request shutdown\n", note); + Genode::log("waiter", note, ": request shutdown"); _shared_state = State::SHUTDOWN; } else if (num_events % 2 == 0) { pthread_cond_wait(_cond.cond(), _mutex.mutex()); @@ -777,8 +777,8 @@ struct Test_cond ++iterations; } - printf("waiter%s: finished (pings=%u, pongs=%u, iterations=%lu)\n", - note, pings, pongs, iterations); + Genode::log("waiter", note, ": finished (pings=", pings, ", pongs=", + pongs, ", iterations=", iterations, ")"); } Test_cond() diff --git a/repos/ports/recipes/src/vbox5-nova/content.mk b/repos/ports/recipes/src/vbox5-nova/content.mk index 4a283df5f..40d0169e2 100644 --- a/repos/ports/recipes/src/vbox5-nova/content.mk +++ b/repos/ports/recipes/src/vbox5-nova/content.mk @@ -37,12 +37,14 @@ MIRROR_FROM_LIBPORTS := lib/mk/libc_pipe.mk \ src/lib/libc_pipe \ lib/mk/libc-mem.mk \ lib/mk/libc-common.inc \ - src/lib/libc/libc_mem_alloc.cc \ - src/lib/libc/internal/mem_alloc.h \ src/lib/libc/internal/init.h \ - src/lib/libc/internal/thread_create.h \ + src/lib/libc/internal/mem_alloc.h \ + src/lib/libc/internal/monitor.h \ src/lib/libc/internal/pthread.h \ + src/lib/libc/internal/thread_create.h \ + src/lib/libc/internal/timer.h \ src/lib/libc/internal/types.h \ + src/lib/libc/libc_mem_alloc.cc \ include/libc-plugin \ lib/import/import-qemu-usb_include.mk \ lib/mk/qemu-usb_include.mk \ diff --git a/repos/ports/recipes/src/vbox5/content.mk b/repos/ports/recipes/src/vbox5/content.mk index 5a7ac4838..4d0e2db95 100644 --- a/repos/ports/recipes/src/vbox5/content.mk +++ b/repos/ports/recipes/src/vbox5/content.mk @@ -38,12 +38,14 @@ MIRROR_FROM_LIBPORTS := lib/mk/libc_pipe.mk \ src/lib/libc_pipe \ lib/mk/libc-mem.mk \ lib/mk/libc-common.inc \ - src/lib/libc/libc_mem_alloc.cc \ - src/lib/libc/internal/mem_alloc.h \ src/lib/libc/internal/init.h \ - src/lib/libc/internal/thread_create.h \ + src/lib/libc/internal/mem_alloc.h \ + src/lib/libc/internal/monitor.h \ src/lib/libc/internal/pthread.h \ + src/lib/libc/internal/thread_create.h \ + src/lib/libc/internal/timer.h \ src/lib/libc/internal/types.h \ + src/lib/libc/libc_mem_alloc.cc \ include/libc-plugin \ lib/import/import-qemu-usb_include.mk \ lib/mk/qemu-usb_include.mk \