Research note: RTEMS SuperCore

Kind: Read-only source analysis, 2026-10-07, on a sparse shallow clone of RTEMS/rtems at commit b3a3b372fa8d (cpukit/score/, cpukit/include/rtems/score/, cpukit/rtems/src/, cpukit/include/rtems/rtems/). Citations are relative to cpukit/; S/ is include/rtems/score/, R/ is include/rtems/rtems/. Line numbers drift; paths and symbols are the durable reference. Synthesized in R-001.


Q1 Scheduler

  • Plugin interface Scheduler_Operations (S/scheduler.h:62-311): initialize, schedule, yield, block, unblock, update_priority, map and unmap_priority, node_initialize and destroy, release_job and cancel_job (EDF hooks), start_idle, and SMP-only ask_for_help, reconsider_help_request, withdraw_node, make and clean_sticky, pin and unpin, add and remove_processor, set_affinity. Scheduler_Control is {context, Operations, maximum_priority, name, is_non_preempt_mode_supported} (S/scheduler.h:337-366). Selection is a const table _Scheduler_Table[] and _Scheduler_Count plus _Scheduler_Initial_assignments[] with PROCESSOR_MANDATORY or OPTIONAL (S/scheduler.h:378-449), emitted by confdefs/scheduler.h (include/rtems/confdefs.h:79).

  • Deterministic Priority: Scheduler_priority_Context is a 16x16 Priority_bit_map_Control plus Chain_Control Ready[] (S/schedulerpriority.h:81-96; S/prioritybitmap.h:58-76); highest is find-first-bit on the major then minor word via __builtin_clz or _CPU_Bitfield_Find_first_bit (S/prioritybitmapimpl.h:75-82, 208-219). The Simple scheduler is one ordered chain (S/schedulersimple.h:83-93).

  • EDF: an RB-tree Ready; the node holds a Priority_Control (S/scheduleredf.h:83-113). A deadline is just another priority: release_job does _Thread_Priority_changed or add with SCHEDULER_PRIORITY_MAP(deadline); non-deadline threads carry SCHEDULER_EDF_PRIO_MSB (bit 63) so they sort as background (score/src/scheduleredfreleasejob.c:45-98; S/scheduleredfimpl.h:55-61). CBS extends EDF with budget-overrun servers (S/schedulercbs.h:109-159).

  • Priority_Control is uint64_t (S/priority.h:91); SCHEDULER_PRIORITY_MAP shifts left by 1, reserving the LSB as the FIFO or LIFO “append” bit (S/schedulernodeimpl.h:65-70).

  • SMP: Scheduler_SMP_Context adds a Scheduled chain beside the ready structure; node states BLOCKED, SCHEDULED, READY with Graphviz state diagrams in the header (S/schedulersmp.h:61-121; S/schedulersmpimpl.h:44-120). Priority SMP is G-FP; Priority Affinity SMP admits it “does not honor that affinity” yet (S/schedulerpriorityaffinitysmp.h:about 50-56); EDF SMP keeps per-processor affine RB queues, index 0 is one-to-all (S/scheduleredfsmp.h:59-136); Strong APA (Cerqueira et al.) does BFS backtracking over CPUs in the affinity set (S/schedulerstrongapa.h:58-67, 99-140).

  • Scheduler_Node is {owner, user (owner or idle), idle placeholder, sticky_level, Wait.Priority aggregation, request queue} (S/schedulernode.h:94-229). One node per scheduler instance per thread: Thread_Scheduler_control {nodes, Wait_nodes, Scheduler_nodes, helping_nodes, pin_level, Affinity} (S/thread.h:297-420).

  • Helping: _Scheduler_SMP_Ask_for_help preempts the lowest scheduled node or parks the node READY (S/schedulersmpimpl.h:1592-1700); requests are appended to Per_CPU_Control::Threads_in_need_for_help with dispatch_necessary=true (S/schedulersmpimpl.h:610-640) and drained in _Thread_Preemption_intervention on the next dispatch (score/src/threaddispatch.c:176-213).

  • Dispatch: Per_CPU_Control holds isr_nest_level, isr_dispatch_disable, thread_dispatch_disable_level, volatile bool dispatch_necessary, executing, heir (S/percpu.h:406-473). _Thread_Dispatch_enable dispatches only on the 1 to 0 transition when dispatch_necessary (threaddispatch.c:375-400); _Thread_Do_dispatch loops heir to executing, calls CPU_budget.at_context_switch, _Context_Switch, repeats while dispatch_necessary, then runs post-switch actions with level 0 (threaddispatch.c:278-358). A remote heir change is an atomic message plus IPI (S/threaddispatch.h:270-283).

  • Idle: _Thread_Idle_body defaults to the port _CPU_Thread_Idle_body (score/src/threadidledefault.c:44; score/cpu/no_cpu/include/rtems/score/cpu.h:1050); idle threads also serve as placeholders for sticky and ceiling nodes across scheduler instances (S/schedulernode.h:122-132).

  • Effective priority: Priority_Aggregation is an RB-tree Contributors plus a Node whose .priority mirrors the minimum (S/priority.h:112-198); _Priority_Insert, Extract, Changed rewrite Node.priority only when the minimum changes (S/priorityimpl.h:565-741). A thread’s Real_priority node (S/thread.h:about 863) is one contributor into its home scheduler node’s Wait.Priority; ceilings, inherited queue minima, and EDF deadlines are further nodes. _Thread_Priority_apply seeds one action and _Thread_Priority_do_perform_actions pushes the result into _Scheduler_Node_set_priority (score/src/threadchangepriority.c:53-65, 284-336).

