Thread System 0.3.1
High-performance C++20 thread pool with work stealing and DAG scheduling
Loading...
Searching...
No Matches
job_cancellation_example.cpp File Reference

Cooperative job cancellation and graceful shutdown patterns. More...

#include <kcenon/thread/core/thread_pool.h>
#include <kcenon/thread/core/job.h>
#include <kcenon/thread/core/thread_worker.h>
#include <kcenon/thread/utils/formatter.h>
#include <iostream>
#include <chrono>
#include <thread>
#include <atomic>
Include dependency graph for job_cancellation_example.cpp:

Go to the source code of this file.

Classes

class  cancellable_long_job
 A job that performs a long-running task with periodic cancellation checks. More...
 
class  non_cancellable_job
 A job that DOES NOT check for cancellation (anti-pattern). More...
 

Functions

void demo_basic_cancellation ()
 Demonstrates basic job cancellation via worker stop.
 
void demo_non_cooperative_job ()
 Demonstrates what happens with non-cooperating jobs.
 
void demo_pool_level_cancellation ()
 Demonstrates pool-level cancellation with multiple workers.
 
void demo_immediate_vs_graceful ()
 Demonstrates immediate vs. graceful shutdown.
 
int main ()
 

Detailed Description

Cooperative job cancellation and graceful shutdown patterns.

Definition in file job_cancellation_example.cpp.

Function Documentation

◆ demo_basic_cancellation()

void demo_basic_cancellation ( )

Demonstrates basic job cancellation via worker stop.

Shows how stopping a worker cancels the currently running job.

Examples
job_cancellation_example.cpp.

Definition at line 137 of file job_cancellation_example.cpp.

138{
139 std::cout << "\n========================================\n";
140 std::cout << "Demo 1: Basic Job Cancellation\n";
141 std::cout << "========================================\n\n";
142
143 // Create pool with single worker
144 auto pool = std::make_shared<thread_pool>("cancellation_demo_pool");
145
146 auto worker = std::make_unique<thread_worker>();
147 pool->enqueue(std::move(worker));
148 pool->start();
149
150 // Submit a long-running cancellable job (10 seconds total)
151 auto long_job = std::make_unique<cancellable_long_job>("long_task", 100);
152 pool->enqueue(std::move(long_job));
153
154 // Let job run for 2 seconds
155 std::cout << "Letting job run for 2 seconds...\n";
156 std::this_thread::sleep_for(std::chrono::seconds(2));
157
158 // Stop the pool (triggers cancellation)
159 std::cout << "\n>>> Calling pool->stop() <<<\n\n";
160 auto stop_start = std::chrono::steady_clock::now();
161 pool->stop();
162 auto stop_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
163 std::chrono::steady_clock::now() - stop_start);
164
165 std::cout << formatter::format("\nPool stopped in {}ms (job cooperated with cancellation)\n",
166 stop_duration.count());
167}
static auto format(const char *formats, const FormatArgs &... args) -> std::string
Formats a narrow-character string with the given arguments.
Definition formatter.h:132

References kcenon::thread::utils::formatter::format().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ demo_immediate_vs_graceful()

void demo_immediate_vs_graceful ( )

Demonstrates immediate vs. graceful shutdown.

Examples
job_cancellation_example.cpp.

Definition at line 256 of file job_cancellation_example.cpp.

257{
258 std::cout << "\n========================================\n";
259 std::cout << "Demo 4: Immediate vs. Graceful Shutdown\n";
260 std::cout << "========================================\n\n";
261
262 // Test 1: Graceful shutdown
263 {
264 std::cout << "--- Graceful Shutdown (immediately_stop = false) ---\n";
265 auto pool = std::make_shared<thread_pool>("graceful_pool");
266 auto worker = std::make_unique<thread_worker>();
267 pool->enqueue(std::move(worker));
268 pool->start();
269
270 // Enqueue multiple jobs
271 for (int i = 0; i < 5; ++i)
272 {
273 auto job = std::make_unique<cancellable_long_job>(
274 formatter::format("graceful_job_{}", i), 20);
275 pool->enqueue(std::move(job));
276 }
277
278 std::this_thread::sleep_for(std::chrono::milliseconds(500));
279
280 std::cout << "Stopping gracefully (pending jobs remain in queue)...\n";
281 pool->stop(false); // Graceful shutdown
282 std::cout << "Done\n\n";
283 }
284
285 // Test 2: Immediate shutdown
286 {
287 std::cout << "--- Immediate Shutdown (immediately_stop = true) ---\n";
288 auto pool = std::make_shared<thread_pool>("immediate_pool");
289 auto worker = std::make_unique<thread_worker>();
290 pool->enqueue(std::move(worker));
291 pool->start();
292
293 // Enqueue multiple jobs
294 for (int i = 0; i < 5; ++i)
295 {
296 auto job = std::make_unique<cancellable_long_job>(
297 formatter::format("immediate_job_{}", i), 20);
298 pool->enqueue(std::move(job));
299 }
300
301 std::this_thread::sleep_for(std::chrono::milliseconds(500));
302
303 std::cout << "Stopping immediately (clearing pending jobs)...\n";
304 pool->stop(true); // Immediate shutdown
305 std::cout << "Done (pending jobs were cleared)\n\n";
306 }
307}
Represents a unit of work (task) to be executed, typically by a job queue.
Definition job.h:136

References kcenon::thread::utils::formatter::format().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ demo_non_cooperative_job()

