mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-07 10:37:54 +00:00
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:
@@ -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,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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user