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.
This commit is contained in:
Christian Helmuth 2020-02-19 10:26:04 +01:00
parent f3ec246b67
commit e52802162c
11 changed files with 485 additions and 312 deletions

View File

@ -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()) { }

View File

@ -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
{

View File

@ -40,8 +40,52 @@
#include <internal/watch.h>
#include <internal/signal.h>
#include <internal/monitor.h>
#include <internal/pthread.h>
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()
*/

View File

@ -19,9 +19,28 @@
#include <base/registry.h>
/* libc-internal includes */
#include <internal/timer.h>
#include <internal/types.h>
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> _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(); }
};

View File

@ -26,11 +26,15 @@
/* libc-internal includes */
#include <internal/types.h>
#include <internal/monitor.h>
#include <internal/timer.h>
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> _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_ */

View File

@ -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);

View File

@ -3,7 +3,6 @@
* \author Christian Prochaska
* \author Christian Helmuth
* \date 2012-03-12
*
*/
/*
@ -27,28 +26,31 @@
#include <stdlib.h> /* malloc, free */
/* libc-internal includes */
#include <internal/pthread.h>
#include <internal/init.h>
#include <internal/suspend.h>
#include <internal/kernel.h>
#include <internal/pthread.h>
#include <internal/resume.h>
#include <internal/monitor.h>
#include <internal/suspend.h>
#include <internal/time.h>
#include <internal/timer.h>
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;
}

View File

@ -24,142 +24,192 @@
#include <time.h>
/* libc-internal includes */
#include <internal/monitor.h>
#include <internal/errno.h>
#include <internal/types.h>
#include <internal/time.h>
#include <internal/init.h>
#include <internal/kernel.h>
#include <internal/monitor.h>
#include <internal/time.h>
#include <internal/types.h>
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 *)
{

View File

@ -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()

View File

@ -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 \

View File

@ -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 \