void demo_non_cooperative_job ( )

Demonstrates what happens with non-cooperating jobs.

Shows the limitation of cooperative cancellation - jobs must check the token.

Examples
job_cancellation_example.cpp.

Definition at line 174 of file job_cancellation_example.cpp.

175{
176 std::cout << "\n========================================\n";
177 std::cout << "Demo 2: Non-Cooperative Job (Anti-Pattern)\n";
178 std::cout << "========================================\n\n";
179
180 auto pool = std::make_shared<thread_pool>("non_coop_pool");
181
182 auto worker = std::make_unique<thread_worker>();
183 pool->enqueue(std::move(worker));
184 pool->start();
185
186 // Submit a job that doesn't check cancellation (5 seconds total)
187 auto stubborn_job = std::make_unique<non_cancellable_job>("stubborn_task", 50);
188 pool->enqueue(std::move(stubborn_job));
189
190 // Let job run for 1 second
191 std::cout << "Letting job run for 1 second...\n";
192 std::this_thread::sleep_for(std::chrono::seconds(1));
193
194 // Stop the pool
195 std::cout << "\n>>> Calling pool->stop() <<<\n";
196 std::cout << "⚠️ Job is NOT checking cancellation token!\n";
197 std::cout << "Worker must wait for job to complete...\n\n";
198
199 auto stop_start = std::chrono::steady_clock::now();
200 pool->stop();
201 auto stop_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
202 std::chrono::steady_clock::now() - stop_start);
203
204 std::cout << formatter::format("\nPool stopped in {}ms (job did NOT cooperate)\n",
205 stop_duration.count());
206 std::cout << "Notice how much longer this took!\n";
207}

References kcenon::thread::utils::formatter::format().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ demo_pool_level_cancellation()

void demo_pool_level_cancellation ( )

Demonstrates pool-level cancellation with multiple workers.

Shows hierarchical cancellation propagating to all workers.

Examples
job_cancellation_example.cpp.

Definition at line 214 of file job_cancellation_example.cpp.

215{
216 std::cout << "\n========================================\n";
217 std::cout << "Demo 3: Pool-Level Multi-Worker Cancellation\n";
218 std::cout << "========================================\n\n";
219
220 auto pool = std::make_shared<thread_pool>("multi_worker_pool");
221
222 // Add 3 workers
223 for (int i = 0; i < 3; ++i)
224 {
225 auto worker = std::make_unique<thread_worker>();
226 pool->enqueue(std::move(worker));
227 }
228 pool->start();
229
230 // Submit multiple jobs to different workers
231 for (int i = 0; i < 3; ++i)
232 {
233 auto job = std::make_unique<cancellable_long_job>(
234 formatter::format("worker_{}_task", i), 100);
235 pool->enqueue(std::move(job));
236 }
237
238 // Let jobs run for 2 seconds
239 std::cout << "All workers running jobs...\n";
240 std::this_thread::sleep_for(std::chrono::seconds(2));
241
242 // Stop the entire pool
243 std::cout << "\n>>> Calling pool->stop() - cancelling ALL workers <<<\n\n";
244 auto stop_start = std::chrono::steady_clock::now();
245 pool->stop();
246 auto stop_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
247 std::chrono::steady_clock::now() - stop_start);
248
249 std::cout << formatter::format("\nAll workers stopped in {}ms\n", stop_duration.count());
250 std::cout << "All jobs received cancellation signal simultaneously!\n";
251}

References kcenon::thread::utils::formatter::format().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int main ( )

Definition at line 309 of file job_cancellation_example.cpp.

310{
311 std::cout << "╔═══════════════════════════════════════════════════════╗\n";
312 std::cout << "║ Thread System - Job Cancellation System Demo ║\n";
313 std::cout << "╚═══════════════════════════════════════════════════════╝\n";
314
315 try
316 {
318 std::this_thread::sleep_for(std::chrono::seconds(1));
319
321 std::this_thread::sleep_for(std::chrono::seconds(1));
322
324 std::this_thread::sleep_for(std::chrono::seconds(1));
325
327
328 std::cout << "\n========================================\n";
329 std::cout << "All demonstrations completed!\n";
330 std::cout << "========================================\n\n";
331
332 std::cout << "Key Takeaways:\n";
333 std::cout << "1. ✅ Jobs MUST check cancellation_token periodically\n";
334 std::cout << "2. ✅ Worker stop() propagates cancellation to running job\n";
335 std::cout << "3. ✅ Pool stop() cancels all workers simultaneously\n";
336 std::cout << "4. ⚠️ Non-cooperative jobs delay shutdown\n";
337 std::cout << "5. ✅ Immediate stop clears pending jobs from queue\n\n";
338 }
339 catch (const std::exception& e)
340 {
341 std::cerr << "Error: " << e.what() << std::endl;
342 return 1;
343 }
344
345 return 0;
346}
void demo_basic_cancellation()
Demonstrates basic job cancellation via worker stop.
void demo_immediate_vs_graceful()
Demonstrates immediate vs. graceful shutdown.
void demo_pool_level_cancellation()
Demonstrates pool-level cancellation with multiple workers.
void demo_non_cooperative_job()
Demonstrates what happens with non-cooperating jobs.

References demo_basic_cancellation(), demo_immediate_vs_graceful(), demo_non_cooperative_job(), and demo_pool_level_cancellation().

Here is the call graph for this function: