Commit graph

6167 commits

Author SHA1 Message Date
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
84369201f4 Library: thread-wrapper design and code-organisation 2023-09-25 17:55:50 +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
6c17204dad Project: Collect a list of »Focus Topics«
See discussion in September developer meeting:
http://localhost:8888/documentation/devel/meeting_summary/2023-09-13.html

See Tag "FocusTopic" in the Lumiera issue tracker
https://issues.lumiera.org/report/17
2023-09-15 18:30:16 +02:00
997fc36c81 Workforce: implementation complete 2023-09-09 23:42:13 +02:00
397ded86df Workforce: verify error handling and wait on shutdown
...seemingly the implementation is complete now
2023-09-09 03:31:46 +02:00
9ccdfa24f7 Workforce: invoke a exit hook prior to worker termination
...essential for clean-up work, especially to drop
claimed resources reliably, even in case of error.
2023-09-09 02:31:16 +02:00
dd62240900 Workforce: terminate after excessive idle cycles
- count each consecutive idle cycle
- by default, terminate after 100 idle cycles (2 sec)
2023-09-09 01:47:15 +02:00
b493f15333 Workforce: configure and demonstrate idle-wait 2023-09-09 01:12:10 +02:00
67a3e87dbc Workforce: demonstrate controlled worker-stop
- workers can be controlled by the return-value from the work functor
- this test could be brittle, since it's based on timing and CPU speed
2023-09-08 14:32:37 +02:00
ef5365057a Workforce: demonstrate standard behaviour
- can activate / scale up
- work functor invoked repeatedly
2023-09-08 14:07:23 +02:00
5e16ed11bd Workforce: detach terminating threads instead of joining
...which however brings the problem that we can no longer block the destructor
of WorkForce by simply joining on all joinable threads (there is a race
between testing joinable() and invoking join(), which does not tolerate
non-joinable state.

There is a second problem: we need to detect and clean-up terminated workers,
even for just finding out how many workers are still active. Fortunately
doing so also solves the waiting problem in the destructor
2023-09-08 04:26:29 +02:00
81cab9a675 Workforce: emergency brake
While in principle it would be possible (and desirable)
to control worker behaviour exclusively through the Work-Functor's return code,
in practice we must concede that Exceptions can always happen from situations
beyond our control. And while it is necessary for the WorkForce-dtor to
join and block (we can not just pull away the resources from running threads),
the same destructor (when called out of order) must somehow be able
at least to ask the running threads to terminate.

Especially for unit tests this becomes an obnoxious problem -- otherwise
each test failure would cause the test runner to hang.

Thus adding an emergency halt, and also improve setup for tests
with a convenience function to inject a work-function-λ
2023-09-08 02:48:30 +02:00
b8e52d008c Workforce: configuration and initialisation of workers
- use a template parameter to allow for hook into local facilities (Scheduler)
- pass config initialisation down through constructors
2023-09-07 17:15:25 +02:00
cf7c2d1327 Workforce: analysis and design
- investigate consistency guarantees through acquire-release
  ==> turns out we do not need a fence, but it is tantamount
      to have a guard variable and actually load and check
      the value to ensure we indeed get a happens-before

- elaborate design of the WorkForce
  + no shared control variables necessary
  + no ability to forcibly shut-down the WorkForce
  + rather, all control will be exerted through the return value
    of the Work-Functor
2023-09-06 19:18:37 +02:00
38ab5a6aa9 Workforce: draft simple usage
...start with an oversimplified implementation...
2023-09-05 00:24:33 +02:00
70cd8af806 Workforce: requirement analysis 2023-09-05 00:22:17 +02:00
2e28f5d278 Activity-Lang: abstracted execution framework complete and tested (closes: #1319) 2023-09-03 01:50:50 +02:00
95ae12bba1 Activity-Lang: complete handling of IO activities 2023-09-03 00:40:37 +02:00
b3b6f7524c Activity-Lang: outline for wiring async IO activities
...relies on the same building pattern, with the notable difference
that the chain is severed, providing an additional NOTIFY as re-entrance point
2023-09-02 22:36:02 +02:00
73a67886f0 Activity-Lang: wiring for internal/planning job
...uses just the minimal wiring and is thus already implemented :-)
2023-09-02 03:35:02 +02:00
e6d233def2 Activity-Lang: instrumentation to make the complete call sequence visible
No new functionality, and implementation works as expected.

This test case covers an especially tricky setup, where a calculation
shall be triggered from an external event, while ensuring that the actual
processing can start only after also the regular time-bound scheduling
has taken place (this might be used to prevent an unexpectedly early
external signal to cause writing into an output buffer before the
defined window of data delivery)
2023-09-02 03:08:13 +02:00
6563688e07 Activity-Lang: now able to demonstrate the intended call sequence
...based on the new ability in the ActivityDetector, we can now assign
a custom λ, which deflects back the ctx.post() call into the ActivityLang
instance used for this test case.

While the previously seen behaviour was correct, it was not the call sequence
expected in the real implementation; with this change, on the main-chain
activation the post() now immediately dispatches the notification, which in turn
dispatches the rest of the chain, so that the JobFunctor is indeed
called in this second test case as expected
2023-09-02 01:48:25 +02:00
f3cf178388 Activity-Lang: ability to hook in a fake implementation
Up to now, the DiagnosticFun mock in ActivityDetector only
created an EventLog entry on invocation and was able to retunr
a canned result value. Yet for the job invocation scenario test,
it would be desirable to hook-in a λ with a fake implementation
into the ExecutionContext. As a further convenience, the
return value is now default initialised, instead of being
marked as uninitialised until invocation of "returning(val)"
2023-09-01 21:59:25 +02:00
fd8716d398 Activity-Lang: demonstrate multi-stage Gate
...seems to work, but not really happy with the test setup,
since in real usage the post()-calls would dispatch, while here,
using the ActivityDetector, these calls just log invoation,
and thus the activation is not passed on
2023-09-01 19:23:27 +02:00
44e840f27c Activity-Lang: implement optional notification builders 2023-09-01 19:03:37 +02:00
963dc38088 Activity-Lang: introduce some shorthand notation
...regarding the kind of activity (the verb),
and also for some special case access of payload data;
deliberately asserting the correct verb, but no mandatory check,
since this whole Activity-Language is conceived as cohesive
and essentially sealed (not meant to be extended)
2023-09-01 17:41:40 +02:00
789bcd72c2 Activity-Lang: better solution to demonstrate time access
...to show in test that indeed the actual time is retrieved on each activation,
we can assign a λ -- which is rigged to increase the time on each access
2023-09-01 17:18:32 +02:00
67c71725a4 Activity-Lang: access current scheduler time dynamically
It is not sufficient just to pass this "current time" as parameter
into the ActivityLang::dispatchChain(), since some Activities within
this chain will essentially be long-running (think rendering); thus
we need a real callback from within the chain. The obvious solution
is to make this part of the Execution Context, which is an abstraction
of the scheduler environment anyway
2023-09-01 02:44:29 +02:00
14effc2349 Activity-Lang: consider logic for dependency notification
...turns out there is still a lot of leeway in the possible implementation,
and seemingly it is too early to decide which case to consider the default.
Thus I'll proceed with the drafted preliminary solution...

- on primary-chain, an inhibited Gate dispatches itself into future for re-check
- on Notification, activation happens if and only if this very notification opens the Gate
- provide a specifically wired requireDirectActivation() to allow enforcing a minimal start time
2023-08-31 20:18:35 +02:00
07fcc89e6a Activity-Lang: complete execution of the basic CalculationJob scheme
...assembled from parts already implemented

TODO
 - need a way to access the »current scheduler time«
 - need builder extension points to connect notifications
2023-08-31 02:24:01 +02:00
32c08c0307 Activity-Lang: also dispatch notifications 2023-08-31 02:11:07 +02:00
900f46b1d5 Activity-Lang: framework to execute a chain of Activities
without and error or concurrency handling (which is the responsibility
of the Scheduler-Layer-2; just the sequencing of individual activations
2023-08-30 22:19:57 +02:00
2746743135 Activity-Lang: invoke the configured Job-Functor
...this completes the basic setup
- Term builder mechanism working properly
- Memory allocator behaves sane
- the simple default wiring allows to invoke a Job
2023-08-29 19:10:24 +02:00
cda1cdd975 Activity-Lang: verify memory allocation and connectivity 2023-08-29 18:46:37 +02:00
3bd4305dab Activity-Lang: create standard wiring for CALC-Term 2023-08-29 17:36:56 +02:00
80a48abcf4 Activity-Lang: determine role of the time window parameters 2023-08-29 16:40:52 +02:00
ae89831275 Activity-Lang: wire Job invocation in the activity::Term builder 2023-08-29 04:19:19 +02:00
e98fe1e78b Activity-Lang: scaffolding to create a simple Term 2023-08-29 03:18:47 +02:00
8e20fa6de1 Activity-Lang: framework for building an Activity-Term
While the ''general direction'' seems clear, some in-depth
analysis was required to find out what information can reasonably
be expected to be available at this point.

The decision was made to shift the actual deadline calculation
into the Job-Planning altogether, assuming that a preliminary solution
based on data implicitly available there will be enough to implement
simple linear playback, while precise management of job start times
can be added in later, when observation of actual timing behaviour
is available...
2023-08-29 01:41:17 +02:00
568957b75d Activity-Lang: prevent spurious activations after notification
Solved by special treatment of a notification, which happens
to decrement the latch to zero: in this case, the chain is
dispatched, but also the Gate is locked permanently to block
any further activations scheduled or forwareded otherwise
2023-08-23 01:03:11 +02:00