Q2 Context switch and interrupts

  • _Context_Switch calls the port _CPU_Context_switch; FP is lazily switched (CPU_USE_DEFERRED_FP_SWITCH) with _Thread_Save_fp and _Restore_fp around the switch (S/context.h:137-183; no_cpu cpu.h:207, 1068-1143; threaddispatch.c:about 328-336).

  • Two orthogonal levels: an ISR_Level cookie saved in a local by _ISR_Local_disable, enable, flash wrapping _CPU_ISR_Disable (S/isrlevel.h:60-118), and the per-CPU thread_dispatch_disable_level, which is the scheduler lock: _Thread_Dispatch_disable* increments (S/threaddispatch.h:183-222), _Thread_Dispatch_enable decrements (threaddispatch.c:375-400).

  • ISR_lock_Control wraps SMP_lock_Control; on UP it degrades to interrupt disable and ISR_LOCK_NEEDS_OBJECT removes the object entirely (S/isrlock.h:54-106).

  • SMP locks: a ticket lock (fetch_add next_ticket, spin on now_serving, release stores +1) (S/smplockticket.h:60-197); an MCS queue lock (exchange tail, spin on own locked, CAS on release) (S/smplockmcs.h:60-280); a Boehm-style seqlock (S/smplockseq.h). SMP_lock_Control defaults to the ticket lock with optional stats (S/smplock.h:86-119).

  • Interrupt exit: the port’s _ISR_Handler (S/isr.h:163-174) consults _Thread_Dispatch_necessary, _ISR_Nest_level, and isr_dispatch_disable (S/percpu.h:402-437, 903-906) and calls _Thread_Dispatch, which sets the level 0 to 1 and runs _Thread_Do_dispatch (score/src/threadplaindispatch.c:43-57). RTEMS_SCORE_ROBUST_THREAD_DISPATCH faults with INTERNAL_ERROR_BAD_THREAD_DISPATCH_ENVIRONMENT if interrupts are disabled at dispatch (threaddispatch.c:282-292; S/threaddispatch.h:64).

  • Interrupt stacks: per-CPU interrupt_stack_low and high carved from the configured _ISR_Stack_area_begin and _ISR_Stack_size (S/percpu.h:395-400; S/isr.h:101-125; score/src/isr.c:58-78). CPU_HAS_SOFTWARE_INTERRUPT_STACK is gone from the template port; every port now gets the per-CPU stack fields.

  • ISR misuse: _ISR_Is_in_progress via the nest level or a port hook (S/percpu.h:722-728; cpu.h:555). Blocking sends from an ISR return STATUS_MESSAGE_QUEUE_WAIT_IN_ISR (score/src/coremsgsubmit.c:139-142), which maps to RTEMS_CALLED_FROM_ISR = 18 (R/status.h:187).

Q3 Time

  • Watchdog_Control is an RB node, cpu, routine, 64-bit expire (S/watchdog.h:110-138); Watchdog_Header caches first (leftmost) (S/watchdog.h:91-102). _Watchdog_Insert descends while tracking whether the new node becomes first (score/src/watchdoginsert.c:41-81); _Watchdog_Remove advances first via _Watchdog_Next_first (watchdogremove.c:41-56); _Watchdog_Do_tickle fires all expire <= now under the per-CPU lock (watchdogtick.c:46-74).

  • Per CPU: Watchdog {Lock, uint64_t ticks, Header[TICKS, REALTIME, MONOTONIC]} (S/percpu.h:345-376, 494-514). _Watchdog_Tick: the boot CPU bumps _Watchdog_Ticks_since_boot, tickles TICKS with ++ticks, MONOTONIC with _Timecounter_Getnanouptime, REALTIME with bintime, then CPU_budget.at_tick (watchdogtick.c:76-156). Clock headers key on seconds<<30 | ns (WATCHDOG_BITS_FOR_1E9_NANOSECONDS, S/watchdogimpl.h:427, 528-586).

  • The timecounter is FreeBSD’s: _Timecounter_Install, Tick, Bintime, Getnanouptime, Set_NTP_update_second, Discipline (S/timecounter.h:65-320); kern_tc.c is outside this sparse clone. Simple counters use _Timecounter_Tick_simple (S/timecounter.h:242).

  • Strictly tick-driven: CONFIGURE_MICROSECONDS_PER_TICK gives _Watchdog_Microseconds_per_tick (include/rtems/config.h:527; S/watchdogticks.h:76-108). No tickless support anywhere in score (grep for “tickless” is empty).

  • Thread timeouts: Thread_Timer_information {Lock, header, Watchdog} (S/thread.h:566-572). _Thread_queue_Add_timeout_ticks, monotonic, realtime, by_clock_id pick the header; an absolute time already past yields immediate _Thread_Continue(STATUS_TIMEOUT), an invalid timespec gives STATUS_INVALID_NUMBER (score/src/threadqtimeout.c:48-170); _Thread_Timeout is just _Thread_Continue(STATUS_TIMEOUT) (score/src/threadtimeout.c:101-111).

  • Rate monotonic: Rate_monotonic_Control {Timer, state, Priority node, owner, Statistics, postponed_jobs, latest_deadline} (R/ratemondata.h:89-153); states INACTIVE, ACTIVE, EXPIRED (R/ratemon.h:96-110). rtems_rate_monotonic_period: non-owner gives RTEMS_NOT_OWNER_OF_RESOURCE; RTEMS_PERIOD_STATUS returns RTEMS_TIMEOUT if EXPIRED; ACTIVE blocks (STATES_WAITING_FOR_PERIOD) until the watchdog fires; EXPIRED or postponed_jobs>0 returns RTEMS_TIMEOUT immediately and renews (rtems/src/ratemonperiod.c:243-400). The timeout increments postponed_jobs and sets EXPIRED when the owner is not blocked (rtems/src/ratemontimeout.c:46-116). Statistics: count, missed_count, min, max, total CPU and wall time (ratemonperiod.c:158-212). Each period release calls _Scheduler_Release_job, so EDF deadlines come from this manager (ratemonperiod.c:108-135).

  • Timer server: the watchdog routine moves the timer onto a Pending chain and sends a system event; the server task runs routines in task context (rtems/src/timerserver.c:71-160); rtems_timer_server_fire_after uses class TIMER_INTERVAL_ON_TASK (rtems/src/timerserverfireafter.c:64).

Q4 Blocking and waiting

  • Thread_queue_Queue {ticket Lock, heads, owner, name} (S/threadq.h:434-468). Thread_queue_Heads is a union: a FIFO chain, or a chain of per-scheduler Thread_queue_Priority_queue, each holding a Priority_Aggregation Queue (S/threadq.h:356-432). Thread_queue_Operations {priority_actions, enqueue, extract, surrender, first} (S/threadq.h:561-610) with FIFO, priority, and priority_inherit tables (score/src/threadqops.c:160-218, 369-690, 693-1424). Because the queue itself is an aggregation, its minimum becomes a contributor to the owner (threadqops.c:1041-1067).

  • _Thread_queue_Enqueue (score/src/threadqenqueue.c:411-498): claim wait, Path_acquire, ops->enqueue, set INTEND_TO_BLOCK, disable dispatch, release the queue lock, run enqueue_callout (arms the timeout), _Thread_Set_state, then CAS INTEND_TO_BLOCK to BLOCKED; if the CAS fails a surrender or timeout already set READY, so it removes the timer and unblocks; finally _Thread_Dispatch_direct.

  • Wait flags: READY 0, INTEND_TO_BLOCK 1, BLOCKED 2 plus class bits EVENT, SYSTEM_EVENT, OBJECT, PERIOD (S/threadimpl.h:2259-2307), changed by atomic CAS (S/threadimpl.h:2426-2431). Wakers try INTEND_TO_BLOCK to READY; if that fails the thread is BLOCKED so they set READY and _Thread_Unblock (threadtimeout.c:45-99; threadqenqueue.c:597-630).

  • Deletion with waiters: _Thread_queue_Flush_critical with a filter that writes STATUS_OBJECT_WAS_DELETED or STATUS_UNAVAILABLE into Wait.return_code (score/src/threadqflush.c:49-90), used by semaphore destroy and barriers (S/coresemimpl.h:123-126; S/corebarrierimpl.h:163-182).

  • Events: a 32-bit rtems_event_set, RTEMS_ALL_EVENTS (R/event.h:96-436); seize and surrender reuse the wait-flag machine without a thread queue (rtems/src/eventseize.c:87-122; eventsurrender.c:102-140); system events are a separate class.

  • Condition variables: self-contained _Condition_Wait* on a FIFO queue, releasing the mutex inside the enqueue callout (score/src/condition.c:55, 120-175).

Q5 Synchronization and PI

  • CORE_mutex_Control is a thread queue; recursive adds nest_level; ceiling adds a Priority_ceiling node and scheduler (S/coremutex.h:74-118). The slow seize enqueues with a deadlock callout (score/src/coremutexseize.c:47-75); surrender via _Thread_queue_Surrender (S/coremuteximpl.h:285-320); ceiling ownership adds the ceiling node to the owner, STATUS_MUTEX_CEILING_VIOLATED otherwise (S/coremuteximpl.h:431-461).

  • PI is _Thread_queue_Operations_priority_inherit (S/coremuteximpl.h:61): enqueue inserts the waiter’s node into the queue aggregation; a new minimum generates an action on the owner’s scheduler node and _Thread_Priority_perform_actions(owner) (threadqops.c:989-1067).

  • Transitive: yes. _Thread_Priority_perform_actions loops: apply actions to the_thread; if actions remain, the_thread = the_thread->Wait.queue->owner and repeat (score/src/threadchangepriority.c:227-282). Inside, _Thread_Priority_do_perform_actions calls operations->priority_actions of the queue the current thread waits on, so an owner that is itself blocked on a PI mutex regenerates actions for the next owner (threadchangepriority.c:127-225). On SMP the whole owner chain is locked first by _Thread_queue_Path_acquire, which collects one Thread_queue_Link per hop (threadchangepriority.c:302-313; threadqenqueue.c:273-383).

  • MrsP: a ceiling priority per scheduler instance (S/mrsp.h:77-92), waiters spin-wait “sticky” via _Thread_queue_Enqueue_sticky and _Thread_Priority_update_and_make_sticky; a preempted owner borrows a waiter’s processor through the helping protocol (S/mrsp.h:about 50-66; S/mrspimpl.h:363-410; threadqenqueue.c:500-575). A recursive seize is STATUS_DEADLOCK (S/mrspimpl.h:459). OMIP-style migratory inheritance is what the per-instance Wait_nodes and helping_nodes deliver for PI mutexes across clusters (S/thread.h:356-420).

  • Semaphore variants derived from attributes: counting, simple binary, binary mutex with no protocol, inherit, ceiling, or MrsP (rtems/src/semcreate.c:95-125; R/semimpl.h:65-72); STATUS_MAXIMUM_COUNT_EXCEEDED on counting overflow (S/coresemimpl.h:148-175).

  • Barrier: automatic release at maximum_count returns STATUS_BARRIER_AUTOMATICALLY_RELEASED (score/src/corebarrierwait.c:76-100); manual release is a flush.

