Library: prepare switch of Thread-wrapper to C++17
The investigation for #1279 leads to the following conclusions - the features and the design of our custom thread-wrapper almost entirely matches the design chosen meanwhile by the C++ committee - the implementation provided by the standard library however uses modern techniques (especially Atomics) and is more precisely worked out than our custom implementation was. - we do not need an *active* threadpool with work-assignment, rather we'll use *active* workers and a *passive* pool, which was easy to implement based on C++17 features ==> decision to drop our POSIX based custom implementation and to retrofit the Thread-wrapper as a drop-in replacement +++ start this refactoring by moving code into the Library +++ create a copy of the Threadwrapper-code to build and test the refactorings while the application itself still uses existing code, until the transition is complete
This commit is contained in:
parent
6c17204dad
commit
416895b5b2
12 changed files with 545 additions and 60 deletions
299
src/lib/thread.hpp
Normal file
299
src/lib/thread.hpp
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
THREAD.hpp - thin convenience wrapper for starting threads
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, 2010 Hermann Vosseler <Ichthyostega@web.de>
|
||||
Christian Thaeter <ct@pipapo.org>
|
||||
|
||||
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 thread.hpp
|
||||
** Convenience front-end for basic thread handling needs.
|
||||
** The Lumiera vault contains a dedicated low-level thread handling framework,
|
||||
** which is relevant for scheduling render activities to make best use of parallelisation
|
||||
** abilities of the given system. Typically, the upper layers should not have to deal much
|
||||
** with thread handling, yet at some point there is the need to implement a self contained
|
||||
** action running within a dedicated thread. The vault::Thread class is a wrapper to
|
||||
** represent such an parallel action conveniently and safely; together with the object
|
||||
** monitor, this allows to abstract away intricacies into self contained objects.
|
||||
**
|
||||
** @todo WIP 9/23 about to be replaced by a thin wrapper on top of C++17 threads ///////////////////////TICKET #1279 : consolidate to C++17 features
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_THREAD_H
|
||||
#define LIB_THREAD_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/result.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "vault/threads.h"
|
||||
}
|
||||
//#include "vault/threadpool-init.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
using lib::Literal;
|
||||
namespace error = lumiera::error;
|
||||
using error::LERR_(STATE);
|
||||
using error::LERR_(EXTERNAL);
|
||||
|
||||
typedef struct nobug_flag* NoBugFlag;
|
||||
|
||||
|
||||
|
||||
/************************************************************************//**
|
||||
* A thin convenience wrapper for dealing with threads,
|
||||
* as implemented by the threadpool in the vault (based on pthread).
|
||||
* Using this wrapper...
|
||||
* - helps with passing data to the function executed in the new thread
|
||||
* - allows to bind to various kinds of functions including member functions
|
||||
* The new thread starts immediately within the ctor; after returning, the new
|
||||
* thread has already copied the arguments and indeed actively started to run.
|
||||
*
|
||||
* # Joining, cancellation and memory management
|
||||
* In the basic version (class Thread), the created thread is completely detached
|
||||
* and not further controllable. There is no way to find out its execution state,
|
||||
* wait on termination or even cancel it. Client code needs to implement such
|
||||
* facilities explicitly, if needed. Care has to be taken with memory management,
|
||||
* as there are no guarantees beyond the existence of the arguments bound into
|
||||
* the operation functor. If the operation in the started thread needs additional
|
||||
* storage, it has to manage it actively.
|
||||
*
|
||||
* There is an extended version (class ThreadJoinable) to allow at least to wait
|
||||
* on the started thread's termination (joining). Building on this it is possible
|
||||
* to create a self-contained "thread in an object"; the dtor of such an class
|
||||
* must join to prevent pulling away member variables the thread function will
|
||||
* continue to use.
|
||||
*
|
||||
* # failures in the thread function
|
||||
* The operation started in the new thread is protected by a top-level catch block.
|
||||
* Error states or caught exceptions can be propagated through the lumiera_error
|
||||
* state flag, when using ThreadJoinable::join(). By invoking `join().maybeThrow()`
|
||||
* on a join-able thread, exceptions can be propagated.
|
||||
* @note any errorstate or caught exception detected on termination of a standard
|
||||
* async Thread is considered a violation of policy and will result in emergency
|
||||
* shutdown of the whole application.
|
||||
*
|
||||
* # synchronisation barriers
|
||||
* Lumiera threads provide a low-level synchronisation mechanism, which is used
|
||||
* to secure the hand-over of additional arguments to the thread function. It
|
||||
* can be used by client code, but care has to be taken to avoid getting out
|
||||
* of sync. When invoking the #sync and #syncPoint functions, the caller will
|
||||
* block until the counterpart has also invoked the corresponding function.
|
||||
* If this doesn't happen, you'll block forever.
|
||||
*/
|
||||
class Thread
|
||||
: util::MoveOnly
|
||||
{
|
||||
/** @internal perfect forwarding through a C-style `void*` */
|
||||
template<class FUN>
|
||||
static FUN&&
|
||||
forwardInitialiser (void* rawPtr) noexcept
|
||||
{
|
||||
REQUIRE (rawPtr);
|
||||
FUN& initialiser = *reinterpret_cast<FUN*> (rawPtr);
|
||||
return static_cast<FUN&&> (initialiser);
|
||||
}
|
||||
|
||||
|
||||
template<class FUN>
|
||||
static void
|
||||
threadMain (void* arg)
|
||||
{
|
||||
using Fun= typename lib::meta::_Fun<FUN>::Functor;
|
||||
Fun _doIt_{forwardInitialiser<FUN> (arg)};
|
||||
|
||||
//lumiera_thread_sync (); // sync point: arguments handed over /////////////////////////////////OOO TOD-oh
|
||||
|
||||
try {
|
||||
_doIt_(); // execute the actual operation in the new thread
|
||||
}
|
||||
|
||||
catch (std::exception& failure)
|
||||
{
|
||||
if (!lumiera_error_peek())
|
||||
LUMIERA_ERROR_SET (sync, STATE
|
||||
,failure.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LUMIERA_ERROR_SET_ALERT (sync, EXTERNAL
|
||||
, "Thread terminated abnormally");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
LumieraThread threadHandle_;
|
||||
|
||||
/** @internal derived classes may create an inactive thread */
|
||||
Thread() : threadHandle_(0) { }
|
||||
|
||||
|
||||
/** @internal use the Lumiera thread manager to start a new thread and hand over the operation */
|
||||
template<class FUN>
|
||||
void
|
||||
launchThread (Literal purpose, FUN&& operation, NoBugFlag logging_flag, uint additionalFlags =0)
|
||||
{
|
||||
REQUIRE (!lumiera_error(), "Error pending at thread start");
|
||||
using Functor = typename std::remove_reference<FUN>::type;
|
||||
threadHandle_ =
|
||||
nullptr; ////////////////////////////////////////////////////////////////////////////////////OOO LaLaLa
|
||||
// lumiera_thread_run ( LUMIERA_THREADCLASS_INTERACTIVE | additionalFlags
|
||||
// , &threadMain<Functor>
|
||||
// , reinterpret_cast<void*> (&operation)
|
||||
// , purpose.c()
|
||||
// , logging_flag
|
||||
// );
|
||||
if (!threadHandle_)
|
||||
throw error::State ("Failed to start a new Thread for \"+purpose+\""
|
||||
, lumiera_error());
|
||||
|
||||
// make sure the new thread had the opportunity to take the Operation
|
||||
// prior to leaving and thereby possibly destroying this local context
|
||||
//lumiera_thread_sync_other (threadHandle_); //////////////////////////////////////////////////OOO Dadü DaDa
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
/** Create a new thread to execute the given operation.
|
||||
* The new thread starts up synchronously, can't be cancelled and it can't be joined.
|
||||
* @param purpose fixed char string used to denote the thread for diagnostics
|
||||
* @param logging_flag NoBug flag to receive diagnostics regarding the new thread
|
||||
* @param operation a functor holding the code to execute within the new thread.
|
||||
* Any function-like entity with signature `void(void)` is acceptable.
|
||||
* @warning The operation functor will be forwarded to create a copy residing
|
||||
* on the stack of the new thread; thus it can be transient, however
|
||||
* anything referred through a lambda closure here must stay alive
|
||||
* until the new thread terminates.
|
||||
*/
|
||||
template<class FUN>
|
||||
Thread (Literal purpose, FUN&& operation, NoBugFlag logging_flag = &NOBUG_FLAG(thread))
|
||||
: threadHandle_{nullptr}
|
||||
{
|
||||
launchThread (purpose, std::forward<FUN> (operation), logging_flag);
|
||||
}
|
||||
|
||||
|
||||
/** @note by design there is no possibility to find out
|
||||
* just based on the thread handle if some thread is alive.
|
||||
* We define our own accounting here based on the internals
|
||||
* of the thread wrapper. This will break down, if you mix
|
||||
* uses of the C++ wrapper with the raw C functions. */
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
return threadHandle_;
|
||||
}
|
||||
|
||||
|
||||
/** Synchronisation barrier. In the function executing in this thread
|
||||
* needs to be a corresponding Thread::syncPoint() call. Blocking until
|
||||
* both the caller and the thread have reached the barrier.
|
||||
*/
|
||||
void
|
||||
sync()
|
||||
{
|
||||
REQUIRE (isValid(), "Thread not running");
|
||||
if (!lumiera_thread_sync_other (threadHandle_))
|
||||
lumiera::throwOnError();
|
||||
}
|
||||
|
||||
/** counterpart of the synchronisation barrier, to be called from
|
||||
* within the thread to be synchronised. Will block until both
|
||||
* this thread and the outward partner reached the barrier.
|
||||
* @warning blocks on the _current_ thread's condition var
|
||||
*/
|
||||
static void
|
||||
syncPoint ()
|
||||
{
|
||||
lumiera_thread_sync ();
|
||||
}
|
||||
|
||||
protected:
|
||||
/** determine if the currently executing code runs within this thread */
|
||||
bool
|
||||
invokedWithinThread() const
|
||||
{
|
||||
REQUIRE (isValid(), "Thread not running");
|
||||
LumieraThread current = nullptr; // lumiera_thread_self (); /////////////////////////////////OOO
|
||||
return current
|
||||
and current == this->threadHandle_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Variant of the standard case, allowing additionally
|
||||
* to join on the termination of this thread.
|
||||
*/
|
||||
class ThreadJoinable
|
||||
: public Thread
|
||||
{
|
||||
public:
|
||||
template<class FUN>
|
||||
ThreadJoinable (Literal purpose, FUN&& operation,
|
||||
NoBugFlag logging_flag = &NOBUG_FLAG(thread))
|
||||
: Thread{}
|
||||
{
|
||||
launchThread<FUN> (purpose, std::forward<FUN> (operation), logging_flag,
|
||||
LUMIERA_THREAD_JOINABLE);
|
||||
}
|
||||
|
||||
|
||||
/** put the caller into a blocking wait until this thread has terminated.
|
||||
* @return token signalling either success or failure.
|
||||
* The caller can find out by invoking `isValid()`
|
||||
* or `maybeThrow()` on this result token
|
||||
*/
|
||||
lib::Result<void>
|
||||
join ()
|
||||
{
|
||||
if (!isValid())
|
||||
throw error::Logic ("joining on an already terminated thread");
|
||||
|
||||
lumiera_err errorInOtherThread =
|
||||
"TODO TOD-oh";//lumiera_thread_join (threadHandle_); //////////////////////////////////OOO
|
||||
threadHandle_ = 0;
|
||||
|
||||
if (errorInOtherThread)
|
||||
return error::State ("Thread terminated with error", errorInOtherThread);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif /*LIB_THREAD_H*/
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
** represent such an parallel action conveniently and safely; together with the object
|
||||
** monitor, this allows to abstract away intricacies into self contained objects.
|
||||
**
|
||||
** @note the thread wrapper is not intended for high performance computations.
|
||||
** @deprecated will be replaced by a thin wrapper on top of C++17 threads //////////////////////////////TICKET #1279 : consolidate to C++17 features
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -106,6 +106,7 @@ namespace vault {
|
|||
* of sync. When invoking the #sync and #syncPoint functions, the caller will
|
||||
* block until the counterpart has also invoked the corresponding function.
|
||||
* If this doesn't happen, you'll block forever.
|
||||
* @deprecated will be replaced by a thin wrapper on top of C++17 threads /////////////////////////////TICKET #1279 : consolidate to C++17 features
|
||||
*/
|
||||
class Thread
|
||||
: util::MoveOnly
|
||||
|
|
@ -255,6 +256,7 @@ namespace vault {
|
|||
/**
|
||||
* Variant of the standard case, allowing additionally
|
||||
* to join on the termination of this thread.
|
||||
* @deprecated will be replaced by a thin wrapper on top of C++17 threads /////////////////////////////TICKET #1279 : consolidate to C++17 features
|
||||
*/
|
||||
class ThreadJoinable
|
||||
: public Thread
|
||||
|
|
|
|||
44
tests/11concurrency.tests
Normal file
44
tests/11concurrency.tests
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
TESTING "Library Test Suite: concurrency helpers" ./test-suite --group=common
|
||||
|
||||
|
||||
|
||||
TEST "Multithread Locking by Monitor" SyncLocking_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Multithread Locking class level" SyncClasslock_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Thread-local diagnostic context" DiagnosticContext_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Wait/Notify with timeout" SyncTimedwait_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Create 20 Threads passing context" ThreadWrapper_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Waiting on Thread termination" ThreadWrapperJoin_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Detect code runing in a given Thread" ThreadWrapperSelfRecognitionTest_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
|
|
@ -115,41 +115,6 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "Multithread Locking by Monitor" SyncLocking_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Multithread Locking whole class" SyncClasslock_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Thread-local diagnostic context" DiagnosticContext_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Create 20 Threads passing context" ThreadWrapper_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Waiting on Thread termination" ThreadWrapperJoin_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Detect code runing in a given Thread" ThreadWrapperSelfRecognitionTest_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Starting and stopping subsystems" SubsystemRunner_test <<END
|
||||
out: -----singleSubsys_complete_cycle-----
|
||||
out: -----singleSubsys_start_failure-----
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@
|
|||
#include "lib/test/run.hpp"
|
||||
#include "lib/sync-classlock.hpp"
|
||||
#include "lib/scoped-collection.hpp"
|
||||
#include "vault/thread-wrapper.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
|
||||
using test::Test;
|
||||
using vault::ThreadJoinable;
|
||||
//using vault::ThreadJoinable; //////////////////WIP
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
#include "lib/error.hpp"
|
||||
|
||||
#include "lib/sync.hpp"
|
||||
#include "vault/thread-wrapper.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
|
@ -126,7 +126,7 @@ namespace test{
|
|||
*/
|
||||
class HavocThread
|
||||
{
|
||||
vault::ThreadJoinable thread_;
|
||||
ThreadJoinable thread_;
|
||||
|
||||
void
|
||||
doIt ()
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
#include "lib/test/run.hpp"
|
||||
#include "lib/error.hpp"
|
||||
|
||||
#include "vault/thread-wrapper.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
#include "lib/sync.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
|
@ -156,7 +156,7 @@ namespace test{
|
|||
void
|
||||
waitPingPong (Token& tok)
|
||||
{
|
||||
typedef vault::ThreadJoinable Thread;
|
||||
typedef ThreadJoinable Thread; //////////////////////////////////////WIP
|
||||
|
||||
Thread ping ("SyncWaiting ping", bind (&Token::getIt, &tok));
|
||||
Thread pong ("SyncWaiting pong", bind (&Token::getIt, &tok));
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
|
||||
#include "vault/thread-wrapper.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
#include "lib/error.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
|
@ -38,7 +38,7 @@ using test::Test;
|
|||
|
||||
|
||||
|
||||
namespace vault {
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
using lumiera::error::LUMIERA_ERROR_LOGIC;
|
||||
|
|
@ -139,4 +139,4 @@ namespace test {
|
|||
|
||||
|
||||
|
||||
}} // namespace vault::test
|
||||
}} // namespace lib::test
|
||||
|
|
@ -24,14 +24,14 @@
|
|||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
|
||||
#include "vault/thread-wrapper.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
#include "lib/error.hpp"
|
||||
|
||||
using test::Test;
|
||||
|
||||
|
||||
|
||||
namespace vault {
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
namespace {
|
||||
|
|
@ -86,4 +86,4 @@ namespace test {
|
|||
|
||||
|
||||
|
||||
}} // namespace vault::test
|
||||
}} // namespace lib::test
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include "lib/test/run.hpp"
|
||||
|
||||
#include "vault/thread-wrapper.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
#include "lib/sync.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ using std::bind;
|
|||
using test::Test;
|
||||
|
||||
|
||||
namespace vault {
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
namespace { // private test classes and data...
|
||||
|
|
@ -145,4 +145,4 @@ namespace vault {
|
|||
|
||||
|
||||
|
||||
}} // namespace vault::test
|
||||
}} // namespace lib::test
|
||||
|
|
@ -79544,7 +79544,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1693787750689" ID="ID_1636630763" MODIFIED="1693787785090" TEXT="lib::Thread wird aufgegeben">
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1693787750689" ID="ID_1636630763" MODIFIED="1695311900187" TEXT="vault::Thread wird aufgegeben">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1693787857875" ID="ID_1964973756" MODIFIED="1693787947679">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
|
|
@ -79559,38 +79559,38 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node CREATED="1693787905997" ID="ID_1862230955" MODIFIED="1693787943205" TEXT="aber: sie verlagern Architektur-Fragen in die LIbrary-Ebene">
|
||||
<icon BUILTIN="closed"/>
|
||||
<node CREATED="1693788079957" ID="ID_84816913" MODIFIED="1693788252833" TEXT="das ist bestenfalls wirkungslos">
|
||||
<node CREATED="1693788079957" ID="ID_84816913" MODIFIED="1695312147060" TEXT="das ist bestenfalls wirkungslos">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
...wenn es sich ohnehin nur um eine Einrichtung innerhalb einer bestimmten Applikation handelt, dann bleibt lediglich die Frage übrig, wohin der Quell/Objektcode gepackt wird
|
||||
...wenn es sich ohnehin nur um eine Einrichtung innerhalb einer bestimmten Applikation handelt, dann bleibt lediglich die Frage übrig, wohin der Quell/Objektcode gepackt wird — wenn man ein bestimmtes Feature braucht, kann man es implementieren (und die bestehende Lib-Implementierung kann das nicht verhindern)
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1693788121089" ID="ID_553756560" MODIFIED="1693788325694" TEXT="schafft aber ungesunde Strukturen">
|
||||
<node CREATED="1693788121089" ID="ID_553756560" MODIFIED="1695312764615" TEXT="schafft aber möglicherweise ungesunde Strukturen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Da Library-Funktionen ohne Weiteres genutzt werden, zwingt das dazu, statische Instanzen und versteckte magische Initialisierung zu verwenden
|
||||
Da Library-Funktionen ohne Weiteres genutzt werden, verleiten solche <i>eingebaute Hürden</i> ehr dazu, schmutzige Workarounds zu machen
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1693788148236" ID="ID_1859841213" MODIFIED="1693788618261" TEXT="und stellt im allgemeineren Fall eine Kompetenzüberschreitung dar">
|
||||
<node CREATED="1693788148236" ID="ID_1859841213" MODIFIED="1695312822573" TEXT="im allgemeineren Fall sollte ein Library sich nicht als Architekt betätigen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Sprachmittel sind stets aus gutem Grund da. Entweder es gibt aktuell adäquaten Nutzen, oder es gab früher mal einen relevanten Nutzen. Daher stellt es eine Anmaßung dar, wenn jemand nun einfach rundheraus behauptet, es sei <i>falsch</i> dieses Sprachmittel zu verwenden. Wenn man einen Anfänger ohne ausreichende Kompetenzen entlasten möchte, oder wenn man unter Einschränkung ein bestimmtes Nutmuster vorgeben möchte, dann baut man ein <b>Framework</b>, und keine Library.
|
||||
Sprachmittel sind stets aus gutem Grund da. Entweder es gibt aktuell adäquaten Nutzen, oder es gab früher mal einen relevanten Nutzen. Daher stellt es eine Kompetenzüberschreitung dar, wenn jemand rundheraus behauptet, es sei <i>falsch</i> dieses Sprachmittel zu verwenden. Wenn man einen Anfänger ohne ausreichende Kompetenzen entlasten möchte, oder wenn man unter Einschränkung ein bestimmtes Nutmuster vorgeben möchte, dann baut man ein <b>Framework</b>, und keine Library.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1693788648105" ID="ID_426109467" MODIFIED="1693788808876">
|
||||
<node CREATED="1693788648105" ID="ID_426109467" MODIFIED="1695312840978">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -79601,10 +79601,176 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
das sich nun auch im Standard als angemessen herausgebildet hat —
|
||||
</p>
|
||||
<p>
|
||||
aber die Standard-Lösung ist ausgereifter und verwendet moderne Sprachmittel
|
||||
aber die Standard-Lösung ist ausgereifter und verwendet moderne Sprachmittel (Atomics)
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695313755664" HGAP="-12" ID="ID_1075854169" MODIFIED="1695313767920" TEXT="Umstellung der Codebasis" VSHIFT="21">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695313774171" ID="ID_1529952681" MODIFIED="1695340435812" TEXT="spezielle Abhängigkeiten">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1695313807671" ID="ID_1031643072" MODIFIED="1695313818061" TEXT="vault::Thread vs vault::ThreadJoinable">
|
||||
<node CREATED="1695335467170" ID="ID_920953501" MODIFIED="1695335514560" TEXT="das erweist sich als praktische Abkürzung"/>
|
||||
<node CREATED="1695335516996" ID="ID_1023062994" MODIFIED="1695335550147" TEXT="der normale Thread würde sich dann stets am Ende selbst detachen"/>
|
||||
<node CREATED="1695335555337" ID="ID_712387888" MODIFIED="1695335682482" TEXT="damit fällt ein Anlaß für Flüchtigkeitsfehler weg"/>
|
||||
<node CREATED="1695335705339" ID="ID_164690747" MODIFIED="1695335719461" TEXT="paßt damit zusammen, daß wir auch einen try-catch-Block einbauen"/>
|
||||
</node>
|
||||
<node CREATED="1695313818614" ID="ID_470584516" MODIFIED="1695313837839" TEXT="Thread::syncPoint() / sync()">
|
||||
<node CREATED="1695315415143" ID="ID_1102877346" MODIFIED="1695315425412" TEXT="bisherige Verwendungen">
|
||||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1695313896379" ID="ID_234820417" MODIFIED="1695313904622" TEXT="CallQueue_test::verify_ThreadSafety">
|
||||
<node CREATED="1695313928470" ID="ID_1264620312" MODIFIED="1695313943649" TEXT="dient hier zur Verschärfung des Thread"/>
|
||||
<node CREATED="1695313944237" ID="ID_844947606" MODIFIED="1695313978092" TEXT="da wir "nur" 500 Iterationen fahren"/>
|
||||
<node CREATED="1695313978944" ID="ID_820170937" MODIFIED="1695314027828" TEXT="...müssen alle Worker möglichst gleichzeitig losrennen"/>
|
||||
</node>
|
||||
<node CREATED="1695315143388" ID="ID_356745606" MODIFIED="1695315156254" TEXT="microbenchmark.hpp : threadBenchmark()">
|
||||
<node CREATED="1695315157154" ID="ID_1607681300" MODIFIED="1695315164920" TEXT="gleiche Problematik..."/>
|
||||
<node CREATED="1695315165449" ID="ID_365200218" MODIFIED="1695315179331" TEXT="die Messung soll erst beginnen, wenn alle Threads starten"/>
|
||||
<node CREATED="1695315180159" ID="ID_669527704" MODIFIED="1695315191154" TEXT="(und die Threads sollen möglichst gleichzeitig unterwegs sein)"/>
|
||||
</node>
|
||||
<node CREATED="1695314335069" ID="ID_841603246" MODIFIED="1695314336372" TEXT="SubsystemRunner_test">
|
||||
<node CREATED="1695314337464" ID="ID_137940927" MODIFIED="1695314376718" TEXT="auch hier: zusätzliche Barriere für beherrschbaren Test"/>
|
||||
<node CREATED="1695314377451" ID="ID_1181532265" MODIFIED="1695314403731" TEXT="der Test spielt explizit das Scheitern in verschiedenen Phasen durch"/>
|
||||
<node CREATED="1695314404385" ID="ID_1452050759" MODIFIED="1695314460156" TEXT="um die Fälle klar unterscheiden zu können, muß der Sub-Thread wenigstens laufen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
sonst könnte der Fall nicht zuverlässig getestet werden, daß der Test-Runner selber kurz nach dem Start stirbt
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1695315325887" ID="ID_625375733" MODIFIED="1695315328199" TEXT="SessionCommandFunction_test">
|
||||
<node CREATED="1695315329875" ID="ID_592889751" MODIFIED="1695315363091" TEXT="Threads erst nach Initialisierung der ganzen Klasse starten"/>
|
||||
<node CREATED="1695315363798" ID="ID_146882783" MODIFIED="1695315376115" TEXT="die Klasse erbt von vault::Thread"/>
|
||||
<node CREATED="1695315376877" ID="ID_1462759116" MODIFIED="1695315388279" TEXT="fügt aber noch weitere Felder hinzu"/>
|
||||
<node CREATED="1695315389155" ID="ID_1933290982" MODIFIED="1695315407516" TEXT="und die Logik setzt wohldefinierten Anfangszustand vorraus"/>
|
||||
</node>
|
||||
<node CREATED="1695314551763" ID="ID_482710510" MODIFIED="1695314558086" TEXT="steam::control::DispatcherLoop">
|
||||
<node CREATED="1695314559226" ID="ID_1190495284" MODIFIED="1695314574980" TEXT="verwendet eine eingebettete »Looper«-Komponente"/>
|
||||
<node CREATED="1695314579779" ID="ID_643512170" MODIFIED="1695314602498" TEXT="diese muß initialisiert sein, bevor der Session-Thread ihre Logik verwendet"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695315426871" ID="ID_1792980090" MODIFIED="1695315442534" TEXT="⟹ Ersatzkonstrukt zwingend notwendig">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1695334520345" ID="ID_1748880887" MODIFIED="1695334539706" TEXT="sollte dann aber eine explizite Lib-Funktionalität sein"/>
|
||||
<node CREATED="1695334551413" ID="ID_257643954" LINK="https://stackoverflow.com/a/24218922" MODIFIED="1695334594067" TEXT="man könnte ein spinning-latch mit yield verwenden">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1695334637793" ID="ID_681204225" MODIFIED="1695334721438" TEXT="spinning ohne yield wäre gefährlich im allgemeinen Fall">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
ein Spinlock sollte man nur verwenden, wenn man sicher sein kann, daß man (a) fast nicht warten muß und (b) die CPU für sich hat. Andernfalls kann das schlimmstenfalls zum Verhungern des ganzen Systems führen...
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1695334757953" ID="ID_1716551234" MODIFIED="1695334788969">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Mutex + ConditionVar ist auch <i>gar nicht so schlecht</i> <font size="2">(aber noch schwergewichtiger)</font>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1695334869074" ID="ID_938671107" MODIFIED="1695335156463" TEXT="yield() gefällt mir — weil es typischerweise genau um einen Thread-Schedule-delay geht">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Der Sprachstandard garantiert bereits, daß der Thread startklar ist, wenn der std::thread-Konstruktor verlassen wird. Allerdings kann es immer passieren, daß dummerweise der OS-Scheduler grade Anderes mit dieser CPU vor hat — und genau dann laufen wir in die Situation, vor der diese Sync-Barriere schützen soll; d.h. <i>spätestens nach einer Schedule-Runde</i> sollten alle Threads einmal vorbeigekommen sein.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1695336070266" ID="ID_1892470569" MODIFIED="1695336082188" TEXT="Thread::invokedWithinThread()">
|
||||
<node CREATED="1695336097750" ID="ID_84784760" MODIFIED="1695336130714" TEXT="es gibt genau einen use-Case (in DispatcherLoop)"/>
|
||||
<node CREATED="1695336140400" ID="ID_661758467" MODIFIED="1695336193541" TEXT="man könnte diese Funktion mit Stdlib-Mitteln auch realisieren (std::this_thread::get_id)"/>
|
||||
<node CREATED="1695336202544" ID="ID_818516761" MODIFIED="1695336271068" TEXT="dafür spricht, daß dadurch eine Abstraktion geschaffen wird"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695336491346" ID="ID_940191365" MODIFIED="1695340427992" TEXT="Code-Anordnung">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1695339482171" ID="ID_781380757" MODIFIED="1695339496962" TEXT="Thread-Pool deaktivieren">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
<node CREATED="1695339498617" ID="ID_1329617125" MODIFIED="1695339543619" TEXT="umgezogene Tests gebrochen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
solange sie noch auf den alten Code gehen, der den Threadpool voraussetzt (welcher aber für Library-Tests nicht zugelinkt wird)
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<node CREATED="1695339558729" ID="ID_1181374984" MODIFIED="1695339576171" TEXT="SyncLocking_test"/>
|
||||
<node CREATED="1695339549851" ID="ID_1030547189" MODIFIED="1695339553543" TEXT="SyncClasslock_test"/>
|
||||
<node CREATED="1695339568255" ID="ID_288867443" MODIFIED="1695339574891" TEXT="ThreadWrapper_test"/>
|
||||
<node CREATED="1695339580526" ID="ID_592772431" MODIFIED="1695339584105" TEXT="ThreadWrapperJoin_test"/>
|
||||
<node CREATED="1695339596332" ID="ID_164644014" MODIFIED="1695339607086" TEXT="ThreadWrapperSelfRecognition_test"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1695340324306" ID="ID_708290532" MODIFIED="1695340369662" TEXT="threadpool-init">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1695340372505" ID="ID_115540630" MODIFIED="1695340397384" TEXT="war ohnehin ein Workaround, da backend-init gar nicht mehr läuft">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1695340337153" ID="ID_1494450459" MODIFIED="1695340350326" TEXT="vorerst noch belassen, damit die Applikation selber noch startfähig ist">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695340351463" ID="ID_1816356053" MODIFIED="1695340361785" TEXT="schließlich zurückbauen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695336500985" ID="ID_1001374907" MODIFIED="1695340319832" TEXT="alles wieder in die Support-Library ziehen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1695336524821" ID="ID_625640883" MODIFIED="1695336587478" TEXT="Begründung: es ist kein »Vault-Feature« mehr">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Denn wir betreiben hier nun keine System-Einrichtungen mehr, keinen Resource-Collector und keinen Threadpool. Es handelt sich lediglich um Adapter über der Std-Lib
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1695336588389" ID="ID_1245830716" MODIFIED="1695336597664" TEXT="außerdem erleichtert das den Schwenk"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1695340272418" ID="ID_1240703198" MODIFIED="1695340308005" TEXT="Code aus namespace vault zurückbauen">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695340289647" ID="ID_456037035" MODIFIED="1695340305094" TEXT="thread-wrapper.hpp">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695340297038" ID="ID_1888990589" MODIFIED="1695340304054" TEXT="die Threadpool-Library">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1693789156806" ID="ID_128127845" MODIFIED="1693789195596" TEXT="Monitor erhalten und umschreiben">
|
||||
|
|
@ -79612,6 +79778,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node CREATED="1693789376921" ID="ID_158507519" MODIFIED="1693789398154" TEXT="ein Monitor gehört typischerweise in ein Framework"/>
|
||||
<node CREATED="1693789399454" ID="ID_1417502572" MODIFIED="1693789408745" TEXT="und hier haben wir ein Applicatino-Framework"/>
|
||||
<node CREATED="1693789410469" ID="ID_1281994248" MODIFIED="1693789423198" TEXT="in diesem Rahmen hat er sich hervorragend bewährt"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695337735932" HGAP="-30" ID="ID_293636243" MODIFIED="1695337749093" TEXT="Umarbeiten der Implementierung" VSHIFT="40">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695337750146" ID="ID_99990028" MODIFIED="1695337765337" TEXT="brauchen wir das timedWait-Feature wirklich?">
|
||||
<font NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1695337787989" ID="ID_154693007" MODIFIED="1695337808761" TEXT="ich errinnere mich, das nebenbei mit eingebaut zu haben..."/>
|
||||
<node CREATED="1695337809498" ID="ID_789251448" MODIFIED="1695337825212" TEXT="inzwischen gibt es da diverse bessere Möglichkeiten (mit Futures)"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue