Commit graph

720 commits

Author SHA1 Message Date
73737f2aee Library/Application: consolidate Monitor implementation
After the fundamental switch from POSIX to the C++14 wrappers
the existing implementation of the Monitor can now be drastically condensed,
removing several layers of indirection. Moreover, all signatures
shall be changed to blend in with the names and patterns established
by the C++ standard.

This is Step-1 : consolidate the Implementation.

(to ensure correctness, the existing API towards application code was retained)
2023-10-15 02:41:41 +02:00
c37871ca78 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.
2023-10-13 23:46:38 +02:00
5db49afafd Library/Application: now able to switch supervisor-thread in OutputDirector
This, and the GUI thread prompted an further round of
design extensions and rework of the thread-wrapper.

Especially there is now support for self-managed threads,
which can be launched and operate completely detached from the
context used to start them. This resolves an occasional SEGFAULT
at shutdown. An alternative (admittedly much simpler) solution
would have been to create a fixed context in a static global
variable and to attach a regular thread wrapper from there,
managed through unique_ptr.

It seems obvious that the new solution is preferable,
since all the tricky technicalities are encapsulated now.
2023-10-12 02:10:50 +02:00
29b9126c26 Library: test coverage for lifecycle management
Add a complete demonstration for a setup akin to what we use
for the Session thread: a threaded component which manages itself
but also exposes an external interface, which is opened/closed alongside
2023-10-11 22:02:52 +02:00
7b25609896 Library: test coverage for self-managed thread
...extract and improve the tuple-rewriting function
...improve instance tracking test dummy objects
...complete test coverage and verify proper memory handling
2023-10-11 21:06:56 +02:00
f6a6b0b68f Library: allow to bind a member function into self-managed thread
Oh my.
Yet another hideously complex problem and workaround...

Since a week I am like "almost done"
2023-10-11 13:21:08 +02:00
42eba8425a Library: now able to provide a self-managed thread
After quite some detours, with this take I'm finally able to
provide a stringent design to embody all the variants of thread start
encountered in practice in the Lumiera code base.

Especially the *self-managed* thread is now represented as a special-case
of a lifecycle-hook, and can be embodied into a builder front-end,
able to work with any client-provided thread-wrapper subclass.
2023-10-10 21:45:41 +02:00
8b3f9e17cd Library: scaffolding to install thread lifecycle hooks
to cover the identified use-cases a wide variety of functors
must be accepted and adapted appropriately. A special twist arises
from the fact that the complete thread-wrapper component stack works
without RTTI; a derived class can not access the thread-wrapper internals
while the policy component to handle those hooks can not directly downcast
to some derived user provided class. But obviously at usage site it
can be expected to access both realms from such a callback.

The solution is to detect the argument type of the given functor
and to build a two step path for a safe static cast.
2023-10-10 19:47:39 +02:00
5f9683ef10 Library: policy for self-managed thread
...after resolving the fundamental design problems,
a policy mix-in can be defined now for a thread that deletes
its own wrapper at the end of the thread-function.

Such a setup would allow for »fire-and-forget« threads, but with
wrapper and ensuring safe allocations. The prominent use case
for such a setup would be the GUI-Thread.
2023-10-10 02:55:23 +02:00
faa0d3e211 Library: solved embedding arbitrary argument sequences
Concept study of the intended solution successful.

Can now transparently embed any conceivable functor
and an arbitrary argument sequence into a launcher-λ
Materialising into a std::tuple<decay_t<TYPES...>> did the trick.
2023-10-09 02:57:03 +02:00
fd0370bd11 Library: still fighting to get the design straight
Considering a solution to shift the actual launch of the new thread
from the initialiser list into the ctor body, to circumvent the possible
"undefined behaviour". This would also be prerequisite for defining
a self-managed variant of the thread-wrapper.

Alternative / Plan.B would be to abandon the idea of a self-contained
"thread" building block, instead relying on precise setup in the usage
context -- however, not willing to yield yet, since that would be exactly
what I wanted to avoid: having technicalities of thread start, argument
handover and failure detection intermingled with the business code.
2023-10-08 17:26:36 +02:00
08c3e76f14 Library: identified design challenges
On a close look, the wrapper design as pursued here
turns out to be prone to insidious data race problems.
This was true also for the existing solution, but becomes
more clear due to the precise definitions from the C++ standard.

This is a confusing situation, because these races typically do not
materialise in practice; due to the latency of the OS scheduler the
new thread starts invoking user code at least 100µs after the Wrapper
object is fully constructed (typically more like 500µs, which is a lot)