Q6 IPC and memory

  • Message queue: preallocated buffers, Pending_messages, notify_handler (S/coremsg.h:117-164); a buffer carries size and int priority (S/coremsgbuffer.h:64-89). Send appends (INT_MAX), urgent prepends (INT_MIN), priority inserts ordered (S/coremsgimpl.h:67-75; score/src/coremsginsert.c:82-103); a waiting receiver gets a direct copy (coremsgsubmit.c:about 85-150).

  • Partition: fixed-size buffers threaded as a chain by _Chain_Initialize (R/partdata.h:57-106; rtems/src/partcreate.c:68-76). Region: Heap_Control plus a FIFO or priority thread queue, STATES_WAITING_FOR_SEGMENT, waiters retried by _Region_Process_queue after each free (R/regiondata.h:60-67; rtems/src/regiongetsegment.c:100-128; regionprocessqueue.c:46-87).

  • Heap: boundary-tagged blocks prev_size and size_and_flag with HEAP_PREV_BLOCK_USED, first-fit over a free list (S/heap.h:60-146, 278-348; score/src/heapallocate.c:217-290; S/heapfirstfitimpl.h). Optional HEAP_PROTECTION adds protector words, an owning task tag, and delayed frees (S/heap.h:50, 239-261). _Protected_heap_* wraps with the allocator mutex (score/src/pheapallocate.c:53-55). The workspace _Workspace_Area is a heap, optionally unified with the C heap (S/wkspace.h:59; S/wkspacedata.h:77-98).

  • Objects: a 32-bit id is class (5 bits at 27) | API (3 at 24) | node (8 at 16) | index (16) (S/object.h:101-174); Objects_Information with local_table, inactive chain, block extension for OBJECTS_UNLIMITED_OBJECTS (S/objectdata.h:198-312; S/object.h:180). _Objects_Get is a single unsigned “delta” range check, a local interrupt disable stored in the lock context, and a table lookup (score/src/objectgetlocal.c:44-80).

  • Configuration: confdefs.h includes topic headers (include/rtems/confdefs.h:61-81) that emit const objects consumed through rtems_configuration_get_* macros (config.h:392-840). Static by default, dynamic only via the unlimited flag.

Q7 Isolation

  • No MPU or MMU task isolation: no MPU, MMU, or protection-domain references in score headers or the template port; the Memory Handler only distributes BSP-provided areas (S/memory.h:63-93, 308-334). Stack overflow is detected after the fact by the stack checker (include/rtems/stackchk.h:70-94; RTEMS_FATAL_SOURCE_STACK_CHECKER, S/interr.h:126); heap protection is a debug aid.

  • rtems_task_mode offers PREEMPT or NO_PREEMPT, TIMESLICE, ASR or NO_ASR, INTERRUPT_LEVEL (R/modes.h:83-256), implemented by toggling is_preemptible, CPU_budget.operations, ASR enable, and _ISR_Set_level; on SMP NO_PREEMPT needs scheduler support and interrupt-level changes are RTEMS_NOT_IMPLEMENTED (rtems/src/taskmode.c:67-155). Timeslicing is a pluggable Thread_CPU_budget_operations (S/threadcpubudget.h:86-94).

Q8 Observability

  • Present in the clone: the capture engine (include/rtems/capture.h:53-413; per-thread hook S/thread.h:822-825), record infrastructure (S/percpu.h:638), rtems_cpu_usage_report and reset (include/rtems/cpuuse.h:123-169), the stack checker, and user extensions {create, start, restart, delete, switch, begin, exitted, fatal, terminate} (S/userext.h:253-263).

  • Per-thread CPU time: cpu_time_used updated on each heir change as the uptime delta against the per-CPU cpu_usage_timestamp (S/threadimpl.h:1260-1293; S/percpu.h:489).

  • Fatal: Internal_errors_Source (S/interr.h:63-179) and core codes including THREAD_QUEUE_DEADLOCK=28, BAD_THREAD_DISPATCH_DISABLE_LEVEL=30, BAD_THREAD_DISPATCH_ENVIRONMENT=31 (S/interr.h:188-237). _Terminate disables interrupts, runs fatal extensions, marks the system terminated, halts in the idle body (score/src/interr.c:51-81).

  • Deadlock detection: _Thread_queue_Path_acquire walks owner to owner->Wait.queue to its owner; reaching the enqueuing thread is THREAD_QUEUE_DEADLOCK_DETECTED. On SMP each hop registers a Thread_queue_Link in a global RB-tree _Thread_queue_Links; _Thread_queue_Link_add finding a reverse edge is the cycle test, and Thread_queue_Gate ordering avoids deadlocking on the SMP locks themselves (score/src/threadqenqueue.c:65-175, 273-383; S/threadq.h:135-139). Enqueue then invokes deadlock_callout: STATUS_DEADLOCK for mutexes or fatal INTERNAL_ERROR_THREAD_QUEUE_DEADLOCK (threadqenqueue.c:400-449; coremutexseize.c:57-60).

