Skip to content

Commit 2a53bd9

Browse files
authored
Merge pull request #13 from ikelaiah/feat/v0.9.0-task-coordination
Feat/v0.9.0 task coordination
2 parents 5610b21 + 505da35 commit 2a53bd9

44 files changed

Lines changed: 4170 additions & 135 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,4 @@ jobs:
7171

7272
- name: Build all examples
7373
shell: bash
74-
run: |
75-
set -e
76-
for lpi in examples/*/*.lpi; do
77-
echo "==> Building $lpi"
78-
lazbuild "$lpi"
79-
done
74+
run: sh ./build-examples.sh Release

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ tests/TestRunner
2727
tests/TestRunner.exe
2828
tests/lib/
2929

30+
# Consolidated example binary outputs
31+
/example-bin/
32+
3033
# Debug files
3134
*.dbg
3235
*.dcu
@@ -58,4 +61,4 @@ $RECYCLE.BIN/
5861
## Linux
5962
*~
6063
.directory
61-
.Trash-*
64+
.Trash-*

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,64 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [0.9.0] - 2026-07-16
8+
9+
### Added
10+
11+
- `ThreadPool.Tasks`, a shared task coordination unit for both pool
12+
implementations
13+
- `Submit` overloads returning `IThreadPoolTask` handles with pending, running,
14+
completed, failed, and cancelled states
15+
- Timeout-aware `TrySubmit` overloads matching all four existing callback forms
16+
- Individual task waiting, failure messages, terminal-state observation, and
17+
race-safe pending cancellation
18+
- `EThreadPoolDeadlock` protection when a callback or error handler attempts to
19+
wait for its own task
20+
- `IThreadPoolTaskBatch` for snapshot-based waits, status counts, indexed task
21+
access, and batch pending cancellation
22+
- Chunked `SubmitRange` overloads for indexed procedures and methods, with
23+
inclusive bounds, automatic or explicit chunk sizes, and a returned batch
24+
- v0.8 interface compile sentinel plus task, batch, cancellation-race, bounded
25+
admission, lifetime, and range regression tests
26+
- `TaskCoordination` API tour, plus production-shaped coordinated file-backup
27+
and parallel log-analysis examples
28+
- Root-level PowerShell and POSIX scripts that build every example into
29+
`example-bin/`
30+
- Complete task API documentation
31+
- Separate benchmark cases for legacy queueing, tracked submission, individual
32+
indexed submission, and chunked ranges
33+
34+
### Changed
35+
36+
- Simple pool queue storage now uses `IWorkItem` consistently, allowing shared
37+
tracked work items while preserving its dynamically growing O(1) FIFO
38+
- Both workers execute tracked work through one shared error/state transition
39+
boundary
40+
- Package version advanced to 0.9.0 and includes `ThreadPool.Tasks`
41+
42+
### Performance
43+
44+
- Legacy `Queue` retains its untracked path, so programs that do not request
45+
task handles do not allocate task state or completion events
46+
- Task completion events are created lazily only when unfinished work is waited
47+
on
48+
- Automatic ranges create at most `ThreadCount * 4` queue entries instead of
49+
one entry per index
50+
- Windows/FPC 3.2.2 release checks kept the legacy 20,000-task median within the
51+
v0.8.5 10% regression budget; a 200,000-index Simple range was over 60x faster
52+
than individual tracked submissions in the orientation run
53+
54+
### Compatibility
55+
56+
- The v0.8 `IThreadPool` interface and GUID are unchanged; new interface-based
57+
callers use the separate `IThreadPoolTaskSource` capability
58+
- Existing `Queue`, `TryQueue`, `WaitForAll`, lifecycle, error, constructor, and
59+
`GlobalThreadPool` contracts remain available
60+
- Cancellation applies only to pending callbacks. It never interrupts running
61+
code, and a bounded-queue tombstone may occupy its slot until dequeued
62+
- Bounded `SubmitRange` calls from the same pool's worker are rejected with
63+
`EThreadPoolDeadlock` to prevent queue-starvation deadlocks
64+
765
## [0.8.5] - 2026-07-14
866

967
### Added

README.md

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# ThreadPool for Free Pascal
66

7-
[![Version](https://img.shields.io/badge/version-0.8.5-8B5CF6.svg)](CHANGELOG.md)
7+
[![Version](https://img.shields.io/badge/version-0.9.0-8B5CF6.svg)](CHANGELOG.md)
88
[![License: MIT](https://img.shields.io/badge/License-MIT-1E3A8A.svg)](LICENSE.md)
99
[![Free Pascal](https://img.shields.io/badge/Free%20Pascal-3.2.2+-3B82F6.svg)](https://www.freepascal.org/)
1010
[![Lazarus](https://img.shields.io/badge/Lazarus-4.0+-60A5FA.svg)](https://www.lazarus-ide.org/)
@@ -19,11 +19,12 @@ producer-consumer workloads that need backpressure.
1919

2020
[Quick start](#quick-start) · [Cheat sheet](docs/CHEATSHEET.md) ·
2121
[API documentation](#documentation) · [Examples](examples/) ·
22-
[v0.8.5 release notes](docs/release-notes-v0.8.5.md)
22+
[v0.9.0 release notes](docs/release-notes-v0.9.0.md)
2323

2424
> [!TIP]
25-
> **New in v0.8.5:** refreshed project identity, a shorter README, and a new
26-
> [cheat sheet](docs/CHEATSHEET.md). Runtime behavior is unchanged from v0.8.0.
25+
> **New in v0.9.0:** observable task handles, task batches, efficient
26+
> chunked ranges, and pending-work cancellation. Existing v0.8 queueing code
27+
> remains source-compatible. See the [task API](docs/ThreadPool.Tasks-API.md).
2728
2829
> [!NOTE]
2930
> This library is designed for simple parallel processing and learning-friendly
@@ -47,6 +48,9 @@ Both implementations provide:
4748
- event-driven workers with no polling sleeps;
4849
- four task forms: procedures, methods, and indexed variants;
4950
- timeout-aware `TryQueue` and `WaitForAll` overloads;
51+
- observable `Submit`/`TrySubmit` task handles;
52+
- task batches and chunked `SubmitRange` processing;
53+
- race-safe cancellation of work that has not started;
5054
- deterministic, draining `Shutdown`;
5155
- captured worker exceptions through `LastError`, `Errors`, and `OnError`; and
5256
- automatic worker-count selection with safety limits.
@@ -130,6 +134,33 @@ end.
130134
The first constructor argument is the worker count; `0` selects
131135
`TThread.ProcessorCount`. The second is queue capacity.
132136

137+
### Tasks, batches, and ranges
138+
139+
Add `ThreadPool.Tasks` when work needs to be observed or coordinated:
140+
141+
```pascal
142+
uses
143+
ThreadPool.Tasks, ThreadPool.Simple;
144+
145+
var
146+
Task: IThreadPoolTask;
147+
Batch: IThreadPoolTaskBatch;
148+
begin
149+
Task := GlobalThreadPool.Submit(@DoWork);
150+
if Task.WaitFor(250) and (Task.State = ttsFailed) then
151+
WriteLn(Task.ErrorMessage);
152+
153+
Batch := GlobalThreadPool.SubmitRange(@ProcessItem, 0, 999);
154+
Batch.WaitFor;
155+
end;
156+
```
157+
158+
`Task.Cancel` succeeds only while the task is pending. It never interrupts a
159+
running callback. A range uses a small number of chunks by default instead of
160+
creating one queue item per index. See the
161+
[task API](docs/ThreadPool.Tasks-API.md) for batch counts, timeouts, explicit
162+
chunk sizes, and bounded-pool rules.
163+
133164
## Lifecycle and timeouts
134165

135166
Both pools follow one monotonic lifecycle:
@@ -173,6 +204,9 @@ expires.
173204
> also have no automatic execution deadline; add cancellation or
174205
> application-level timeouts where needed.
175206
207+
Task handles and batches do not retain their pool. They may be kept after a
208+
pool is freed, because pool destruction drains accepted work first.
209+
176210
## Error handling
177211

178212
Task exceptions are caught so a worker failure does not terminate the pool.
@@ -226,12 +260,30 @@ Requirements:
226260

227261
## Examples
228262

263+
Build every example in Release mode from the repository root:
264+
265+
```powershell
266+
.\build-examples.ps1
267+
```
268+
269+
```sh
270+
sh ./build-examples.sh
271+
```
272+
273+
Both scripts discover `examples/*/*.lpi` automatically and place the
274+
executables in the ignored root-level `example-bin/` directory. Pass `Default`
275+
to build the default mode, or use `-Rebuild` in PowerShell / `--rebuild` in the
276+
shell script to force a complete rebuild.
277+
229278
| Start with | Demonstrates |
230279
| --- | --- |
231280
| [`Starter`](examples/Starter/) | Smallest compilable program with explanatory comments |
232281
| [`SimpleDemo`](examples/SimpleDemo/) | Procedures, methods, indexes, and the global pool |
233282
| [`ProdConSimpleDemo`](examples/ProdConSimpleDemo/) | Basic bounded-pool ownership and queueing |
234283
| [`SimpleErrorHandlingBasic`](examples/SimpleErrorHandlingBasic/) | Reading captured errors after completion |
284+
| [`TaskCoordination`](examples/TaskCoordination/) | Task handles, batches, ranges, and cancellation |
285+
| [`CoordinatedFileBackup`](examples/CoordinatedFileBackup/) | Per-file progress, critical-failure policy, and pending cancellation |
286+
| [`ParallelLogAnalyzer`](examples/ParallelLogAnalyzer/) | Chunked analysis followed by a parallel reporting phase |
235287

236288
More focused samples cover:
237289

@@ -241,8 +293,11 @@ More focused samples cover:
241293
[`SimpleWordCounter`](examples/SimpleWordCounter/), and
242294
[`ProdConMessageProcessor`](examples/ProdConMessageProcessor/);
243295
- advanced callbacks: [`SimpleErrorHandling`](examples/SimpleErrorHandling/);
244-
- real I/O: [`ParallelFileHasher`](examples/ParallelFileHasher/) and
245-
[`ParallelUrlFetcher`](examples/ParallelUrlFetcher/).
296+
- real I/O: [`ParallelFileHasher`](examples/ParallelFileHasher/),
297+
[`ParallelUrlFetcher`](examples/ParallelUrlFetcher/), and
298+
[`CoordinatedFileBackup`](examples/CoordinatedFileBackup/);
299+
- coordinated data processing:
300+
[`ParallelLogAnalyzer`](examples/ParallelLogAnalyzer/).
246301

247302
## Documentation
248303

@@ -251,9 +306,10 @@ More focused samples cover:
251306
| [Cheat sheet](docs/CHEATSHEET.md) | Calls and safety rules at a glance |
252307
| [Simple API](docs/ThreadPool.Simple-API.md) | Complete unbounded-pool reference |
253308
| [Producer-Consumer API](docs/ThreadPool.ProducerConsumer-API.md) | Complete bounded-pool reference |
309+
| [Tasks API](docs/ThreadPool.Tasks-API.md) | Submit, wait, batch, range, and cancellation contracts |
254310
| [Simple technical guide](docs/ThreadPool.Simple-Technical.md) | Internal design and synchronization |
255311
| [Producer-Consumer technical guide](docs/ThreadPool.ProducerConsumer-Technical.md) | Queue and backpressure internals |
256-
| [v0.8.5 release notes](docs/release-notes-v0.8.5.md) | Current release scope and compatibility |
312+
| [v0.9.0 release notes](docs/release-notes-v0.9.0.md) | Current release scope and compatibility |
257313
| [Changelog](CHANGELOG.md) | Full version history |
258314

259315
The banner's editable source is

benchmarks/README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,23 @@ lazbuild --build-mode=Release benchmarks/ThreadPoolBenchmark.lpi
77
./benchmarks/ThreadPoolBenchmark
88
```
99

10-
The same source compiles against v0.7.0 and v0.8.0. It reports:
10+
The v0.9.0 benchmark reports:
1111

12-
- completion time for a 20,000-task burst through each pool;
12+
- completion time for a 20,000-task legacy `Queue` burst through each pool;
13+
- completion time for an equivalent tracked `Submit` burst;
14+
- individual tracked submission versus chunked `SubmitRange` for 200,000
15+
indexed calls;
1316
- average queue-to-start latency after workers have been idle.
1417

18+
For comparisons with v0.7.0 and v0.8.x, use the legacy queue and idle fields;
19+
the tracked and range APIs did not exist in those releases.
20+
1521
Run each version several times on the same otherwise-idle machine and compare
1622
medians. Debug logging must be disabled in both versions so console I/O is not
1723
included in the scheduler measurement.
24+
25+
Do not enforce absolute millisecond thresholds on shared CI runners. Compare
26+
five-run medians on the same otherwise-idle machine. The v0.9.0 release budget
27+
allows at most a 10% regression in the legacy queue medians and expects the
28+
Simple chunked range case to be at least 5x faster than individual tracked
29+
indexed submissions.

0 commit comments

Comments
 (0)