mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
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:
parent
f3ec246b67
commit
e52802162c
@ -64,7 +64,7 @@ class Libc::Env_implementation : public Libc::Env, public Config_accessor
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Env_implementation(Genode::Env &env, Allocator &alloc)
|
Env_implementation(Genode::Env &env, Genode::Allocator &alloc)
|
||||||
: _env(env), _vfs_env(_env, alloc, _vfs_config()) { }
|
: _env(env), _vfs_env(_env, alloc, _vfs_config()) { }
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ namespace Libc {
|
|||||||
struct Watch;
|
struct Watch;
|
||||||
struct Signal;
|
struct Signal;
|
||||||
struct File_descriptor_allocator;
|
struct File_descriptor_allocator;
|
||||||
|
struct Timer_accessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for shared libraries
|
* Support for shared libraries
|
||||||
@ -107,8 +108,8 @@ namespace Libc {
|
|||||||
/**
|
/**
|
||||||
* Pthread/semaphore support
|
* Pthread/semaphore support
|
||||||
*/
|
*/
|
||||||
void init_pthread_support(Monitor &, Suspend &, Resume &);
|
void init_pthread_support(Suspend &, Resume &, Timer_accessor &);
|
||||||
void init_semaphore_support(Monitor &);
|
void init_semaphore_support(Timer_accessor &);
|
||||||
|
|
||||||
struct Config_accessor : Interface
|
struct Config_accessor : Interface
|
||||||
{
|
{
|
||||||
|
@ -40,8 +40,52 @@
|
|||||||
#include <internal/watch.h>
|
#include <internal/watch.h>
|
||||||
#include <internal/signal.h>
|
#include <internal/signal.h>
|
||||||
#include <internal/monitor.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.
|
* Not mirrored to forked processes. Preserved across 'execve' calls.
|
||||||
*/
|
*/
|
||||||
Allocator &_heap;
|
Genode::Allocator &_heap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the current binary's ROM module
|
* Name of the current binary's ROM module
|
||||||
@ -344,7 +388,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Kernel(Genode::Env &env, Allocator &heap);
|
Kernel(Genode::Env &env, Genode::Allocator &heap);
|
||||||
|
|
||||||
~Kernel() { error(__PRETTY_FUNCTION__, " should not be executed!"); }
|
~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
|
bool _monitor(Genode::Lock &mutex, Function &fn, uint64_t timeout_ms) override
|
||||||
{
|
{
|
||||||
if (_main_context()) {
|
if (_main_context()) {
|
||||||
|
Main_job job { fn, timeout_ms };
|
||||||
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 };
|
|
||||||
|
|
||||||
_monitors.monitor(mutex, job);
|
_monitors.monitor(mutex, job);
|
||||||
return job.completed();
|
return job.completed();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Monitor::Job job { fn, _timer_accessor, timeout_ms };
|
Pthread_job job { fn, _timer_accessor, timeout_ms };
|
||||||
|
|
||||||
_monitors.monitor(mutex, job);
|
_monitors.monitor(mutex, job);
|
||||||
return job.completed();
|
return job.completed();
|
||||||
@ -609,6 +613,14 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
|
|||||||
*/
|
*/
|
||||||
bool main_context() const { return _main_context(); }
|
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()
|
* Execute application code while already executing in run()
|
||||||
*/
|
*/
|
||||||
|
@ -19,9 +19,28 @@
|
|||||||
#include <base/registry.h>
|
#include <base/registry.h>
|
||||||
|
|
||||||
/* libc-internal includes */
|
/* libc-internal includes */
|
||||||
#include <internal/timer.h>
|
|
||||||
#include <internal/types.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; };
|
namespace Libc { class Monitor; };
|
||||||
|
|
||||||
|
|
||||||
@ -32,10 +51,10 @@ class Libc::Monitor : Interface
|
|||||||
struct Job;
|
struct Job;
|
||||||
struct Pool;
|
struct Pool;
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
struct Function : Interface { virtual bool execute() = 0; };
|
struct Function : Interface { virtual bool execute() = 0; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
virtual bool _monitor(Genode::Lock &, Function &, uint64_t) = 0;
|
virtual bool _monitor(Genode::Lock &, Function &, uint64_t) = 0;
|
||||||
virtual void _charge_monitors() = 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:
|
private:
|
||||||
|
|
||||||
Monitor::Function &_fn;
|
Monitor::Function &_fn;
|
||||||
|
Blockade &_blockade;
|
||||||
protected:
|
|
||||||
|
|
||||||
bool _completed { false };
|
|
||||||
bool _expired { false };
|
|
||||||
|
|
||||||
Lock _blockade { Lock::LOCKED };
|
|
||||||
Constructible<Timeout> _timeout;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Job(Monitor::Function &fn,
|
Job(Monitor::Function &fn, Blockade &blockade)
|
||||||
Timer_accessor &timer_accessor, uint64_t timeout_ms)
|
: _fn(fn), _blockade(blockade) { }
|
||||||
: _fn(fn)
|
|
||||||
{
|
|
||||||
if (timeout_ms) {
|
|
||||||
_timeout.construct(timer_accessor, *this);
|
|
||||||
_timeout->start(timeout_ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool completed() const { return _completed; }
|
virtual ~Job() { }
|
||||||
bool expired() const { return _expired; }
|
|
||||||
bool execute() { return _fn.execute(); }
|
|
||||||
|
|
||||||
virtual void wait_for_completion() { _blockade.lock(); }
|
bool execute() { return _fn.execute(); }
|
||||||
|
|
||||||
virtual void complete()
|
bool completed() const { return _blockade.woken_up(); }
|
||||||
{
|
bool expired() const { return _blockade.expired(); }
|
||||||
_completed = true;
|
|
||||||
_blockade.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
void wait_for_completion() { _blockade.block(); }
|
||||||
* Timeout_handler interface
|
void complete() { _blockade.wakeup(); }
|
||||||
*/
|
|
||||||
void handle_timeout() override
|
|
||||||
{
|
|
||||||
_expired = true;
|
|
||||||
_blockade.unlock();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,11 +26,15 @@
|
|||||||
|
|
||||||
/* libc-internal includes */
|
/* libc-internal includes */
|
||||||
#include <internal/types.h>
|
#include <internal/types.h>
|
||||||
|
#include <internal/monitor.h>
|
||||||
|
#include <internal/timer.h>
|
||||||
|
|
||||||
namespace Libc {
|
namespace Libc {
|
||||||
|
|
||||||
struct Pthread;
|
struct Pthread;
|
||||||
struct Pthread_registry;
|
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_ */
|
#endif /* _LIBC__INTERNAL__PTHREAD_H_ */
|
||||||
|
@ -19,6 +19,27 @@
|
|||||||
Libc::Kernel * Libc::Kernel::_kernel_ptr;
|
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)
|
* 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);
|
atexit(close_file_descriptors_on_exit);
|
||||||
|
|
||||||
init_semaphore_support(*this);
|
init_semaphore_support(_timer_accessor);
|
||||||
init_pthread_support(*this, *this, *this);
|
init_pthread_support(*this, *this, _timer_accessor);
|
||||||
|
|
||||||
_env.ep().register_io_progress_handler(*this);
|
_env.ep().register_io_progress_handler(*this);
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
* \author Christian Prochaska
|
* \author Christian Prochaska
|
||||||
* \author Christian Helmuth
|
* \author Christian Helmuth
|
||||||
* \date 2012-03-12
|
* \date 2012-03-12
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -27,28 +26,31 @@
|
|||||||
#include <stdlib.h> /* malloc, free */
|
#include <stdlib.h> /* malloc, free */
|
||||||
|
|
||||||
/* libc-internal includes */
|
/* libc-internal includes */
|
||||||
#include <internal/pthread.h>
|
|
||||||
#include <internal/init.h>
|
#include <internal/init.h>
|
||||||
#include <internal/suspend.h>
|
#include <internal/kernel.h>
|
||||||
|
#include <internal/pthread.h>
|
||||||
#include <internal/resume.h>
|
#include <internal/resume.h>
|
||||||
#include <internal/monitor.h>
|
#include <internal/suspend.h>
|
||||||
#include <internal/time.h>
|
#include <internal/time.h>
|
||||||
|
#include <internal/timer.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace Libc;
|
using namespace Libc;
|
||||||
|
|
||||||
|
|
||||||
static Thread *_main_thread_ptr;
|
static Thread *_main_thread_ptr;
|
||||||
static Resume *_resume_ptr;
|
static Resume *_resume_ptr;
|
||||||
static Suspend *_suspend_ptr;
|
static Suspend *_suspend_ptr;
|
||||||
static Monitor *_monitor_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();
|
_main_thread_ptr = Thread::myself();
|
||||||
_monitor_ptr = &monitor;
|
_suspend_ptr = &suspend;
|
||||||
_suspend_ptr = &suspend;
|
_resume_ptr = &resume;
|
||||||
_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'
|
* This class is named 'struct pthread_mutex' because the 'pthread_mutex_t'
|
||||||
* type is defined as 'struct pthread_mutex *' in '_pthreadtypes.h'
|
* type is defined as 'struct pthread_mutex *' in '_pthreadtypes.h'
|
||||||
*/
|
*/
|
||||||
struct pthread_mutex
|
class pthread_mutex : Genode::Noncopyable
|
||||||
{
|
{
|
||||||
pthread_t _owner { nullptr };
|
private:
|
||||||
unsigned _applicants { 0 };
|
|
||||||
Lock _data_mutex;
|
|
||||||
Lock _monitor_mutex;
|
|
||||||
|
|
||||||
struct Missing_call_of_init_pthread_support : Exception { };
|
struct Applicant : Genode::Noncopyable
|
||||||
|
|
||||||
struct Applicant
|
|
||||||
{
|
{
|
||||||
pthread_mutex &m;
|
pthread_t const thread;
|
||||||
|
|
||||||
Applicant(pthread_mutex &m) : m(m)
|
Applicant *next { nullptr };
|
||||||
{
|
|
||||||
Lock::Guard lock_guard(m._data_mutex);
|
|
||||||
++m._applicants;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Applicant()
|
Libc::Blockade &blockade;
|
||||||
{
|
|
||||||
Lock::Guard lock_guard(m._data_mutex);
|
Applicant(pthread_t thread, Libc::Blockade &blockade)
|
||||||
--m._applicants;
|
: thread(thread), blockade(blockade)
|
||||||
}
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
Monitor & _monitor()
|
Applicant *_applicants { nullptr };
|
||||||
{
|
|
||||||
if (!_monitor_ptr)
|
|
||||||
throw Missing_call_of_init_pthread_support();
|
|
||||||
return *_monitor_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex() { }
|
protected:
|
||||||
|
|
||||||
virtual ~pthread_mutex() { }
|
pthread_t _owner { nullptr };
|
||||||
|
Lock _data_mutex;
|
||||||
|
|
||||||
/*
|
/* _data_mutex must be hold when calling the following methods */
|
||||||
* The behavior of the following function follows the "robust mutex"
|
|
||||||
* described IEEE Std 1003.1 POSIX.1-2017
|
void _append_applicant(Applicant *applicant)
|
||||||
* https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html
|
{
|
||||||
*/
|
Applicant **tail = &_applicants;
|
||||||
virtual int lock() = 0;
|
|
||||||
virtual int timedlock(timespec const &) = 0;
|
for (; *tail; tail = &(*tail)->next) ;
|
||||||
virtual int trylock() = 0;
|
|
||||||
virtual int unlock() = 0;
|
*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
|
struct Libc::Pthread_mutex_normal : pthread_mutex
|
||||||
{
|
{
|
||||||
|
/* unsynchronized try */
|
||||||
int _try_lock(pthread_t thread)
|
int _try_lock(pthread_t thread)
|
||||||
{
|
{
|
||||||
Lock::Guard lock_guard(_data_mutex);
|
|
||||||
|
|
||||||
if (!_owner) {
|
if (!_owner) {
|
||||||
_owner = thread;
|
_owner = thread;
|
||||||
return 0;
|
return 0;
|
||||||
@ -251,30 +321,25 @@ struct Libc::Pthread_mutex_normal : pthread_mutex
|
|||||||
|
|
||||||
int lock() override final
|
int lock() override final
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
|
|
||||||
pthread_t const myself = pthread_self();
|
pthread_t const myself = pthread_self();
|
||||||
|
|
||||||
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
/* fast path without lock contention */
|
/* fast path without lock contention */
|
||||||
if (_try_lock(myself) == 0)
|
if (_try_lock(myself) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
{
|
_apply_for_mutex(myself, 0);
|
||||||
Applicant guard { *this };
|
|
||||||
|
|
||||||
_monitor().monitor(_monitor_mutex,
|
|
||||||
[&] { return _try_lock(myself) == 0; });
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int timedlock(timespec const &abs_timeout) override final
|
int timedlock(timespec const &abs_timeout) override final
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
|
|
||||||
pthread_t const myself = pthread_self();
|
pthread_t const myself = pthread_self();
|
||||||
|
|
||||||
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
/* fast path without lock contention - does not check abstimeout according to spec */
|
/* fast path without lock contention - does not check abstimeout according to spec */
|
||||||
if (_try_lock(myself) == 0)
|
if (_try_lock(myself) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -286,37 +351,29 @@ struct Libc::Pthread_mutex_normal : pthread_mutex
|
|||||||
if (!timeout_ms)
|
if (!timeout_ms)
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
|
|
||||||
{
|
if (_apply_for_mutex(myself, timeout_ms))
|
||||||
Applicant guard { *this };
|
return 0;
|
||||||
|
else
|
||||||
auto fn = [&] { return _try_lock(myself) == 0; };
|
return ETIMEDOUT;
|
||||||
|
|
||||||
if (_monitor().monitor(_monitor_mutex, fn, timeout_ms))
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int trylock() override final
|
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
|
int unlock() override final
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
Lock::Guard lock_guard(_data_mutex);
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
if (_owner != pthread_self())
|
if (_owner != pthread_self())
|
||||||
return EPERM;
|
return EPERM;
|
||||||
|
|
||||||
_owner = nullptr;
|
_next_applicant_to_owner();
|
||||||
|
|
||||||
if (_applicants)
|
|
||||||
_monitor().charge_monitors();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -325,73 +382,56 @@ struct Libc::Pthread_mutex_normal : pthread_mutex
|
|||||||
|
|
||||||
struct Libc::Pthread_mutex_errorcheck : pthread_mutex
|
struct Libc::Pthread_mutex_errorcheck : pthread_mutex
|
||||||
{
|
{
|
||||||
enum Try_lock_result { SUCCESS, BUSY, DEADLOCK };
|
/* unsynchronized try */
|
||||||
|
int _try_lock(pthread_t thread)
|
||||||
Try_lock_result _try_lock(pthread_t thread)
|
|
||||||
{
|
{
|
||||||
Lock::Guard lock_guard(_data_mutex);
|
|
||||||
|
|
||||||
if (!_owner) {
|
if (!_owner) {
|
||||||
_owner = thread;
|
_owner = thread;
|
||||||
return SUCCESS;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _owner == thread ? DEADLOCK : BUSY;
|
return _owner == thread ? EDEADLK : EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lock() override final
|
int lock() override final
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
|
|
||||||
pthread_t const myself = pthread_self();
|
pthread_t const myself = pthread_self();
|
||||||
|
|
||||||
/* fast path without lock contention */
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
switch (_try_lock(myself)) {
|
|
||||||
case SUCCESS: return 0;
|
|
||||||
case DEADLOCK: return EDEADLK;
|
|
||||||
case BUSY: [[fallthrough]];
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
/* fast path without lock contention (or deadlock) */
|
||||||
Applicant guard { *this };
|
int const result = _try_lock(myself);
|
||||||
|
if (!result || result == EDEADLK)
|
||||||
|
return result;
|
||||||
|
|
||||||
_monitor().monitor(_monitor_mutex, [&] {
|
_apply_for_mutex(myself, 0);
|
||||||
/* DEADLOCK already handled above - just check for SUCCESS */
|
|
||||||
return _try_lock(myself) == SUCCESS;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int timedlock(timespec const &) override final
|
int timedlock(timespec const &) override final
|
||||||
{
|
{
|
||||||
|
/* XXX not implemented yet */
|
||||||
return ENOSYS;
|
return ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int trylock() override final
|
int trylock() override final
|
||||||
{
|
{
|
||||||
switch (_try_lock(pthread_self())) {
|
pthread_t const myself = pthread_self();
|
||||||
case SUCCESS: return 0;
|
|
||||||
case DEADLOCK: return EDEADLK;
|
|
||||||
case BUSY: return EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EBUSY;
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
|
return _try_lock(myself);
|
||||||
}
|
}
|
||||||
|
|
||||||
int unlock() override final
|
int unlock() override final
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
Lock::Guard lock_guard(_data_mutex);
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
if (_owner != pthread_self())
|
if (_owner != pthread_self())
|
||||||
return EPERM;
|
return EPERM;
|
||||||
|
|
||||||
_owner = nullptr;
|
_next_applicant_to_owner();
|
||||||
|
|
||||||
if (_applicants)
|
|
||||||
_monitor().charge_monitors();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -402,13 +442,11 @@ struct Libc::Pthread_mutex_recursive : pthread_mutex
|
|||||||
{
|
{
|
||||||
unsigned _nesting_level { 0 };
|
unsigned _nesting_level { 0 };
|
||||||
|
|
||||||
|
/* unsynchronized try */
|
||||||
int _try_lock(pthread_t thread)
|
int _try_lock(pthread_t thread)
|
||||||
{
|
{
|
||||||
Lock::Guard lock_guard(_data_mutex);
|
|
||||||
|
|
||||||
if (!_owner) {
|
if (!_owner) {
|
||||||
_owner = thread;
|
_owner = thread;
|
||||||
_nesting_level = 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (_owner == thread) {
|
} else if (_owner == thread) {
|
||||||
++_nesting_level;
|
++_nesting_level;
|
||||||
@ -420,20 +458,15 @@ struct Libc::Pthread_mutex_recursive : pthread_mutex
|
|||||||
|
|
||||||
int lock() override final
|
int lock() override final
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
|
|
||||||
pthread_t const myself = pthread_self();
|
pthread_t const myself = pthread_self();
|
||||||
|
|
||||||
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
/* fast path without lock contention */
|
/* fast path without lock contention */
|
||||||
if (_try_lock(myself) == 0)
|
if (_try_lock(myself) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
{
|
_apply_for_mutex(myself, 0);
|
||||||
Applicant guard { *this };
|
|
||||||
|
|
||||||
_monitor().monitor(_monitor_mutex,
|
|
||||||
[&] { return _try_lock(myself) == 0; });
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -445,23 +478,24 @@ struct Libc::Pthread_mutex_recursive : pthread_mutex
|
|||||||
|
|
||||||
int trylock() override final
|
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
|
int unlock() override final
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
Lock::Guard lock_guard(_data_mutex);
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
if (_owner != pthread_self())
|
if (_owner != pthread_self())
|
||||||
return EPERM;
|
return EPERM;
|
||||||
|
|
||||||
--_nesting_level;
|
if (_nesting_level == 0)
|
||||||
if (_nesting_level == 0) {
|
_next_applicant_to_owner();
|
||||||
_owner = nullptr;
|
else
|
||||||
if (_applicants)
|
--_nesting_level;
|
||||||
_monitor().charge_monitors();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -24,142 +24,192 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
/* libc-internal includes */
|
/* libc-internal includes */
|
||||||
#include <internal/monitor.h>
|
|
||||||
#include <internal/errno.h>
|
#include <internal/errno.h>
|
||||||
#include <internal/types.h>
|
|
||||||
#include <internal/time.h>
|
|
||||||
#include <internal/init.h>
|
#include <internal/init.h>
|
||||||
|
#include <internal/kernel.h>
|
||||||
|
#include <internal/monitor.h>
|
||||||
|
#include <internal/time.h>
|
||||||
|
#include <internal/types.h>
|
||||||
|
|
||||||
using namespace Libc;
|
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:
|
||||||
|
|
||||||
/*
|
struct Applicant : Genode::Noncopyable
|
||||||
* 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
|
|
||||||
{
|
{
|
||||||
sem &s;
|
Applicant *next { nullptr };
|
||||||
|
|
||||||
Applicant(sem &s) : s(s)
|
Libc::Blockade &blockade;
|
||||||
{
|
|
||||||
Lock::Guard lock_guard(s._data_mutex);
|
|
||||||
++s._applicants;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Applicant()
|
Applicant(Libc::Blockade &blockade) : blockade(blockade) { }
|
||||||
{
|
|
||||||
Lock::Guard lock_guard(s._data_mutex);
|
|
||||||
--s._applicants;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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)
|
Applicant **tail = &_applicants;
|
||||||
throw Missing_call_of_init_pthread_support();
|
|
||||||
return *_monitor_ptr;
|
for (; *tail; tail = &(*tail)->next) ;
|
||||||
|
|
||||||
|
*tail = applicant;
|
||||||
}
|
}
|
||||||
|
|
||||||
sem(int value) : _count(value) { }
|
void _remove_applicant(Applicant *applicant)
|
||||||
|
{
|
||||||
int trydown()
|
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) {
|
if (_count > 0) {
|
||||||
_count--;
|
--_count;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return EBUSY;
|
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()
|
int down()
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
/* fast path without contention */
|
/* fast path */
|
||||||
if (trydown() == 0)
|
if (_try_down() == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
{
|
_apply_for_semaphore(0);
|
||||||
Applicant guard { *this };
|
|
||||||
|
|
||||||
auto fn = [&] { return trydown() == 0; };
|
|
||||||
|
|
||||||
(void)_monitor().monitor(_monitor_mutex, fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int down_timed(timespec const &abs_timeout)
|
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 */
|
/* fast path */
|
||||||
if (trydown() == 0)
|
if (_try_down() == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
timespec abs_now;
|
timespec abs_now;
|
||||||
clock_gettime(CLOCK_REALTIME, &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)
|
if (!timeout_ms)
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
|
|
||||||
{
|
if (_apply_for_semaphore(timeout_ms))
|
||||||
Applicant guard { *this };
|
return 0;
|
||||||
|
else
|
||||||
auto fn = [&] { return trydown() == 0; };
|
return ETIMEDOUT;
|
||||||
|
|
||||||
if (_monitor().monitor(_monitor_mutex, fn, timeout_ms))
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int up()
|
int up()
|
||||||
{
|
{
|
||||||
Lock::Guard monitor_guard(_monitor_mutex);
|
|
||||||
Lock::Guard lock_guard(_data_mutex);
|
Lock::Guard lock_guard(_data_mutex);
|
||||||
|
|
||||||
_count++;
|
_count_up();
|
||||||
|
|
||||||
if (_applicants)
|
|
||||||
_monitor().charge_monitors();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
int count()
|
|
||||||
{
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
int sem_close(sem_t *)
|
int sem_close(sem_t *)
|
||||||
{
|
{
|
||||||
|
@ -325,7 +325,7 @@ static void *thread_mutex_func(void *arg)
|
|||||||
/* unlock normal mutex */
|
/* unlock normal mutex */
|
||||||
|
|
||||||
if (pthread_mutex_unlock(&test_mutex_data->normal_mutex) != 0) {
|
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);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,15 +568,15 @@ struct Test_mutex_stress
|
|||||||
Test_mutex_stress()
|
Test_mutex_stress()
|
||||||
{
|
{
|
||||||
printf("main thread: start %s stress test\n", mutex.type_string());
|
printf("main thread: start %s stress test\n", mutex.type_string());
|
||||||
|
pthread_mutex_lock(mutex.mutex());
|
||||||
for (Thread &t : threads) t.start();
|
for (Thread &t : threads) t.start();
|
||||||
|
pthread_mutex_unlock(mutex.mutex());
|
||||||
for (Thread &t : threads) t.join();
|
for (Thread &t : threads) t.join();
|
||||||
printf("main thread: finished %s stress test\n", mutex.type_string());
|
printf("main thread: finished %s stress test\n", mutex.type_string());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
extern "C" void wait_for_continue();
|
|
||||||
|
|
||||||
static void test_mutex_stress()
|
static void test_mutex_stress()
|
||||||
{
|
{
|
||||||
printf("main thread: stressing mutexes\n");
|
printf("main thread: stressing mutexes\n");
|
||||||
@ -689,7 +689,7 @@ struct Test_cond
|
|||||||
|
|
||||||
void signaller()
|
void signaller()
|
||||||
{
|
{
|
||||||
printf("signaller: started\n");
|
Genode::log("signaller: started");
|
||||||
|
|
||||||
unsigned num_events = 0;
|
unsigned num_events = 0;
|
||||||
bool test_done = false;
|
bool test_done = false;
|
||||||
@ -708,7 +708,7 @@ struct Test_cond
|
|||||||
pthread_cond_signal(_cond.cond());
|
pthread_cond_signal(_cond.cond());
|
||||||
break;
|
break;
|
||||||
case State::SHUTDOWN:
|
case State::SHUTDOWN:
|
||||||
printf("signaller: shutting down\n");
|
Genode::log("signaller: shutting down");
|
||||||
_shared_state = State::END;
|
_shared_state = State::END;
|
||||||
++num_events;
|
++num_events;
|
||||||
pthread_cond_broadcast(_cond.cond());
|
pthread_cond_broadcast(_cond.cond());
|
||||||
@ -723,7 +723,7 @@ struct Test_cond
|
|||||||
usleep(1000);
|
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)
|
static void *waiter_fn(void *arg)
|
||||||
@ -736,7 +736,7 @@ struct Test_cond
|
|||||||
{
|
{
|
||||||
char const * const note = main_thread ? "(main thread)" : "";
|
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 pings = 0, pongs = 0;
|
||||||
unsigned long iterations = 0;
|
unsigned long iterations = 0;
|
||||||
@ -747,7 +747,7 @@ struct Test_cond
|
|||||||
auto handle_state = [&] {
|
auto handle_state = [&] {
|
||||||
unsigned const num_events = pings + pongs;
|
unsigned const num_events = pings + pongs;
|
||||||
if (num_events == 2000) {
|
if (num_events == 2000) {
|
||||||
printf("waiter%s: request shutdown\n", note);
|
Genode::log("waiter", note, ": request shutdown");
|
||||||
_shared_state = State::SHUTDOWN;
|
_shared_state = State::SHUTDOWN;
|
||||||
} else if (num_events % 2 == 0) {
|
} else if (num_events % 2 == 0) {
|
||||||
pthread_cond_wait(_cond.cond(), _mutex.mutex());
|
pthread_cond_wait(_cond.cond(), _mutex.mutex());
|
||||||
@ -777,8 +777,8 @@ struct Test_cond
|
|||||||
++iterations;
|
++iterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("waiter%s: finished (pings=%u, pongs=%u, iterations=%lu)\n",
|
Genode::log("waiter", note, ": finished (pings=", pings, ", pongs=",
|
||||||
note, pings, pongs, iterations);
|
pongs, ", iterations=", iterations, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
Test_cond()
|
Test_cond()
|
||||||
|
@ -37,12 +37,14 @@ MIRROR_FROM_LIBPORTS := lib/mk/libc_pipe.mk \
|
|||||||
src/lib/libc_pipe \
|
src/lib/libc_pipe \
|
||||||
lib/mk/libc-mem.mk \
|
lib/mk/libc-mem.mk \
|
||||||
lib/mk/libc-common.inc \
|
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/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/pthread.h \
|
||||||
|
src/lib/libc/internal/thread_create.h \
|
||||||
|
src/lib/libc/internal/timer.h \
|
||||||
src/lib/libc/internal/types.h \
|
src/lib/libc/internal/types.h \
|
||||||
|
src/lib/libc/libc_mem_alloc.cc \
|
||||||
include/libc-plugin \
|
include/libc-plugin \
|
||||||
lib/import/import-qemu-usb_include.mk \
|
lib/import/import-qemu-usb_include.mk \
|
||||||
lib/mk/qemu-usb_include.mk \
|
lib/mk/qemu-usb_include.mk \
|
||||||
|
@ -38,12 +38,14 @@ MIRROR_FROM_LIBPORTS := lib/mk/libc_pipe.mk \
|
|||||||
src/lib/libc_pipe \
|
src/lib/libc_pipe \
|
||||||
lib/mk/libc-mem.mk \
|
lib/mk/libc-mem.mk \
|
||||||
lib/mk/libc-common.inc \
|
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/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/pthread.h \
|
||||||
|
src/lib/libc/internal/thread_create.h \
|
||||||
|
src/lib/libc/internal/timer.h \
|
||||||
src/lib/libc/internal/types.h \
|
src/lib/libc/internal/types.h \
|
||||||
|
src/lib/libc/libc_mem_alloc.cc \
|
||||||
include/libc-plugin \
|
include/libc-plugin \
|
||||||
lib/import/import-qemu-usb_include.mk \
|
lib/import/import-qemu-usb_include.mk \
|
||||||
lib/mk/qemu-usb_include.mk \
|
lib/mk/qemu-usb_include.mk \
|
||||||
|
Loading…
Reference in New Issue
Block a user