source: rtems/doc/user/smp.t @ 38b59a6

4.115
Last change on this file since 38b59a6 was 38b59a6, checked in by Sebastian Huber <sebastian.huber@…>, on May 2, 2014 at 8:31:09 AM

score: Implement forced thread migration

The current implementation of task migration in RTEMS has some
implications with respect to the interrupt latency. It is crucial to
preserve the system invariant that a task can execute on at most one
processor in the system at a time. This is accomplished with a boolean
indicator in the task context. The processor architecture specific
low-level task context switch code will mark that a task context is no
longer executing and waits that the heir context stopped execution
before it restores the heir context and resumes execution of the heir
task. So there is one point in time in which a processor is without a
task. This is essential to avoid cyclic dependencies in case multiple
tasks migrate at once. Otherwise some supervising entity is necessary to
prevent life-locks. Such a global supervisor would lead to scalability
problems so this approach is not used. Currently the thread dispatch is
performed with interrupts disabled. So in case the heir task is
currently executing on another processor then this prolongs the time of
disabled interrupts since one processor has to wait for another
processor to make progress.

It is difficult to avoid this issue with the interrupt latency since
interrupts normally store the context of the interrupted task on its
stack. In case a task is marked as not executing we must not use its
task stack to store such an interrupt context. We cannot use the heir
stack before it stopped execution on another processor. So if we enable
interrupts during this transition we have to provide an alternative task
independent stack for this time frame. This issue needs further
investigation.

  • Property mode set to 100644
