Library/Application: switch Locking from POSIX to C++14
While not directly related to the thread handling framework, it seems indicated to clean-up this part of the application alongside. For »everyday« locking concerns, an Object Monitor abstraction was built several years ago and together with the thread-wrapper, both at that time based on direct usage of POSIX. This changeset does a mere literal replacement of the POSIX calls with the corresponding C++ wrappers on the lowest level. The resulting code is needlessly indirect, yet at API-level this change is totally a drop-in replacment.
This commit is contained in:
parent
1c4f605e8f
commit
c37871ca78
7 changed files with 510 additions and 293 deletions
|
|
@ -166,6 +166,9 @@ namespace lumiera {
|
|||
wait ()
|
||||
{
|
||||
Lock wait_blocking(this, &SubsystemRunner::allDead);
|
||||
////////////////////////////////////////////////////////////OOO Emergency-Exit richtig implementieren
|
||||
if (isEmergencyExit())
|
||||
usleep(2*1000*1000);
|
||||
return isEmergencyExit();
|
||||
}
|
||||
|
||||
|
|
@ -223,9 +226,11 @@ namespace lumiera {
|
|||
{
|
||||
if (isEmergencyExit())
|
||||
{
|
||||
Lock sync(this);
|
||||
if (!sync.isTimedWait())
|
||||
sync.setTimeout(EMERGENCYTIMEOUT);
|
||||
// Lock sync(this);
|
||||
// if (!sync.isTimedWait())
|
||||
// sync.setTimeout(EMERGENCYTIMEOUT);
|
||||
////////////////////////////////////////////////////////////OOO Emergency-Exit richtig implementieren
|
||||
return true;
|
||||
}
|
||||
|
||||
return isnil (running_); // end wait if no running subsystem left
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
Sync - generic helper for object based locking and synchronisation
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
/** @file sync.cpp
|
||||
** This compilation unit holds the static attribute struct
|
||||
** for initialising pthread's recursive mutex.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "lib/sync.hpp"
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace sync {
|
||||
|
||||
|
||||
namespace { // private pthread attributes
|
||||
|
||||
pthread_mutexattr_t attribute_;
|
||||
|
||||
void
|
||||
initAttribute()
|
||||
{
|
||||
pthread_mutexattr_init (&attribute_);
|
||||
pthread_mutexattr_settype (&attribute_, PTHREAD_MUTEX_RECURSIVE);
|
||||
}
|
||||
|
||||
|
||||
inline pthread_mutexattr_t*
|
||||
recursive_flag()
|
||||
{
|
||||
static pthread_once_t _is_init_sync_mutex_attribute_(PTHREAD_ONCE_INIT);
|
||||
pthread_once (&_is_init_sync_mutex_attribute_, initAttribute);
|
||||
return &attribute_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @internal creating a recursive mutex.
|
||||
* Defined here in a separate compilation unit,
|
||||
* so it can refer to a single mutex attribute flag.
|
||||
*/
|
||||
Wrapped_RecursiveMutex::Wrapped_RecursiveMutex()
|
||||
{
|
||||
pthread_mutex_init (&mutex_, recursive_flag());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}// lib::sync
|
||||
187
src/lib/sync.hpp
187
src/lib/sync.hpp
|
|
@ -14,7 +14,7 @@
|
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
|
@ -46,16 +46,17 @@
|
|||
** - You can't use the Lock#wait and Lock#notify functions unless you pick
|
||||
** a parametrisation including a condition variable.
|
||||
** - The "this" pointer is fed to the ctor of the Lock guard object. Thus
|
||||
** you may use any object's monitor as appropriate, especially in cases
|
||||
** you may use any object's monitor as appropriate, especially in cases
|
||||
** when adding the monitor to a given class may cause size problems.
|
||||
** - For sake of completeness, this implementation provides the ability for
|
||||
** timed waits. But please consider that in most cases there are better
|
||||
** solutions for running an operation with given timeout by utilising the
|
||||
** Lumiera scheduler. Thus use of timed waits is \b discouraged.
|
||||
** - There is a special variant of the Lock guard called ClassLock, which
|
||||
** can be used to lock based on a type, not an instance.
|
||||
** can be used to lock based on a type, not an instance.
|
||||
** - in DEBUG mode, the implementation includes NoBug resource tracking.
|
||||
**
|
||||
** @todo WIP-WIP 10/2023 switch from POSIX to C++14 ///////////////////////////////////////////////////////TICKET #1279 : also clean-up the Object-Monitor implementation
|
||||
** @see mutex.h
|
||||
** @see sync-locking-test.cpp
|
||||
** @see sync-waiting-test.cpp
|
||||
|
|
@ -70,14 +71,10 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "lib/lockerror.h"
|
||||
}
|
||||
|
||||
#include <pthread.h>
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
|
||||
|
|
@ -92,62 +89,47 @@ namespace lib {
|
|||
/* ========== adaptation layer for accessing backend/system level code ============== */
|
||||
|
||||
struct Wrapped_ExclusiveMutex
|
||||
: util::NonCopyable
|
||||
{
|
||||
pthread_mutex_t mutex_;
|
||||
std::mutex mutex_;
|
||||
|
||||
protected:
|
||||
Wrapped_ExclusiveMutex()
|
||||
{
|
||||
pthread_mutex_init (&mutex_, NULL);
|
||||
}
|
||||
~Wrapped_ExclusiveMutex()
|
||||
{
|
||||
if (pthread_mutex_destroy (&mutex_))
|
||||
ERROR (sync, "Failure destroying mutex.");
|
||||
} // shouldn't happen in a correct program
|
||||
Wrapped_ExclusiveMutex() = default;
|
||||
|
||||
void
|
||||
lock()
|
||||
{
|
||||
if (pthread_mutex_lock (&mutex_))
|
||||
throw lumiera::error::Fatal ("Mutex acquire failed");
|
||||
} // shouldn't happen in a correct program
|
||||
mutex_.lock();
|
||||
}
|
||||
|
||||
void
|
||||
unlock()
|
||||
{
|
||||
if (pthread_mutex_unlock (&mutex_))
|
||||
ERROR (sync, "Failure unlocking mutex.");
|
||||
} // shouldn't happen in a correct program
|
||||
mutex_.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct Wrapped_RecursiveMutex
|
||||
: util::NonCopyable
|
||||
{
|
||||
pthread_mutex_t mutex_;
|
||||
std::recursive_mutex mutex_;
|
||||
|
||||
protected:
|
||||
Wrapped_RecursiveMutex();
|
||||
~Wrapped_RecursiveMutex()
|
||||
{
|
||||
if (pthread_mutex_destroy (&mutex_))
|
||||
ERROR (sync, "Failure destroying (rec)mutex.");
|
||||
} // shouldn't happen in a correct program
|
||||
Wrapped_RecursiveMutex() = default;
|
||||
|
||||
void
|
||||
lock()
|
||||
{
|
||||
if (pthread_mutex_lock (&mutex_))
|
||||
throw lumiera::error::Fatal ("(rec)Mutex acquire failed");
|
||||
} // shouldn't happen in a correct program
|
||||
mutex_.lock();
|
||||
}
|
||||
|
||||
void
|
||||
unlock()
|
||||
{
|
||||
if (pthread_mutex_unlock (&mutex_))
|
||||
ERROR (sync, "Failure unlocking (rec)mutex.");
|
||||
} // shouldn't happen in a correct program
|
||||
mutex_.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -155,44 +137,27 @@ namespace lib {
|
|||
struct Wrapped_Condition
|
||||
: MTX
|
||||
{
|
||||
pthread_cond_t cond_;
|
||||
std::condition_variable_any cond_;
|
||||
|
||||
protected:
|
||||
Wrapped_Condition()
|
||||
{
|
||||
pthread_cond_init (&cond_, NULL);
|
||||
}
|
||||
~Wrapped_Condition()
|
||||
{
|
||||
if (pthread_cond_destroy (&cond_))
|
||||
ERROR (sync, "Failure destroying condition variable.");
|
||||
} // shouldn't happen in a correct program
|
||||
Wrapped_Condition() = default;
|
||||
|
||||
bool
|
||||
void
|
||||
wait()
|
||||
{
|
||||
int err;
|
||||
do { err = pthread_cond_wait (&this->cond_, &this->mutex_);
|
||||
} while(err == EINTR);
|
||||
|
||||
if (err) lumiera_lockerror_set (err, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC);
|
||||
return not err;
|
||||
cond_.wait (this->mutex_);
|
||||
}
|
||||
|
||||
|
||||
template<class REPR, class PERI>
|
||||
bool
|
||||
timedwait (const struct timespec* timeout)
|
||||
timedwait (std::chrono::duration<REPR, PERI> const& timeout)
|
||||
{
|
||||
int err;
|
||||
do { err = pthread_cond_timedwait (&this->cond_, &this->mutex_, timeout);
|
||||
} while(err == EINTR);
|
||||
|
||||
if (err) lumiera_lockerror_set (err, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC);
|
||||
return not err;
|
||||
auto ret = cond_.wait_for (this->mutex_, timeout);
|
||||
return (std::cv_status::no_timeout == ret);
|
||||
}
|
||||
|
||||
|
||||
void signal() { pthread_cond_signal (&cond_); }
|
||||
void broadcast() { pthread_cond_broadcast (&cond_); }
|
||||
void signal() { cond_.notify_one(); }
|
||||
void broadcast() { cond_.notify_all(); }
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -208,10 +173,7 @@ namespace lib {
|
|||
: protected MTX
|
||||
{
|
||||
protected:
|
||||
~Mutex () { }
|
||||
Mutex () { }
|
||||
Mutex (const Mutex&); ///< noncopyable...
|
||||
const Mutex& operator= (const Mutex&);
|
||||
Mutex () = default;
|
||||
|
||||
public:
|
||||
void
|
||||
|
|
@ -229,43 +191,6 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* helper for specifying an optional timeout for an timed wait.
|
||||
* Wrapping a timespec-struct, it allows for easy initialisation
|
||||
* by a given relative offset.
|
||||
* @todo integrate with std::chrono //////////////////////////TICKET #1055
|
||||
*/
|
||||
struct Timeout
|
||||
: timespec
|
||||
{
|
||||
Timeout() { reset(); }
|
||||
|
||||
void reset() { tv_sec=tv_nsec=0; }
|
||||
|
||||
/** initialise to NOW() + offset (in milliseconds) */
|
||||
Timeout&
|
||||
setOffset (ulong offs)
|
||||
{
|
||||
if (offs)
|
||||
{
|
||||
clock_gettime(CLOCK_REALTIME, this); //////////////////////////TICKET #886
|
||||
tv_sec += offs / 1000;
|
||||
tv_nsec += 1000000 * (offs % 1000);
|
||||
if (tv_nsec >= 1000000000)
|
||||
{
|
||||
tv_sec += tv_nsec / 1000000000;
|
||||
tv_nsec %= 1000000000;
|
||||
} }
|
||||
else
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() { return 0 != tv_sec; } // allows if (timeout_)....
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class MTX>
|
||||
class Condition
|
||||
: public Mutex<Wrapped_Condition<MTX>>
|
||||
|
|
@ -283,20 +208,19 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
/** @return `false` in case of timeout */
|
||||
template<class BF>
|
||||
bool
|
||||
wait (BF& predicate, Timeout& waitEndTime)
|
||||
wait (BF& predicate, uint timeout_ms =0)
|
||||
{
|
||||
bool ok = true;
|
||||
while (ok and !predicate())
|
||||
if (waitEndTime)
|
||||
ok = Cond::timedwait (&waitEndTime);
|
||||
while (not predicate())
|
||||
if (timeout_ms != 0)
|
||||
{
|
||||
if (not Cond::timedwait (std::chrono::milliseconds (timeout_ms)))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
ok = Cond::wait ();
|
||||
|
||||
if (not ok and lumiera_error_expect(LUMIERA_ERROR_LOCK_TIMEOUT)) return false;
|
||||
lumiera::throwOnError(); // any other error throws
|
||||
|
||||
Cond::wait ();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
@ -340,15 +264,13 @@ namespace lib {
|
|||
class Monitor
|
||||
: IMPL
|
||||
{
|
||||
Timeout timeout_;
|
||||
|
||||
public:
|
||||
Monitor() {}
|
||||
~Monitor() {}
|
||||
|
||||
/** allow copy, without interfering with the identity of IMPL */
|
||||
Monitor (Monitor const& ref) : IMPL(), timeout_(ref.timeout_) { }
|
||||
const Monitor& operator= (Monitor const& ref) { timeout_ = ref.timeout_; return *this; }
|
||||
Monitor (Monitor const& ref) : IMPL() { }
|
||||
const Monitor& operator= (Monitor const& ref) { /*prevent assignment to base*/ return *this; }
|
||||
|
||||
|
||||
void acquireLock() { IMPL::acquire(); }
|
||||
|
|
@ -357,22 +279,19 @@ namespace lib {
|
|||
void signal(bool a){ IMPL::signal(a); }
|
||||
|
||||
bool
|
||||
wait (Flag flag, ulong timedwait=0)
|
||||
wait (Flag flag, ulong timedwait_ms=0)
|
||||
{
|
||||
BoolFlagPredicate checkFlag(flag);
|
||||
return IMPL::wait(checkFlag, timeout_.setOffset(timedwait));
|
||||
return IMPL::wait(checkFlag, timedwait_ms);
|
||||
}
|
||||
|
||||
template<class X>
|
||||
bool
|
||||
wait (X& instance, bool (X::*method)(void), ulong timedwait=0) ///////////////////////TICKET #1051 : add support for lambdas
|
||||
wait (X& instance, bool (X::*method)(void), ulong timedwait_ms=0) /////////////////////TICKET #1051 : add support for lambdas
|
||||
{
|
||||
BoolMethodPredicate<X> invokeMethod(instance, method); ///////////////////////TICKET #1057 : const correctness, allow use of const member functions
|
||||
return IMPL::wait(invokeMethod, timeout_.setOffset(timedwait));
|
||||
return IMPL::wait(invokeMethod, timedwait_ms);
|
||||
}
|
||||
|
||||
void setTimeout(ulong relative) {timeout_.setOffset(relative);}
|
||||
bool isTimedWait() {return bool{timeout_};}
|
||||
};
|
||||
|
||||
typedef Mutex<Wrapped_ExclusiveMutex> NonrecursiveLock_NoWait;
|
||||
|
|
@ -455,8 +374,6 @@ namespace lib {
|
|||
|
||||
void notify() { mon_.signal(false);}
|
||||
void notifyAll() { mon_.signal(true); }
|
||||
void setTimeout(ulong time) { mon_.setTimeout(time); }
|
||||
bool isTimedWait() { return mon_.isTimedWait(); }
|
||||
|
||||
template<typename C>
|
||||
bool
|
||||
|
|
@ -467,7 +384,7 @@ namespace lib {
|
|||
|
||||
template<typename X>
|
||||
bool
|
||||
wait (X& instance, bool (X::*predicate)(void), ulong timeout=0) //////////////////////TICKET #1051 : enable use of lambdas
|
||||
wait (X& instance, bool (X::*predicate)(void), ulong timeout=0) //////////////////////TICKET #1051 : enable use of lambdas
|
||||
{
|
||||
return mon_.wait(instance,predicate,timeout);
|
||||
}
|
||||
|
|
@ -480,8 +397,8 @@ namespace lib {
|
|||
*/
|
||||
template<class X>
|
||||
Lock(X* it, bool (X::*method)(void))
|
||||
: mon_(getMonitor(it))
|
||||
{
|
||||
: mon_(getMonitor(it))
|
||||
{
|
||||
mon_.acquireLock();
|
||||
mon_.wait(*it,method);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ namespace test{
|
|||
inline double
|
||||
benchmarkTime (FUN const& invokeTestLoop, const size_t repeatCnt = DEFAULT_RUNS)
|
||||
{
|
||||
using std::chrono::system_clock;
|
||||
using std::chrono::system_clock;; /////////////////////////////////////////TICKET #886
|
||||
using Dur = std::chrono::duration<double, CLOCK_SCALE>;
|
||||
|
||||
auto start = system_clock::now();
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "lib/test/test-helper.hpp"
|
||||
|
||||
#include "lib/diagnostic-context.hpp"
|
||||
#include "lib/iter-explorer.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
|
@ -171,10 +172,14 @@ namespace test{
|
|||
}
|
||||
};
|
||||
|
||||
std::array<TestThread, NUM_THREADS> testcase;
|
||||
|
||||
for (uint i=0; i < NUM_THREADS; ++i)
|
||||
verifyResult (testcase[i].join());
|
||||
std::array<TestThread, NUM_THREADS> testcases;
|
||||
|
||||
auto results = lib::explore(testcases)
|
||||
.transform([](TestThread& t){ return t.join(); })
|
||||
.effuse();
|
||||
|
||||
for (auto& res : results)
|
||||
verifyResult (res);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,27 +27,25 @@
|
|||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/error.hpp"
|
||||
|
||||
#include "lib/sync.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
using std::cout;
|
||||
using test::Test;
|
||||
using std::chrono::system_clock;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
namespace { // private test classes and data...
|
||||
namespace { // test parameters...
|
||||
|
||||
const uint WAIT_mSec = 200; ///< milliseconds to wait before timeout
|
||||
const uint WAIT_mSec = 20; ///< milliseconds to wait before timeout
|
||||
|
||||
using CLOCK_SCALE = std::milli; // Results are in ms
|
||||
using Dur = std::chrono::duration<double, CLOCK_SCALE>;
|
||||
|
||||
} // (End) test classes and data....
|
||||
|
||||
|
||||
|
||||
|
||||
}//(End) parameters
|
||||
|
||||
|
||||
|
||||
|
|
@ -55,25 +53,16 @@ namespace test{
|
|||
|
||||
|
||||
/****************************************************************************//**
|
||||
* @test timeout feature on condition wait as provided by pthread and accessible
|
||||
* via the object monitor based locking/waiting mechanism. Without creating
|
||||
* multiple threads, we engage into a blocking wait, which aborts due to
|
||||
* setting a timeout. Our waiting facility is written such as to invoke
|
||||
* the condition prior to entering wait state (and consecutively whenever
|
||||
* awakened). This test switches into wait-with-timeout mode right from
|
||||
* within this condition check and thus works even while there is no
|
||||
* other thread and thus an unconditional wait would stall forever.
|
||||
*
|
||||
* @note it is discouraged to use the timed wait feature for "timing";
|
||||
* when possible you should prefer relying on the Lumiera scheduler
|
||||
*
|
||||
* @test timeout feature on condition wait as provided by the underlying implementation
|
||||
* and accessible via the object monitor based locking/waiting mechanism. Without
|
||||
* creating multiple threads, we engage into a blocking wait, which aborts due to
|
||||
* setting a timeout.
|
||||
* @see SyncWaiting_test
|
||||
* @see sync::Timeout
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncTimedwait_test
|
||||
: public Test,
|
||||
Sync<RecursiveLock_Waitable>
|
||||
Sync<NonrecursiveLock_Waitable>
|
||||
{
|
||||
|
||||
friend class Lock; // allows inheriting privately from Sync
|
||||
|
|
@ -82,60 +71,19 @@ namespace test{
|
|||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
checkTimeoutStruct();
|
||||
Lock lock(this);
|
||||
|
||||
Lock block(this, &SyncTimedwait_test::neverHappens);
|
||||
auto start = system_clock::now();
|
||||
|
||||
bool salvation{false};
|
||||
bool fulfilled = lock.wait (salvation, WAIT_mSec);
|
||||
|
||||
cout << "back from LaLaLand, alive and thriving!\n";
|
||||
CHECK (block.isTimedWait());
|
||||
CHECK (not fulfilled); // condition not fulfilled, but timeout
|
||||
|
||||
Dur duration = system_clock::now () - start;
|
||||
CHECK (WAIT_mSec <= duration.count());
|
||||
CHECK (duration.count() < 2*WAIT_mSec);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
neverHappens() ///< the "condition test" used for waiting....
|
||||
{
|
||||
Lock currentLock(this); // get the Lock recursively
|
||||
if (!currentLock.isTimedWait()) // right from within the condition check:
|
||||
currentLock.setTimeout(WAIT_mSec); // switch waiting mode to timed wait and set timeout
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
checkTimeoutStruct()
|
||||
{
|
||||
sync::Timeout tout;
|
||||
|
||||
CHECK (!tout);
|
||||
CHECK (0 == tout.tv_sec);
|
||||
CHECK (0 == tout.tv_nsec);
|
||||
|
||||
tout.setOffset (0);
|
||||
CHECK (!tout);
|
||||
CHECK (0 == tout.tv_sec);
|
||||
CHECK (0 == tout.tv_nsec);
|
||||
|
||||
timespec ref;
|
||||
clock_gettime(CLOCK_REALTIME, &ref);
|
||||
tout.setOffset (1);
|
||||
CHECK (tout);
|
||||
CHECK (0 < tout.tv_sec);
|
||||
CHECK (ref.tv_sec <= tout.tv_sec);
|
||||
CHECK (ref.tv_nsec <= 1000000 + tout.tv_nsec || ref.tv_nsec > 1000000000-100000);
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &ref);
|
||||
tout.setOffset (1000);
|
||||
CHECK (tout);
|
||||
if (ref.tv_nsec!=0) // should have gotten an overflow to the seconds part
|
||||
{
|
||||
CHECK (ref.tv_sec <= 2 + tout.tv_sec );
|
||||
CHECK ((ref.tv_nsec + 1000000 * 999) % 1000000000
|
||||
<= tout.tv_nsec);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -66343,9 +66343,9 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1696538243989" ID="ID_963020368" MODIFIED="1696538317493" TEXT="Objekt-Monitor (Wrapper)">
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1696538243989" ID="ID_963020368" MODIFIED="1697212009165" TEXT="Objekt-Monitor (Wrapper)">
|
||||
<linktarget COLOR="#e81e74" DESTINATION="ID_963020368" ENDARROW="Default" ENDINCLINATION="-1386;137;" ID="Arrow_ID_750207085" SOURCE="ID_1209350338" STARTARROW="None" STARTINCLINATION="-379;-555;"/>
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1696538320106" ID="ID_1371656828" MODIFIED="1696538327925" TEXT="Wrapper bestand schon bisher">
|
||||
<node CREATED="1696538329153" ID="ID_23495962" MODIFIED="1696538341970" TEXT="war zunächst aufgebaut auf Christian's NoBug-Adapter"/>
|
||||
<node CREATED="1696538342567" ID="ID_1615743688" MODIFIED="1696538354117" TEXT="dann umgestellt auf direkte Verwendung von POSIX"/>
|
||||
|
|
@ -66360,10 +66360,334 @@
|
|||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1696538455872" ID="ID_795722171" MODIFIED="1696538460260" TEXT="C++14-Wrapper"/>
|
||||
<node CREATED="1696538455872" ID="ID_795722171" MODIFIED="1696538460260" TEXT="C++14-Wrapper">
|
||||
<node CREATED="1697151693240" ID="ID_254039362" MODIFIED="1697151699355" TEXT="relevant">
|
||||
<node CREATED="1697151748256" ID="ID_1985516619" MODIFIED="1697151819444" TEXT="mutex">
|
||||
<linktarget COLOR="#a9b4c1" DESTINATION="ID_1985516619" ENDARROW="Default" ENDINCLINATION="133;0;" ID="Arrow_ID_591394395" SOURCE="ID_1259850998" STARTARROW="None" STARTINCLINATION="115;0;"/>
|
||||
</node>
|
||||
<node CREATED="1697151750928" ID="ID_1683342529" MODIFIED="1697151811512" TEXT="recursive_mutex">
|
||||
<linktarget COLOR="#a9b4c1" DESTINATION="ID_1683342529" ENDARROW="Default" ENDINCLINATION="51;0;" ID="Arrow_ID_1845277195" SOURCE="ID_1259850998" STARTARROW="None" STARTINCLINATION="51;0;"/>
|
||||
</node>
|
||||
<node CREATED="1697151767230" ID="ID_1435647108" MODIFIED="1697151769601" TEXT="condition_var">
|
||||
<node CREATED="1697152084523" HGAP="95" ID="ID_605165052" MODIFIED="1697152104685" TEXT="zwingend an unique_lock + ein mutex gebunden" VSHIFT="6"/>
|
||||
</node>
|
||||
<node CREATED="1697151774377" ID="ID_1259850998" MODIFIED="1697151819444" TEXT="unique_lock">
|
||||
<arrowlink DESTINATION="ID_1683342529" ENDARROW="Default" ENDINCLINATION="51;0;" ID="Arrow_ID_1845277195" STARTARROW="None" STARTINCLINATION="51;0;"/>
|
||||
<arrowlink DESTINATION="ID_1985516619" ENDARROW="Default" ENDINCLINATION="133;0;" ID="Arrow_ID_591394395" STARTARROW="None" STARTINCLINATION="115;0;"/>
|
||||
<node CREATED="1697152113215" HGAP="93" ID="ID_950739856" MODIFIED="1697152434282" TEXT="ist ein (flexiblerer) lock_guard..." VSHIFT="5">
|
||||
<arrowlink DESTINATION="ID_944756854" ENDARROW="Default" ENDINCLINATION="171;0;" ID="Arrow_ID_120128551" STARTARROW="None" STARTINCLINATION="-9;17;"/>
|
||||
<node CREATED="1697152145940" ID="ID_276145575" MODIFIED="1697152151934" TEXT="normalerweise im ctor gelockt"/>
|
||||
<node CREATED="1697152152402" ID="ID_1887916464" MODIFIED="1697152158165" TEXT="kann aber ungelockt erstellt werden"/>
|
||||
<node CREATED="1697152161546" ID="ID_802566279" MODIFIED="1697152196264" TEXT="kann trylock machen, auch im ctor"/>
|
||||
<node CREATED="1697152207265" ID="ID_1526785209" MODIFIED="1697152220429" TEXT="kann explizit unlocked werden, und dann später wieder gelockt"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1697151783060" ID="ID_944756854" MODIFIED="1697152438468" TEXT="lock_guard">
|
||||
<linktarget COLOR="#a9b4c1" DESTINATION="ID_944756854" ENDARROW="Default" ENDINCLINATION="171;0;" ID="Arrow_ID_120128551" SOURCE="ID_950739856" STARTARROW="None" STARTINCLINATION="-9;17;"/>
|
||||
<linktarget COLOR="#a9b4c1" DESTINATION="ID_944756854" ENDARROW="Default" ENDINCLINATION="300;0;" ID="Arrow_ID_253725530" SOURCE="ID_6034412" STARTARROW="None" STARTINCLINATION="300;0;"/>
|
||||
</node>
|
||||
<node CREATED="1697151896093" HGAP="22" ID="ID_1465275644" MODIFIED="1697151914560" TEXT="andere" VSHIFT="17">
|
||||
<node CREATED="1697151926997" ID="ID_801192171" MODIFIED="1697151931804" TEXT="shared_mutex">
|
||||
<node CREATED="1697152263803" ID="ID_1586207113" MODIFIED="1697152273528" TEXT="hat zwei lock-Modi: exclusive und shared"/>
|
||||
<node CREATED="1697152275370" ID="ID_1656757256" MODIFIED="1697152297738" TEXT="shared-lock kann nur erlangt werden, wenn es kein exclusive lock gibt"/>
|
||||
<node CREATED="1697152298207" ID="ID_1153249756" MODIFIED="1697152311099" TEXT="use-case: shared reading, exclusive writing"/>
|
||||
</node>
|
||||
<node CREATED="1697151937271" ID="ID_1732379198" MODIFIED="1697151940029" TEXT="timed_mutex">
|
||||
<node CREATED="1697152313437" ID="ID_1264555835" MODIFIED="1697152324127" TEXT="bietet zusätzlich timeouts"/>
|
||||
</node>
|
||||
<node CREATED="1697152072365" ID="ID_1696650993" MODIFIED="1697152075403" TEXT="scoped_lock">
|
||||
<node CREATED="1697152328107" ID="ID_6034412" MODIFIED="1697152438468" TEXT="ein spezieller lock-guard">
|
||||
<arrowlink DESTINATION="ID_944756854" ENDARROW="Default" ENDINCLINATION="300;0;" ID="Arrow_ID_253725530" STARTARROW="None" STARTINCLINATION="300;0;"/>
|
||||
</node>
|
||||
<node CREATED="1697152345096" ID="ID_234957929" MODIFIED="1697152357178" TEXT="kann aber mehrere Locks atomar erlangen"/>
|
||||
<node CREATED="1697152358271" ID="ID_35680042" MODIFIED="1697152371417" TEXT="und dabei eine Deadlock-Vermeidungsstrategie anwenden">
|
||||
<node CREATED="1697152546245" ID="ID_1179913701" MODIFIED="1697152553352" TEXT="ruft eigentlich den algo std::lock auf"/>
|
||||
<node CREATED="1697152558001" ID="ID_721786307" MODIFIED="1697152570462" TEXT="dieser prüft intelligent den locking-Zustand"/>
|
||||
<node CREATED="1697152611045" ID="ID_16304346" MODIFIED="1697152621727" TEXT="und entsperrt alle Locks im Fehlerfall"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1697152640957" ID="ID_1468664263" MODIFIED="1697152644272" TEXT="Einschätzung">
|
||||
<node CREATED="1697152645136" ID="ID_1133656270" MODIFIED="1697152651011" TEXT="völlig unproblematisch"/>
|
||||
<node CREATED="1697152651631" ID="ID_683393307" MODIFIED="1697152662130" TEXT="hier wird die POSIX-Semantik 1:1 durchgereicht"/>
|
||||
<node CREATED="1697152671557" ID="ID_591975919" MODIFIED="1697152945571" TEXT="Twist">
|
||||
<node CREATED="1697152946704" ID="ID_828959106" MODIFIED="1697152946704" TEXT="condition_var will ein bereits erlangtes unique_lock">
|
||||
<node CREATED="1697153345130" ID="ID_1006609083" MODIFIED="1697153357503" TEXT="bei POSIX ist das einfach eine Precondition"/>
|
||||
<node CREATED="1697153358136" ID="ID_833851692" MODIFIED="1697153372234" TEXT="unsere bisherige Impl stellt das auch nur kontextuell sicher"/>
|
||||
<node CREATED="1697153375126" ID="ID_84530625" MODIFIED="1697153390904" TEXT="dagegen durch das unique_lock wird das materialisiert"/>
|
||||
<node COLOR="#5b280f" CREATED="1697153721983" ID="ID_1120978458" MODIFIED="1697211295181" TEXT="es gibt aber den »adopt lock«-Mechanismus">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1697211296788" ID="ID_516008091" MODIFIED="1697211306007" TEXT="nein: das hilft nicht"/>
|
||||
<node CREATED="1697211306619" ID="ID_20444784" MODIFIED="1697211430279" TEXT="unique_lock kann selber nicht rekursiv assoziiert werden">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Es kann zwar ein rekursives Mutex verwenden, aber jede Ebene von Locking muß dann als eigenes unique_lock-Token repräsentiert werden.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1697211431074" ID="ID_921453654" MODIFIED="1697211474012" TEXT="⟹ unique_lock kann daher nicht gemeinsam mit mehreren Threads arbeiten"/>
|
||||
</node>
|
||||
<node CREATED="1697211475596" ID="ID_1432080537" MODIFIED="1697211499297" TEXT="alternativ: die allgemeine condition_var_any verwenden">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1697211738347" ID="ID_1470675670" MODIFIED="1697211757821">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
diese setzt nur ein <i>BasicLockable</i> voraus
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1697211758557" ID="ID_1308356052" MODIFIED="1697211769888">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Mutex selber ist bereits <i>BasicLockable</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1697153008914" ID="ID_939982348" MODIFIED="1697153033086" TEXT="keine Error-Rückgabecodes mehr. Stattdessen throw std::system_error"/>
|
||||
<node COLOR="#435e98" CREATED="1697208294038" ID="ID_724408266" MODIFIED="1697208642249">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
die C++ - Wrapper sind <b>non-copyable</b>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1697208611826" ID="ID_1407647722" MODIFIED="1697226243538" TEXT="was man synchronisiert, hat typischerweise Referenz-Semantik">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697208590782" ID="ID_1383871199" MODIFIED="1697226552108" TEXT="aber: class Monitor erlaubt Brechen dieser Bedingung">
|
||||
<linktarget COLOR="#c36885" DESTINATION="ID_1383871199" ENDARROW="Default" ENDINCLINATION="-768;38;" ID="Arrow_ID_1367033811" SOURCE="ID_1311077272" STARTARROW="None" STARTINCLINATION="-220;-13;"/>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1697226328908" ID="ID_1205179392" MODIFIED="1697226343430" TEXT="er hat einen Copy-Konstruktor und Assignment"/>
|
||||
<node CREATED="1697226345928" ID="ID_244225602" MODIFIED="1697226360003" TEXT="diese erzeugen jeweils einfach eine neue Implementierung"/>
|
||||
<node CREATED="1697226360536" ID="ID_1800556938" MODIFIED="1697226426877" TEXT="nicht die feine englische Art">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...gegebenfalls kann es dadurch passieren, daß man ein grade gesperrtes Mutex einfach „fahren läßt“ — mit unabsehbaren Folgen
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1697153970230" ID="ID_1218189224" MODIFIED="1697153991860" TEXT="Herangehensweise?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1697153994987" ID="ID_678749516" MODIFIED="1697153998214" TEXT="Situation">
|
||||
<node CREATED="1697153999066" ID="ID_1857771089" MODIFIED="1697154022235" TEXT="ich habe ein relativ kohäsives Stück Code mit einem schmalen API nach außen"/>
|
||||
<node CREATED="1697154049196" ID="ID_1905385027" MODIFIED="1697154074365" TEXT="der Code wurde bereits mehrfach umgeschrieben und „aufgebohrt“ für Timeouts"/>
|
||||
<node CREATED="1697154093695" ID="ID_680368704" MODIFIED="1697154108078" TEXT="der Code ist sehr gut gegliedert; es gibt einen low-Level-Layer"/>
|
||||
<node CREATED="1697154131456" ID="ID_1645036069" MODIFIED="1697154252320" TEXT="der Code orientiert sich an POSIX ⟹ er ist Konventions-orientiert">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Genauer gesagt, der Code versucht, die Konventions-basierte Herangehensweise aus POSIX / C in einen Token-orientierten Ansatz für C++ zu übersetzen. Das bedeutet aber, auch das Monitor-API ist noch Konventions-basiert
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1697154260247" ID="ID_685378325" MODIFIED="1697154263707" TEXT="Möglichkeiten">
|
||||
<node CREATED="1697154266598" ID="ID_1270232575" MODIFIED="1697154286783" TEXT="transparenter Switch">
|
||||
<node CREATED="1697154290739" ID="ID_760570676" MODIFIED="1697154353526" TEXT="in den low-Level Wrapper wird einfach der POSIX-Code durch C++-Wrapper ersetzt"/>
|
||||
<node CREATED="1697154355514" ID="ID_165826479" MODIFIED="1697154371436" TEXT="nach Möglichkeit sollte bereits das API der low-Level Wrapper unverändert bleiben"/>
|
||||
<node CREATED="1697154383255" ID="ID_1951392587" MODIFIED="1697211542815" TEXT="kein unique_lock verwenden, sondern die unspezifische condition_var_any"/>
|
||||
<node CREATED="1697154499291" ID="ID_1226519053" MODIFIED="1697154527047" TEXT="in ConditionVar wird versucht, die Rückgabe-Werte und den Timeout analog nachzubilden"/>
|
||||
<node CREATED="1697154535460" ID="ID_353347483" MODIFIED="1697154557475" TEXT="Resultat ⟹ der Lock-Guard darüber verhält sich 1:1 identisch"/>
|
||||
<node CREATED="1697154566590" ID="ID_1192462258" MODIFIED="1697154587375" TEXT="anschließend könnte man noch λ-support einbauen"/>
|
||||
</node>
|
||||
<node CREATED="1697154592299" ID="ID_1809860327" MODIFIED="1697154630897" TEXT="strukturelle Verbesserung">
|
||||
<node CREATED="1697154638261" ID="ID_1493391144" MODIFIED="1697154656991" TEXT="nur die Monitor-Klasse bleibt äquivalent erhalten"/>
|
||||
<node CREATED="1697154664352" ID="ID_453928483" MODIFIED="1697154681027" TEXT="die zwei Ebenen Wrapper darunter werden durch Typedefs auf C++-Wrapper ersetzt"/>
|
||||
<node CREATED="1697155716573" ID="ID_1758887952" MODIFIED="1697155738480" TEXT="oder genauer: in Policy-Klassen zusammengefaßt"/>
|
||||
<node CREATED="1697155742090" ID="ID_311928425" MODIFIED="1697211635523" TEXT="auch hier kann kein unique_lock verwendet werden"/>
|
||||
</node>
|
||||
<node CREATED="1697155790803" ID="ID_1122062252" MODIFIED="1697155795238" TEXT="Monitor-Refactoring">
|
||||
<node CREATED="1697155797458" ID="ID_1050112760" MODIFIED="1697155862237" TEXT="Monitor löst sich vom Lehrbuch-Schema"/>
|
||||
<node CREATED="1697155866508" ID="ID_607921464" MODIFIED="1697155887706" TEXT="stattdessen wird der Token-Ansatz bereits im Monitor verankert"/>
|
||||
<node CREATED="1697155965676" ID="ID_736666954" MODIFIED="1697155999305" TEXT="das heißt: man bekommt vom Monitor einen Guard-mit-Prozeß-API"/>
|
||||
<node CREATED="1697156007104" ID="ID_1780881398" MODIFIED="1697156030745" TEXT="der Lock-Guard ist dann nur noch ein Konstruktor-Wrapper darüber"/>
|
||||
<node CREATED="1697156077365" ID="ID_1535123758" MODIFIED="1697156103949" TEXT="Zusätzlich Ergänzung um eine TryLock-Variante (als optional<Guard>)"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1697156128590" ID="ID_731556592" MODIFIED="1697156131743" TEXT="Abwägung">
|
||||
<node CREATED="1697156137517" ID="ID_817522107" MODIFIED="1697156191121" TEXT="der »transparente Switch« ist rasch und ohne Risiko umsetzbar">
|
||||
<node CREATED="1697156194805" ID="ID_520707349" MODIFIED="1697156316185" TEXT="Ziel ist, den status-quo zu erhalten"/>
|
||||
<node CREATED="1697156375349" ID="ID_854423215" MODIFIED="1697156397382" TEXT="erweiterte Analyse + Tests sind nicht notwendig"/>
|
||||
<node CREATED="1697156317135" ID="ID_935065583" MODIFIED="1697156350020" TEXT="das wird erkauft durch eine exzessiv indirekte Code-Struktur"/>
|
||||
<node CREATED="1697156412448" ID="ID_659475954" MODIFIED="1697156527005" TEXT="das Ergebnis ist offen für „wilden“ Direkt-Gebrauch">
|
||||
<node CREATED="1697156532544" ID="ID_273574678" MODIFIED="1697156637966" TEXT="denn ursprünglich war das sogar die Intention">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Ganz am Anfang hatte ich die Makros aus NoBug, deren Gebrauch im C++-Code schwierig ist. Daher dachte ich, ich packe die jeweils in einen Wrapper. Der Monitor ist dann nur ein Zustatz-Feature
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1697156544607" ID="ID_1900999328" MODIFIED="1697156780356" TEXT="solcher Gebrauch ist dann aber in der Praxis bisher nie notwendig gewesen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...aber nachdem ich den Monitor entwickelt hatte, war mir klar, daß ich an den Basis-Elementen gar kein Interesse mehr habe, weil der Monitor ein Design-Pattern ist und damit ordnend auf den Code wirkt. Im Lauf der Jahre hat sich dann gezeigt, daß ich überhaupt nichts anderes brauche, als nur das Lock-Guard front-End (+Atomics für alle nicht-trivialen Fälle).
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1697156786102" ID="ID_1309147992" MODIFIED="1697156842803" TEXT="die C++ Wrapper allerdings machen „wilden“ Gebrauch unattraktiv und unwahrscheinlich"/>
|
||||
</node>
|
||||
<node CREATED="1697156922980" ID="ID_1251565315" MODIFIED="1697156937622" TEXT="das Ergebnis stellt aber keine strukturelle Verschlechterung dar"/>
|
||||
<node CREATED="1697156941858" ID="ID_933669206" MODIFIED="1697156948146" TEXT="...und könnte daher ein Zwischenschritt sein"/>
|
||||
</node>
|
||||
<node CREATED="1697156852566" ID="ID_1056176020" MODIFIED="1697156881277" TEXT="die »strukturelle Verbesserung« stellt längerfristig das Minimum an Code-Qualität dar">
|
||||
<node CREATED="1697156959663" ID="ID_1687873684" MODIFIED="1697156975190" TEXT="da klar ist, daß nur der Monitor verwendet werden soll..."/>
|
||||
<node CREATED="1697156976195" ID="ID_681854744" MODIFIED="1697156985008" TEXT="deshalb kann auch alles Andere zurückgebaut werden"/>
|
||||
<node CREATED="1697157070044" ID="ID_1501146985" MODIFIED="1697157223059" TEXT="die unique_lock-Instanz ist zugleich Verbesserung und zusätzlicher Ballast">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Verbesserung, weil sie die Implementierung einfach und klar macht, und mehr dem C++-Stil entspricht. Aber sie ist auch ein stets vorhandener zusätzlicher Storage-Ballast, der eigentlich nicht notwendig wäre
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1697157236623" ID="ID_1796494876" MODIFIED="1697157269817" TEXT="die Implementierung im Monitor müßte schrittweise aufgebaut/konsolidiert werden"/>
|
||||
<node CREATED="1697157270541" ID="ID_1316555147" MODIFIED="1697157286077" TEXT="dazu sollte vorher für ausreichende Test-Coverage gesorgt sein"/>
|
||||
</node>
|
||||
<node CREATED="1697157349262" ID="ID_871141135" MODIFIED="1697157367972" TEXT="das »Monitor-Refactoring« entspricht meinem eigenen Qualitäts-Anspruch">
|
||||
<node CREATED="1697157372112" ID="ID_247839273" MODIFIED="1697157389225" TEXT="das bedeutet: längerfristig sollte das das Ziel sein"/>
|
||||
<node CREATED="1697157390189" ID="ID_1494264417" MODIFIED="1697157417062" TEXT="Nachteil: es ist ein Privat-Konzept und theoretisch nicht untersucht"/>
|
||||
<node CREATED="1697157432440" ID="ID_30899784" MODIFIED="1697157455160" TEXT="es würde das Ballast-Problem der mittleren Variante beheben"/>
|
||||
<node CREATED="1697157469806" ID="ID_1885906639" MODIFIED="1697157489020" TEXT="das Ausarbeiten eines sinnvollen Prozeß-API braucht Zeit und Experimente"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1697157515644" ID="ID_91141549" MODIFIED="1697157775531" TEXT="Fazit">
|
||||
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1697157519076" ID="ID_319583115" MODIFIED="1697157532140" TEXT="es handelt sich nicht um Alternativen, sondern einen Stufen-Plan"/>
|
||||
<node CREATED="1697157566462" ID="ID_652673137" MODIFIED="1697157722839" TEXT="zur Vermeidung von Entwicklungs-Risiken sollten alle Schritte durchlaufen werden">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
zwar traue ich mir zu, die mittlere Stufe der »strukturellen Verbesserung« direkt zu implementieren, aber in dieser Hinsicht überschätze ich häufig die reale Komplexität und die Projekt-Risiken. Schon allein um aufwendige Regressionen zu vermeiden sollte stets auf gute Test-Coverage geachtet werden, was nurch durch schritweises Vorgehen möglich ist
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1697157736647" ID="ID_1786481655" MODIFIED="1697157768097" TEXT="da ich unter Zeitdruck stehe, ist es denkbar, zunächst nur die erste Stufe umzusetzen"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696538492114" ID="ID_1214186481" MODIFIED="1696538497123" TEXT="Umarbeiten">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1697208267170" ID="ID_1417792791" MODIFIED="1697208273283" TEXT="Stufe-1">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1697211709960" ID="ID_1334084058" MODIFIED="1697211722231" TEXT="mutex, shared_mutex und condition_var_any einführen"/>
|
||||
<node CREATED="1697212577546" ID="ID_701908821" MODIFIED="1697212604297" TEXT="elementare Wait-Logik in wrapped_condition nachbauen">
|
||||
<node CREATED="1697212819465" ID="ID_1989441029" MODIFIED="1697212843810" TEXT="die Timeout-Flag wird hintenrum durch lumiera_lockerror hochgegeben">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1697212844861" ID="ID_1361936196" MODIFIED="1697212864910" TEXT="aber die nächste Ebene in Condition::wait() setzt das bereits in einen bool um"/>
|
||||
<node CREATED="1697212907981" ID="ID_1359746473" MODIFIED="1697212958572" TEXT="ebenso wird die Timeout-Zeit im Monitor in einen ulong(millis) umgesetzt"/>
|
||||
</node>
|
||||
<node CREATED="1697212978323" ID="ID_1878302471" MODIFIED="1697213004811" TEXT="also sollte den »timeout« sofort modernisieren">
|
||||
<node CREATED="1697226451516" ID="ID_1234718286" MODIFIED="1697226471469" TEXT="erst mal auf Millisekunden-Parameter, der 0 sein kann"/>
|
||||
<node CREATED="1697226473433" ID="ID_1085819281" MODIFIED="1697226495115" TEXT="brauche keine Klasse Timeout mehr, die einen Timeout fest speichert"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697226910607" ID="ID_1996109798" MODIFIED="1697226924118" TEXT="bisher gab es das Konzept isTimedWait()">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1697227170044" ID="ID_1914088992" MODIFIED="1697227366621" TEXT="der Subsystem-Runner verwendet das (einzige Verwendung)"/>
|
||||
<node CREATED="1697227176603" ID="ID_1461108141" MODIFIED="1697227196532" TEXT="er kann im Emergency-Fall nachträglich ein Timeout setzen"/>
|
||||
<node CREATED="1697227368355" ID="ID_8720299" MODIFIED="1697227492130" TEXT="vermeidet Deadlock mit nur einem Thread">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...zumindest war das wohl die Motivation, wenn ich die Kommentare im Test hinzunehme.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1697227493003" ID="ID_1899195103" MODIFIED="1697227520087" TEXT="erscheint mir halbgar">
|
||||
<icon BUILTIN="smiley-angry"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697229575552" ID="ID_756306663" MODIFIED="1697232150319" TEXT="saubere Lösung">
|
||||
<arrowlink COLOR="#4b4593" DESTINATION="ID_1987547541" ENDARROW="Default" ENDINCLINATION="-595;-40;" ID="Arrow_ID_1122128450" STARTARROW="None" STARTINCLINATION="-613;91;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1697226501663" ID="ID_1038438154" MODIFIED="1697226512124" TEXT="Problem Kopierbarkeit">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1697226517446" ID="ID_1311077272" MODIFIED="1697226570220" TEXT="Monitor hat das Problem bisher verborgen">
|
||||
<arrowlink COLOR="#c36885" DESTINATION="ID_1383871199" ENDARROW="Default" ENDINCLINATION="-768;38;" ID="Arrow_ID_1367033811" STARTARROW="None" STARTINCLINATION="-220;-13;"/>
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697238392317" ID="ID_785406259" MODIFIED="1697238398404" TEXT="Stufe-2">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1697238399214" ID="ID_38924085" MODIFIED="1697238402259" TEXT="Stufe-3">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1697212016574" ID="ID_567956121" MODIFIED="1697212026079" TEXT="Rückbau">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<node CREATED="1697212020164" ID="ID_69317330" MODIFIED="1697212022087" TEXT="POSIX"/>
|
||||
<node CREATED="1697212022651" ID="ID_1532773678" MODIFIED="1697212023483" TEXT="lumiera_lockerror_set"/>
|
||||
<node CREATED="1697213046906" ID="ID_1074499968" MODIFIED="1697213052317" TEXT="timespec und clock_gettime"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -66536,6 +66860,96 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1697229705261" ID="ID_764850645" MODIFIED="1697229713192" TEXT="Steuerung der Subsysteme">
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1697229736548" ID="ID_327697277" MODIFIED="1697229753890" TEXT="Entwicklung Subsystem-Konzept">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<node CREATED="1697229756390" ID="ID_1054796650" MODIFIED="1697230627356" TEXT="erster Wurf seit vielen Jahren im Einsatz">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...hatte ich damals sehr schnell geschrieben, um zu zeigen daß eine C++ - Lösung auch »einfach« sein kann. Chistian wollte damals unbedingt die Application-main in C implementieren, „damit alles wirklich einfach und verständlich bleibt“. Ich hatte das Gefühl, da stand eine Agenda im Raum, daß alles Wichtige in C sein sollte. Ich vertrat (und vertrete bis heute) den Standpunkt, daß die Erweiterungen in C++ aus gutem Grunde geschaffen wurden, weil C in wesentlichen Aspekten mutwillig zu einfach gehalten ist. Für den Traum von der <i>schönen einfachen Lösung</i> zahlt man dann jeden Tag Zinsen für technische Schulden.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="smiley-oh"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697230306599" ID="ID_1415549362" MODIFIED="1697230637141" TEXT="Kritik an der ersten Version des Konzepts">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1697230653730" ID="ID_777823460" MODIFIED="1697230691378">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
sie erfüllt alle Anforderungen, ist aber <i>zu sehr vereinfacht</i>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1697230694524" ID="ID_939615565" MODIFIED="1697230717045" TEXT="der Subsystem-Lebenszyklus setzt zu stark auf Konventionen"/>
|
||||
<node CREATED="1697230726208" ID="ID_1811646071" MODIFIED="1697231115768" TEXT="es fehlen Übergangs-Zustände, die in der Realität eben auftreten">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Vor allem...
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
ein Subsystem ist nicht einfach <i>gestartet </i>oder <i>nicht-gestartet. </i>Vielmehr ist der Start zunächst <i>initiiert, </i>und nach Erfüllung lokaler Kriterien ist der Start <i>vollständig.</i>
|
||||
</li>
|
||||
<li>
|
||||
entsprechend ist ein Subsystem nicht einfach <i>gestoppt. </i>Vielmehr ist <i>der Shutdown-angekündigt, </i>dann die <i>aktive-Phase-verlassen </i>und schließlich der <i>shutdown-abgeschlossen.</i>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1697231128386" ID="ID_148831636" MODIFIED="1697231157458" TEXT="bedingt dadurch wird die Fehlerbehandlung extrem komplex und brüchig"/>
|
||||
<node CREATED="1697231166101" ID="ID_1734528491" MODIFIED="1697231545488" TEXT="die Bedeutung des »emergency Shutdown« ist nicht geklärt">
|
||||
<arrowlink COLOR="#fdfbae" DESTINATION="ID_1349710577" ENDARROW="Default" ENDINCLINATION="-59;-2;" ID="Arrow_ID_1841665742" STARTARROW="None" STARTINCLINATION="-62;3;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1697231321617" ID="ID_1987547541" MODIFIED="1697232150319" TEXT="Überarbeitung »Emergency-Shutdown«">
|
||||
<linktarget COLOR="#4b4593" DESTINATION="ID_1987547541" ENDARROW="Default" ENDINCLINATION="-595;-40;" ID="Arrow_ID_1122128450" SOURCE="ID_756306663" STARTARROW="None" STARTINCLINATION="-613;91;"/>
|
||||
<node CREATED="1697231405941" ID="ID_1349710577" MODIFIED="1697231547383" TEXT="Vorschlag: es soll ein Deadlock vermieden werden">
|
||||
<linktarget COLOR="#fdfbae" DESTINATION="ID_1349710577" ENDARROW="Default" ENDINCLINATION="-59;-2;" ID="Arrow_ID_1841665742" SOURCE="ID_1734528491" STARTARROW="None" STARTINCLINATION="-62;3;"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1697231438932" ID="ID_1169428609" MODIFIED="1697231453874" TEXT="normalerweise wird beliebig lange gewartet"/>
|
||||
<node CREATED="1697231455104" ID="ID_330146897" MODIFIED="1697231479415" TEXT="Application-main wird erst verlassen, wenn alle Subsysteme sich abgemeldet haben"/>
|
||||
<node CREATED="1697231502864" ID="ID_903747246" MODIFIED="1697231589888" TEXT="Fehlerausgang aus einem Subsystem ⟹ Heuristik für korrumpiertes Gesamtsystem"/>
|
||||
<node CREATED="1697231841691" ID="ID_480459566" MODIFIED="1697231861116" TEXT="daher bei Emergency die Wartezeit begrenzen (und Folgefehler in Kauf nehmen)"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1697231867607" ID="ID_894790570" MODIFIED="1697232116770" TEXT="durch einen Trick im Object-Monitor implementiert">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
In lib::Sync (genauer: in der Implementierung Monitor-wait) war eine Unterstützung für Timeout nach POSIX eingebaut worden. Dies erfordert, daß die Timeout-Spec in der Storage des Client bereitgehalten wird (weil man POSIX-Funktionen nur einen Pointer übergibt). Das habe ich hierfür ausgenützt, indem nachträglich, im Fall einer Emergency, noch ein Timeout definiert wird. Da Condition-Variablen zur Prüfung der Bedingung immer wieder aufgeweckt werden, kann man so nachträglich eine Art vorzeitigen Abbruch realisieren...
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697232118254" ID="ID_793166861" MODIFIED="1697232131987" TEXT="besser: explizit einen zweiten Wait mit Timemout machen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697231265769" ID="ID_28627055" MODIFIED="1697231276175" TEXT="Subsystem-Definition">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697231270063" ID="ID_1888527843" MODIFIED="1697231276175" TEXT="Subsystem-Runner">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1482524498822" ID="ID_431883229" MODIFIED="1557498707236" TEXT="Datenstrom">
|
||||
|
|
|
|||
Loading…
Reference in a new issue