XTOS / OPEN SOURCE REAL-TIME SYSTEMOPEN SOURCE · VERSION 0.9.0 · BUILD FROM SOURCE
Docslibxtos-rt

libxtos-rt

Linux scheduling, affinity, locked memory, and single-producer single-consumer frame queues.

On this page

Policy and permissions

xtos_rt_set_scheduler(policy, priority) calls sched_setscheduler(0, ...) for the calling thread.

SCHED_OTHER requires priority=0; this is not a nice value.

SCHED_FIFO/RR usually use 1–99, with larger static priority; query sched_get_priority_min/max for the actual range.

Do not use this interface for SCHED_DEADLINE, which has a different sched_setattr contract.

PolicySemanticsPrerequisites and errors
SCHED_OTHERTime-sharing; suitable for device-free examples, not real-time latency.priority=0; subject to system security policy.
SCHED_FIFOEqual priorities do not time-slice; block or yield to avoid monopolizing a CPU.Needs CAP_SYS_NICE or eligible RLIMIT_RTPRIO; insufficient permission usually gives EPERM.
SCHED_RRRound-robin at equal priority; still above ordinary policy.Same permissions as FIFO; invalid policy or priority gives EINVAL.

Success is 0 and failure is -1. The implementation checks the range, sets EINVAL for an out-of-range priority, then calls sched_*; errno may also come from the system call. Containers, CPU cgroups, and security policy can restrict setup even for uid 0. The example uses SCHED_OTHER and needs no real-time privilege.

Affinity and apply

c
struct xtos_rt_config {
    int policy;
    int priority;
    int cpu;
};

int xtos_rt_apply(const struct xtos_rt_config *config);
int xtos_rt_set_scheduler(int policy, int priority);
int xtos_rt_set_affinity(int cpu);
int xtos_rt_get_current(int *policy, int *priority);

cpu is a logical CPU ID, not an index in an allowed list. XTOS_RT_NO_CPU (-1) skips affinity only in apply; direct set_affinity(-1) returns -1/EINVAL. set_affinity accepts 0≤cpu<CPU_SETSIZE and sets a single-CPU mask. An offline CPU, a CPU outside the cpuset, or an empty intersection can yield EINVAL; CPU 0 is not portable.

apply requires non-NULL config, sets scheduling first, then optional affinity.

If the second step fails, the first remains applied; there is no rollback.

Save the original policy and affinity for recovery. get_current requires two non-NULL output pointers and returns the calling thread’s policy and static priority, not affinity; read affinity with sched_getaffinity.

Configuration functions return 0/-1 and retain no caller pointers.

Configure inside the worker that will run.

Locked memory

InterfaceParameters and ownershipResult and errors
lock_memory(address,length)address is non-NULL and length is positive valid bytes; caller owns the memory.0/-1; NULL or zero gives EINVAL; other errors come from mlock.
alloc_locked(length)length>0; posix_memalign page-aligns before mlock; memory is uninitialized.Pointer or NULL; EINVAL, ENOMEM, ENOSYS, or mlock errno.
free_locked(address,length)Use only the original alloc_locked pointer and length, not stack, subrange, or freed storage.munlock then free; frees even if munlock fails; do not retry after -1.

mlock is limited by RLIMIT_MEMLOCK; CAP_IPC_LOCK changes the privilege condition.

Limits, unmapped ranges, or resource exhaustion can give ENOMEM; policy can give EPERM/EAGAIN.

Allocate, clear, and touch memory during startup.

Unlock lock_memory ranges with munlock; free_locked cannot release arbitrary addresses.

Queue internals, stacks, temporaries, and future allocations are not locked automatically.

SPSC queue

c
struct xtos_sample_frame {
    uint64_t timestamp_ns;
    uint64_t sequence;
    uint32_t channels;
    uint32_t sample_rate_hz;
    uint32_t quality_flags;
    const int32_t *samples_nv;
};

struct xtos_spsc_queue *xtos_spsc_create(size_t capacity, uint32_t channels);
int xtos_spsc_push(struct xtos_spsc_queue *queue,
                   const struct xtos_sample_frame *frame);
int xtos_spsc_pop(struct xtos_spsc_queue *queue,
                  struct xtos_sample_frame *frame,
                  int32_t *samples_nv, size_t samples_capacity);
void xtos_spsc_destroy(struct xtos_spsc_queue *queue);

SPSC requires one producer for every push and one consumer for every pop; sequential single-thread use is valid. Multiple acquisition threads cannot share push, and multiple workers cannot share pop. Head, tail, and slot contents use acquire/release publication; there is no MPMC arbitration.

create requires capacity>0 and channels>0; capacity counts complete frames, not bytes, and need not be a power of two. Each slot allocates its channel array at creation; invalid values give EINVAL and allocation failure usually ENOMEM. push/pop do not allocate. Stop both endpoints before destruction. push checks non-NULL pointers, matching channels, and nonzero timestamp_ns, but not time order, sequence continuity, nonzero sample_rate_hz, or quality_flags; samples_nv must expose at least channels readable elements.

Queue returns and lifetime

ConditionResultHandling
push success0; metadata and samples are copied into the slot.The source array may be reused after return.
push full-1/EAGAIN; rejects the new frame without overwriting old frames.Record drops and sequence gaps; avoid unbounded high-priority spinning.
invalid push-1/EINVAL; rejected_invalid increases for a valid queue.Fix channels, timestamp, or pointers.
pop success0; samples_capacity≥channels, measured in elements.frame.samples_nv points to caller output.
empty/invalid popEmpty is -1/EAGAIN; NULL or short output is -1/EINVAL.Do not use unwritten frame or output after failure.

get_stats reads pushed, popped, rejected_full, rejected_invalid, and overwritten atomics independently, not as a consistent cross-field snapshot; the current reject-on-full path does not increment overwritten.

NULL stats is ignored; size/capacity return 0 for a NULL queue.

Concurrent size is not a reservation or synchronization primitive; use push/pop results.

Sources and tests are sdk/libxtos-rt/include/xtos/{rt,spsc}.h, src/{rt,spsc}.c, tests/test_rt.c, and tests/test_spsc.c.

Confirm C11 atomic lock-freedom on the target.