ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
dedicatedworker.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header file is part of module \alib_threadmodel of the \aliblong.
4///
5/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6/// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8#ifndef HPP_ALIB_THREADMODEL_DEDICATED_WORKER
9#define HPP_ALIB_THREADMODEL_DEDICATED_WORKER
10#pragma once
11
12#if !defined(DOXYGEN)
13# include "alib/alib.hpp"
14#endif
15ALIB_ASSERT_MODULE(THREADMODEL)
16
17#include "alib/threadmodel/jobs.hpp"
20#include "alib/threads/lock.hpp"
21#include "alib/boxing/enum.hpp"
24#include <list>
25#include <map>
26
27namespace alib { namespace threadmodel {
28class DWManager;
29
30//==================================================================================================
31/// \attention This class belongs to module \alib_threadmodel, which is not in a stable and
32/// consistent state, yet.
33/// Also this type is considered experimental.
34///
35/// This \alib{singletons;Singleton;singleton-class} manages worker threads of type
36/// \alib{threadmodel;DedicatedWorker}.
37/// Such threads are started by adding them to the singleton instance of this type and are stopped
38/// with removal.
39///
40/// \b %DedicatedWorkers use this type's \b PoolAllocator to create and dispose \alib{threadmodel;Job}
41/// objects and derived types are allowed to allocate resources in its \b %MonoAllocator.
42//==================================================================================================
43class DWManager : public singletons::Singleton<DWManager>
44 , public threads::Lock
45
46{
47 friend class singletons::Singleton<DWManager>;
48
49 protected:
50 /// Mono allocator. Used for commands and by <b>DedicatedWorker</b>s.
52
53 /// Pool allocator. Used for command objects.
55
56 /// The list of workers.
58
59 private:
60 /// Constructor.
62
63 public:
64 /// Allows access to the singleton instances' allocator.
65 /// @return The monotonic allocator.
67
68 /// Allows access to the singleton instances' pool allocator.
69 /// @return The pool allocator.
71
72 /// Adds and starts a worker.
73 /// @param thread The thread to add.
74 ALIB_API void Add( DedicatedWorker& thread );
75
76 /// Remove a previously added worker.
77 /// The thread is stopped by invoking \alib{threadmodel;DedicatedWorker::ScheduleStop} using the given
78 /// priority. It is waited until it exits, and finally the thread is joined.<br>
79 /// With debug-compilations, an \alib warning is raised every second if the thread
80 /// does not stop.
81 /// @param thread The thread to remove.
82 /// @param stopPriority The priority passed to <b>DedicatedWorker::ScheduleStop</b>.
83 /// Use \b Lowest (the default) to ensure that all other commands are duly
84 /// executed before exiting.
85 /// @return \c true if the thread was found and removed, \c false otherwise.
86 ALIB_API bool Remove( DedicatedWorker& thread,
87 Priority stopPriority= Priority::Lowest );
88
89
90 #if DOXYGEN
91 /// Waits until all threads are idle.
92 /// @param timeout The maximum time to wait.
93 /// @param dbgWarnAfter The time after which a warning message will be printed to the
94 /// debug log if the timeout was reached.<br>
95 /// This parameter is only available in debug-compilations and thus
96 /// should be passed using macro \ref ALIB_DBG.
97 /// @return \c true if all threads are idle, \c false otherwise.
99 bool WaitForAllIdle( Ticks::Duration timeout,
100 Ticks::Duration dbgWarnAfter );
101 #else
102 ALIB_API bool WaitForAllIdle( Ticks::Duration timeout
103 ALIB_DBG(, Ticks::Duration dbgWarnAfter) );
104 bool WaitForAllIdle( Ticks::Duration::TDuration timeout
105 ALIB_DBG(, Ticks::Duration::TDuration dbgWarnAfter) )
106 { return WaitForAllIdle( Ticks::Duration(timeout)
107 ALIB_DBG(, Ticks::Duration(dbgWarnAfter) ) ); }
108
109 #endif
110
111
112 /// Stops execution and terminates all workers by invoking
113 /// \alib{threadmodel;DedicatedWorker::ScheduleStop}.<br>
114 /// After this method has been invoked, no further commands should be scheduled, even if
115 /// the given priority equals \b Lowest. This <b>should be assured</b> by the using code.
116 ///
117 /// With debug-compilations, an \alib warning is raised every second until all managed
118 /// threads stopped.
119 /// @param stopPriority The priority passed to <b>DedicatedWorker::ScheduleStop</b>.
120 /// Use \b Lowest (the default) to ensure that all other commands are duly
121 /// executed before exiting.
122 ALIB_API void RemoveAll(Priority stopPriority= Priority::Lowest);
123
124}; // class DWManager
125
126//==================================================================================================
127/// \attention
128/// This class is part of \b experimental module \alib_threadmodel and thus is experimental
129/// in respect to interface, functionality, and documentation.
130///
131///
132/// This class implements a worker thread that receives jobs from a private queue.
133/// Jobs can have a different \alib{threadmodel;Priority}, and thus may not be executed in the
134/// order of insertion.
135/// This concept comprises a very simple scheduling approach and should not be overused.
136/// In the end, any multithreading software has to be designed in a way that a job queue never
137/// grows to a high level, so that all jobs will be processed in a maximum time.
138///
139/// This class is virtual, and it is designed for inheritance: Using software has to derive custom
140/// types that provide the custom functionality.
141///
142/// Jobs are represented with the protected type \alib{threadmodel;Job}.
143///
144/// Derived custom types need to duly fulfill a certain contract of interaction.
145/// In short, the following rules are given:
146/// 1. Derived types define jobs by exposing a type derived from virtual class
147/// \alib{threadmodel;Job}.
148/// Preferably, the derived types are designed to be public inner types because they
149/// usually explicitly belong to the scope of an \b %DedicatedWorker.<br>
150/// A \b %Job - besides its inherited type-ID - contains input data to the \b %DedicatedWorker
151/// as well as output data to the requesting thread.
152/// (Both are optional, as can be seen, for example, with the very simple built-in type
153/// \alib{threadmodel::DedicatedWorker;JobTrigger} that has neither input nor output data.)
154/// <br><br>
155/// 2. The derived type exposes interface methods according to the different jobs available
156/// to be scheduled.
157/// Here, the \b %Job is constructed and returned to the caller, usually as a reference
158/// in its derived form.<br>
159/// Thus, the worker has knowledge of what is to be expected when extracting a job from its
160/// queue, and with that is able to safely cast the virtual type "up" to the according
161/// derived type.
162/// <br><br>
163/// 3. The \b %Jobs returned by the interface (usually) contain some sort of thread-safe acknowledge
164/// mechanics, for example, a \alib{threads;Promise} or a \alib{threads;Condition} that a caller
165/// can decide to wait on.
166/// <br><br>
167/// 4. Disposal of \b %Job instances has to be explicitly performed by the thread that schedules
168/// a job. There are two methods provided for doing it.
169/// The first is #DeleteJob, which performs instant deletion. This can be called \b after
170/// the job was processed. The latter has to be awaited, as described in the previous paragraph.
171/// The second is #DeleteJobDeferred, which covers the case that the caller decides not to wait
172/// for an acknowledgment.
173/// Here, the instance must not be deleted because the \b %DedicatedWorker will access it when
174/// it is extracted from the queue (or already is working on it).
175/// Thus, this second version schedules a deletion job and lets the worker do it.<br>
176/// The deletion job has a low priority of
177/// \alib{threadmodel;DedicatedWorker::Priority;DeferredDeletion}. Jobs scheduled with an
178/// even lower priority must not be deferred-deleted.
179/// \note A similar concept of deferred-deletion is provided with sibling type
180/// \alib{threadmodel;ThreadPool}.
181/// There, deferred-deletion has a huge negative impact on performance of the pool.
182/// With type \b %DedicatedWorker, the negative impact of deferred deletion is negligible.
183/// <br><br>
184///
185/// 5. The custom types \b %Job types have to likewise fulfill a few contractual rules.
186/// Those are given with the type's \alib{threadmodel;Job;reference documentation}.
187///
188/// ### Starting and Stopping: ###
189/// The class inherits type \alib{threads;Thread} as a protected base type. With that, the
190/// conventional methods to start and stop a thread are hidden.<br>
191/// Starting and stopping a \b %DedicatedWorker is instead performed by adding, respectively
192/// removing an instance of this type to singleton class \alib{threadmodel;DWManager}.
193///
194/// ### Triggering: ###
195/// This type implements abstract interface \alib{threadmodel;Triggered}.
196/// If this is considered useful by a derived type, then three things have to be performed:
197/// - The parameterless job \alib{threadmodel::DedicatedWorker;JobTrigger} has to be processed
198/// in the overridden method #process.
199/// - Field #triggerDuration has to be adjusted as required, or as an alternative, the method
200/// #triggerPeriod has to be overridden. The latter might allow more flexibility to
201/// adjust the trigger time dependent on certain custom states.
202/// - The instance of the custom \b %DedicatedWorker has to be registered by calling method
203/// \alib{threadmodel;Trigger::Add}.
204//==================================================================================================
206 , protected Triggered
207 , protected TCondition<DedicatedWorker>
208{
209 friend class DWManager;
211
212
213 protected:
214 friend class lang::Owner<DedicatedWorker&>; /// needed as we inherit TCondition
215
216 //==============================================================================================
217 // Fields
218 //==============================================================================================
219
220 /// Reference to \b %DWManager instance.
222
223 /// Statistical information: Point in time of last job execution.
224 /// In case no job was executed, yet, this is the creation time of the object.
226
227 /// Flag which is set when the stop-job was scheduled.
228 bool stopJobPushed = false;
229
230 /// Flag which is set when the stop-job was executed.
231 bool stopJobExecuted = false;
232
233 /// If this \b %DedicatedWorker is (in addition) attached to a \alib{threadmodel;DWManager} as a
234 /// triggered object, this duration is returned by overridden method
235 /// \alib{threadmodel;Triggered::triggerPeriod} to determine the interval between
236 /// scheduling two trigger jobs.<br>
237 /// Defaults to one second, which usually is changed by a derived type.
238 Ticks::Duration triggerDuration = Ticks::Duration::FromSeconds(1);
239
240 //==============================================================================================
241 // The queue
242 //==============================================================================================
243 /// Container element of the queue.
245 {
246 Job* job; ///< The job containing the pool-allocated shared data.
247 Priority priority; ///< The job's priority.
248 bool keepJob; ///< Flag which indicates whether the job should be deleted after
249 ///< execution.
250 };
251
252 /// The queue of jobs.
253 /// This is a simple list, instead of a 'real' priority queue.
254 /// This design decision was taken because there should never be too many jobs queued
255 /// and the naive iteration is more efficient than using a 'real' priority queue type.
257
258 /// The current number of jobs in the queue.
260
261 /// Mandatory method needed and invoked by templated base type \alib{threads;TCondition}.
262 /// @return \c true if field #queue is not empty, \c false otherwise.
263 bool isConditionMet() { return length > 0; }
264
265 /// Pushes the given \p{jobInfo} into the priority queue that this class implements.
266 /// When invoked, the thread-manager as well as this instance are both locked.
267 /// @param jobInfo The job, the priority, and a flag if this job is to be deleted
268 /// automatically.
270 void pushAndRelease(QueueElement&& jobInfo);
271
272 /// Moves the job of highest priority out of the queue.
273 /// Blocks the thread until a job is available.
274 /// @return The job with the highest priority.
275 std::pair<Job*, bool> pop();
276
277 //==============================================================================================
278 // Inner Job types
279 //==============================================================================================
280 /// The stop job sent by method #ScheduleStop.
281 struct JobStop : Job
282 {
283 /// Constructor.
284 JobStop() : Job( typeid(JobStop) ) {}
285 };
286
287 /// The job sent by method #DeleteJobDeferred.
289 {
290 /// The job to be deleted.
292
293 /// Constructor.
294 /// @param job The job that is scheduled to be deleted.
296 : Job( typeid(JobDeleter) )
297 , JobToDelete( job ) {}
298
299 /// Overrides the parent function as necessary.
300 /// @return The sizeof this derived type.
301 virtual size_t SizeOf() override { return sizeof(JobDeleter); }
302 };
303
304 /// The job sent if (optional) trigger-interface \alib{threadmodel;Triggered::trigger}
305 /// is invoked.
307 {
308 /// Constructor.
309 JobTrigger() : Job( typeid(JobTrigger) ) {}
310 };
311
312 /// Pushes a custom job into the priority queue.<br>
313 /// This method is protected because derived types should provide dedicated "speaking"
314 /// interfaces for scheduling jobs.
315 /// Those are typically short inline methods, which/ are optimized out by C++ compilers.
316 /// @tparam TJob The job type as well as the job's shared data type.
317 /// @tparam TArgs Types of the variadic arguments \p{args} that construct \p{TJob}.
318 /// @param priority The priority of the job.
319 /// @param keepJob Denotes whether the job should be deleted after execution or not.
320 /// @param args Variadic arguments forwarded to the constructor of \p{TJob}.
321 /// @return The shared data. Deletion of the object is the responsibility of the caller and
322 /// is to be done by either invoking \alib{threadmodel:DedicatedWorker;DeleteJob}
323 /// or \alib{threadmodel:DedicatedWorker;DeleteJobDeferred}.
324 template<typename TJob, typename... TArgs>
325 [[nodiscard]]
326 TJob& schedule( Priority priority,
327 bool keepJob,
328 TArgs&&... args )
329 {
331 TJob* job= manager.GetPoolAllocator()().New<TJob>( std::forward<TArgs>(args)... );
333 ALIB_ASSERT_ERROR( job->SizeOf()==sizeof(TJob), "MGTHR", NString256() <<
334 "Error in DedicatedWorker::schedule: Job size mismatch. Expected " << sizeof(TJob) <<
335 " while virtual method SizeOf returns "<< job->SizeOf() << ".\n"
336 "Override this method for job-type <" << typeid(*job) << ">" )
337
339 || GetState() == State::Running, "MGTHR", NString256() <<
340 "Error in DedicatedWorker::schedule: Job pushed while this thread was not started, yet. "
341 "State: " << GetState() )
342
343 pushAndRelease( {job, priority, keepJob} );
344 return *job;
345 }
346
347 /// Pushes a custom job into the priority queue.<br>
348 /// The job is returned to the caller to be able to await results.
349 /// It is the responsibility of the caller to pass the job to either method
350 /// #DeleteJob or #DeleteJobDeferred for disposal.
351 /// @tparam TJob The job type to create and schedule.
352 /// @tparam TArgs Types of the variadic arguments \p{args} that construct \p{TJob}.
353 /// @param priority The priority of the job.
354 /// @param args Variadic arguments forwarded to the constructor of \p{TJob}.
355 /// @return The scheduled job.
356 template<typename TJob, typename... TArgs>
357 TJob& Schedule( Priority priority, TArgs&&... args )
358 { return schedule<TJob, TArgs...>( priority, true, std::forward<TArgs>(args)... ); }
359
360 /// Pushes a custom job into the priority queue.
361 /// In contrast to the sibling method #Schedule, the job is not returned by this method.
362 /// Instead, it is scheduled for automatic disposal after execution.
363 /// @tparam TJob The job type to create and schedule.
364 /// @tparam TArgs Types of the variadic arguments \p{args} that construct \p{TJob}.
365 /// @param priority The priority of the job.
366 /// @param args Variadic arguments forwarded to the constructor of \p{TJob}.
367 template<typename TJob, typename... TArgs>
368 void ScheduleVoid( Priority priority, TArgs&&... args )
369 { (void) schedule<TJob, TArgs...>( priority, false, std::forward<TArgs>(args)... ); }
370
371
372 //==============================================================================================
373 // Triggered interface implementation
374 //==============================================================================================
375 /// Return the sleep time, between two trigger events.
376 /// Precisely, this method is called after #trigger has been executed and defines the
377 /// next sleep time.
378 /// @return The desired sleep time between two trigger events.
379 virtual Ticks::Duration triggerPeriod() override { return triggerDuration; }
380
381 /// Schedules \alib{threadmodel::DedicatedWorker;JobTrigger} need to implement this function and
382 /// perform their trigger actions here.
383 virtual void trigger() override
384 { (void) schedule<JobTrigger>( Priority::Low, false ); }
385
386 //==============================================================================================
387 // Protected virtual execution methods
388 //==============================================================================================
389 /// This implementation of method \alib{threads;Thread::Run} constitutes a very simple loop
390 /// that waits for jobs in the #queue and passes them to likewise virtual method #process.
391 /// The (only) condition to continuing the loop is that the flag #stopJobExecuted is \c false.
392 /// This flag will be set by the internal job type \alib{threadmodel::DedicatedWorker;JobStop}
393 /// which is pushed with the method #ScheduleStop.
395 virtual void Run() override;
396
397
398 /// This virtual method is called during thread execution (method #Run) for each job
399 /// extracted from the internal job-queue.<br>
400 /// While this default-implementation is empty, internally, before a call to this method,
401 /// the following jobs are already detected and processed:
402 /// - \alib{threadmodel::DedicatedWorker;JobStop}, which signals flag #stopJobExecuted, and
403 /// this way lets calling method #Run leave its loop, and
404 /// - \alib{threadmodel::DedicatedWorker;JobDeleter}, which deletes the given other job.
405 /// (See method #DeleteJobDeferred.)
406 ///
407 /// A derived type may decide to call this parent method before or after checking for its own
408 /// jobs. If the call returns \c true, then the job was processed.
409 ///
410 /// It is also allowed to implement a custom processing, for example, with \b %JobStop and thus
411 /// not to call this version for the internal jobs covered by the custom version.
412 ///
413 /// It is important that overridden versions do return the \c true if the job was processed
414 /// and \c false if not.
415 /// @param vjob The job to process.
416 /// @return \c true if the job was processed, \c false if not.
417 virtual bool process(Job& vjob) { (void)vjob; return false; }
418
419 public:
420 #if ALIB_DEBUG
421 /// The maximum number of jobs in the queue at any time.
422 /// Available only with debug-compilations.
424 #endif
425
426
427 //==============================================================================================
428 // Construction/Destruction
429 //==============================================================================================
430 /// Constructor taking a thread name that is passed to parent class \alib{threads;Thread}.
431 /// @param threadName The name of this thread. The string's data has to survive this
432 /// threads' lifespan.
433 DedicatedWorker(const alib::String& threadName)
434 : Thread ( threadName )
435 , Triggered ( threadName )
436 , TCondition ( ALIB_DBG(threadName) )
437 , manager { DWManager::GetSingleton() }
438 , statLastJobExecution(lang::Initialization::Nulled )
439 , length {0}
441 {}
442
443 /// Destructor.
445 {
446 ALIB_ASSERT_WARNING( Load() == 0, "MGTHR", NString256() <<
447 "DedicatedWorker \""<< GetName() <<"\" destructed, while job-queue is not empty." )
448 }
449
450 using Thread::GetName;
451
452 //==============================================================================================
453 // Load/Stop
454 //==============================================================================================
455 /// Returns the current number of jobs in the queue.
456 /// @return The number of jobs to process, including any currently processed one.
457 int Load() const { return length; }
458
459 /// Returns the state of the internal flag, which is set with the invocation of #ScheduleStop.
460 /// @return \c true, if the #ScheduleStop was called, \c false otherwise.
462
463 /// Returns the state of the internal flag, which is set with the execution of #ScheduleStop.
464 /// @return \c true, if the #ScheduleStop was processed, \c false otherwise.
466
467
468 //==============================================================================================
469 // Job Interface
470 //==============================================================================================
471 /// Schedules a stop job into this thread's job queue.
472 /// When this job is processed from the queue, this thread will exit.
473 /// @param priority The priority of the job. Use \b Lowest if it is assured that no
474 /// other jobs will be pushed.
475 void ScheduleStop(Priority priority)
476 {
477 stopJobPushed= true;
478 (void) schedule<JobStop>( priority, false );
479 }
480
481 /// Deletes a data object previously received via method #schedule, one of its siblings, or
482 /// scheduling methods of derived types.
483 ///
484 /// The latter might have names that do not contain the term "schedule" but still internally
485 /// create and return data objects. Any object returned needs to be deleted.
486 ///
487 /// \attention
488 /// Deletion of data objects must not be done by the caller of a schedule-method before
489 /// the job is processed by this \b %DedicatedWorker.
490 /// For example, if a derived type's interface returns an job instance of type
491 /// \alib{threadmodel;JPromise}, then such a result must only be deleted once the promise is
492 /// fulfilled.
493 /// <p>
494 /// This can be cumbersome in case the calling thread is just not interested in
495 /// the result. For this case, the alternative method # DeleteJobDeferred is given.
496 ///
497 /// @param job The job returned when scheduling a command.
498 void DeleteJob(Job& job)
499 {
501 auto size= job.SizeOf();
502 job.~Job();
503 manager.GetPoolAllocator().free(&job, size);
504 }
505
506 /// Same as #DeleteJob but schedules the deletion to be performed. Scheduling is
507 /// done with \alib{threadmodel;Priority;Priority::DeferredDeletion}.<br>
508 /// This assures that the job deletion is performed \b after the job execution.
509 ///
510 /// This method is useful when the thread that schedules a job with this \b %DedicatedWorker is
511 /// not interested in the result, i.e., does not perform an asynchronous wait.
512 /// Custom jobs where this is rather usually the case, should be exposed in two versions,
513 /// one that returns a result and another that does not.
514 /// With the second, the derived \b %DedicatedWorker would delete the given shared data
515 /// with processing the job.
516 /// If this is offered, then this method does not need to be called (and cannot be called
517 /// because the caller does not receive any data).
518 /// @param job The job object to delete.
520 { (void) schedule<JobDeleter>( Priority::DeferredDeletion, false, &job ); }
521
522}; // class DedicatedWorker
523
524
525} // namespace alib[::threadmodel]
526
527/// Type alias in namespace \b alib.
529
530/// Type alias in namespace \b alib.
532
533} // namespace [alib]
534
536#endif // HPP_ALIB_THREADMODEL_DEDICATED_WORKER
537
void free(void *mem, size_t size)
ALIB_API bool WaitForAllIdle(Ticks::Duration timeout, Ticks::Duration dbgWarnAfter)
List< MonoAllocator, DedicatedWorker * > workers
The list of workers.
MonoAllocator ma
Mono allocator. Used for commands and by DedicatedWorkers.
ALIB_API void Add(DedicatedWorker &thread)
ALIB_API void RemoveAll(Priority stopPriority=Priority::Lowest)
ALIB_API bool Remove(DedicatedWorker &thread, Priority stopPriority=Priority::Lowest)
ALIB_API DWManager()
Constructor.
PoolAllocator pool
Pool allocator. Used for command objects.
TJob & schedule(Priority priority, bool keepJob, TArgs &&... args)
List< HeapAllocator, QueueElement > queue
bool stopJobPushed
Flag which is set when the stop-job was scheduled.
virtual ALIB_API void Run() override
void ScheduleVoid(Priority priority, TArgs &&... args)
DedicatedWorker(const alib::String &threadName)
ALIB_API void pushAndRelease(QueueElement &&jobInfo)
virtual const CString GetName() const
Definition thread.hpp:225
bool stopJobExecuted
Flag which is set when the stop-job was executed.
DWManager & manager
needed as we inherit TCondition
int length
The current number of jobs in the queue.
virtual Ticks::Duration triggerPeriod() override
TJob & Schedule(Priority priority, TArgs &&... args)
ALIB_API void Acquire(ALIB_DBG_TAKE_CI)
Definition locks.cpp:312
ALIB_API void Release(ALIB_DBG_TAKE_CI)
Definition locks.cpp:360
virtual const CString GetName() const
Definition thread.hpp:225
@ Running
The thread's Run method is currently processed.
@ Started
Method Start was invoked but not running, yet.
#define ALIB_ASSERT_MODULE(modulename)
Definition alib.hpp:223
#define ALIB_ENUMS_ASSIGN_RECORD(TEnum, TRecord)
Definition records.hpp:712
#define ALIB_API
Definition alib.hpp:639
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:1272
#define ALIB_DBG(...)
Definition alib.hpp:390
#define ALIB_CALLER_PRUNED
Definition alib.hpp:1170
#define ALIB_LOCK_WITH(lock)
Definition owner.hpp:456
Priority
Possible priorities of jobs assigned to an DedicatedWorker.
Definition jobs.hpp:187
Definition alib.cpp:69
threadmodel::DWManager DWManager
Type alias in namespace alib.
NLocalString< 256 > NString256
Type alias name for TLocalString<nchar,256>.
The job sent by method DeleteJobDeferred.
The stop job sent by method ScheduleStop.
Job * job
The job containing the pool-allocated shared data.
virtual size_t SizeOf()
Definition jobs.hpp:112
virtual ~Job()=default
Protected destructor.