diff options
| author | bernard <[email protected]> | 2017-10-15 22:31:53 +0800 |
|---|---|---|
| committer | bernard <[email protected]> | 2017-10-15 22:31:53 +0800 |
| commit | b27c7e482648535760e012cd6f1afef1d800e2d2 (patch) | |
| tree | ad040fd4e7579e67ba6ee2edae468b63f2099a0e /src/signal.c | |
| parent | cd215b25455bf06dfeb8885fe4d8f5b30174a8c3 (diff) | |
[Kernel] Add signal implementation.
1. Add signal implementation;
2. Change the 'rt_uint8_t' of cmd to 'int';
Diffstat (limited to 'src/signal.c')
| -rw-r--r-- | src/signal.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/src/signal.c b/src/signal.c new file mode 100644 index 0000000000..d70d25087b --- /dev/null +++ b/src/signal.c @@ -0,0 +1,382 @@ +/* + * File : signal.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2017, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2017/10/5 Bernard the first version + */ + +#include <stdint.h> +#include <string.h> + +#include <rthw.h> +#include <rtthread.h> + +#ifdef RT_USING_SIGNALS + +#ifndef RT_SIG_INFO_MAX +#define RT_SIG_INFO_MAX 32 +#endif + +// #define DBG_ENABLE +#define DBG_SECTION_NAME "[SIGN]" +#define DBG_COLOR +#define DBG_LEVEL DBG_LOG +#include <rtdbg.h> + +#define sig_mask(sig_no) (1u << sig_no) +#define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX) + +struct siginfo_node +{ + struct siginfo si; + struct rt_slist_node list; +}; + +static struct rt_mempool *_rt_siginfo_pool; +static void _signal_deliver(rt_thread_t tid); +void rt_thread_handle_sig(rt_bool_t clean_state); + +static void _signal_default_handler(int signo) +{ + dbg_log(DBG_INFO, "handled signo[%d] with default action.\n", signo); + return ; +} + +static void _signal_entry(void* parameter) +{ + rt_thread_t tid = rt_thread_self(); + + dbg_enter; + + /* handle signal */ + rt_thread_handle_sig(RT_FALSE); + + /* never come back... */ + rt_hw_interrupt_disable(); + /* return to thread */ + tid->sp = tid->sig_ret; + tid->sig_ret = RT_NULL; + + dbg_log(DBG_LOG, "switch back to: 0x%08x\n", tid->sp); + tid->stat &= ~RT_THREAD_STAT_SIGNAL; + + rt_hw_context_switch_to((rt_uint32_t)&(tid->sp)); +} + +static void _signal_deliver(rt_thread_t tid) +{ + rt_ubase_t level; + + /* thread is not interested in pended signals */ + if (!(tid->sig_pending & tid->sig_mask)) return; + + level = rt_hw_interrupt_disable(); + if (tid->stat == RT_THREAD_SUSPEND) + { + /* resume thread to handle signal */ + rt_thread_resume(tid); + /* add signal state */ + tid->stat |= RT_THREAD_STAT_SIGNAL; + + rt_hw_interrupt_enable(level); + + /* re-schedule */ + rt_schedule(); + } + else + { + if (tid == rt_thread_self()) + { + /* add signal state */ + tid->stat |= RT_THREAD_STAT_SIGNAL; + rt_hw_interrupt_enable(level); + + /* do signal action in self thread context */ + rt_thread_handle_sig(RT_TRUE); + } + else if (!(tid->stat & RT_THREAD_STAT_SIGNAL)) + { + tid->stat |= RT_THREAD_STAT_SIGNAL; + /* point to the signal handle entry */ + tid->sig_ret = tid->sp; + tid->sp = rt_hw_stack_init((void*)_signal_entry, RT_NULL, + (void *)((char *)tid->sig_ret - 32), RT_NULL); + + rt_hw_interrupt_enable(level); + dbg_log(DBG_LOG, "signal stack pointer @ 0x%08x\n", tid->sp); + + /* re-schedule */ + rt_schedule(); + } + else + { + rt_hw_interrupt_enable(level); + } + } +} + +rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) +{ + rt_sighandler_t old; + rt_thread_t tid = rt_thread_self(); + + if (!sig_valid(signo)) return SIG_ERR; + + rt_enter_critical(); + if (tid->sig_vectors == RT_NULL) + { + rt_thread_alloc_sig(tid); + } + + if (tid->sig_vectors) + { + old = tid->sig_vectors[signo]; + + if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL; + else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler; + else tid->sig_vectors[signo] = handler; + } + rt_exit_critical(); + + return old; +} + +void rt_signal_mask(int signo) +{ + rt_base_t level; + rt_thread_t tid = rt_thread_self(); + + level = rt_hw_interrupt_disable(); + + tid->sig_mask &= ~sig_mask(signo); + + rt_hw_interrupt_enable(level); +} + +void rt_signal_unmask(int signo) +{ + rt_base_t level; + rt_thread_t tid = rt_thread_self(); + + level = rt_hw_interrupt_disable(); + + tid->sig_mask |= sig_mask(signo); + + /* let thread handle pended signals */ + if (tid->sig_mask & tid->sig_pending) + { + rt_hw_interrupt_enable(level); + _signal_deliver(tid); + } + else + { + rt_hw_interrupt_enable(level); + } +} + +void rt_thread_handle_sig(rt_bool_t clean_state) +{ + rt_base_t level; + + rt_thread_t tid = rt_thread_self(); + struct siginfo_node *si_node; + + level = rt_hw_interrupt_disable(); + while (tid->sig_pending & tid->sig_mask) + { + int signo, error; + rt_sighandler_t handler; + + si_node = (struct siginfo_node *)tid->si_list; + if (!si_node) break; + + /* remove this sig info node from list */ + if (si_node->list.next == RT_NULL) + tid->si_list = RT_NULL; + else + tid->si_list = (void*)rt_slist_entry(si_node->list.next, struct siginfo_node, list); + + signo = si_node->si.si_signo; + handler = tid->sig_vectors[signo]; + rt_hw_interrupt_enable(level); + + dbg_log(DBG_LOG, "handle signal: %d, handler 0x%08x\n", signo, handler); + if (handler) handler(signo); + + level = rt_hw_interrupt_disable(); + tid->sig_pending &= ~sig_mask(signo); + error = si_node->si.si_errno; + + rt_mp_free(si_node); /* release this siginfo node */ + /* set errno in thread tcb */ + tid->error = error; + } + + /* whether clean signal status */ + if (clean_state == RT_TRUE) tid->stat &= ~RT_THREAD_STAT_SIGNAL; + + rt_hw_interrupt_enable(level); +} + +void rt_thread_alloc_sig(rt_thread_t tid) +{ + int index; + rt_base_t level; + rt_sighandler_t *vectors; + + vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX); + RT_ASSERT(vectors != RT_NULL); + + for (index = 0; index < RT_SIG_MAX; index ++) + { + vectors[index] = _signal_default_handler; + } + + level = rt_hw_interrupt_disable(); + tid->sig_vectors = vectors; + rt_hw_interrupt_enable(level); +} + +void rt_thread_free_sig(rt_thread_t tid) +{ + rt_base_t level; + struct siginfo_node *si_list; + rt_sighandler_t *sig_vectors; + + level = rt_hw_interrupt_disable(); + si_list = (struct siginfo_node*)tid->si_list; + tid->si_list = RT_NULL; + + sig_vectors = tid->sig_vectors; + tid->sig_vectors = RT_NULL; + rt_hw_interrupt_enable(level); + + if (si_list) + { + struct rt_slist_node *node; + struct siginfo_node *si_node; + + dbg_log(DBG_LOG, "free signal info list\n"); + node = &(si_list->list); + do { + si_node = rt_slist_entry(node, struct siginfo_node, list); + rt_mp_free(si_node); + + node = node->next; + } while (node); + } + + if (sig_vectors) + { + RT_KERNEL_FREE(sig_vectors); + } +} + +int rt_thread_kill(rt_thread_t tid, int sig) +{ + siginfo_t si; + rt_base_t level; + struct siginfo_node *si_node; + + RT_ASSERT(tid != RT_NULL); + if (!sig_valid(sig)) return -RT_EINVAL; + + dbg_log(DBG_INFO, "send signal: %d\n", sig); + si.si_errno = RT_EINTR; + si.si_signo = sig; + si.si_code = SI_USER; + si.si_value.sival_ptr = RT_NULL; + + level = rt_hw_interrupt_disable(); + if (tid->sig_pending & sig_mask(sig)) + { + /* whether already emits this signal? */ + struct rt_slist_node *node; + struct siginfo_node *entry; + + node = (struct rt_slist_node *)tid->si_list; + rt_hw_interrupt_enable(level); + + /* update sig infor */ + rt_enter_critical(); + for (; (node) != RT_NULL; node = node->next) + { + entry = rt_slist_entry(node, struct siginfo_node, list); + if (entry->si.si_signo == sig) + { + memcpy(&(entry->si), &si, sizeof(siginfo_t)); + rt_exit_critical(); + return 0; + } + } + rt_exit_critical(); + + /* disable interrupt to protect tcb */ + level = rt_hw_interrupt_disable(); + } + else + { + /* a new signal */ + tid->sig_pending |= sig_mask(sig); + } + rt_hw_interrupt_enable(level); + + si_node = (struct siginfo_node*) rt_mp_alloc(_rt_siginfo_pool, 0); + if (si_node) + { + rt_slist_init(&(si_node->list)); + memcpy(&(si_node->si), &si, sizeof(siginfo_t)); + + level = rt_hw_interrupt_disable(); + if (!tid->si_list) tid->si_list = si_node; + else + { + struct siginfo_node *si_list; + + si_list = (struct siginfo_node*)tid->si_list; + rt_slist_append(&(si_list->list), &(si_node->list)); + } + rt_hw_interrupt_enable(level); + } + else + { + dbg_log(DBG_ERROR, "The allocation of signal infor node failed.\n"); + } + + /* deliver signal to this thread */ + _signal_deliver(tid); + + return RT_EOK; +} + +int rt_system_signal_init(void) +{ + _rt_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node)); + if (_rt_siginfo_pool == RT_NULL) + { + dbg_log(DBG_ERROR, "create memory pool for signal info failed.\n"); + RT_ASSERT(0); + } + + return 0; +} + +#endif + |
