Commit graph

120 commits

Author SHA1 Message Date
ee09a2eff2 Scheduler: completed implementation of Layer-2
...some further checks
...one integration test case needs to be written
2023-10-18 17:29:41 +02:00
93fcebb331 Scheduler: implement and verify postDispatch 2023-10-18 16:39:08 +02:00
666546856f Scheduler: design the core API operation - postDispatch
This central operation sits at a crossroad and is used
- from external clients to fed new work to the Scheduler
- from Workers to engage into execution of the next Activity
- recursively from the execution of an Activity-chain

From these requirements the semantics of behaviour can be derived
regarding the GroomingToken and the result values, which indicate
when follow-up work should be processed
2023-10-18 15:50:11 +02:00
55967cd649 Scheduler: work retrieval implementation
- simple approach, delegating to Layer-1
- deliberately no error handling
- GroomingToken not dropped
2023-10-18 04:18:01 +02:00
b57503fb97 Scheduler: define expected behaviour for work retrieval
still not quite sure how to implement it,
but working down from first principles to define test scenarios first...
2023-10-18 02:59:58 +02:00
aa60869082 Scheduler: decision logic for actual dispatch of activities 2023-10-18 01:38:58 +02:00
fa391d1267 Scheduler: torture test the thread access logic
Ensure the GroomingToken mechanism indeed creates an
exclusive section protected against concurrent corruption:

Use a without / with-protection test and verify
the results are exact vs. grossly broken
2023-10-17 21:35:37 +02:00
1223772f14 Scheduler: implement thread access logic
T thread holding the »Grooming Token" is permitted to
manipulate scheduler internals and thus also to define new
activities; this logic is implemented as an Atomic lock,
based on the current thread's ID.
2023-10-17 20:37:32 +02:00
862933e809 Scheduler: define API for Layer-2
Notably both Layers are conceived as functionality providers;
only at Scheduler top-Level will functionality be combined with
external dependencies to create the actual service.
2023-10-17 19:20:53 +02:00
0431a14584 Scheduler: Layer-1 complete and tested 2023-10-17 04:35:58 +02:00
430f1af4c5 Scheduler: define water-level for prioritisation 2023-10-17 03:38:28 +02:00
152413589c Scheduler: clarify role of the Time parameter
At first sight, this seems confusing; there is a time window,
there is sometimes a `when` parameter, and mostly a `now` parameter
is passed through the activation chain.

However, taking the operational semantics into account, the existing
definitions seem to be (mostly) adequate already: The scheduler is
assumed to activate a chain only ''when'' the defined start time is reached.
2023-10-17 03:04:19 +02:00
c76e5488bd Scheduler: plot steps towards integration
(1) SchedulerInvocation_test
    »Layer-1« : Queue operation

(2) SchedulerCommutator_test
    »Layer-2« : Activity execution

(3) SchedulerUsage_test
    Component End-to-End
2023-10-16 23:57:22 +02:00
3af6a54219 Library/Application: complete technology switch (closes #1279)
As follow-up to the rework of thread-handling, likewise also
the implementation base for locking was switched over from direct
usage of POSIX primitives to the portable wrappers available in
the C++ standard library. All usages have been reviewed and
modernised to prefer λ-functions where possible.

With this series of changes, the old threadpool implementation
and a lot of further low-level support facilities are not used
any more and can be dismantled. Due to the integration efforts
spurred by the »Playback Vertical Slice«, several questions of
architecture could be decided over the last months. The design
of the Scheduler and Engine turned out different than previously
anticipated; notably the Scheduler now covers a wider array of
functionality, including some asynchronous messaging. This has
ramifications for the organisation of work tasks and threads,
and leads to a more deterministic memory management. Resource
management will be done on a higher level, partially superseding
some of the concepts from the early phase of the Lumiera project.
2023-10-16 01:44:04 +02:00
685be1b039 Library/Application: consolidate Monitor API and usage
This is Step-2 : change the API towards application

Notably all invocation variants to support member functions
or a reference to bool flags are retracted, since today a
λ-binding directly at usage site tends to be more readable.

The function names are harmonised with the C++ standard and
emergency shutdown in the Subsystem-Runner is rationalised.

The old thread-wrapper test is repurposed to demonstrate
the effectiveness of monitor based locking.
2023-10-15 20:42:55 +02:00
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
1c4f605e8f Library/Application: switch WorkForce
The WorkForce (passive worker pool) has been coded just recently,
and -- in anticipation of this refactoring -- directly against std::thread
instead of using the old framework.

...the switch is straight-forward, using the default case
...add the ability to decorate the thread-IDs with a running counter
2023-10-12 22:00:55 +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
332ad0e920 Testsuite: fix regression
FamilyMember::allocateNextMember() was actually a post-increment,
so (different than with TypedCounter) here no correction is necessary


As an asside, WorkForce_test is sometimes unstable immediately after a build.
Seemingly a headstart of 50µs is not enough to compensate for scheduler leeway
2023-10-05 00:39:29 +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
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
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
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
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