base: add mutex as derivate of lock

The mutex class is more restrictive in usage compared to
Genode::Lock.

- At initialiation time it is ever unlocked.
- No thread is permitted to lock twice. Warn about it
  in case it happens.
- Only the lock onwer is permitted to unlock the mutex.
  Warn about it and don't unlock the mutex in case it happens.

Issue #3612
This commit is contained in:
Alexander Boettcher 2020-01-24 13:46:09 +01:00 committed by Christian Helmuth
parent d1609e771a
commit 00f69bc70d
15 changed files with 133 additions and 16 deletions

View File

@ -13,6 +13,7 @@
/* Genode includes */
#include <base/cancelable_lock.h>
#include <base/thread.h>
#include <cpu/atomic.h>
#include <cpu/memory_barrier.h>
@ -33,6 +34,13 @@ Cancelable_lock::Cancelable_lock(Cancelable_lock::State initial)
void Cancelable_lock::lock()
{
Applicant myself(Thread::myself());
lock(myself);
}
void Cancelable_lock::lock(Applicant &myself)
{
/*
* XXX: How to notice cancel-blocking signals issued when being outside the
@ -41,11 +49,14 @@ void Cancelable_lock::lock()
while (!Genode::cmpxchg(&_state, UNLOCKED, LOCKED))
if (Fiasco::l4_ipc_sleep(Fiasco::l4_ipc_timeout(0, 0, 500, 0)) != L4_IPC_RETIMEOUT)
throw Genode::Blocking_canceled();
_owner = myself;
}
void Cancelable_lock::unlock()
{
_owner = Applicant(nullptr);
Genode::memory_barrier();
_state = UNLOCKED;
}

View File

@ -78,7 +78,7 @@ static inline void thread_switch_to(Genode::Thread *thread_base)
__attribute__((optimize("-fno-omit-frame-pointer")))
__attribute__((noinline))
__attribute__((used))
static void thread_stop_myself()
static void thread_stop_myself(Genode::Thread *)
{
using namespace Fiasco;

View File

@ -12,6 +12,7 @@
*/
#include <base/lock.h>
#include <base/mutex.h>
#include <hw/assert.h>
Genode::Cancelable_lock::Cancelable_lock(Genode::Cancelable_lock::State state)
@ -30,3 +31,13 @@ void Genode::Cancelable_lock::lock()
assert(_state == UNLOCKED);
_state = LOCKED;
}
void Genode::Mutex::acquire()
{
_lock.lock();
}
void Genode::Mutex::release()
{
_lock.unlock();
}

View File

@ -62,7 +62,7 @@ thread_check_stopped_and_restart(Genode::Thread * const t)
/**
* Pause execution of current thread
*/
static inline void thread_stop_myself() { Kernel::stop_thread(); }
static inline void thread_stop_myself(Genode::Thread *) { Kernel::stop_thread(); }
#endif /* _INCLUDE__BASE__INTERNAL__LOCK_HELPER_H_ */

View File

