diff --git a/CHANGELOG.md b/CHANGELOG.md index c5fe5a4..123bbdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ User-visible changes are recorded here. There are no released versions or releas ### Added +- Static definitions for the other object types (A64): `EMB_SEM_DEFINE`, `EMB_MUTEX_DEFINE`, `EMB_EVENT_DEFINE`, `EMB_COND_DEFINE`, `EMB_MSGQ_DEFINE`, `EMB_WORKQ_DEFINE` and, for interrupt-context callbacks, `EMB_TIMER_DEFINE` declare storage (with the ring or stack) and the handle and construct the object in `emb_kernel_init()`. + - Round-robin time slicing (`CONFIG_EMB_TIME_SLICING`, off by default): a thread at or below `CONFIG_EMB_TIMESLICE_PRIORITY` that runs for `CONFIG_EMB_TIMESLICE_TICKS` without blocking moves behind its READY equal-priority peers; without a peer its quantum is renewed (KRN-SCH-007, 026, 037; A63). The quantum is a deadline in the tickless computation and a comparison in the periodic tick. The reference model implements it, the new `native-timeslice` preset checks it through the differential bridge, and `native-timeslice-tickless` runs the conformance cases with the tickless clock. List scheduler only. - The M2 contract decisions are accepted: amendments A29 to A61 and ADR-045 to ADR-048 and ADR-050 (project-owner review, 2026-10-31). diff --git a/docs/requirements/API.md b/docs/requirements/API.md index 6bf8a68..098c3d9 100644 --- a/docs/requirements/API.md +++ b/docs/requirements/API.md @@ -151,8 +151,8 @@ Group `API`. Design: `docs/specs/SPEC-001-api-conventions.md`. Decisions: ADR-00 ### API-021 Construction and destruction **Statement.** Every object type shall provide `emb__init(storage, attr, out_handle)` with context class pre-kernel and thread, which shall set `*out_handle` to `EMB_HANDLE_NULL` on failure, and `emb__destroy(handle)`. A compile-time `EMB__DEFINE` form shall declare storage and handle and register the object in the generated initialization table. **Rationale.** Static definition without hand-written init code is how tables of objects stay in data, not code (principle 14). -**Status.** Accepted 2026-10-07 -**Verification.** Test: conformance init/destroy and `_DEFINE` tests per object type. +**Status.** Accepted 2026-10-07; implemented for threads, semaphores, mutexes, event flags, condition variables, message queues, work queues and interrupt-context timers (A64). +**Verification.** Test: conformance init/destroy and `_DEFINE` tests per object type (`thread_define_registers_in_init_table` and the `*_define_constructs_in_init_table` cases). **Trace.** SPEC-001 §6.4; KRN-OBJ-001, KRN-OBJ-003 ### API-022 Distinct time types diff --git a/docs/specs/AMENDMENTS-M2.md b/docs/specs/AMENDMENTS-M2.md index 08b3bf1..72c4536 100644 --- a/docs/specs/AMENDMENTS-M2.md +++ b/docs/specs/AMENDMENTS-M2.md @@ -41,3 +41,4 @@ Corrections found while preparing the M2 primitives, where an accepted specifica | A61 | SPEC-006 §4.1, §4.2, §4.3, KRN-WQ-007 | `emb_dwork_t` is a public `emb_work_t` (the handler's argument) followed by opaque storage for an internal ISR_CONTEXT timer. The queue is named at schedule time and holds a timer reference while the delay runs, so it cannot be destroyed. `schedule` returns `EMB_EEXIST` while the delay runs or the item is queued, `EMB_EBUSY` while its handler runs on another queue; `reschedule` replaces a running delay and returns `EMB_EEXIST` for a queued item. Cancel reports `EMB_WORK_PENDING` for a stopped delay or a removed item. The trace record is `EMB_TRACE_DWORK_SCHEDULE`; the delay's own start and expiry are the timer records. | `kernel/work/dwork.c`, `include/emb/work.h` | | A62 | KRN-SCH-036, 03 §2.1 | The priority count's lower bound is 2, as `CONFIG_EMB_PRIORITY_COUNT` has always allowed, not 4: idle plus one application level is a valid tiny configuration. Accepted by the project owner 2026-10-31. | `kernel/Kconfig` | | A63 | SPEC-003 §8, KRN-SCH-007, KRN-SCH-026, KRN-SCH-037, KRN-SCH-041, KRN-TIM-029 | Time slicing is implemented with one quantum (`CONFIG_EMB_TIMESLICE_TICKS`) and one threshold (`CONFIG_EMB_TIMESLICE_PRIORITY`, 255 for every level). It requires the list scheduler: `CONFIG_EMB_TIME_SLICING` depends on `!CONFIG_EMB_SCHED_TABLE`, since a table level holds one thread. The quantum restarts at every dispatch, also for a preempted thread resuming ahead of its peers, and a yield without a peer keeps it running. Expiry renews the quantum and, with a READY peer, sets a per-CPU rotation flag that makes the next decision place the thread behind its peers. A thread raised above the threshold stops being sliced at its next expiry; one lowered below it starts at its next dispatch. The reference model implements the same rules; the differential bridge checks them with the periodic clock only (`native-timeslice`), because a tickless quantum starts from the free-running counter, which can run ahead of the ticks the model counts. Implemented 2026-10-31 at the project owner's request; the details await review. | `kernel/time/time.c`, `kernel/sched/sched.c`, `kernel/Kconfig` | +| A64 | SPEC-001 §6.4, SPEC-009 §5.2, API-021, A40 | The deferred cross-type `_DEFINE` change: `EMB_SEM_DEFINE(sym, initial, max)`, `EMB_MUTEX_DEFINE(sym, protocol, ceiling, flags)`, `EMB_EVENT_DEFINE(sym, initial, flags)`, `EMB_COND_DEFINE(sym, flags)`, `EMB_MSGQ_DEFINE(sym, item_size, capacity, flags)` with its ring, and `EMB_WORKQ_DEFINE(sym, priority, stack_size)` with its stack. They take positional fields like `EMB_THREAD_DEFINE` rather than an attribute initializer, because a partial initializer would zero fields whose defaults are not zero (a semaphore's `max`, a work queue's priority). The table entry stays one init-wrapper pointer, as for threads, not `{init_fn, &storage, &attr, &handle}`. `EMB_TIMER_DEFINE(sym, fn, arg)` defines interrupt-context timers only: a work-queue timer needs its queue at construction and table entries, the system queue's included, run in link order, while `EMB_INIT_AFTER` is not implemented. The object name is the symbol where `CONFIG_EMB_OBJ_NAMES` keeps names, else none. A construction failure faults through `emb_check_status()`. | `include/emb/*.h` | diff --git a/docs/specs/SPEC-001-api-conventions.md b/docs/specs/SPEC-001-api-conventions.md index 063144a..afb4f0c 100644 --- a/docs/specs/SPEC-001-api-conventions.md +++ b/docs/specs/SPEC-001-api-conventions.md @@ -244,9 +244,11 @@ emb_status_t emb_mutex_destroy(emb_mutex_t mutex); `init` is `prekernel thread`. On failure `*out_mutex` is `EMB_HANDLE_NULL`. `destroy` follows KRN-OBJ-001. Compile-time definition helpers exist for the common static case: ```c -EMB_MUTEX_DEFINE(name, attr_initializer...) /* storage + handle variable + registration in the init table */ +EMB_MUTEX_DEFINE(name, protocol, ceiling, flags) /* storage + handle variable + registration in the init table */ ``` +Each type's macro takes the attribute fields a static object needs as positional arguments, like `EMB_THREAD_DEFINE`; the rest keep their `attr_default` values (A64). + The `_DEFINE` form registers the object in a generated initialization table so it is constructed during `emb_kernel_init()`, before the scheduler starts without hand-written init code; the table is one of the bracketed sections `embld` and GNU ld both provide (09 §7). ## 7. Time diff --git a/docs/specs/SPEC-009-objects-capabilities-and-storage.md b/docs/specs/SPEC-009-objects-capabilities-and-storage.md index c4e42ac..a2a5625 100644 --- a/docs/specs/SPEC-009-objects-capabilities-and-storage.md +++ b/docs/specs/SPEC-009-objects-capabilities-and-storage.md @@ -124,7 +124,7 @@ Two passes over the compiler are the price; CMake handles them as one target wit ### 5.2 `_DEFINE` and the init table -`EMB__DEFINE(name, attr...)` expands to the storage, the handle variable, a constant attribute struct, and an entry `{init_fn, &storage, &attr, &handle}` in the bracketed section `.emb_init_table`, which `embld` and GNU ld both provide on every target (09 §7; no linker script is needed on AVR). `emb_kernel_init()` walks the bracketed table in link order and calls each init wrapper before scheduler start; thread entries are also started at `emb_kernel_start()` unless marked `EMB_THREAD_NO_AUTOSTART` (SPEC-008 §4). Entries from different translation units have no defined relative order; an object that depends on another at init time (a timer on a work queue) must be initialized in code or by the `EMB_INIT_AFTER(other)` attribute, which the generator honours by ordering the table it emits for system-described objects. +`EMB__DEFINE(name, fields...)` expands to the storage, the handle variable, an init wrapper that fills the attributes from their defaults and the positional fields and calls `emb__init()`, and an entry holding the wrapper in the bracketed section `.emb_init_table` (A64), which `embld` and GNU ld both provide on every target (09 §7; no linker script is needed on AVR). `emb_kernel_init()` walks the bracketed table in link order and calls each init wrapper before scheduler start; thread entries are also started at `emb_kernel_start()` unless marked `EMB_THREAD_NO_AUTOSTART` (SPEC-008 §4). Entries from different translation units have no defined relative order; an object that depends on another at init time (a timer on a work queue) must be initialized in code or by the `EMB_INIT_AFTER(other)` attribute, which the generator honours by ordering the table it emits for system-described objects. ### 5.3 Objects in flash diff --git a/docs/user/synchronization.md b/docs/user/synchronization.md index 11be62c..d007fa0 100644 --- a/docs/user/synchronization.md +++ b/docs/user/synchronization.md @@ -101,6 +101,8 @@ int main(void) Expected output is `produced=5 consumed=5` and exit status zero. This is a unit-counting example: it does not transmit payloads or claim to implement a message queue. The default mutex uses inheritance; this program requires `CONFIG_EMB_SEM`, `CONFIG_EMB_NOTIFY`, `CONFIG_EMB_MUTEX` and `CONFIG_EMB_MUTEX_INHERIT`. The reference native GCC configurations provide them. +Objects that live for the whole program can be defined statically instead, like the threads: `EMB_SEM_DEFINE(ready, 1u, EMB_SEM_COUNT_MAX)` and `EMB_MUTEX_DEFINE(state, EMB_MUTEX_INHERIT, 0u, 0u)` declare the storage and the handle and construct the object in `emb_kernel_init()`; a construction failure faults. `EMB_EVENT_DEFINE`, `EMB_COND_DEFINE`, `EMB_MSGQ_DEFINE` (with its ring), `EMB_WORKQ_DEFINE` (with its stack) and `EMB_TIMER_DEFINE` (interrupt-context callbacks only) follow the same pattern. Definitions run in link order, so nothing defined may depend on another definition at construction time: bindings, and timers whose callbacks run on a work queue, are set up in code after `emb_kernel_init()`. + ## Why the bound consumer drains first Binding takes a **one-bit mask**, such as `EMB_NOTIFY_BIT(0)`, rather than the integer bit index. It registers one target per semaphore. The implementation sets that bit on a later count transition from zero to one; binding an already-positive semaphore does not set it. The initial unit above therefore needs an initial readiness check. diff --git a/include/emb/cond.h b/include/emb/cond.h index 77eb6f7..d2d182b 100644 --- a/include/emb/cond.h +++ b/include/emb/cond.h @@ -4,6 +4,7 @@ #ifndef EMB_COND_H #define EMB_COND_H +#include #include #include #include @@ -167,6 +168,25 @@ emb_status_t emb_cond_signal(emb_cond_t cond); */ emb_status_t emb_cond_broadcast(emb_cond_t cond); +/* EMB_COND_DEFINE(sym, flags): storage, the handle variable `sym`, and an init-table + * entry that constructs the condition variable in emb_kernel_init() (SPEC-001 §6.4, A64); + * a failure faults. MISRA Dev CS-12: rule 20.7, `sym` is a token. */ +/* NOLINTBEGIN(bugprone-macro-parentheses): MISRA Dev CS-12 rule 20.7, as above */ +#define EMB_COND_DEFINE(sym, flags_) \ + static emb_cond_storage_t sym##_storage_; \ + emb_cond_t sym; \ + static void sym##_init_(void) \ + { \ + emb_cond_attr_t attr_; \ + emb_cond_attr_default(&attr_); \ + attr_.name = EMB_DEFINE_NAME_(#sym); \ + attr_.flags = (uint8_t)(flags_); \ + (void)emb_check_status(emb_cond_init(&sym##_storage_, &attr_, &sym), \ + "EMB_COND_DEFINE(" #sym ")"); \ + } \ + EMB_INIT_TABLE_ENTRY(sym##_init_) +/* NOLINTEND(bugprone-macro-parentheses) */ + #ifdef __cplusplus } #endif diff --git a/include/emb/event.h b/include/emb/event.h index 8ebc011..e954d0b 100644 --- a/include/emb/event.h +++ b/include/emb/event.h @@ -236,6 +236,26 @@ emb_status_t emb_event_bind_notify(emb_event_t event, emb_thread_t thread, emb_n emb_status_t emb_event_unbind_notify(emb_event_t event); #endif +/* EMB_EVENT_DEFINE(sym, initial, flags): storage, the handle variable `sym`, and an + * init-table entry that constructs the event flags in emb_kernel_init() (SPEC-001 §6.4, + * A64); a failure faults. MISRA Dev CS-12: rule 20.7, `sym` is a token. */ +/* NOLINTBEGIN(bugprone-macro-parentheses): MISRA Dev CS-12 rule 20.7, as above */ +#define EMB_EVENT_DEFINE(sym, initial_, flags_) \ + static emb_event_storage_t sym##_storage_; \ + emb_event_t sym; \ + static void sym##_init_(void) \ + { \ + emb_event_attr_t attr_; \ + emb_event_attr_default(&attr_); \ + attr_.name = EMB_DEFINE_NAME_(#sym); \ + attr_.initial = (emb_event_bits_t)(initial_); \ + attr_.flags = (uint8_t)(flags_); \ + (void)emb_check_status(emb_event_init(&sym##_storage_, &attr_, &sym), \ + "EMB_EVENT_DEFINE(" #sym ")"); \ + } \ + EMB_INIT_TABLE_ENTRY(sym##_init_) +/* NOLINTEND(bugprone-macro-parentheses) */ + #ifdef __cplusplus } #endif diff --git a/include/emb/kernel.h b/include/emb/kernel.h index c110706..c2aaf88 100644 --- a/include/emb/kernel.h +++ b/include/emb/kernel.h @@ -66,6 +66,14 @@ typedef struct EMB_ALIGNED(sizeof(void *)) emb_init_entry { static EMB_TABLE_CONST emb_init_entry_t fn##_init_entry_ EMB_USED EMB_TABLE_ENTRY_ATTR \ EMB_SECTION(EMB_TABLE_SECTION("emb_init_table")) = {fn} +/* The name an object _DEFINE macro gives its object: none where the kernel keeps no + * names, so Harvard ports do not copy the literal to RAM. */ +#if CONFIG_EMB_OBJ_NAMES +#define EMB_DEFINE_NAME_(s) (s) +#else +#define EMB_DEFINE_NAME_(s) ((const char *)0) +#endif + /* ---- EMB_CHECK --------------------------------------------------------------- */ /** diff --git a/include/emb/msgq.h b/include/emb/msgq.h index 7698802..13760ee 100644 --- a/include/emb/msgq.h +++ b/include/emb/msgq.h @@ -302,6 +302,31 @@ emb_status_t emb_msgq_bind_notify(emb_msgq_t msgq, emb_thread_t thread, emb_noti emb_status_t emb_msgq_unbind_notify(emb_msgq_t msgq); #endif +/* EMB_MSGQ_DEFINE(sym, item_size, capacity, flags): storage, a ring of @capacity items + * of @item_size bytes, the handle variable `sym`, and an init-table entry that constructs + * the queue in emb_kernel_init() (SPEC-001 §6.4, A64); a failure faults. MISRA Dev CS-12: + * rule 20.7, `sym` is a token. */ +/* NOLINTBEGIN(bugprone-macro-parentheses): MISRA Dev CS-12 rule 20.7, as above */ +#define EMB_MSGQ_DEFINE(sym, item_size_, capacity_, flags_) \ + static emb_msgq_storage_t sym##_storage_; \ + static EMB_MSGQ_RING(sym##_ring_, (item_size_), (capacity_)); \ + emb_msgq_t sym; \ + static void sym##_init_(void) \ + { \ + emb_msgq_attr_t attr_; \ + emb_msgq_attr_default(&attr_); \ + attr_.name = EMB_DEFINE_NAME_(#sym); \ + attr_.ring = sym##_ring_; \ + attr_.ring_size = sizeof(sym##_ring_); \ + attr_.item_size = (uint16_t)(item_size_); \ + attr_.capacity = (uint16_t)(capacity_); \ + attr_.flags = (uint8_t)(flags_); \ + (void)emb_check_status(emb_msgq_init(&sym##_storage_, &attr_, &sym), \ + "EMB_MSGQ_DEFINE(" #sym ")"); \ + } \ + EMB_INIT_TABLE_ENTRY(sym##_init_) +/* NOLINTEND(bugprone-macro-parentheses) */ + #ifdef __cplusplus } #endif diff --git a/include/emb/mutex.h b/include/emb/mutex.h index 2682014..940dcba 100644 --- a/include/emb/mutex.h +++ b/include/emb/mutex.h @@ -10,6 +10,7 @@ #ifndef EMB_MUTEX_H #define EMB_MUTEX_H +#include #include #include #include @@ -189,6 +190,27 @@ bool emb_mutex_is_owner(emb_mutex_t mutex); */ emb_status_t emb_mutex_mark_consistent(emb_mutex_t mutex); +/* EMB_MUTEX_DEFINE(sym, protocol, ceiling, flags): storage, the handle variable `sym`, + * and an init-table entry that constructs the mutex in emb_kernel_init() (SPEC-001 §6.4, + * A64); a failure faults. MISRA Dev CS-12: rule 20.7, `sym` is a token. */ +/* NOLINTBEGIN(bugprone-macro-parentheses): MISRA Dev CS-12 rule 20.7, as above */ +#define EMB_MUTEX_DEFINE(sym, protocol_, ceiling_, flags_) \ + static emb_mutex_storage_t sym##_storage_; \ + emb_mutex_t sym; \ + static void sym##_init_(void) \ + { \ + emb_mutex_attr_t attr_; \ + emb_mutex_attr_default(&attr_); \ + attr_.name = EMB_DEFINE_NAME_(#sym); \ + attr_.protocol = (uint8_t)(protocol_); \ + attr_.ceiling = (uint8_t)(ceiling_); \ + attr_.flags = (uint8_t)(flags_); \ + (void)emb_check_status(emb_mutex_init(&sym##_storage_, &attr_, &sym), \ + "EMB_MUTEX_DEFINE(" #sym ")"); \ + } \ + EMB_INIT_TABLE_ENTRY(sym##_init_) +/* NOLINTEND(bugprone-macro-parentheses) */ + #ifdef __cplusplus } #endif diff --git a/include/emb/sem.h b/include/emb/sem.h index 1811cca..b3bacb4 100644 --- a/include/emb/sem.h +++ b/include/emb/sem.h @@ -229,6 +229,26 @@ emb_status_t emb_sem_bind_notify(emb_sem_t sem, emb_thread_t thread, emb_notify_ emb_status_t emb_sem_unbind_notify(emb_sem_t sem); #endif +/* EMB_SEM_DEFINE(sym, initial, max): storage, the handle variable `sym`, and an + * init-table entry that constructs the semaphore in emb_kernel_init() (SPEC-001 §6.4, + * A64); a failure faults. MISRA Dev CS-12: rule 20.7, `sym` is a token. */ +/* NOLINTBEGIN(bugprone-macro-parentheses): MISRA Dev CS-12 rule 20.7, as above */ +#define EMB_SEM_DEFINE(sym, initial_, max_) \ + static emb_sem_storage_t sym##_storage_; \ + emb_sem_t sym; \ + static void sym##_init_(void) \ + { \ + emb_sem_attr_t attr_; \ + emb_sem_attr_default(&attr_); \ + attr_.name = EMB_DEFINE_NAME_(#sym); \ + attr_.initial = (emb_sem_count_t)(initial_); \ + attr_.max = (emb_sem_count_t)(max_); \ + (void)emb_check_status(emb_sem_init(&sym##_storage_, &attr_, &sym), \ + "EMB_SEM_DEFINE(" #sym ")"); \ + } \ + EMB_INIT_TABLE_ENTRY(sym##_init_) +/* NOLINTEND(bugprone-macro-parentheses) */ + #ifdef __cplusplus } #endif diff --git a/include/emb/timer.h b/include/emb/timer.h index 470a466..4d9d022 100644 --- a/include/emb/timer.h +++ b/include/emb/timer.h @@ -4,6 +4,7 @@ #ifndef EMB_TIMER_H #define EMB_TIMER_H +#include #include #include #include @@ -281,6 +282,29 @@ emb_status_t emb_timer_remaining(emb_timer_t timer, emb_duration_t *out_rem); emb_status_t emb_timer_get_counts(emb_timer_t timer, uint32_t *out_expiries, uint32_t *out_overruns); +/* EMB_TIMER_DEFINE(sym, fn, arg): storage, the handle variable `sym`, and an init-table + * entry that constructs a stopped EMB_TIMER_ISR_CONTEXT timer in emb_kernel_init() + * (SPEC-001 §6.4, A64); a failure faults. A timer whose callback runs on a work queue is + * constructed with emb_timer_init() once its queue exists: init-table entries, the system + * queue's included, run in link order. MISRA Dev CS-12: rule 20.7, `sym` is a token. */ +/* NOLINTBEGIN(bugprone-macro-parentheses): MISRA Dev CS-12 rule 20.7, as above */ +#define EMB_TIMER_DEFINE(sym, fn_, arg_) \ + static emb_timer_storage_t sym##_storage_; \ + emb_timer_t sym; \ + static void sym##_init_(void) \ + { \ + emb_timer_attr_t attr_; \ + emb_timer_attr_default(&attr_); \ + attr_.name = EMB_DEFINE_NAME_(#sym); \ + attr_.fn = (fn_); \ + attr_.arg = (arg_); \ + attr_.flags = EMB_TIMER_ISR_CONTEXT; \ + (void)emb_check_status(emb_timer_init(&sym##_storage_, &attr_, &sym), \ + "EMB_TIMER_DEFINE(" #sym ")"); \ + } \ + EMB_INIT_TABLE_ENTRY(sym##_init_) +/* NOLINTEND(bugprone-macro-parentheses) */ + #ifdef __cplusplus } #endif diff --git a/include/emb/work.h b/include/emb/work.h index f274487..668e493 100644 --- a/include/emb/work.h +++ b/include/emb/work.h @@ -4,8 +4,10 @@ #ifndef EMB_WORK_H #define EMB_WORK_H +#include #include #include +#include #include #include @@ -379,6 +381,29 @@ emb_status_t emb_dwork_cancel(emb_dwork_t *dwork, emb_work_state_t *out_state); emb_status_t emb_dwork_cancel_sync(emb_dwork_t *dwork, emb_timeout_t timeout); #endif +/* EMB_WORKQ_DEFINE(sym, priority, stack_size): storage, the queue thread's stack, the + * handle variable `sym`, and an init-table entry that constructs the queue in + * emb_kernel_init() (SPEC-001 §6.4, A64); a failure faults. Entries run in link order, so + * another entry must not rely on the queue. MISRA Dev CS-12: rule 20.7, `sym` is a token. */ +/* NOLINTBEGIN(bugprone-macro-parentheses): MISRA Dev CS-12 rule 20.7, as above */ +#define EMB_WORKQ_DEFINE(sym, priority_, stack_size_) \ + static emb_workq_storage_t sym##_storage_; \ + EMB_THREAD_STACK(sym##_stack_, (stack_size_)); \ + emb_workq_t sym; \ + static void sym##_init_(void) \ + { \ + emb_workq_attr_t attr_; \ + emb_workq_attr_default(&attr_); \ + attr_.name = EMB_DEFINE_NAME_(#sym); \ + attr_.stack = sym##_stack_; \ + attr_.stack_size = sizeof(sym##_stack_); \ + attr_.priority = (uint8_t)(priority_); \ + (void)emb_check_status(emb_workq_init(&sym##_storage_, &attr_, &sym), \ + "EMB_WORKQ_DEFINE(" #sym ")"); \ + } \ + EMB_INIT_TABLE_ENTRY(sym##_init_) +/* NOLINTEND(bugprone-macro-parentheses) */ + #ifdef __cplusplus } #endif diff --git a/tests/conformance/test_cond.c b/tests/conformance/test_cond.c index d51c748..98abaca 100644 --- a/tests/conformance/test_cond.c +++ b/tests/conformance/test_cond.c @@ -160,3 +160,12 @@ EMB_TEST(cond_storage_reuse_requires_destroy) teardown(); } #endif + +EMB_COND_DEFINE(defined_cond, 0u); + +EMB_TEST(cond_define_constructs_in_init_table) +{ + EMB_TEST_REQ("API-021", "KRN-OBJ-009"); + EMB_ASSERT_OK(emb_cond_signal(defined_cond)); /* no waiter: a no-op on a live object */ + EMB_ASSERT_OK(emb_cond_destroy(defined_cond)); +} diff --git a/tests/conformance/test_event.c b/tests/conformance/test_event.c index 18590ac..c76cf29 100644 --- a/tests/conformance/test_event.c +++ b/tests/conformance/test_event.c @@ -187,3 +187,11 @@ EMB_TEST(event_storage_reuse_requires_destroy) EMB_ASSERT_OK(emb_event_destroy(ev)); } #endif + +EMB_EVENT_DEFINE(defined_event, 0x5u, 0u); + +EMB_TEST(event_define_constructs_in_init_table) +{ + EMB_TEST_REQ("API-021", "KRN-OBJ-009"); + EMB_ASSERT_EQ(emb_event_get(defined_event), 0x5u); +} diff --git a/tests/conformance/test_msgq.c b/tests/conformance/test_msgq.c index 3de770a..299da7b 100644 --- a/tests/conformance/test_msgq.c +++ b/tests/conformance/test_msgq.c @@ -310,3 +310,18 @@ EMB_TEST(msgq_small_counts_refuse_a_capacity_above_255) EMB_ASSERT_MISUSE(emb_msgq_init(&storage, &attr, &q), EMB_EINVAL); /* 8-bit counts (A45) */ } #endif + +EMB_MSGQ_DEFINE(defined_msgq, sizeof(uint32_t), 2u, 0u); + +EMB_TEST(msgq_define_constructs_in_init_table) +{ + uint32_t v = 7u; + uint32_t out = 0u; + EMB_TEST_REQ("API-021", "KRN-OBJ-009"); + EMB_ASSERT_OK(emb_msgq_send(defined_msgq, &v, EMB_NO_WAIT)); + EMB_ASSERT_OK(emb_msgq_send(defined_msgq, &v, EMB_NO_WAIT)); + EMB_ASSERT_STATUS(emb_msgq_send(defined_msgq, &v, EMB_NO_WAIT), EMB_ETIMEDOUT); /* capacity 2 */ + EMB_ASSERT_OK(emb_msgq_receive(defined_msgq, &out, EMB_NO_WAIT)); + EMB_ASSERT_EQ(out, 7u); + EMB_ASSERT_OK(emb_msgq_receive(defined_msgq, &out, EMB_NO_WAIT)); +} diff --git a/tests/conformance/test_mutex.c b/tests/conformance/test_mutex.c index ff29312..4e5565f 100644 --- a/tests/conformance/test_mutex.c +++ b/tests/conformance/test_mutex.c @@ -333,3 +333,14 @@ EMB_TEST(mutex_init_validates_arguments) EMB_ASSERT_MISUSE(emb_mutex_init(&st1, &attr, &m1), EMB_EINVAL); EMB_ASSERT_MISUSE(emb_mutex_init(NULL, NULL, &m1), EMB_EINVAL); } + +EMB_MUTEX_DEFINE(defined_mutex, EMB_MUTEX_INHERIT, 0u, EMB_MUTEX_RECURSIVE); + +EMB_TEST(mutex_define_constructs_in_init_table) +{ + EMB_TEST_REQ("API-021", "KRN-OBJ-009"); + EMB_ASSERT_OK(emb_mutex_lock(defined_mutex, EMB_NO_WAIT)); + EMB_ASSERT_OK(emb_mutex_lock(defined_mutex, EMB_NO_WAIT)); /* recursive */ + EMB_ASSERT_OK(emb_mutex_unlock(defined_mutex)); + EMB_ASSERT_OK(emb_mutex_unlock(defined_mutex)); +} diff --git a/tests/conformance/test_sem.c b/tests/conformance/test_sem.c index 50ccd8e..a9b7179 100644 --- a/tests/conformance/test_sem.c +++ b/tests/conformance/test_sem.c @@ -184,3 +184,16 @@ EMB_TEST(sem_blocking_take_from_prekernel_or_isr_rejected) EMB_ASSERT_OK(emb_sem_take(sem, EMB_WAIT_FOREVER)); /* satisfied without blocking */ EMB_ASSERT_OK(emb_sem_destroy(sem)); } + +EMB_SEM_DEFINE(defined_sem, 1u, 2u); + +EMB_TEST(sem_define_constructs_in_init_table) +{ + EMB_TEST_REQ("API-021", "KRN-OBJ-009"); + EMB_ASSERT_OK(emb_sem_take(defined_sem, EMB_NO_WAIT)); /* initial 1 */ + EMB_ASSERT_STATUS(emb_sem_take(defined_sem, EMB_NO_WAIT), EMB_ETIMEDOUT); + EMB_ASSERT_OK(emb_sem_give(defined_sem)); + EMB_ASSERT_OK(emb_sem_give(defined_sem)); + EMB_ASSERT_STATUS(emb_sem_give(defined_sem), EMB_EOVERFLOW); /* max 2 */ + EMB_ASSERT_OK(emb_sem_take(defined_sem, EMB_NO_WAIT)); +} diff --git a/tests/conformance/test_timer.c b/tests/conformance/test_timer.c index 50629c3..14e3c32 100644 --- a/tests/conformance/test_timer.c +++ b/tests/conformance/test_timer.c @@ -244,3 +244,20 @@ EMB_TEST(timer_missed_periods_are_skipped_in_closed_form) teardown(EMB_TIMER_ISR_CONTEXT); } #endif + +static void defined_callback(emb_timer_t timer, void *arg) +{ + (void)timer; + emb_test_mark((int)(intptr_t)arg); +} + +EMB_TIMER_DEFINE(defined_timer, defined_callback, (void *)6); + +EMB_TEST(timer_define_constructs_in_init_table) +{ + EMB_TEST_REQ("API-021", "KRN-OBJ-009"); + EMB_ASSERT_TRUE(!emb_timer_is_running(defined_timer)); /* constructed stopped */ + EMB_ASSERT_OK(emb_timer_start(defined_timer, EMB_TICKS(1))); + EMB_ASSERT_OK(emb_thread_sleep(EMB_TICKS(2))); + EMB_ASSERT_MARKS(6); +} diff --git a/tests/conformance/test_workq.c b/tests/conformance/test_workq.c index 0f3aa41..06968aa 100644 --- a/tests/conformance/test_workq.c +++ b/tests/conformance/test_workq.c @@ -330,3 +330,21 @@ EMB_TEST(dwork_cancel_sync_waits_for_a_running_handler) EMB_ASSERT_OK(emb_workq_destroy(wq)); } #endif + +EMB_WORKQ_DEFINE(defined_workq, 4u, EMB_TEST_STACK_SIZE); + +static void defined_handler(emb_work_t *work) +{ + (void)work; + emb_test_mark(4); +} + +EMB_TEST(workq_define_constructs_in_init_table) +{ + static emb_work_t w; + EMB_TEST_REQ("API-021", "KRN-OBJ-009"); + emb_work_init(&w, defined_handler); + EMB_ASSERT_OK(emb_work_submit(defined_workq, &w)); /* the queue thread outranks the runner */ + emb_test_mark(1); + EMB_ASSERT_MARKS(4, 1); +}