File size: 23.1 KB
Line 
1@c
2@c  COPYRIGHT (c) 2014.
3@c  On-Line Applications Research Corporation (OAR).
4@c  All rights reserved.
5@c
6
7@chapter Symmetric Multiprocessing Services
8
9@section Introduction
10
11This chapter describes the services related to Symmetric Multiprocessing
12provided by RTEMS.
13
14The application level services currently provided are:
15
16@itemize @bullet
17@item @code{rtems_get_processor_count} - Get processor count
18@item @code{rtems_get_current_processor} - Get current processor index
19@item @code{rtems_scheduler_ident} - Get ID of a scheduler
20@item @code{rtems_scheduler_get_processor_set} - Get processor set of a scheduler
21@item @code{rtems_task_get_scheduler} - Get scheduler of a task
22@item @code{rtems_task_set_scheduler} - Set scheduler of a task
23@item @code{rtems_task_get_affinity} - Get task processor affinity
24@item @code{rtems_task_set_affinity} - Set task processor affinity
25@end itemize
26
27@c
28@c
29@c
30@section Background
31
32@subsection Glossary
33
34@table @dfn
35
36@item scheduler
37
38A @dfn{scheduler} or @dfn{scheduling algorithm} allocates processors to a
39subset of its set of ready tasks.  So it manages access to the processor
40resource.  Various algorithms exist to choose the tasks allowed to use a
41processor out of the set of ready tasks.  One method is to assign each task a
42priority number and assign the tasks with the lowest priority number to one
43processor of the set of processors owned by a scheduler instance.
44
45@item scheduler instance
46
47A @dfn{scheduler instance} is a scheduling algorithm with a corresponding
48context to store its internal state.  Each processor in the system is owned by
49at most one scheduler instance.  The processor to scheduler instance assignment
50is determined at application configuration time.  @xref{Configuring a System
51Configuring Clustered/Partitioned Schedulers}.
52
53@item cluster
54
55We have clustered scheduling in case the set of processors of a system is
56partitioned into non-empty pairwise disjoint subsets.  These subsets are called
57@dfn{clusters}.  Each cluster is owned by exactly one scheduler instance.
58
59@item partition
60
61Clusters with a cardinality of one are @dfn{partitions}.
62
63@item task
64
65A @dfn{task} or @dfn{thread} is a context which can execute on a processor.  It
66consists normally of a set of registers and a stack.  The terms @dfn{task} and
67@dfn{thread} are synonym in RTEMS
68
69@item task processor affinity
70
71The set of processors on which a task is allowed to execute.
72
73@item task migration
74
75@dfn{Task migration} happens in case a task stops execution on one processor
76and resumes execution on another processor.
77
78@item blocked task
79
80A task is @dfn{blocked} if it is not allowed to execute.  There are several
81reasons why a task is blocked, for example
82
83@itemize @bullet
84
85@item it has to wait for a resource, currently owned by another task,
86
87@item some time must elapse, for example the next period start or some time
88out,
89
90@item it is explicitly suspended, or
91
92@item it is not yet started.
93
94@end itemize
95
96Blocked tasks are not an element of the set of ready tasks of a scheduler
97instance.
98
99@item ready task
100
101A task is @dfn{ready} if it is allowed to execute, but has no processor
102assigned.  The scheduler decided that other tasks are currently more important.
103
104@item scheduled task
105
106A task is @dfn{scheduled} if it is allowed to execute and has a processor
107assigned.  Such a task executes currently on a processor or is about to start
108execution.  A task about to start execution it is an heir task on exactly one
109processor in the system.
110
111@item in the air task
112
113A task is @dfn{in the air} if it is in a transient state and has a processor
114assigned.  The next scheduler operation will turn it into a blocked, ready or
115scheduled task.
116
117@item atomic operations
118
119Atomic operations are defined in terms of @cite{ISO/IEC 9899:2011}.
120
121@item SMP locks
122
123The @dfn{SMP locks} ensure mutual exclusion on the lowest level and are a
124replacement for the sections of disabled interrupts.  Interrupts are usually
125disabled while holding an SMP lock.  They are implemented using atomic
126operations.  Currently a ticket lock is used in RTEMS.
127
128@item SMP barriers
129
130The @dfn{SMP locks} ensure that a set of processors reaches a common
131synchronization point in time.  They are implemented using atomic operations.
132Currently a sense barrier is used in RTEMS.
133
134@item Giant lock
135
136The @dfn{Giant lock} is a recursive SMP lock protecting most parts of the
137operating system state.  Virtually every operating system service must acquire
138and release the Giant lock during its operation.
139
140@end table
141
142@subsection Uniprocessor versus SMP Parallelism
143
144Uniprocessor systems have long been used in embedded systems. In this hardware
145model, there are some system execution characteristics which have long been
146taken for granted:
147
148@itemize @bullet
149@item one task executes at a time
150@item hardware events result in interrupts
151@end itemize
152
153There is no true parallelism. Even when interrupts appear to occur
154at the same time, they are processed in largely a serial fashion.
155This is true even when the interupt service routines are allowed to
156nest.  From a tasking viewpoint,  it is the responsibility of the real-time
157operatimg system to simulate parallelism by switching between tasks.
158These task switches occur in response to hardware interrupt events and explicit
159application events such as blocking for a resource or delaying.
160
161With symmetric multiprocessing, the presence of multiple processors
162allows for true concurrency and provides for cost-effective performance
163improvements. Uniprocessors tend to increase performance by increasing
164clock speed and complexity. This tends to lead to hot, power hungry
165microprocessors which are poorly suited for many embedded applications.
166
167The true concurrency is in sharp contrast to the single task and
168interrupt model of uniprocessor systems. This results in a fundamental
169change to uniprocessor system characteristics listed above. Developers
170are faced with a different set of characteristics which, in turn, break
171some existing assumptions and result in new challenges. In an SMP system
172with N processors, these are the new execution characteristics.
173
174@itemize @bullet
175@item N tasks execute in parallel
176@item hardware events result in interrupts
177@end itemize
178
179There is true parallelism with a task executing on each processor and
180the possibility of interrupts occurring on each processor. Thus in contrast
181to their being one task and one interrupt to consider on a uniprocessor,
182there are N tasks and potentially N simultaneous interrupts to consider
183on an SMP system.
184
185This increase in hardware complexity and presence of true parallelism
186results in the application developer needing to be even more cautious
187about mutual exclusion and shared data access than in a uniprocessor
188embedded system. Race conditions that never or rarely happened when an
189application executed on a uniprocessor system, become much more likely
190due to multiple threads executing in parallel. On a uniprocessor system,
191these race conditions would only happen when a task switch occurred at
192just the wrong moment. Now there are N-1 tasks executing in parallel
193all the time and this results in many more opportunities for small
194windows in critical sections to be hit.
195
196@subsection Task Affinity
197
198@cindex task affinity
199@cindex thread affinity
200
201RTEMS provides services to manipulate the affinity of a task. Affinity
202is used to specify the subset of processors in an SMP system on which
203a particular task can execute.
204
205By default, tasks have an affinity which allows them to execute on any
206available processor.
207
208Task affinity is a possible feature to be supported by SMP-aware
209schedulers. However, only a subset of the available schedulers support
210affinity. Although the behavior is scheduler specific, if the scheduler
211does not support affinity, it is likely to ignore all attempts to set
212affinity.
213
214@subsection Task Migration
215
216@cindex task migration
217@cindex thread migration
218
219With more than one processor in the system tasks can migrate from one processor
220to another.  There are three reasons why tasks migrate in RTEMS.
221
222@itemize @bullet
223@item The scheduler changes explicitly via @code{rtems_task_set_scheduler()} or
224similar directives.
225@item The task resumes execution after a blocking operation.  On a priority
226based scheduler it will evict the lowest priority task currently assigned to a
227processor in the processor set managed by the scheduler instance.
228@item The task moves temporarily to another scheduler instance due to locking
229protocols like @cite{Migratory Priority Inheritance} or the
230@cite{Multiprocessor Resource Sharing Protocol}.
231@end itemize
232
233Task migration should be avoided so that the working set of a task can stay on
234the most local cache level.
235
236The current implementation of task migration in RTEMS has some implications
237with respect to the interrupt latency.  It is crucial to preserve the system
238invariant that a task can execute on at most one processor in the system at a
239time.  This is accomplished with a boolean indicator in the task context.  The
240processor architecture specific low-level task context switch code will mark
241that a task context is no longer executing and waits that the heir context
242stopped execution before it restores the heir context and resumes execution of
243the heir task.  So there is one point in time in which a processor is without a
244task.  This is essential to avoid cyclic dependencies in case multiple tasks
245migrate at once.  Otherwise some supervising entity is necessary to prevent
246life-locks.  Such a global supervisor would lead to scalability problems so
247this approach is not used.  Currently the thread dispatch is performed with
248interrupts disabled.  So in case the heir task is currently executing on
249another processor then this prolongs the time of disabled interrupts since one
250processor has to wait for another processor to make progress.
251
252It is difficult to avoid this issue with the interrupt latency since interrupts
253normally store the context of the interrupted task on its stack.  In case a
254task is marked as not executing we must not use its task stack to store such an
255interrupt context.  We cannot use the heir stack before it stopped execution on
256another processor.  So if we enable interrupts during this transition we have
257to provide an alternative task independent stack for this time frame.  This
258issue needs further investigation.
259
260@subsection Critical Section Techniques and SMP
261
262As discussed earlier, SMP systems have opportunities for true parallelism
263which was not possible on uniprocessor systems. Consequently, multiple
264techniques that provided adequate critical sections on uniprocessor
265systems are unsafe on SMP systems. In this section, some of these
266unsafe techniques will be discussed.
267
268In general, applications must use proper operating system provided mutual
269exclusion mechanisms to ensure correct behavior. This primarily means
270the use of binary semaphores or mutexes to implement critical sections.
271
272@subsubsection Disable Interrupts
273
274Again on a uniprocessor system, there is only a single processor which
275logically executes a single task and takes interrupts. On an SMP system,
276each processor may take an interrupt. When the application disables
277interrupts, it generally does so by altering a processor register to
278mask interrupts and later to re-enable them. On a uniprocessor system,
279changing this in the single processor is sufficient. However, on an SMP
280system, this register in @strong{ALL} processors must be changed. There
281are no comparable capabilities in an SMP system to disable all interrupts
282across all processors.
283
284@subsubsection Highest Priority Task Assumption
285
286On a uniprocessor system, it is safe to assume that when the highest
287priority task in an application executes, it will execute without being
288preempted until it voluntarily blocks. Interrupts may occur while it is
289executing, but there will be no context switch to another task unless
290the highest priority task voluntarily initiates it.
291
292Given the assumption that no other tasks will have their execution
293interleaved with the highest priority task, it is possible for this
294task to be constructed such that it does not need to acquire a binary
295semaphore or mutex for protected access to shared data.
296
297In an SMP system, it cannot be assumed there will never be a single task
298executing. It should be assumed that every processor is executing another
299application task. Further, those tasks will be ones which would not have
300been executed in a uniprocessor configuration and should be assumed to
301have data synchronization conflicts with what was formerly the highest
302priority task which executed without conflict.
303
304@subsubsection Disable Preemption
305
306On a uniprocessor system, disabling preemption in a task is very similar
307to making the highest priority task assumption. While preemption is
308disabled, no task context switches will occur unless the task initiates
309them voluntarily. And, just as with the highest priority task assumption,
310there are N-1 processors also running tasks. Thus the assumption that no
311other tasks will run while the task has preemption disabled is violated.
312
313@subsection Task Unique Data and SMP
314
315Per task variables are a service commonly provided by real-time operating
316systems for application use. They work by allowing the application
317to specify a location in memory (typically a @code{void *}) which is
318logically added to the context of a task. On each task switch, the
319location in memory is stored and each task can have a unique value in
320the same memory location. This memory location is directly accessed as a
321variable in a program.
322
323This works well in a uniprocessor environment because there is one task
324executing and one memory location containing a task-specific value. But
325it is fundamentally broken on an SMP system because there are always N
326tasks executing. With only one location in memory, N-1 tasks will not
327have the correct value.
328
329This paradigm for providing task unique data values is fundamentally
330broken on SMP systems.
331
332@subsubsection Classic API Per Task Variables
333
334The Classic API provides three directives to support per task variables. These are:
335
336@itemize @bullet
337@item @code{@value{DIRPREFIX}task_variable_add} - Associate per task variable
338@item @code{@value{DIRPREFIX}task_variable_get} - Obtain value of a a per task variable
339@item @code{@value{DIRPREFIX}task_variable_delete} - Remove per task variable
340@end itemize
341
342As task variables are unsafe for use on SMP systems, the use of these
343services should be eliminated in all software that is to be used in
344an SMP environment. It is recommended that the application developer
345consider the use of POSIX Keys or Thread Local Storage (TLS). POSIX Keys
346are not enabled in all RTEMS configurations.
347
348@b{STATUS}: As of March 2014, some support services in the
349@code{rtems/cpukit} use per task variables. When these uses are
350eliminated, the per task variable directives will be disabled when
351building RTEMS in SMP configuration.
352
353@c
354@c
355@c
356@section Operations
357
358@subsection Setting Affinity to a Single Processor
359
360On some embedded applications targeting SMP systems, it may be beneficial to
361lock individual tasks to specific processors.  In this way, one can designate a
362processor for I/O tasks, another for computation, etc..  The following
363illustrates the code sequence necessary to assign a task an affinity for
364processor with index @code{processor_index}.
365
366@example
367@group
368#include <rtems.h>
369#include <assert.h>
370
371void pin_to_processor(rtems_id task_id, int processor_index)
372@{
373  rtems_status_code sc;
374  cpu_set_t         cpuset;
375
376  CPU_ZERO(&cpuset);
377  CPU_SET(processor_index, &cpuset);
378
379  sc = rtems_task_set_affinity(task_id, sizeof(cpuset), &cpuset);
380  assert(sc == RTEMS_SUCCESSFUL);
381@}
382@end group
383@end example
384
385It is important to note that the @code{cpuset} is not validated until the
386@code{@value{DIRPREFIX}task_set_affinity} call is made. At that point,
387it is validated against the current system configuration.
388
389@c
390@c
391@c
392@section Directives
393
394This section details the symmetric multiprocessing services.  A subsection
395is dedicated to each of these services and describes the calling sequence,
396related constants, usage, and status codes.
397
398@c
399@c rtems_get_processor_count
400@c
401@page
402@subsection GET_PROCESSOR_COUNT - Get processor count
403
404@subheading CALLING SEQUENCE:
405
406@ifset is-C
407@example
408uint32_t rtems_get_processor_count(void);
409@end example
410@end ifset
411
412@ifset is-Ada
413@end ifset
414
415@subheading DIRECTIVE STATUS CODES:
416
417The count of processors in the system.
418
419@subheading DESCRIPTION:
420
421On uni-processor configurations a value of one will be returned.
422
423On SMP configurations this returns the value of a global variable set during
424system initialization to indicate the count of utilized processors.  The
425processor count depends on the physically or virtually available processors and
426application configuration.  The value will always be less than or equal to the
427maximum count of application configured processors.
428
429@subheading NOTES:
430
431None.
432
433@c
434@c rtems_get_current_processor
435@c
436@page
437@subsection GET_CURRENT_PROCESSOR - Get current processor index
438
439@subheading CALLING SEQUENCE:
440
441@ifset is-C
442@example
443uint32_t rtems_get_current_processor(void);
444@end example
445@end ifset
446
447@ifset is-Ada
448@end ifset
449
450@subheading DIRECTIVE STATUS CODES:
451
452The index of the current processor.
453
454@subheading DESCRIPTION:
455
456On uni-processor configurations a value of zero will be returned.
457
458On SMP configurations an architecture specific method is used to obtain the
459index of the current processor in the system.  The set of processor indices is
460the range of integers starting with zero up to the processor count minus one.
461
462Outside of sections with disabled thread dispatching the current processor
463index may change after every instruction since the thread may migrate from one
464processor to another.  Sections with disabled interrupts are sections with
465thread dispatching disabled.
466
467@subheading NOTES:
468
469None.
470
471@c
472@c rtems_scheduler_ident
473@c
474@page
475@subsection SCHEDULER_IDENT - Get ID of a scheduler
476
477@subheading CALLING SEQUENCE:
478
479@ifset is-C
480@example
481rtems_status_code rtems_scheduler_ident(
482  rtems_name  name,
483  rtems_id   *id
484);
485@end example
486@end ifset
487
488@ifset is-Ada
489@end ifset
490
491@subheading DIRECTIVE STATUS CODES:
492
493@code{@value{RPREFIX}SUCCESSFUL} - successful operation@*
494@code{@value{RPREFIX}INVALID_ADDRESS} - @code{id} is NULL@*
495@code{@value{RPREFIX}INVALID_NAME} - invalid scheduler name@*
496@code{@value{RPREFIX}UNSATISFIED} - - a scheduler with this name exists, but
497the processor set of this scheduler is empty
498
499@subheading DESCRIPTION:
500
501Identifies a scheduler by its name.  The scheduler name is determined by the
502scheduler configuration.  @xref{Configuring a System Configuring
503Clustered/Partitioned Schedulers}.
504
505@subheading NOTES:
506
507None.
508
509@c
510@c rtems_scheduler_get_processor_set
511@c
512@page
513@subsection SCHEDULER_GET_PROCESSOR_SET - Get processor set of a scheduler
514
515@subheading CALLING SEQUENCE:
516
517@ifset is-C
518@example
519rtems_status_code rtems_scheduler_get_processor_set(
520  rtems_id   scheduler_id,
521  size_t     cpusetsize,
522  cpu_set_t *cpuset
523);
524@end example
525@end ifset
526
527@ifset is-Ada
528@end ifset
529
530@subheading DIRECTIVE STATUS CODES:
531
532@code{@value{RPREFIX}SUCCESSFUL} - successful operation@*
533@code{@value{RPREFIX}INVALID_ADDRESS} - @code{cpuset} is NULL@*
534@code{@value{RPREFIX}INVALID_ID} - invalid scheduler id@*
535@code{@value{RPREFIX}INVALID_NUMBER} - the affinity set buffer is too small for
536set of processors owned by the scheduler
537
538@subheading DESCRIPTION:
539
540Returns the processor set owned by the scheduler in @code{cpuset}.  A set bit
541in the processor set means that this processor is owned by the scheduler and a
542cleared bit means the opposite.
543
544@subheading NOTES:
545
546None.
547
548@c
549@c rtems_task_get_scheduler
550@c
551@page
552@subsection TASK_GET_SCHEDULER - Get scheduler of a task
553
554@subheading CALLING SEQUENCE:
555
556@ifset is-C
557@example
558rtems_status_code rtems_task_get_scheduler(
559  rtems_id  id,
560  rtems_id *scheduler_id
561);
562@end example
563@end ifset
564
565@ifset is-Ada
566@end ifset
567
568@subheading DIRECTIVE STATUS CODES:
569
570@code{@value{RPREFIX}SUCCESSFUL} - successful operation@*
571@code{@value{RPREFIX}INVALID_ADDRESS} - @code{scheduler_id} is NULL@*
572@code{@value{RPREFIX}INVALID_ID} - invalid task id
573
574@subheading DESCRIPTION:
575
576Returns the scheduler identifier of a task in @code{scheduler_id}.
577
578@subheading NOTES:
579
580None.
581
582@c
583@c rtems_task_set_scheduler
584@c
585@page
586@subsection TASK_SET_SCHEDULER - Set scheduler of a task
587
588@subheading CALLING SEQUENCE:
589
590@ifset is-C
591@example
592rtems_status_code rtems_task_set_scheduler(
593  rtems_id id,
594  rtems_id scheduler_id
595);
596@end example
597@end ifset
598
599@ifset is-Ada
600@end ifset
601
602@subheading DIRECTIVE STATUS CODES:
603
604@code{@value{RPREFIX}SUCCESSFUL} - successful operation@*
605@code{@value{RPREFIX}INVALID_ID} - invalid task or scheduler id@*
606@code{@value{RPREFIX}INCORRECT_STATE} - the task is in the wrong state to
607perform a scheduler change
608
609@subheading DESCRIPTION:
610
611Sets the scheduler of a task specified by @code{scheduler_id}.  The scheduler
612of a task is initialized to the scheduler of the task that created it.
613
614@subheading NOTES:
615
616None.
617
618@subheading EXAMPLE:
619
620@example
621@group
622#include <rtems.h>
623#include <assert.h>
624
625void task(rtems_task_argument arg);
626
627void example(void)
628@{
629  rtems_status_code sc;
630  rtems_id          task_id;
631  rtems_id          scheduler_id;
632  rtems_name        scheduler_name;
633
634  scheduler_name = rtems_build_name('W', 'O', 'R', 'K');
635
636  sc = rtems_scheduler_ident(scheduler_name, &scheduler_id);
637  assert(sc == RTEMS_SUCCESSFUL);
638
639  sc = rtems_task_create(
640    rtems_build_name('T', 'A', 'S', 'K'),
641    1,
642    RTEMS_MINIMUM_STACK_SIZE,
643    RTEMS_DEFAULT_MODES,
644    RTEMS_DEFAULT_ATTRIBUTES,
645    &task_id
646  );
647  assert(sc == RTEMS_SUCCESSFUL);
648
649  sc = rtems_task_set_scheduler(task_id, scheduler_id);
650  assert(sc == RTEMS_SUCCESSFUL);
651
652  sc = rtems_task_start(task_id, task, 0);
653  assert(sc == RTEMS_SUCCESSFUL);
654@}
655@end group
656@end example
657
658@c
659@c rtems_task_get_affinity
660@c
661@page
662@subsection TASK_GET_AFFINITY - Get task processor affinity
663
664@subheading CALLING SEQUENCE:
665
666@ifset is-C
667@example
668rtems_status_code rtems_task_get_affinity(
669  rtems_id   id,
670  size_t     cpusetsize,
671  cpu_set_t *cpuset
672);
673@end example
674@end ifset
675
676@ifset is-Ada
677@end ifset
678
679@subheading DIRECTIVE STATUS CODES:
680
681@code{@value{RPREFIX}SUCCESSFUL} - successful operation@*
682@code{@value{RPREFIX}INVALID_ADDRESS} - @code{cpuset} is NULL@*
683@code{@value{RPREFIX}INVALID_ID} - invalid task id@*
684@code{@value{RPREFIX}INVALID_NUMBER} - the affinity set buffer is too small for
685the current processor affinity set of the task
686
687@subheading DESCRIPTION:
688
689Returns the current processor affinity set of the task in @code{cpuset}.  A set
690bit in the affinity set means that the task can execute on this processor and a
691cleared bit means the opposite.
692
693@subheading NOTES:
694
695None.
696
697@c
698@c rtems_task_set_affinity
699@c
700@page
701@subsection TASK_SET_AFFINITY - Set task processor affinity
702
703@subheading CALLING SEQUENCE:
704
705@ifset is-C
706@example
707rtems_status_code rtems_task_set_affinity(
708  rtems_id         id,
709  size_t           cpusetsize,
710  const cpu_set_t *cpuset
711);
712@end example
713@end ifset
714
715@ifset is-Ada
716@end ifset
717
718@subheading DIRECTIVE STATUS CODES:
719
720@code{@value{RPREFIX}SUCCESSFUL} - successful operation@*
721@code{@value{RPREFIX}INVALID_ADDRESS} - @code{cpuset} is NULL@*
722@code{@value{RPREFIX}INVALID_ID} - invalid task id@*
723@code{@value{RPREFIX}INVALID_NUMBER} - invalid processor affinity set
724
725@subheading DESCRIPTION:
726
727Sets the processor affinity set for the task specified by @code{cpuset}.  A set
728bit in the affinity set means that the task can execute on this processor and a
729cleared bit means the opposite.
730
731@subheading NOTES:
732
733None.
Note: See TracBrowser for help on using the repository browser.