The standard case (lib::Thread) in its current form is correct, but borderline
to undefined behaviour, and any initialisation of members in a derived class
would be off limits (the thread-wrapper should not be used as baseclass,
rather as member)
2023-10-07 03:25:39 +02:00
88b91d204c Library: identified further use-case variants to cover
...while reworking the application code, it became clear that
actually there are two further quite distinct variants of usage.
And while these could be implemented with some trickery based on
the Thread-wrapper defined thus far, it seems prudent better to
establish a safely confined explicit setup for these cases:

- a fire-and-forget-thread, which manages its own memory autonomously
- a thread with explicit lifecycle, with detectable not-running state
2023-10-05 23:35:52 +02:00
ff052ec5a2 Library/Application: switch Microbenchmark + SyncBarrier tests
...these were already written envisionaging he new API,
so it's more or less a drop-in replacement.

- cant use vector anymore, since thread objects are move-only
- use ScopedCollection instead, which also has the benefit of
  allocating the requires space up-front. Allow to deduce the
  type parameter of the placed elements
2023-10-03 22:56:09 +02:00
d879ae7fbd Library: fix cause of the deadlock in Session-Thread
... which became apparent after switching to the new Thread-wrapper implementation
... the reason is a bug in the Thread-Monitor (which will also be reworked soon)
2023-10-01 20:29:11 +02:00
9cb0a9b680 Library: discontinue setting error flag from Exceptions (see #1341)
While seemingly subtle, this is a ''deep change.''
Up to now, the project attempted to maintain two mutually disjoint
systems of error reporting: C-style error flags and C++ exceptions.
Most notably, an attempt was made to keep both error states synced.

During the recent integration efforts, this increasingly turned out
as an obstacle and source for insidious problems (like deadlocks).


As a resolve, hereby the relation of both systems is **clarified**:
 * C-style error flags shall only be set and used by C code henceforth
 * C++ exceptions can (optionally) be thrown by retrieving the C-style error code
 * but the opposite is now ''discontinued'' : Exceptions ''do not set'' the error flag anymore
2023-10-01 20:11:45 +02:00
d79e33f797 Library: verify thread self-recognition
...this function was also ported to the new wrapper,
and can be verified now in a much more succinct way.

''This completes porting of the thread-wrapper''
2023-09-30 00:10:09 +02:00
1d625a01e0 Library: complete and modernise ThreadWrapperJoin_test
Since the decision was taken to retain support for this special feature,
and even extend it to allow passing values, the additional functionality
should be documented in the test. Doing so also highlighted subtle problems
with argument binding.
2023-09-29 23:42:22 +02:00
d37a3abd6c Library: actually verify parallelism
Now the ThreadWrapper_test offers both

- a really simple usage example

- a comprehensive test to verify that actually the
  thread-function is invoked the expected number of times
  and that this invocations must have been parallelised
2023-09-29 19:21:28 +02:00
1d30d47b9a Library: add a simple usage for clarity 2023-09-29 18:45:47 +02:00
bfc4a60a09 Library: rework ThreadWrapper_test
...while the change in the thread-wrapper implementation was drop-in,
this test in the existing from is questionable: it actually tests locking
2023-09-29 17:46:24 +02:00
201672a0ad Library: reconsider join / stringify API
- it is not directly possible to provide a variadic join(args...),
  due to overload resolution ambiguities

- as a remedy, simplify the invocation of stringify() for the typical cases,
  and provide some frequently used shortcuts
2023-09-29 17:00:13 +02:00
691d2b43fa Library: add shortcut-ctor for own-member function
A common usage pattern is to derive from lib::Thread
and then implement the actual thread function as a member function
of this special-Thread-object (possibly also involving other data members)

Provide a simplified invocation for this special case,
also generating the thread-id automatically from the arguments
2023-09-28 17:45:32 +02:00
2c18c39c18 Library: complete the Thread-joining policy
after all this groundwork, implementing the invocation,
capturing and hand-over of results is simple, and the
thread-wrapper classes became fairly understandable.
2023-09-28 02:09:36 +02:00
620639b7ce Library: augment the »Either« wrapper to funciton invocation
This relieves the Thread policy from a lot of technicalities,
while also creating a generally useful tool: the ability to invoke
/anything callable/ (thanks to std::invoke) in a fail-safe way and
transform the exception into an Either type
2023-09-27 23:17:56 +02:00
9c0fa7139d Library: capture and transport the exception itself
...using the std::exception_ptr and helpers, we can now reliably
transport any exception object as the »right« value of the »Either«
2023-09-27 02:51:00 +02:00
4348e110cb Library: change of plan - retain the »Either« wrapper
on second thought, the ability to transport an exception still seems
worthwhile, and can be achieved by some rearrangements in the design.

As preparation, reorganise the design of the Either-wrapper (lib::Result)
2023-09-27 01:27:53 +02:00
3fa4f02737 Library: new thread-wrapper implementation complete
- relocate some code into a dedicated translation unit to reduce #includes
- actually set the thread-ID (the old implementation had only a TODO at that point)
2023-09-26 02:32:48 +02:00
67b010ba7e Library: (re)introduce the distinction join / detach
While it would be straight forward from an implementation POV
to just expose both variants on the API (as the C++ standard does),
it seems prudent to enforce the distinction, and to highlight the
auto-detaching behaviour as the preferred standard case.

Creating worker threads just for one computation and joining the results
seemed like a good idea 30 years ago; today we prefer Futures or asynchronous
messaging to achieve similar results in a robust and performant way.

ThreadJoinable can come in handy however for writing unit tests, were
the controlling master thread has to wait prior to perform verification.

So the old design seems well advised in this respect and will be retained
2023-09-26 01:00:00 +02:00
c9a0203492 Library: gut and remould the existing thread-wrapper
- cut the ties to the old POSIX-based custom threadpool framework
- remove operations deemed no longer necessary
- sync() obsoleted by the new SyncBarrier
- support anything std::invoke supports
2023-09-25 16:27:38 +02:00
11cb53a406 Library: investigate Mutex+Condition-Var for comparison
...which is the technique used in the existing Threadpool framwork.
As expected, such a solution is significantly slower than the new
atomics-based implementation. Yet how much slower is still striking.
2023-09-24 21:52:38 +02:00
7474f56e89 Library: investigate performance of SyncBarrier
Timing measurements in concurrent usage situation.
Observed delay is in the order of magnitude of known scheduling leeway;
assuming thus no relevant overhead related to implementation technique
2023-09-24 20:38:27 +02:00
c183045dfa Library: switch Microbenchmark setup to C++17 threads
Over time, a collection of microbenchmark helper functions was
extracted from occasional use -- including a variant to perform
parallelised microbenchmarks. While not used beyond sporadic experiments yet,
this framework seems a perfect fit for measuring the SyncBarrier performance.

There is only one catch:
 - it uses the old Threadpool + POSIX thread support
 - these require the Threadpool service to be started...
 - which in turn prohibits using them for libary tests

And last but not least: this setup already requires a barrier.

==> switch the existing microbenchmark setup to c++17 threads preliminarily
    (until the thread-wrapper has been reworked).
==> also introduce the new SyncBarrier here immediately
==> use this as a validation test of the setup + SyncBarrier
2023-09-24 18:07:28 +02:00
35ff53a716 Library: generalise pipeline summation into fold-left
Using the same building blocks, this operation can be generalised even more,
leading to a much cleaner implementation (also with better type deduction).

The feature actually used here, namely summing up all values,
can then be provided as a convenience shortcut, filling in std::plus
as a default reduction operator.
2023-09-24 02:45:43 +02:00
b416a67bb9 Library: extract summation of pipeline results
...first used as part of the test harness;
seemingly this is a generic and generally useful shortcut,
similar to algorithm::reduce (or some kind of fold-left operation)
2023-09-23 19:39:08 +02:00
b15281d44b Library: implement and verify SyncBarrier 2023-09-23 18:05:17 +02:00
6735857f3b Library: draft a SyncBarrier latch
Intended as replacement for the Mutex/ConditionVar based barrier
built into the exiting Lumiera thread handling framework and used
to ensure safe hand-over of a bound functor into the starting new
thread. The standard requires a comparable guarantee for the C++17
concurrency framework, expressed as a "synchronizes_with" assertion
along the lines of the Atomics framework.

While in most cases dedicated synchronisation is thus not required
anymore when swtiching to C++17, some special extended use cases
remain to be addressed, where the complete initialisation of
further support framework must be ensured.

With C++20 this would be easy to achieve with a std::latch, so we
need a simple workaround for the time being. After consideration of
the typical use case, I am aiming at a middle ground in terms of
performance, by using a yield-wait until satisfying the latch condition.
2023-09-22 21:55:53 +02:00
416895b5b2 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
2023-09-21 23:23:55 +02:00
1c6ee62c1a Activity-Lang: allow to verify invocation param in test
requires to supplement EventLog matching primitives
to pick and verify a specific positional argument.

Moreover, it is more or less arbitrary which job invocation parameters
are unpacked and exposed for verification; we'll have to see what is
actually required for writing tests...
2023-08-15 20:03:01 +02:00
e3f1aa4f7c Activity-Lang: support negative assertions for tests
Testcase (detect function invocation) passes now as expected


Some Library / Framework changes

- rename event-log-test.cpp
- allow the ExpectString also to work with concatenated expectation strings


Remark: there was a warning in the comment in event-log.hpp,
pointing out that negative assertions are shallow.

However, after the rework in 9/2018 (commit: d923138d1)
...this should no longer be true, since we perform proper backtracking,
leading to an exhaustive search.
2023-08-14 19:25:56 +02:00
49f2e34e4c Library: extract type rebinding helper
...turns out this is entirely generic and not tied to the context
within ActivityDetector, where it was first introduced to build a
mock functor to log all invocations.

Basically this meta-function generates a new instantiation of the
template X, using the variadic argument pack from template U<ARGS...>
2023-08-01 14:52:20 +02:00
824a626c2e Block-Flow: investigate proper working of on-demand allocation
Library: add "obvious" utility to the IterExplorer, allowing to
         materialise all contents of the Pipeline into a container

...use this to take a snapshot of all currently active Extent addresses
2023-07-12 19:19:41 +02:00
ccf0710903 Block-Flow: maintain an »Epoch« within the raw allocation Extent
- the idea is to use slot-0 in each extent for administrative metadata
- to that end, a specialised GATE-Activity is placed into slot-0
- decision to use the next-pointer for managing the next free slot
- thus we need the help of the underlying ExtentFamily for navigating Extents

Decision to refrain from any attempt to "fix" excessive memory usage,
caused by Epochs still blocked by pending IO operations. Rather, we
assume the engine uses sane parametrisation (possibly with dynamic adjustment)
Yet still there will be some safety limit, but when exceeding this limit,
the allocator will just throw, thereby killing the playback/render process
2023-07-09 01:32:27 +02:00
130bc095d9 the new design takes the old name
The second design from 2017, based on a pipeline builder,
is now renamed `TreeExplorer` ⟼ `IterExplorer` and uses
the memorable entrance point `lib::explore(<seq>)`

✔
2023-06-22 20:23:55 +02:00
d109f5e1fb bye bye Monad (closes #1276)
after completing the recent clean-up and refactoring work,
the monad based framework for recursive tree expansion
can be abandoned and retracted.

This approach from functional programming leads to code,
which is ''cool to write'' yet ''hard to understand.''

A second design attempt was based on the pipeline and decorator pattern
and integrates the monadic expansion as a special case, used here to
discover the prerequisites for a render job. This turned out to be
more effective and prolific and became standard for several exploring
and backtracking algorithms in Lumiera.
2023-06-22 20:23:55 +02:00
a1c1456849 Job-Planning: dispose of FrameCoord in pipeline and Dispatcher interface
...as a preparation for solving a logical problem with the Planning-Pipeline;
it can not quite work as intended just by passing down the pair of
current ticket and dependent ticket, since we have to calculate a chained
calculation of job deadlines, leading up to the root ticket for a frame.

My solution idea is to create the JobPlanning earlier in the pipeline,
already *before* the expansion of prerequisites, and rather to integrate
the representation of the dependency relation direcly into JobPlanning
2023-06-18 03:50:48 +02:00
f84517547b Dispatcher-Pipeline: coordination of base tick and prerequisite expansion
- had to fix a logical inconsistency in the underlying Expander implementation
  in TreeExplorer: the source-pipeline was pulled in advance on expansion,
  in order to "consume" the expanded element immediately; now we retain
  this element (actually inaccessible) until all of the immediate
  children are consumed; thus the (visible) state of the PipeFrameTick
  stays at the frame number corresponding to the top-level frame Job,
  while possibly expanding a complete tree of flexible prerequisites

This test now gives a nice visualisation of the interconnected states
in the Job-Planning pipeline. This can be quite complex, yet I still think
that this semi-functional approach with a stateful pipeline and expand functors
is the cleanest way to handle this while encapsulating all details
2023-06-14 18:12:41 +02:00
ad173540d9 Library: allow for a stop condition in iterator pipeline
...introduced in preparation for building the Dispatcher pipeline,
which at its core means to iterate over a sequence of frame positions;
thus we need a way to stop rendering at a predetermined point...
2023-06-01 16:48:27 +02:00
94cec423d0 Job-Planning: switch to processing references
...which uncovers further deeply nested problems,
especially when referring to non-copyable types.

Thus need to construct a common type that can be used
both to refer to the source elements and the expanded elements,
and use this common type as result type and also attempt to
produce better diagnostic messages on type mismatch....
2023-05-23 01:08:05 +02:00
0df0fd001e Library: fix follow-up problems with const correctness
...the improved const correctness on STL iterators uncovered another
latent problem with out diagnositc format helper, which provide
consistently rounded float and double output, but failed to take
CV-qualifiaction into account
2023-05-23 01:07:53 +02:00