2012-04-12 11:59:29 +02:00
|
|
|
/*
|
|
|
|
* \brief POSIX thread implementation
|
|
|
|
* \author Christian Prochaska
|
|
|
|
* \date 2012-03-12
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-20 13:23:52 +01:00
|
|
|
* Copyright (C) 2012-2017 Genode Labs GmbH
|
2012-04-12 11:59:29 +02:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 13:23:52 +01:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2012-04-12 11:59:29 +02:00
|
|
|
*/
|
|
|
|
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
/* Genode includes */
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
#include <base/log.h>
|
2018-06-28 17:43:39 +02:00
|
|
|
#include <base/sleep.h>
|
2012-04-12 11:59:29 +02:00
|
|
|
#include <base/thread.h>
|
2013-08-09 14:53:53 +02:00
|
|
|
#include <util/list.h>
|
2019-05-23 12:30:18 +02:00
|
|
|
#include <libc/allocator.h>
|
2012-04-12 11:59:29 +02:00
|
|
|
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
/* libc includes */
|
2012-04-12 11:59:29 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <pthread.h>
|
2017-04-19 21:25:03 +02:00
|
|
|
#include <stdlib.h> /* malloc, free */
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
|
|
|
|
/* libc-internal includes */
|
|
|
|
#include <internal/pthread.h>
|
|
|
|
#include <internal/timed_semaphore.h>
|
|
|
|
#include <internal/init.h>
|
|
|
|
#include <internal/suspend.h>
|
|
|
|
#include <internal/resume.h>
|
2018-01-11 18:24:59 +01:00
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
using namespace Libc;
|
2012-04-12 11:59:29 +02:00
|
|
|
|
2015-09-23 12:12:20 +02:00
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
static Genode::Env *_env_ptr; /* solely needed to spawn the timeout thread for the
|
|
|
|
timed semaphore */
|
2019-01-18 11:40:23 +01:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
static Thread *_main_thread_ptr;
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
static Suspend *_suspend_ptr;
|
|
|
|
static Resume *_resume_ptr;
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
|
2019-01-18 11:40:23 +01:00
|
|
|
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
void Libc::init_pthread_support(Genode::Env &env, Suspend &suspend, Resume &resume)
|
2019-09-16 13:52:39 +02:00
|
|
|
{
|
|
|
|
_env_ptr = &env;
|
|
|
|
_main_thread_ptr = Thread::myself();
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
_suspend_ptr = &suspend;
|
|
|
|
_resume_ptr = &resume;
|
2019-09-16 13:52:39 +02:00
|
|
|
}
|
2019-01-18 11:40:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
static Libc::Timeout_entrypoint &_global_timeout_ep()
|
|
|
|
{
|
|
|
|
class Missing_call_of_init_pthread_support { };
|
|
|
|
if (!_env_ptr)
|
|
|
|
throw Missing_call_of_init_pthread_support();
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
static Timeout_entrypoint timeout_ep { *_env_ptr };
|
2019-01-18 11:40:23 +01:00
|
|
|
return timeout_ep;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
/*************
|
|
|
|
** Pthread **
|
|
|
|
*************/
|
|
|
|
|
|
|
|
void Libc::Pthread::Thread_object::entry()
|
2018-01-11 18:24:59 +01:00
|
|
|
{
|
2018-06-12 15:21:08 +02:00
|
|
|
/* obtain stack attributes of new thread */
|
|
|
|
Thread::Stack_info info = Thread::mystack();
|
|
|
|
_stack_addr = (void *)info.base;
|
|
|
|
_stack_size = info.top - info.base;
|
|
|
|
|
2018-06-28 17:43:39 +02:00
|
|
|
pthread_exit(_start_routine(_arg));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
void Libc::Pthread::join(void **retval)
|
2018-06-28 17:43:39 +02:00
|
|
|
{
|
2019-09-19 20:37:17 +02:00
|
|
|
struct Check : Suspend_functor
|
2018-06-28 17:43:39 +02:00
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
Pthread &_thread;
|
2018-06-28 17:43:39 +02:00
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
Check(Pthread &thread) : _thread(thread) { }
|
2019-11-05 12:32:55 +01:00
|
|
|
|
2018-06-28 17:43:39 +02:00
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
retry = !_thread._exiting;
|
|
|
|
return retry;
|
2019-01-18 11:40:23 +01:00
|
|
|
}
|
2018-06-28 17:43:39 +02:00
|
|
|
} check(*this);
|
|
|
|
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
struct Missing_call_of_init_pthread_support : Exception { };
|
|
|
|
if (!_suspend_ptr)
|
|
|
|
throw Missing_call_of_init_pthread_support();
|
|
|
|
|
2018-06-28 17:43:39 +02:00
|
|
|
do {
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
_suspend_ptr->suspend(check);
|
2018-06-28 17:43:39 +02:00
|
|
|
} while (check.retry);
|
|
|
|
|
|
|
|
_join_lock.lock();
|
|
|
|
|
|
|
|
if (retval)
|
|
|
|
*retval = _retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
void Libc::Pthread::cancel()
|
2018-06-28 17:43:39 +02:00
|
|
|
{
|
2018-01-11 18:24:59 +01:00
|
|
|
_exiting = true;
|
libc: split task.cc into multiple files
This patch is the first step of re-organizing the internal structure of
the libc. The original version involved many direct calls of global
functions (often with side effects) across compilation units, which
made the control flow (e.g., the initialization sequence) hard to
follow.
The new version replaces those ad-hoc interactions with dedicated
interfaces (like suspend.h, resume.h, select.h, current_time.h). The
underlying facilities are provided by the central Libc::Kernel and
selectively propagated to the various compilation units. The latter is
done by a sequence of 'init_*' calls, which eventually will be replaced
by constructor calls.
The addition of new headers increases the chance for name clashes with
existing (public) headers. To disambiguate libc-internal header files
from public headers, this patch moves the former into a new 'internal/'
subdirectory. This makes the include directives easier to follow and the
libc's source-tree structure more tidy.
There are still a few legacies left, which cannot easily be removed
right now (e.g., because noux relies on them). However, the patch moves
those bad apples to legacy.h and legacy.cc, which highlights the
deprecation of those functions.
Issue #3497
2019-09-18 20:19:10 +02:00
|
|
|
|
|
|
|
struct Missing_call_of_init_pthread_support : Exception { };
|
|
|
|
if (!_resume_ptr)
|
|
|
|
throw Missing_call_of_init_pthread_support();
|
|
|
|
|
|
|
|
_resume_ptr->resume_all();
|
|
|
|
|
2018-06-28 17:43:39 +02:00
|
|
|
_join_lock.unlock();
|
2018-01-11 18:24:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Registry
|
|
|
|
*/
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
void Libc::Pthread_registry::insert(Pthread &thread)
|
2016-08-25 16:05:25 +02:00
|
|
|
{
|
|
|
|
/* prevent multiple insertions at the same location */
|
2019-09-19 20:37:17 +02:00
|
|
|
static Lock insert_lock;
|
|
|
|
Lock::Guard insert_lock_guard(insert_lock);
|
2016-08-25 16:05:25 +02:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < MAX_NUM_PTHREADS; i++) {
|
|
|
|
if (_array[i] == 0) {
|
2019-09-19 20:37:17 +02:00
|
|
|
_array[i] = &thread;
|
2016-08-25 16:05:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
error("pthread registry overflow, pthread_self() might fail");
|
2016-08-25 16:05:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
void Libc::Pthread_registry::remove(Pthread &thread)
|
2016-08-25 16:05:25 +02:00
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < MAX_NUM_PTHREADS; i++) {
|
2019-09-19 20:37:17 +02:00
|
|
|
if (_array[i] == &thread) {
|
2016-08-25 16:05:25 +02:00
|
|
|
_array[i] = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
error("could not remove unknown pthread from registry");
|
2016-08-25 16:05:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
bool Libc::Pthread_registry::contains(Pthread &thread)
|
2016-08-25 16:05:25 +02:00
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < MAX_NUM_PTHREADS; i++)
|
2019-09-19 20:37:17 +02:00
|
|
|
if (_array[i] == &thread)
|
2016-08-25 16:05:25 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
Libc::Pthread_registry &pthread_registry()
|
2016-08-25 16:05:25 +02:00
|
|
|
{
|
2019-09-19 20:37:17 +02:00
|
|
|
static Libc::Pthread_registry instance;
|
2016-08-25 16:05:25 +02:00
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
/***********
|
|
|
|
** Mutex **
|
|
|
|
***********/
|
|
|
|
|
|
|
|
namespace Libc {
|
|
|
|
struct Pthread_mutex_normal;
|
|
|
|
struct Pthread_mutex_errorcheck;
|
|
|
|
struct Pthread_mutex_recursive;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This class is named 'struct pthread_mutex_attr' because the
|
|
|
|
* 'pthread_mutexattr_t' type is defined as 'struct pthread_mutex_attr *'
|
|
|
|
* in '_pthreadtypes.h'
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
pthread_t _owner { nullptr };
|
|
|
|
Lock _data_mutex;
|
|
|
|
|
|
|
|
struct Missing_call_of_init_pthread_support : Exception { };
|
2019-09-18 11:53:59 +02:00
|
|
|
|
2019-11-05 12:32:55 +01:00
|
|
|
void _suspend(Suspend_functor &func)
|
|
|
|
{
|
|
|
|
if (!_suspend_ptr)
|
|
|
|
throw Missing_call_of_init_pthread_support();
|
|
|
|
_suspend_ptr->suspend(func);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _resume_all()
|
|
|
|
{
|
|
|
|
if (!_resume_ptr)
|
|
|
|
throw Missing_call_of_init_pthread_support();
|
|
|
|
_resume_ptr->resume_all();
|
|
|
|
}
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
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 trylock() = 0;
|
|
|
|
virtual int unlock() = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Libc::Pthread_mutex_normal : pthread_mutex
|
|
|
|
{
|
|
|
|
int lock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
struct Try_lock : Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false }; /* have to try after resume */
|
|
|
|
|
|
|
|
Pthread_mutex_normal &_mutex;
|
|
|
|
|
|
|
|
Try_lock(Pthread_mutex_normal &mutex) : _mutex(mutex) { }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
retry = _mutex.trylock() == EBUSY;
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} try_lock(*this);
|
|
|
|
|
|
|
|
do { _suspend(try_lock); } while (try_lock.retry);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int trylock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
Lock::Guard lock_guard(_data_mutex);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
if (!_owner) {
|
|
|
|
_owner = pthread_self();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
int unlock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
Lock::Guard lock_guard(_data_mutex);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
if (_owner != pthread_self())
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
_owner = nullptr;
|
2019-11-05 12:32:55 +01:00
|
|
|
_resume_all();
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Libc::Pthread_mutex_errorcheck : pthread_mutex
|
|
|
|
{
|
|
|
|
int lock() override final
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We can't use trylock() as it returns EBUSY also for the
|
|
|
|
* EDEADLK case.
|
|
|
|
*/
|
2019-11-05 12:32:55 +01:00
|
|
|
struct Try_lock : Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false }; /* have to try after resume */
|
|
|
|
int result { 0 };
|
|
|
|
|
|
|
|
Pthread_mutex_errorcheck &_mutex;
|
|
|
|
|
|
|
|
Try_lock(Pthread_mutex_errorcheck &mutex) : _mutex(mutex) { }
|
|
|
|
|
|
|
|
bool suspend() override
|
2019-09-18 11:53:59 +02:00
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
Lock::Guard lock_guard(_mutex._data_mutex);
|
|
|
|
|
|
|
|
if (!_mutex._owner) {
|
|
|
|
_mutex._owner = pthread_self();
|
|
|
|
retry = false;
|
|
|
|
result = 0;
|
|
|
|
} else if (_mutex._owner == pthread_self()) {
|
|
|
|
retry = false;
|
|
|
|
result = EDEADLK;
|
|
|
|
} else {
|
|
|
|
retry = true;
|
2019-09-18 11:53:59 +02:00
|
|
|
}
|
2019-11-05 12:32:55 +01:00
|
|
|
|
|
|
|
return retry;
|
2019-09-18 11:53:59 +02:00
|
|
|
}
|
2019-11-05 12:32:55 +01:00
|
|
|
} try_lock(*this);
|
|
|
|
|
|
|
|
do { _suspend(try_lock); } while (try_lock.retry);
|
|
|
|
|
|
|
|
return try_lock.result;
|
2019-09-18 11:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int trylock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
Lock::Guard lock_guard(_data_mutex);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
if (!_owner) {
|
|
|
|
_owner = pthread_self();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
int unlock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
Lock::Guard lock_guard(_data_mutex);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
if (_owner != pthread_self())
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
_owner = nullptr;
|
2019-11-05 12:32:55 +01:00
|
|
|
_resume_all();
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Libc::Pthread_mutex_recursive : pthread_mutex
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
unsigned _nesting_level { 0 };
|
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
int lock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
struct Try_lock : Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false }; /* have to try after resume */
|
|
|
|
|
|
|
|
Pthread_mutex_recursive &_mutex;
|
|
|
|
|
|
|
|
Try_lock(Pthread_mutex_recursive &mutex) : _mutex(mutex) { }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
retry = _mutex.trylock() == EBUSY;
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} try_lock(*this);
|
|
|
|
|
|
|
|
do { _suspend(try_lock); } while (try_lock.retry);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int trylock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
Lock::Guard lock_guard(_data_mutex);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
if (!_owner) {
|
2019-11-05 12:32:55 +01:00
|
|
|
_owner = pthread_self();
|
|
|
|
_nesting_level = 1;
|
2019-09-18 11:53:59 +02:00
|
|
|
return 0;
|
|
|
|
} else if (_owner == pthread_self()) {
|
2019-11-05 12:32:55 +01:00
|
|
|
++_nesting_level;
|
2019-09-18 11:53:59 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
int unlock() override final
|
|
|
|
{
|
2019-11-05 12:32:55 +01:00
|
|
|
Lock::Guard lock_guard(_data_mutex);
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
if (_owner != pthread_self())
|
|
|
|
return EPERM;
|
|
|
|
|
2019-11-05 12:32:55 +01:00
|
|
|
--_nesting_level;
|
|
|
|
if (_nesting_level == 0) {
|
2019-09-18 11:53:59 +02:00
|
|
|
_owner = nullptr;
|
2019-11-05 12:32:55 +01:00
|
|
|
_resume_all();
|
2019-09-18 11:53:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-04-12 11:59:29 +02:00
|
|
|
extern "C" {
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
/* Thread */
|
|
|
|
|
2017-08-08 14:10:18 +02:00
|
|
|
int pthread_join(pthread_t thread, void **retval)
|
|
|
|
{
|
2018-06-28 17:43:39 +02:00
|
|
|
thread->join(retval);
|
2017-08-08 14:10:18 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, thread);
|
2017-08-08 14:10:18 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
int pthread_attr_init(pthread_attr_t *attr)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!attr)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
*attr = new (alloc) pthread_attr;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_attr_destroy(pthread_attr_t *attr)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!attr || !*attr)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, *attr);
|
2013-08-09 14:53:53 +02:00
|
|
|
*attr = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-12 11:59:29 +02:00
|
|
|
int pthread_cancel(pthread_t thread)
|
|
|
|
{
|
2018-06-28 17:43:39 +02:00
|
|
|
thread->cancel();
|
2012-04-12 11:59:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-28 17:43:39 +02:00
|
|
|
|
2012-04-12 11:59:29 +02:00
|
|
|
void pthread_exit(void *value_ptr)
|
|
|
|
{
|
2018-06-28 17:43:39 +02:00
|
|
|
pthread_self()->exit(value_ptr);
|
2019-09-19 20:37:17 +02:00
|
|
|
sleep_forever();
|
2012-04-12 11:59:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-04 12:36:08 +01:00
|
|
|
/* special non-POSIX function (for example used in libresolv) */
|
|
|
|
int _pthread_main_np(void)
|
|
|
|
{
|
2019-09-16 13:52:39 +02:00
|
|
|
return (Thread::myself() == _main_thread_ptr);
|
2015-12-04 12:36:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-12 11:59:29 +02:00
|
|
|
pthread_t pthread_self(void)
|
|
|
|
{
|
2018-01-04 14:47:16 +01:00
|
|
|
try {
|
|
|
|
pthread_t pthread_myself =
|
|
|
|
static_cast<pthread_t>(&Thread::Tls::Base::tls());
|
2012-04-12 11:59:29 +02:00
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
if (pthread_registry().contains(*pthread_myself))
|
2018-01-04 14:47:16 +01:00
|
|
|
return pthread_myself;
|
|
|
|
}
|
|
|
|
catch (Thread::Tls::Base::Undefined) { }
|
2014-04-09 15:47:26 +02:00
|
|
|
|
2015-12-04 12:36:08 +01:00
|
|
|
/*
|
|
|
|
* We pass here if the main thread or an alien thread calls
|
|
|
|
* pthread_self(). So check for aliens (or other bugs) and opt-out
|
|
|
|
* early.
|
|
|
|
*/
|
|
|
|
if (!_pthread_main_np()) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
error("pthread_self() called from alien thread named ",
|
2018-01-04 14:47:16 +01:00
|
|
|
"'", Thread::myself()->name().string(), "'");
|
2015-12-04 12:36:08 +01:00
|
|
|
return nullptr;
|
2013-08-09 14:53:53 +02:00
|
|
|
}
|
|
|
|
|
2015-12-04 12:36:08 +01:00
|
|
|
/*
|
2018-01-04 14:47:16 +01:00
|
|
|
* We create a pthread object associated to the main thread's Thread
|
|
|
|
* object. We ensure the pthread object does never get deleted by
|
|
|
|
* allocating it in heap via new(). Otherwise, the static destruction
|
|
|
|
* of the pthread object would also destruct the 'Thread' of the main
|
|
|
|
* thread.
|
2015-12-04 12:36:08 +01:00
|
|
|
*/
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
static pthread *main = new (alloc) pthread(*Thread::myself());
|
2015-12-04 12:36:08 +01:00
|
|
|
return main;
|
2013-08-09 14:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-26 12:47:33 +02:00
|
|
|
pthread_t thr_self(void) { return pthread_self(); }
|
|
|
|
|
|
|
|
__attribute__((alias("thr_self")))
|
|
|
|
pthread_t __sys_thr_self(void);
|
|
|
|
|
|
|
|
|
2017-11-23 14:33:31 +01:00
|
|
|
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
|
|
|
|
{
|
|
|
|
if (!attr || !*attr)
|
|
|
|
return EINVAL;
|
|
|
|
|
2017-09-27 09:48:13 +02:00
|
|
|
if (stacksize < 4096)
|
2017-11-23 14:33:31 +01:00
|
|
|
return EINVAL;
|
|
|
|
|
2017-09-27 09:48:13 +02:00
|
|
|
size_t max_stack = Thread::stack_virtual_size() - 4 * 4096;
|
|
|
|
if (stacksize > max_stack) {
|
|
|
|
warning(__func__, ": requested stack size is ", stacksize, " limiting to ", max_stack);
|
|
|
|
stacksize = max_stack;
|
|
|
|
}
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
(*attr)->stack_size = align_addr(stacksize, 12);
|
2017-11-23 14:33:31 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
int pthread_attr_getstack(const pthread_attr_t *attr,
|
|
|
|
void **stackaddr,
|
2016-09-15 14:40:37 +02:00
|
|
|
::size_t *stacksize)
|
2013-08-09 14:53:53 +02:00
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!attr || !*attr || !stackaddr || !stacksize)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2018-06-12 15:21:08 +02:00
|
|
|
*stackaddr = (*attr)->stack_addr;
|
|
|
|
*stacksize = (*attr)->stack_size;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-24 13:55:08 +02:00
|
|
|
int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr)
|
|
|
|
{
|
|
|
|
size_t stacksize;
|
|
|
|
return pthread_attr_getstack(attr, stackaddr, &stacksize);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
|
|
|
|
{
|
|
|
|
void *stackaddr;
|
|
|
|
return pthread_attr_getstack(attr, &stackaddr, stacksize);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
int pthread_attr_get_np(pthread_t pthread, pthread_attr_t *attr)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!attr)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2018-06-12 15:21:08 +02:00
|
|
|
(*attr)->stack_addr = pthread->stack_addr();
|
|
|
|
(*attr)->stack_size = pthread->stack_size();
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_equal(pthread_t t1, pthread_t t2)
|
|
|
|
{
|
|
|
|
return (t1 == t2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Mutex */
|
|
|
|
|
|
|
|
int pthread_mutexattr_init(pthread_mutexattr_t *attr)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!attr)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
2019-09-18 11:53:59 +02:00
|
|
|
*attr = new (alloc) pthread_mutex_attr { PTHREAD_MUTEX_NORMAL };
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!attr || !*attr)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, *attr);
|
2019-09-18 11:53:59 +02:00
|
|
|
*attr = nullptr;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!attr || !*attr)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
(*attr)->type = (pthread_mutextype)type;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
int pthread_mutex_init(pthread_mutex_t *mutex,
|
|
|
|
pthread_mutexattr_t const *attr)
|
2013-08-09 14:53:53 +02:00
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!mutex)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
2019-09-18 11:53:59 +02:00
|
|
|
|
|
|
|
pthread_mutextype const type = (!attr || !*attr)
|
|
|
|
? PTHREAD_MUTEX_NORMAL : (*attr)->type;
|
|
|
|
switch (type) {
|
|
|
|
case PTHREAD_MUTEX_NORMAL: *mutex = new (alloc) Pthread_mutex_normal; break;
|
|
|
|
case PTHREAD_MUTEX_ERRORCHECK: *mutex = new (alloc) Pthread_mutex_errorcheck; break;
|
|
|
|
case PTHREAD_MUTEX_RECURSIVE: *mutex = new (alloc) Pthread_mutex_recursive; break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
*mutex = nullptr;
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if ((!mutex) || (*mutex == PTHREAD_MUTEX_INITIALIZER))
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, *mutex);
|
2013-08-09 14:53:53 +02:00
|
|
|
*mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_mutex_lock(pthread_mutex_t *mutex)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!mutex)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
if (*mutex == PTHREAD_MUTEX_INITIALIZER)
|
2019-09-18 11:53:59 +02:00
|
|
|
pthread_mutex_init(mutex, nullptr);
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
return (*mutex)->lock();
|
2013-08-09 14:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-18 13:01:03 +02:00
|
|
|
int pthread_mutex_trylock(pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
if (!mutex)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
if (*mutex == PTHREAD_MUTEX_INITIALIZER)
|
2019-09-18 11:53:59 +02:00
|
|
|
pthread_mutex_init(mutex, nullptr);
|
2016-10-18 13:01:03 +02:00
|
|
|
|
|
|
|
return (*mutex)->trylock();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!mutex)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
if (*mutex == PTHREAD_MUTEX_INITIALIZER)
|
2019-09-18 11:53:59 +02:00
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
return (*mutex)->unlock();
|
2013-08-09 14:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Condition variable */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Implementation based on
|
|
|
|
* http://web.archive.org/web/20010914175514/http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html#Workshop
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct pthread_cond
|
|
|
|
{
|
|
|
|
int num_waiters;
|
|
|
|
int num_signallers;
|
|
|
|
Lock counter_lock;
|
2019-09-19 20:37:17 +02:00
|
|
|
Timed_semaphore signal_sem { _global_timeout_ep() };
|
2013-08-09 14:53:53 +02:00
|
|
|
Semaphore handshake_sem;
|
|
|
|
|
|
|
|
pthread_cond() : num_waiters(0), num_signallers(0) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-12-02 12:50:56 +01:00
|
|
|
int pthread_condattr_init(pthread_condattr_t *attr)
|
|
|
|
{
|
|
|
|
if (!attr)
|
|
|
|
return EINVAL;
|
|
|
|
|
2017-08-03 16:44:17 +02:00
|
|
|
*attr = nullptr;
|
2013-12-02 12:50:56 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_condattr_destroy(pthread_condattr_t *attr)
|
|
|
|
{
|
2017-08-03 16:44:17 +02:00
|
|
|
/* assert that the attr was produced by the init no-op */
|
|
|
|
if (!attr || *attr != nullptr)
|
2013-12-02 12:50:56 +01:00
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_condattr_setclock(pthread_condattr_t *attr,
|
|
|
|
clockid_t clock_id)
|
|
|
|
{
|
2017-08-03 16:44:17 +02:00
|
|
|
/* assert that the attr was produced by the init no-op */
|
|
|
|
if (!attr || *attr != nullptr)
|
2013-12-02 12:50:56 +01:00
|
|
|
return EINVAL;
|
|
|
|
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
warning(__func__, " not implemented yet");
|
2013-12-02 12:50:56 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-13 13:33:45 +01:00
|
|
|
static int cond_init(pthread_cond_t *__restrict cond,
|
|
|
|
const pthread_condattr_t *__restrict attr)
|
2013-08-09 14:53:53 +02:00
|
|
|
{
|
2019-09-19 20:37:17 +02:00
|
|
|
static Lock cond_init_lock { };
|
2018-11-13 13:33:45 +01:00
|
|
|
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!cond)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2018-11-13 13:33:45 +01:00
|
|
|
try {
|
2019-09-19 20:37:17 +02:00
|
|
|
Lock::Guard g(cond_init_lock);
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
*cond = new (alloc) pthread_cond;
|
2018-11-13 13:33:45 +01:00
|
|
|
return 0;
|
|
|
|
} catch (...) { return ENOMEM; }
|
|
|
|
}
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2018-11-13 13:33:45 +01:00
|
|
|
|
|
|
|
int pthread_cond_init(pthread_cond_t *__restrict cond,
|
|
|
|
const pthread_condattr_t *__restrict attr)
|
|
|
|
{
|
|
|
|
return cond_init(cond, attr);
|
2012-04-12 11:59:29 +02:00
|
|
|
}
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2013-12-02 12:50:56 +01:00
|
|
|
int pthread_cond_destroy(pthread_cond_t *cond)
|
|
|
|
{
|
|
|
|
if (!cond || !*cond)
|
|
|
|
return EINVAL;
|
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, *cond);
|
2013-12-02 12:50:56 +01:00
|
|
|
*cond = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-08 11:41:24 +01:00
|
|
|
static uint64_t timeout_ms(struct timespec currtime,
|
|
|
|
struct timespec abstimeout)
|
2013-08-09 14:53:53 +02:00
|
|
|
{
|
2017-03-08 11:41:24 +01:00
|
|
|
enum { S_IN_MS = 1000, S_IN_NS = 1000 * 1000 * 1000 };
|
|
|
|
|
|
|
|
if (currtime.tv_nsec >= S_IN_NS) {
|
|
|
|
currtime.tv_sec += currtime.tv_nsec / S_IN_NS;
|
|
|
|
currtime.tv_nsec = currtime.tv_nsec % S_IN_NS;
|
|
|
|
}
|
|
|
|
if (abstimeout.tv_nsec >= S_IN_NS) {
|
|
|
|
abstimeout.tv_sec += abstimeout.tv_nsec / S_IN_NS;
|
|
|
|
abstimeout.tv_nsec = abstimeout.tv_nsec % S_IN_NS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check whether absolute timeout is in the past */
|
|
|
|
if (currtime.tv_sec > abstimeout.tv_sec)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint64_t diff_ms = (abstimeout.tv_sec - currtime.tv_sec) * S_IN_MS;
|
|
|
|
uint64_t diff_ns = 0;
|
|
|
|
|
|
|
|
if (abstimeout.tv_nsec >= currtime.tv_nsec)
|
|
|
|
diff_ns = abstimeout.tv_nsec - currtime.tv_nsec;
|
|
|
|
else {
|
|
|
|
/* check whether absolute timeout is in the past */
|
|
|
|
if (diff_ms == 0)
|
|
|
|
return 0;
|
|
|
|
diff_ns = S_IN_NS - currtime.tv_nsec + abstimeout.tv_nsec;
|
|
|
|
diff_ms -= S_IN_MS;
|
|
|
|
}
|
|
|
|
|
|
|
|
diff_ms += diff_ns / 1000 / 1000;
|
|
|
|
|
|
|
|
/* if there is any diff then let the timeout be at least 1 MS */
|
|
|
|
if (diff_ms == 0 && diff_ns != 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return diff_ms;
|
2013-08-09 14:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_cond_timedwait(pthread_cond_t *__restrict cond,
|
|
|
|
pthread_mutex_t *__restrict mutex,
|
|
|
|
const struct timespec *__restrict abstime)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
|
2018-11-13 13:33:45 +01:00
|
|
|
if (!cond)
|
2013-10-30 12:19:04 +01:00
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2018-11-13 13:33:45 +01:00
|
|
|
if (*cond == PTHREAD_COND_INITIALIZER)
|
|
|
|
cond_init(cond, NULL);
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
pthread_cond *c = *cond;
|
|
|
|
|
|
|
|
c->counter_lock.lock();
|
|
|
|
c->num_waiters++;
|
|
|
|
c->counter_lock.unlock();
|
|
|
|
|
|
|
|
pthread_mutex_unlock(mutex);
|
|
|
|
|
|
|
|
if (!abstime)
|
|
|
|
c->signal_sem.down();
|
|
|
|
else {
|
|
|
|
struct timespec currtime;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &currtime);
|
2017-03-08 11:41:24 +01:00
|
|
|
|
|
|
|
Alarm::Time timeout = timeout_ms(currtime, *abstime);
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
try {
|
|
|
|
c->signal_sem.down(timeout);
|
2019-09-19 20:37:17 +02:00
|
|
|
} catch (Timeout_exception) {
|
2013-10-30 12:19:04 +01:00
|
|
|
result = ETIMEDOUT;
|
2019-09-19 20:37:17 +02:00
|
|
|
} catch (Nonblocking_exception) {
|
2014-01-16 14:15:13 +01:00
|
|
|
errno = ETIMEDOUT;
|
|
|
|
result = ETIMEDOUT;
|
2013-08-09 14:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c->counter_lock.lock();
|
|
|
|
if (c->num_signallers > 0) {
|
2013-10-30 12:19:04 +01:00
|
|
|
if (result == ETIMEDOUT) /* timeout occured */
|
2013-08-09 14:53:53 +02:00
|
|
|
c->signal_sem.down();
|
|
|
|
c->handshake_sem.up();
|
|
|
|
--c->num_signallers;
|
|
|
|
}
|
|
|
|
c->num_waiters--;
|
|
|
|
c->counter_lock.unlock();
|
|
|
|
|
|
|
|
pthread_mutex_lock(mutex);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_cond_wait(pthread_cond_t *__restrict cond,
|
|
|
|
pthread_mutex_t *__restrict mutex)
|
|
|
|
{
|
|
|
|
return pthread_cond_timedwait(cond, mutex, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_cond_signal(pthread_cond_t *cond)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!cond || !*cond)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
pthread_cond *c = *cond;
|
|
|
|
|
|
|
|
c->counter_lock.lock();
|
|
|
|
if (c->num_waiters > c->num_signallers) {
|
|
|
|
++c->num_signallers;
|
|
|
|
c->signal_sem.up();
|
|
|
|
c->counter_lock.unlock();
|
|
|
|
c->handshake_sem.down();
|
|
|
|
} else
|
|
|
|
c->counter_lock.unlock();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_cond_broadcast(pthread_cond_t *cond)
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!cond || !*cond)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
pthread_cond *c = *cond;
|
|
|
|
|
|
|
|
c->counter_lock.lock();
|
|
|
|
if (c->num_waiters > c->num_signallers) {
|
|
|
|
int still_waiting = c->num_waiters - c->num_signallers;
|
|
|
|
c->num_signallers = c->num_waiters;
|
|
|
|
for (int i = 0; i < still_waiting; i++)
|
|
|
|
c->signal_sem.up();
|
|
|
|
c->counter_lock.unlock();
|
|
|
|
for (int i = 0; i < still_waiting; i++)
|
|
|
|
c->handshake_sem.down();
|
|
|
|
} else
|
|
|
|
c->counter_lock.unlock();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TLS */
|
|
|
|
|
|
|
|
|
|
|
|
struct Key_element : List<Key_element>::Element
|
|
|
|
{
|
|
|
|
const void *thread_base;
|
|
|
|
const void *value;
|
|
|
|
|
|
|
|
Key_element(const void *thread_base, const void *value)
|
|
|
|
: thread_base(thread_base),
|
|
|
|
value(value) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
static Lock &key_list_lock()
|
|
|
|
{
|
|
|
|
static Lock inst { };
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Keys
|
|
|
|
{
|
|
|
|
List<Key_element> key[PTHREAD_KEYS_MAX];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static Keys &keys()
|
|
|
|
{
|
|
|
|
static Keys inst { };
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
|
|
|
|
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
|
|
|
|
{
|
2013-10-30 12:19:04 +01:00
|
|
|
if (!key)
|
|
|
|
return EINVAL;
|
2013-08-09 14:53:53 +02:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Lock_guard<Lock> key_list_lock_guard(key_list_lock());
|
2014-11-26 11:13:45 +01:00
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
for (int k = 0; k < PTHREAD_KEYS_MAX; k++) {
|
|
|
|
/*
|
|
|
|
* Find an empty key slot and insert an element for the current
|
|
|
|
* thread to mark the key slot as used.
|
|
|
|
*/
|
2019-09-16 13:52:39 +02:00
|
|
|
if (!keys().key[k].first()) {
|
|
|
|
Libc::Allocator alloc { };
|
|
|
|
Key_element *key_element = new (alloc) Key_element(Thread::myself(), 0);
|
|
|
|
keys().key[k].insert(key_element);
|
2013-08-09 14:53:53 +02:00
|
|
|
*key = k;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-30 12:19:04 +01:00
|
|
|
return EAGAIN;
|
2013-08-09 14:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-26 11:13:45 +01:00
|
|
|
int pthread_key_delete(pthread_key_t key)
|
|
|
|
{
|
2019-09-16 13:52:39 +02:00
|
|
|
if (key < 0 || key >= PTHREAD_KEYS_MAX || !keys().key[key].first())
|
2014-11-26 11:13:45 +01:00
|
|
|
return EINVAL;
|
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Lock_guard<Lock> key_list_lock_guard(key_list_lock());
|
2014-11-26 11:13:45 +01:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
while (Key_element * element = keys().key[key].first()) {
|
|
|
|
keys().key[key].remove(element);
|
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, element);
|
2014-11-26 11:13:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-09 14:53:53 +02:00
|
|
|
int pthread_setspecific(pthread_key_t key, const void *value)
|
|
|
|
{
|
2014-11-26 11:13:45 +01:00
|
|
|
if (key < 0 || key >= PTHREAD_KEYS_MAX)
|
|
|
|
return EINVAL;
|
|
|
|
|
2016-05-04 12:27:17 +02:00
|
|
|
void *myself = Thread::myself();
|
2014-11-26 11:13:45 +01:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Lock_guard<Lock> key_list_lock_guard(key_list_lock());
|
2014-11-26 11:13:45 +01:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
for (Key_element *key_element = keys().key[key].first(); key_element;
|
2013-08-09 14:53:53 +02:00
|
|
|
key_element = key_element->next())
|
|
|
|
if (key_element->thread_base == myself) {
|
|
|
|
key_element->value = value;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* key element does not exist yet - create a new one */
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
Key_element *key_element = new (alloc) Key_element(Thread::myself(), value);
|
|
|
|
keys().key[key].insert(key_element);
|
2013-08-09 14:53:53 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void *pthread_getspecific(pthread_key_t key)
|
|
|
|
{
|
2014-11-26 11:13:45 +01:00
|
|
|
if (key < 0 || key >= PTHREAD_KEYS_MAX)
|
|
|
|
return nullptr;
|
|
|
|
|
2016-05-04 12:27:17 +02:00
|
|
|
void *myself = Thread::myself();
|
2014-11-26 11:13:45 +01:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
Lock_guard<Lock> key_list_lock_guard(key_list_lock());
|
2014-11-26 11:13:45 +01:00
|
|
|
|
2019-09-16 13:52:39 +02:00
|
|
|
for (Key_element *key_element = keys().key[key].first(); key_element;
|
2013-08-09 14:53:53 +02:00
|
|
|
key_element = key_element->next())
|
|
|
|
if (key_element->thread_base == myself)
|
|
|
|
return (void*)(key_element->value);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-10-14 17:09:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
int pthread_once(pthread_once_t *once, void (*init_once)(void))
|
|
|
|
{
|
|
|
|
if (!once || ((once->state != PTHREAD_NEEDS_INIT) &&
|
|
|
|
(once->state != PTHREAD_DONE_INIT)))
|
2019-09-18 11:53:59 +02:00
|
|
|
return EINVAL;
|
2014-10-14 17:09:54 +02:00
|
|
|
|
|
|
|
if (!once->mutex) {
|
2019-09-18 11:53:59 +02:00
|
|
|
pthread_mutex_t p;
|
|
|
|
pthread_mutex_init(&p, nullptr);
|
|
|
|
if (!p) return EINVAL;
|
2014-10-14 17:09:54 +02:00
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
{
|
|
|
|
static Lock lock;
|
|
|
|
Lock::Guard guard(lock);
|
2014-10-14 17:09:54 +02:00
|
|
|
|
2019-09-18 11:53:59 +02:00
|
|
|
if (!once->mutex) {
|
|
|
|
once->mutex = p;
|
|
|
|
p = nullptr;
|
|
|
|
}
|
2014-10-14 17:09:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If another thread concurrently allocated a mutex and was faster,
|
|
|
|
* free our mutex since it is not used.
|
|
|
|
*/
|
2019-09-18 11:53:59 +02:00
|
|
|
if (p) pthread_mutex_destroy(&p);
|
2014-10-14 17:09:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
once->mutex->lock();
|
|
|
|
|
|
|
|
if (once->state == PTHREAD_DONE_INIT) {
|
|
|
|
once->mutex->unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
init_once();
|
|
|
|
|
|
|
|
once->state = PTHREAD_DONE_INIT;
|
|
|
|
|
|
|
|
once->mutex->unlock();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-12 11:59:29 +02:00
|
|
|
}
|