Q9 Configuration, portability, quality

  • Port contract (score/cpu/no_cpu/include/rtems/score/cpu.h): feature macros CPU_SIMPLE_VECTORED_INTERRUPTS, CPU_HARDWARE_FP, CPU_USE_DEFERRED_FP_SWITCH, CPU_ENABLE_ROBUST_THREAD_DISPATCH, CPU_STACK_GROWS_UP, CPU_CACHE_LINE_BYTES, CPU_MAXIMUM_PROCESSORS, CPU_STACK_MINIMUM_SIZE, alignments (cpu.h:80-636); functions _CPU_ISR_Disable, Enable, Flash, Set_level, Get_level, _CPU_Context_Initialize, switch, restore, save_fp, restore_fp, _CPU_Thread_Idle_body, _CPU_Bitfield_Find_first_bit, _CPU_Counter_read and frequency, _CPU_Exception_frame_print, _CPU_Spin_wait (cpu.h:654-1362); cpuimpl.h adds the per-CPU control size, TLS variant, _CPU_Get_current_per_CPU_control, context validate and clobber (cpuimpl.h:58-204). Ten ports under score/cpu.

  • Configuration is confdefs.h plus per-topic headers (include/rtems/confdefs.h:61-81), compile-time only.

  • Quality discipline: SPDX BSD-2-Clause on every file (S/thread.h:1), dense Doxygen (248 tags in the 1,517-line S/threadqimpl.h), _clang-format at the repository root, and 34 files in the clone are “part of the RTEMS quality process and was automatically generated” from specification items (R/tasks.h:38-45, 423 spec:/rtems/task/if/create), the ESA pre-qualification toolchain. No RTEMS_PRE_QUALIFIED macro or test-name references appear in the clone.

Q10 Synthesis

RTEMS turns every priority influence (base, ceiling, inheritance, EDF deadline, MrsP ceiling) into a Priority_Node in a min-tree, so one algebra serves UP, SMP, and clustered scheduling; its lock-free wait-flag CAS, per-hop path locking, and cycle detection make blocking provably race- and deadlock-aware. The price is heavy abstraction, a large footprint, a tick-only clock, and no memory isolation.

Ideas to borrow

  • Priority aggregation trees as the single effective-priority mechanism (S/priority.h:147-198; S/priorityimpl.h:565-741).

  • The INTEND_TO_BLOCK, BLOCKED, READY CAS state machine shared by queues, events, and periods (S/threadimpl.h:2259-2431; threadqenqueue.c:458-498).

  • Deadlock detection in PI chains with a status-or-fatal callout (threadqenqueue.c:273-383, 400-449).

  • Transitive PI via the owner-chain action loop (threadchangepriority.c:227-282).

  • Per-CPU watchdog headers with 64-bit expiry and a cached first node, separate tick, monotonic, and realtime trees (S/percpu.h:494-514; watchdoginsert.c:41-81).

  • The rate monotonic manager with postponed-job counting and CPU and wall statistics (ratemonperiod.c:158-212; ratemontimeout.c:46-66).

  • A scheduler plugin table selected at configuration time (S/scheduler.h:62-449).

  • MrsP sticky spin-wait plus helping (S/mrspimpl.h:363-410; threadqenqueue.c:500-575).

  • Flush filters that encode the wake reason into the waiter (threadqflush.c:49-90).

  • Spec-generated API headers with constraint blocks (R/tasks.h:38-45).

Weaknesses

  • Complexity: a 1,575-line threadqops.c, a 919-line threadqenqueue.c, a 2,000-plus-line schedulersmpimpl.h; helping, sticky, and pinning interactions are hard to audit (S/thread.h:297-420).

  • Footprint: 64-bit priorities, per-scheduler nodes per thread, RB-trees everywhere, per-CPU locks and stats (S/percpu.h:384-641; S/schedulernode.h:94-229).

  • No isolation: no MPU or MMU domains; only after-the-fact stack checking (S/memory.h; include/rtems/stackchk.h).

  • Tick-only timing, no tickless idle (watchdogtick.c:76-156; config.h:527).

  • Configuration burden: dozens of CONFIGURE_* headers, compile-time static tables (include/rtems/confdefs.h:61-81).

  • Incomplete features: the affinity scheduler ignores affinity (S/schedulerpriorityaffinitysmp.h:about 50-56); SMP task modes return RTEMS_NOT_IMPLEMENTED (taskmode.c:67-81).

  • The global _Thread_queue_Links registry is a shared contention point on SMP (threadqenqueue.c:65-175).