@ -49,14 +49,13 @@ static inline bool thread_check_stopped_and_restart(Genode::Thread *thread_base)
static inline void thread_switch_to(Genode::Thread *) { thread_yield(); }
static inline void thread_stop_myself()
static inline void thread_stop_myself(Genode::Thread *myself)
{
/*
* Just go to sleep without modifying the counter value. The
* 'thread_check_stopped_and_restart()' function will get called
* repeatedly until this thread has actually executed the syscall.
*/
Genode::Thread *myself = Genode::Thread::myself();
const int *futex_counter_ptr = myself ?
&myself->native_thread().futex_counter :
&main_thread_futex_counter;

View File

@ -47,13 +47,12 @@ static inline bool thread_check_stopped_and_restart(Genode::Thread *thread_base)
static inline void thread_switch_to(Genode::Thread *) { }
static inline void thread_stop_myself()
static inline void thread_stop_myself(Genode::Thread *myself)
{
using namespace Genode;
using namespace Nova;
addr_t sem;
Thread *myself = Thread::myself();
if (myself)
sem = myself->native_thread().exc_pt_sel + SM_SEL_EC;
else

View File

@ -77,9 +77,8 @@ static inline void thread_switch_to(Genode::Thread *thread_base)
/**
* Unconditionally block the calling thread
*/
static inline void thread_stop_myself()
static inline void thread_stop_myself(Genode::Thread *myself)
{
Genode::Thread *myself = Genode::Thread::myself();
Okl4::L4_ThreadId_t tid = myself ?
myself->native_thread().l4id :
main_thread_tid;

View File

@ -81,9 +81,8 @@ static inline void thread_switch_to(Genode::Thread *thread_base)
/**
* Unconditionally block the calling thread
*/
static inline void thread_stop_myself()
static inline void thread_stop_myself(Genode::Thread *myself)
{
Genode::Thread *myself = Genode::Thread::myself();
Pistachio::L4_ThreadId_t tid = myself ?
myself->native_thread().l4id :
main_thread_tid;

View File

@ -44,14 +44,12 @@ static inline bool thread_check_stopped_and_restart(Genode::Thread *thread)
}
static inline void thread_stop_myself()
static inline void thread_stop_myself(Genode::Thread *myself)
{
Genode::Thread *myself = Genode::Thread::myself();
unsigned lock_sel = Genode::INITIAL_SEL_LOCK; /* main thread */
if (myself)
lock_sel = Genode::Thread::myself()->native_thread().lock_sel;
lock_sel = myself->native_thread().lock_sel;
seL4_Word sender = ~0U;
seL4_Wait(lock_sel, &sender);

View File

@ -21,11 +21,14 @@ namespace Genode {
class Thread;
class Cancelable_lock;
class Mutex;
}
class Genode::Cancelable_lock
{
friend class Mutex;
private:
class Applicant
@ -68,6 +71,9 @@ class Genode::Cancelable_lock
Applicant _owner;
bool lock_owner(Applicant &myself) { return _owner == myself; }
void lock(Applicant &);
public:
enum State { LOCKED, UNLOCKED };

View File

@ -0,0 +1,46 @@
/*
* \brief Mutex primitives
* \author Alexander Boettcher
* \date 2020-01-24
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__BASE__MUTEX_H_
#define _INCLUDE__BASE__MUTEX_H_
#include <base/lock.h>
#include <util/noncopyable.h>
namespace Genode { class Mutex; }
class Genode::Mutex : Noncopyable
{
private:
Lock _lock { };
public:
explicit Mutex() { }
void acquire();
void release();
class Guard
{
private:
Mutex &_mutex;
public:
explicit Guard(Mutex &mutex) : _mutex(mutex) { _mutex.acquire(); }
~Guard() { _mutex.release(); }
};
};
#endif /* _INCLUDE__BASE__MUTEX_H_ */

View File

@ -17,6 +17,7 @@ SRC_CC += session_state.cc
SRC_CC += elf_binary.cc
SRC_CC += ipc.cc
SRC_CC += lock.cc
SRC_CC += mutex.cc
SRC_CC += log.cc
SRC_CC += raw_output.cc
SRC_CC += rpc_entrypoint.cc

View File

@ -32,7 +32,7 @@
#
#
# Copyright (C) 2016-2019 Genode Labs GmbH
# Copyright (C) 2016-2020 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU Affero General Public License version 3.
@ -270,6 +270,8 @@ _ZN6Genode5ChildC2ERNS_10Region_mapERNS_14Rpc_entrypointERNS_12Child_policyE T
_ZN6Genode5ChildD0Ev T
_ZN6Genode5ChildD1Ev T
_ZN6Genode5ChildD2Ev T
_ZN6Genode5Mutex7acquireEv T
_ZN6Genode5Mutex7releaseEv T
_ZN6Genode5Stack4sizeEm T
_ZN6Genode5Trace6Logger17_evaluate_controlEv T
_ZN6Genode5Trace6Logger3logEPKcm T

View File

@ -63,7 +63,11 @@ void Cancelable_lock::Applicant::wake_up()
void Cancelable_lock::lock()
{
Applicant myself(Thread::myself());
lock(myself);
}
void Cancelable_lock::lock(Applicant &myself)
{
spinlock_lock(&_spinlock_state);
if (cmpxchg(&_state, UNLOCKED, LOCKED)) {
@ -121,7 +125,7 @@ void Cancelable_lock::lock()
* ! for (int i = 0; i < 10; i++)
* ! thread_yield();
*/
thread_stop_myself();
thread_stop_myself(myself.thread_base());
/*
* We expect to be the lock owner when woken up. If this is not

View File

@ -0,0 +1,42 @@
/*
* \brief Mutex primitives
* \author Alexander Boettcher
* \date 2020-01-24
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/mutex.h>
#include <base/log.h>
#include <base/thread.h>
void Genode::Mutex::acquire()
{
Lock::Applicant myself(Thread::myself());
if (_lock.lock_owner(myself))
Genode::error("deadlock ahead, mutex=", this, ", return ip=",
__builtin_return_address(0));
while (1)
try {
_lock.Cancelable_lock::lock(myself);
return;
} catch (Blocking_canceled) { }
}
void Genode::Mutex::release()
{
Lock::Applicant myself(Thread::myself());
if (!_lock.lock_owner(myself)) {
Genode::error("denied non mutex owner the release, mutex=",
this, ", return ip=",
__builtin_return_address(0));
return;
}
_lock.unlock();
}