babblesim: Use pthreads instead of setlongjmp for tasks

This changes tasks handling to native threads instead of setlongjmp()
which resolves issue with calling the setlongjmp() from nested signal
handlers but also simplifies code, makes debugging much easier and can
work nicely with e.g. Valgrind.

Each task is wrapped in a thread and all threads are synchronized on
a global mutex to make sure only one task executes at any time. If
context switch is requested (this is always done via os_sched() in
critical section), a flag is set to indicate pending context switch
which will be handled after exiting from critical setion and handling
all other pending interrupts. This mimics the way it's done on a real
hardware.
This commit is contained in:
Andrzej Kaczmarek
2022-02-17 15:55:31 +01:00
parent b0c69ce9d4
commit 2332051c61
9 changed files with 104 additions and 957 deletions
-6
View File
@@ -57,9 +57,6 @@ void posix_interrupt_raised(void)
*/
void posix_irq_handler_im_from_sw(void)
{
int sr = 0;
sr = sig_block_irq_on();
/*
* if a higher priority interrupt than the possibly currently running is
* pending we go immediately into irq_handler() to vector into its
@@ -68,7 +65,4 @@ void posix_irq_handler_im_from_sw(void)
if (hw_irq_ctrl_get_highest_prio_irq() != -1) {
posix_interrupt_raised();
}
if (sr) {
sig_unblock_irq_off();
}
}
+5
View File
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <unistd.h>
#include "NRF_HW_model_top.h"
#include "NRF_HWLowL.h"
#include "bs_tracing.h"
@@ -65,6 +66,10 @@ bsim_init(int argc, char** argv, int (*main_fn)(int argc, char **arg))
nrf_hw_initialize(&args->nrf_hw);
os_init(main_fn);
os_start();
while (1) {
sleep(1);
}
}
void
@@ -17,72 +17,129 @@
* under the License.
*/
#include "os/mynewt.h"
#include "os/sim.h"
#include "sim_priv.h"
#define _GNU_SOURCE
#include <pthread.h>
#include <syscfg/syscfg.h>
#include <os/os_task.h>
#include <hal/hal_os_tick.h>
#include <irq_ctrl.h>
/*
* From HAL_CM4.s
*/
extern void SVC_Handler(void);
extern void PendSV_Handler(void);
extern void SysTick_Handler(void);
static pthread_mutex_t bsim_ctx_sw_mutex = PTHREAD_MUTEX_INITIALIZER;
static int bsim_pend_sv;
/*
* Assert that 'sf_mainsp' and 'sf_jb' are at the specific offsets where
* os_arch_frame_init() expects them to be.
*/
CTASSERT(offsetof(struct stack_frame, sf_mainsp) == 0);
CTASSERT(offsetof(struct stack_frame, sf_jb) == 4);
struct task_info {
pthread_t tid;
pthread_cond_t cond;
void *arg;
};
void
os_arch_task_start(struct stack_frame *sf, int rc)
static void *
task_wrapper(void *arg)
{
sim_task_start(sf, rc);
struct os_task *me = arg;
struct task_info *ti = me->t_arg;
pthread_mutex_lock(&bsim_ctx_sw_mutex);
if (g_current_task != me) {
pthread_cond_wait(&ti->cond, &bsim_ctx_sw_mutex);
assert(g_current_task == me);
}
me->t_func(ti->arg);
assert(0);
}
os_stack_t *
os_arch_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size)
{
return sim_task_stack_init(t, stack_top, size);
struct task_info *ti;
int err;
ti = calloc(1, sizeof(*ti));
pthread_cond_init(&ti->cond, NULL);
ti->arg = t->t_arg;
t->t_arg = ti;
err = pthread_create(&ti->tid, NULL, task_wrapper, t);
assert(err == 0);
pthread_setname_np(ti->tid, t->t_name);
return stack_top;
}
os_error_t
os_arch_os_start(void)
{
return sim_os_start();
}
struct os_task *next_t;
struct task_info *ti;
void
os_arch_os_stop(void)
{
sim_os_stop();
}
os_tick_init(OS_TICKS_PER_SEC, 7);
void
PendSV_Handler(void)
{
sim_switch_tasks();
next_t = os_sched_next_task();
assert(next_t);
os_sched_set_current_task(next_t);
g_os_started = 1;
ti = next_t->t_arg;
pthread_cond_signal(&ti->cond);
return 0;
}
os_error_t
os_arch_os_init(void)
{
NVIC_SetVector(PendSV_IRQn, (uint32_t)PendSV_Handler);
return sim_os_init();
STAILQ_INIT(&g_os_task_list);
TAILQ_INIT(&g_os_run_list);
TAILQ_INIT(&g_os_sleep_list);
os_init_idle_task();
return OS_OK;
}
void
os_arch_ctx_sw(struct os_task *next_t)
{
sim_ctx_sw(next_t);
os_sched_ctx_sw_hook(next_t);
bsim_pend_sv = 1;
}
static void
do_ctx_sw(void)
{
struct os_task *next_t;
struct os_task *me;
struct task_info *ti, *next_ti;
next_t = os_sched_next_task();
assert(next_t);
bsim_pend_sv = 0;
assert(g_current_task);
me = g_current_task;
ti = me->t_arg;
if (me == next_t) {
return;
}
g_current_task = next_t;
next_ti = g_current_task->t_arg;
pthread_cond_signal(&next_ti->cond);
pthread_cond_wait(&ti->cond, &bsim_ctx_sw_mutex);
assert(g_current_task == me);
}
os_sr_t
os_arch_save_sr(void)
{
sim_save_sr();
return hw_irq_ctrl_change_lock(1);
}
@@ -90,14 +147,16 @@ void
os_arch_restore_sr(os_sr_t osr)
{
hw_irq_ctrl_change_lock(osr);
sim_restore_sr(osr);
}
if (!osr && bsim_pend_sv) {
do_ctx_sw();
}
}
int
os_arch_in_critical(void)
{
return sim_in_critical();
return hw_irq_ctrl_get_current_lock();
}
void
@@ -1,104 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#if defined MN_LINUX
#define sigsetjmp __sigsetjmp
#define CNAME(x) x
#elif defined MN_OSX
#define sigsetjmp sigsetjmp
#define CNAME(x) _ ## x
#elif defined MN_FreeBSD
#define sigsetjmp sigsetjmp
#define CNAME(x) x
#else
#error "unsupported platform"
#endif
.text
.code32
.p2align 4, 0x90 /* align on 16-byte boundary and fill with NOPs */
.globl CNAME(os_arch_frame_init)
.globl _os_arch_frame_init
/*
* void os_arch_frame_init(struct stack_frame *sf)
*/
CNAME(os_arch_frame_init):
push %ebp /* function prologue for backtrace */
mov %esp,%ebp
push %esi /* save %esi before using it as a tmpreg */
/*
* At this point we are executing on the main() stack:
* ----------------
* stack_frame ptr 0xc(%esp)
* ----------------
* return address 0x8(%esp)
* ----------------
* saved ebp 0x4(%esp)
* ----------------
* saved esi 0x0(%esp)
* ----------------
*/
movl 0xc(%esp),%esi /* %esi = 'sf' */
movl %esp,0x0(%esi) /* sf->mainsp = %esp */
/*
* Switch the stack so the stack pointer stored in 'sf->sf_jb' points
* to the task stack. This is slightly complicated because OS X wants
* the incoming stack pointer to be 16-byte aligned.
*
* ----------------
* sf (other fields)
* ----------------
* sf (sf_jb) 0x4(%esi)
* ----------------
* sf (sf_mainsp) 0x0(%esi)
* ----------------
* alignment padding variable (0 to 12 bytes)
* ----------------
* savemask (0) 0x4(%esp)
* ----------------
* pointer to sf_jb 0x0(%esp)
* ----------------
*/
movl %esi,%esp
subl $0x8,%esp /* make room for sigsetjmp() arguments */
andl $0xfffffff0,%esp /* align %esp on 16-byte boundary */
leal 0x4(%esi),%eax /* %eax = &sf->sf_jb */
movl %eax,0x0(%esp)
movl $0, 0x4(%esp)
call CNAME(sigsetjmp) /* sigsetjmp(sf->sf_jb, 0) */
test %eax,%eax
jne 1f
movl 0x0(%esi),%esp /* switch back to the main() stack */
pop %esi
pop %ebp
ret /* return to os_arch_task_stack_init() */
1:
lea 2f,%ecx
push %ecx /* retaddr */
push $0 /* frame pointer */
movl %esp,%ebp /* handcrafted prologue for backtrace */
push %eax /* rc */
push %esi /* sf */
call CNAME(os_arch_task_start) /* os_arch_task_start(sf, rc) */
/* never returns */
2:
nop
@@ -1,44 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef H_SIM_PRIV_
#define H_SIM_PRIV_
#include <sys/types.h>
#include "os/mynewt.h"
#include "mcu/mcu_sim.h"
#ifdef __cplusplus
extern "C" {
#endif
#define OS_USEC_PER_TICK (1000000 / OS_TICKS_PER_SEC)
void sim_switch_tasks(void);
void sim_tick(void);
void sim_signals_init(void);
void sim_signals_cleanup(void);
extern pid_t sim_pid;
#ifdef __cplusplus
}
#endif
#endif
@@ -1,240 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* This file contains code that is shared by both sim implementations (signals
* and no-signals).
*/
#include "os/mynewt.h"
#include <hal/hal_bsp.h>
#ifdef __APPLE__
#define _XOPEN_SOURCE
#endif
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <assert.h>
#include <hal/hal_os_tick.h>
#include "os/sim.h"
#include "sim_priv.h"
#define sim_setjmp(__jb) sigsetjmp(__jb, 0)
#define sim_longjmp(__jb, __ret) siglongjmp(__jb, __ret)
pid_t sim_pid;
void
sim_switch_tasks(void)
{
struct os_task *t, *next_t;
struct stack_frame *sf;
int rc;
OS_ASSERT_CRITICAL();
t = os_sched_get_current_task();
next_t = os_sched_next_task();
if (t == next_t) {
/*
* Context switch not needed - just return.
*/
return;
}
if (t) {
sf = (struct stack_frame *) t->t_stackptr;
rc = sim_setjmp(sf->sf_jb);
if (rc != 0) {
OS_ASSERT_CRITICAL();
return;
}
}
os_sched_ctx_sw_hook(next_t);
os_sched_set_current_task(next_t);
sf = (struct stack_frame *) next_t->t_stackptr;
sim_longjmp(sf->sf_jb, 1);
}
void
sim_tick(void)
{
struct timeval time_now, time_diff;
int ticks;
static struct timeval time_last;
static int time_inited;
OS_ASSERT_CRITICAL();
if (!time_inited) {
gettimeofday(&time_last, NULL);
time_inited = 1;
}
gettimeofday(&time_now, NULL);
if (timercmp(&time_now, &time_last, <)) {
/*
* System time going backwards.
*/
time_last = time_now;
} else {
timersub(&time_now, &time_last, &time_diff);
ticks = time_diff.tv_sec * OS_TICKS_PER_SEC;
ticks += time_diff.tv_usec / OS_USEC_PER_TICK;
/*
* Update 'time_last' but account for the remainder usecs that did not
* contribute towards whole 'ticks'.
*/
time_diff.tv_sec = 0;
time_diff.tv_usec %= OS_USEC_PER_TICK;
timersub(&time_now, &time_diff, &time_last);
os_time_advance(ticks);
}
}
#define OS_TICK_PRIO 7
static void
sim_start_timer(void)
{
/* Intitialize and start system clock timer */
os_tick_init(OS_TICKS_PER_SEC, OS_TICK_PRIO);
}
static void
sim_stop_timer(void)
{
struct itimerval it;
int rc;
memset(&it, 0, sizeof(it));
rc = setitimer(ITIMER_REAL, &it, NULL);
assert(rc == 0);
}
/*
* Called from 'os_arch_frame_init()' when setjmp returns indirectly via
* longjmp. The return value of setjmp is passed to this function as 'rc'.
*/
void
sim_task_start(struct stack_frame *sf, int rc)
{
struct os_task *task;
/*
* Interrupts are disabled when a task starts executing. This happens in
* two different ways:
* - via sim_os_start() for the first task.
* - via os_sched() for all other tasks.
*
* Enable interrupts before starting the task.
*/
OS_EXIT_CRITICAL(0);
task = sf->sf_task;
task->t_func(task->t_arg);
/* A task handler should never return. */
assert(0);
}
os_stack_t *
sim_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size)
{
struct stack_frame *sf;
sf = (struct stack_frame *) ((uint8_t *) stack_top - sizeof(*sf));
sf->sf_task = t;
os_arch_frame_init(sf);
return ((os_stack_t *)sf);
}
os_error_t
sim_os_start(void)
{
struct stack_frame *sf;
struct os_task *t;
os_sr_t sr;
/*
* Disable interrupts before enabling any interrupt sources. Pending
* interrupts will be recognized when the first task starts executing.
*/
OS_ENTER_CRITICAL(sr);
assert(sr == 0);
/* Enable the interrupt sources */
sim_start_timer();
t = os_sched_next_task();
os_sched_set_current_task(t);
g_os_started = 1;
sf = (struct stack_frame *) t->t_stackptr;
sim_longjmp(sf->sf_jb, 1);
return 0;
}
/**
* Stops the tick timer and clears the "started" flag. This function is only
* implemented for sim.
*/
void
sim_os_stop(void)
{
sim_stop_timer();
sim_signals_cleanup();
g_os_started = 0;
}
os_error_t
sim_os_init(void)
{
sim_pid = getpid();
g_current_task = NULL;
STAILQ_INIT(&g_os_task_list);
TAILQ_INIT(&g_os_run_list);
TAILQ_INIT(&g_os_sleep_list);
sim_signals_init();
os_init_idle_task();
return OS_OK;
}
@@ -1,238 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* This file implements the "no-signals" version of sim. This implementation
* does not use signals to perform context switches. This is the less correct
* version of sim: the OS tick timer only runs while the idle task is active.
* Therefore, a sleeping high-priority task will not preempt a low-priority
* task due to a timing event (e.g., delay or callout expired). However, this
* version of sim does not suffer from the stability issues that affect the
* "signals" implementation.
*
* To use this version of sim, disable the MCU_NATIVE_USE_SIGNALS syscfg
* setting.
*/
#include "os/mynewt.h"
#if !MYNEWT_VAL(MCU_NATIVE_USE_SIGNALS)
#include <hal/hal_bsp.h>
#ifdef __APPLE__
#define _XOPEN_SOURCE
#endif
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <assert.h>
#include "sim_priv.h"
static sigset_t nosigs;
static sigset_t suspsigs; /* signals delivered in sigsuspend() */
static int ctx_sw_pending;
static int interrupts_enabled = 1;
void
sim_ctx_sw(struct os_task *next_t)
{
if (interrupts_enabled) {
/* Perform the context switch immediately. */
sim_switch_tasks();
} else {
/* Remember that we want to perform a context switch. Perform it when
* interrupts are re-enabled.
*/
ctx_sw_pending = 1;
}
}
/*
* Enter a critical section.
*
* Returns 1 if interrupts were already disabled; 0 otherwise.
*/
os_sr_t
sim_save_sr(void)
{
if (!interrupts_enabled) {
return 1;
}
interrupts_enabled = 0;
return 0;
}
void
sim_restore_sr(os_sr_t osr)
{
OS_ASSERT_CRITICAL();
assert(osr == 0 || osr == 1);
if (osr == 1) {
/* Exiting a nested critical section */
return;
}
if (ctx_sw_pending) {
/* A context switch was requested while interrupts were disabled.
* Perform it now that interrupts are enabled again.
*/
ctx_sw_pending = 0;
sim_switch_tasks();
}
interrupts_enabled = 1;
}
int
sim_in_critical(void)
{
return !interrupts_enabled;
}
/**
* Unblocks the SIGALRM signal that is delivered by the OS tick timer.
*/
static void
unblock_timer(void)
{
sigset_t sigs;
int rc;
sigemptyset(&sigs);
sigaddset(&sigs, SIGALRM);
rc = sigprocmask(SIG_UNBLOCK, &sigs, NULL);
assert(rc == 0);
}
/**
* Blocks the SIGALRM signal that is delivered by the OS tick timer.
*/
static void
block_timer(void)
{
sigset_t sigs;
int rc;
sigemptyset(&sigs);
sigaddset(&sigs, SIGALRM);
rc = sigprocmask(SIG_BLOCK, &sigs, NULL);
assert(rc == 0);
}
static void
sig_handler_alrm(int sig)
{
/* Wake the idle task. */
sigaddset(&suspsigs, sig);
}
void
sim_tick_idle(os_time_t ticks)
{
int rc;
struct itimerval it;
OS_ASSERT_CRITICAL();
if (ticks > 0) {
/*
* Enter tickless regime and set the timer to fire after 'ticks'
* worth of time has elapsed.
*/
it.it_value.tv_sec = ticks / OS_TICKS_PER_SEC;
it.it_value.tv_usec = (ticks % OS_TICKS_PER_SEC) * OS_USEC_PER_TICK;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = OS_USEC_PER_TICK;
rc = setitimer(ITIMER_REAL, &it, NULL);
assert(rc == 0);
}
unblock_timer();
sigemptyset(&suspsigs);
sigsuspend(&nosigs); /* Wait for a signal to wake us up */
block_timer();
/*
* Call handlers for signals delivered to the process during sigsuspend().
* The SIGALRM handler is called before any other handlers to ensure that
* OS time is always correct.
*/
if (sigismember(&suspsigs, SIGALRM)) {
sim_tick();
}
if (ticks > 0) {
/*
* Enable the periodic timer interrupt.
*/
it.it_value.tv_sec = 0;
it.it_value.tv_usec = OS_USEC_PER_TICK;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = OS_USEC_PER_TICK;
rc = setitimer(ITIMER_REAL, &it, NULL);
assert(rc == 0);
}
}
void
sim_signals_init(void)
{
sigset_t sigset_alrm;
struct sigaction sa;
int error;
block_timer();
sigemptyset(&nosigs);
sigemptyset(&sigset_alrm);
sigaddset(&sigset_alrm, SIGALRM);
memset(&sa, 0, sizeof sa);
sa.sa_handler = sig_handler_alrm;
sa.sa_mask = sigset_alrm;
sa.sa_flags = SA_RESTART;
error = sigaction(SIGALRM, &sa, NULL);
assert(error == 0);
}
void
sim_signals_cleanup(void)
{
int error;
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = SIG_DFL;
error = sigaction(SIGALRM, &sa, NULL);
assert(error == 0);
}
#endif /* !MYNEWT_VAL(MCU_NATIVE_USE_SIGNALS) */
@@ -1,288 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* This file implements the "signals" version of sim. This implementation uses
* signals to perform context switches. This is the more correct version of
* sim: the OS tick timer will cause a high-priority task to preempt a
* low-priority task. Unfortunately, there are stability issues because a task
* can be preempted while it is in the middle of a system call, potentially
* causing deadlock or memory corruption.
*
* To use this version of sim, enable the MCU_NATIVE_USE_SIGNALS syscfg
* setting.
*/
#include "os/mynewt.h"
#if MYNEWT_VAL(MCU_NATIVE_USE_SIGNALS)
#include "sim_priv.h"
#include <hal/hal_bsp.h>
#ifdef __APPLE__
#define _XOPEN_SOURCE
#endif
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <assert.h>
static bool suspended; /* process is blocked in sigsuspend() */
static sigset_t suspsigs; /* signals delivered in sigsuspend() */
static sigset_t allsigs;
static sigset_t nosigs;
static int in_irq;
int counter = 0;
int
sig_block_irq_on()
{
int error;
counter++;
error = sigprocmask(SIG_BLOCK, &allsigs, NULL);
in_irq = 1;
assert(error == 0);
return 1;
}
void
sig_unblock_irq_off()
{
int error;
in_irq = 0;
if (counter > 0) {
counter--;
return;
}
error = sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
assert(error == 0);
}
void
sim_ctx_sw(struct os_task *next_t)
{
/*
* gdb will stop execution of the program on most signals (e.g. SIGUSR1)
* whereas it passes SIGURG to the process without any special settings.
*/
kill(sim_pid, SIGURG);
}
static void
ctxsw_handler(int sig)
{
assert(in_irq==0);
OS_ASSERT_CRITICAL();
/*
* Just record that this handler was called when the process was blocked.
* The handler will be called after sigsuspend() returns in the correct
* order.
*/
if (suspended) {
sigaddset(&suspsigs, sig);
} else {
sim_switch_tasks();
}
}
/*
* Disable signals and enter a critical section.
*
* Returns 1 if signals were already blocked and 0 otherwise.
*/
os_sr_t
sim_save_sr(void)
{
int error;
sigset_t omask;
counter++;
error = sigprocmask(SIG_BLOCK, &allsigs, &omask);
assert(error == 0);
/*
* If any one of the signals in 'allsigs' is present in 'omask' then
* we are already inside a critical section.
*/
return (sigismember(&omask, SIGURG));
}
void
sim_restore_sr(os_sr_t osr)
{
int error;
OS_ASSERT_CRITICAL();
assert(osr == 0 || osr == 1);
if (counter > 0) {
counter--;
}
if (osr == 1 || in_irq == 1 || counter > 0) {
/* Exiting a nested critical section */
return;
}
error = sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
assert(error == 0);
}
int
sim_in_critical(void)
{
int error;
sigset_t omask;
error = sigprocmask(SIG_SETMASK, NULL, &omask);
assert(error == 0);
/*
* If any one of the signals in 'allsigs' is present in 'omask' then
* we are already inside a critical section.
*/
return (sigismember(&omask, SIGURG));
}
static struct {
int num;
void (*handler)(int sig);
} signals[] = {
// { SIGALRM, timer_handler },
{ SIGURG, ctxsw_handler },
};
#define NUMSIGS (sizeof(signals)/sizeof(signals[0]))
void
sim_tick_idle(os_time_t ticks)
{
int i, rc, sig;
struct itimerval it;
void (*handler)(int sig);
OS_ASSERT_CRITICAL();
if (ticks > 0) {
/*
* Enter tickless regime and set the timer to fire after 'ticks'
* worth of time has elapsed.
*/
it.it_value.tv_sec = ticks / OS_TICKS_PER_SEC;
it.it_value.tv_usec = (ticks % OS_TICKS_PER_SEC) * OS_USEC_PER_TICK;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = OS_USEC_PER_TICK;
rc = setitimer(ITIMER_REAL, &it, NULL);
assert(rc == 0);
}
suspended = true;
sigemptyset(&suspsigs);
sigsuspend(&nosigs); /* Wait for a signal to wake us up */
suspended = false;
/*
* Call handlers for signals delivered to the process during sigsuspend().
* The SIGALRM handler is called before any other handlers to ensure that
* OS time is always correct.
*/
if (sigismember(&suspsigs, SIGALRM)) {
sim_tick();
}
for (i = 0; i < NUMSIGS; i++) {
sig = signals[i].num;
handler = signals[i].handler;
if (sig != SIGALRM && sigismember(&suspsigs, sig)) {
handler(sig);
}
}
if (ticks > 0) {
/*
* Enable the periodic timer interrupt.
*/
it.it_value.tv_sec = 0;
it.it_value.tv_usec = OS_USEC_PER_TICK;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = OS_USEC_PER_TICK;
rc = setitimer(ITIMER_REAL, &it, NULL);
assert(rc == 0);
}
}
void
sim_signals_init(void)
{
int i, error;
struct sigaction sa;
sigemptyset(&nosigs);
sigemptyset(&allsigs);
for (i = 0; i < NUMSIGS; i++) {
sigaddset(&allsigs, signals[i].num);
}
for (i = 0; i < NUMSIGS; i++) {
memset(&sa, 0, sizeof sa);
sa.sa_handler = signals[i].handler;
sa.sa_mask = allsigs;
sa.sa_flags = SA_RESTART;
error = sigaction(signals[i].num, &sa, NULL);
assert(error == 0);
}
/*
* We use SIGALRM as a proxy for 'allsigs' to check if we are inside
* a critical section (for e.g. see sim_in_critical()). Make sure
* that SIGALRM is indeed present in 'allsigs'.
*/
// assert(sigismember(&allsigs, SIGALRM));
}
void
sim_signals_cleanup(void)
{
int i, error;
struct sigaction sa;
for (i = 0; i < NUMSIGS; i++) {
memset(&sa, 0, sizeof sa);
sa.sa_handler = SIG_DFL;
error = sigaction(signals[i].num, &sa, NULL);
assert(error == 0);
}
}
#endif /* MYNEWT_VAL(MCU_NATIVE_USE_SIGNALS) */
@@ -22,6 +22,9 @@ pkg.description: nRF52 on BabbleSim
pkg.author: "Apache Mynewt <[email protected]>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.lflags:
- -lpthread
pkg.deps:
- "babblesim/nrfx"