1 /* 2 * Copyright (c) 2006-2018, RT-Thread Development Team 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Change Logs: 7 * Date Author Notes 8 * 2017/10/5 Bernard the first version 9 * 2018/09/17 Jesven fix: in _signal_deliver RT_THREAD_STAT_MASK to RT_THREAD_STAT_SIGNAL_MASK 10 * 2018/11/22 Jesven in smp version rt_hw_context_switch_to add a param 11 */ 12 13 #include <stdint.h> 14 #include <string.h> 15 16 #include <rthw.h> 17 #include <rtthread.h> 18 19 #ifdef RT_USING_SIGNALS 20 21 #ifndef RT_SIG_INFO_MAX 22 #define RT_SIG_INFO_MAX 32 23 #endif 24 25 // #define DBG_ENABLE 26 #define DBG_SECTION_NAME "SIGN" 27 #define DBG_COLOR 28 #define DBG_LEVEL DBG_LOG 29 #include <rtdbg.h> 30 31 #define sig_mask(sig_no) (1u << sig_no) 32 #define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX) 33 34 struct siginfo_node 35 { 36 siginfo_t si; 37 struct rt_slist_node list; 38 }; 39 40 static struct rt_mempool *_rt_siginfo_pool; 41 static void _signal_deliver(rt_thread_t tid); 42 void rt_thread_handle_sig(rt_bool_t clean_state); 43 44 static void _signal_default_handler(int signo) 45 { 46 LOG_I("handled signo[%d] with default action.", signo); 47 return ; 48 } 49 50 static void _signal_entry(void *parameter) 51 { 52 rt_thread_t tid = rt_thread_self(); 53 54 dbg_enter; 55 56 /* handle signal */ 57 rt_thread_handle_sig(RT_FALSE); 58 59 /* never come back... */ 60 rt_hw_interrupt_disable(); 61 /* return to thread */ 62 tid->sp = tid->sig_ret; 63 tid->sig_ret = RT_NULL; 64 65 LOG_D("switch back to: 0x%08x\n", tid->sp); 66 tid->stat &= ~RT_THREAD_STAT_SIGNAL; 67 68 #ifdef RT_USING_SMP 69 rt_hw_context_switch_to((rt_ubase_t)&(tid->sp), tid); 70 #else 71 rt_hw_context_switch_to((rt_ubase_t)&(tid->sp)); 72 #endif /*RT_USING_SMP*/ 73 } 74 75 /* 76 * To deliver a signal to thread, there are cases: 77 * 1. When thread is suspended, function resumes thread and 78 * set signal stat; 79 * 2. When thread is ready: 80 * - If function delivers a signal to self thread, just handle 81 * it. 82 * - If function delivers a signal to another ready thread, OS 83 * should build a slice context to handle it. 84 */ 85 static void _signal_deliver(rt_thread_t tid) 86 { 87 rt_ubase_t level; 88 89 /* thread is not interested in pended signals */ 90 if (!(tid->sig_pending & tid->sig_mask)) return; 91 92 level = rt_hw_interrupt_disable(); 93 if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) 94 { 95 /* resume thread to handle signal */ 96 rt_thread_resume(tid); 97 /* add signal state */ 98 tid->stat |= RT_THREAD_STAT_SIGNAL; 99 100 rt_hw_interrupt_enable(level); 101 102 /* re-schedule */ 103 rt_schedule(); 104 } 105 else 106 { 107 if (tid == rt_thread_self()) 108 { 109 /* add signal state */ 110 tid->stat |= RT_THREAD_STAT_SIGNAL; 111 112 rt_hw_interrupt_enable(level); 113 114 /* do signal action in self thread context */ 115 rt_thread_handle_sig(RT_TRUE); 116 } 117 else if (!((tid->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL)) 118 { 119 /* add signal state */ 120 tid->stat |= RT_THREAD_STAT_SIGNAL; 121 122 /* point to the signal handle entry */ 123 tid->sig_ret = tid->sp; 124 tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL, 125 (void *)((char *)tid->sig_ret - 32), RT_NULL); 126 127 rt_hw_interrupt_enable(level); 128 LOG_D("signal stack pointer @ 0x%08x", tid->sp); 129 130 /* re-schedule */ 131 rt_schedule(); 132 } 133 else 134 { 135 rt_hw_interrupt_enable(level); 136 } 137 } 138 } 139 140 rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) 141 { 142 rt_sighandler_t old = RT_NULL; 143 rt_thread_t tid = rt_thread_self(); 144 145 if (!sig_valid(signo)) return SIG_ERR; 146 147 rt_enter_critical(); 148 if (tid->sig_vectors == RT_NULL) 149 { 150 rt_thread_alloc_sig(tid); 151 } 152 153 if (tid->sig_vectors) 154 { 155 old = tid->sig_vectors[signo]; 156 157 if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL; 158 else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler; 159 else tid->sig_vectors[signo] = handler; 160 } 161 rt_exit_critical(); 162 163 return old; 164 } 165 166 void rt_signal_mask(int signo) 167 { 168 rt_base_t level; 169 rt_thread_t tid = rt_thread_self(); 170 171 level = rt_hw_interrupt_disable(); 172 173 tid->sig_mask &= ~sig_mask(signo); 174 175 rt_hw_interrupt_enable(level); 176 } 177 178 void rt_signal_unmask(int signo) 179 { 180 rt_base_t level; 181 rt_thread_t tid = rt_thread_self(); 182 183 level = rt_hw_interrupt_disable(); 184 185 tid->sig_mask |= sig_mask(signo); 186 187 /* let thread handle pended signals */ 188 if (tid->sig_mask & tid->sig_pending) 189 { 190 rt_hw_interrupt_enable(level); 191 _signal_deliver(tid); 192 } 193 else 194 { 195 rt_hw_interrupt_enable(level); 196 } 197 } 198 199 int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout) 200 { 201 int ret = RT_EOK; 202 rt_base_t level; 203 rt_thread_t tid = rt_thread_self(); 204 struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL; 205 206 /* current context checking */ 207 RT_DEBUG_IN_THREAD_CONTEXT; 208 209 /* parameters check */ 210 if (set == NULL || *set == 0 || si == NULL ) 211 { 212 ret = -RT_EINVAL; 213 goto __done_return; 214 } 215 216 /* clear siginfo to avoid unknown value */ 217 memset(si, 0x0, sizeof(rt_siginfo_t)); 218 219 level = rt_hw_interrupt_disable(); 220 221 /* already pending */ 222 if (tid->sig_pending & *set) goto __done; 223 224 if (timeout == 0) 225 { 226 ret = -RT_ETIMEOUT; 227 goto __done_int; 228 } 229 230 /* suspend self thread */ 231 rt_thread_suspend(tid); 232 /* set thread stat as waiting for signal */ 233 tid->stat |= RT_THREAD_STAT_SIGNAL_WAIT; 234 235 /* start timeout timer */ 236 if (timeout != RT_WAITING_FOREVER) 237 { 238 /* reset the timeout of thread timer and start it */ 239 rt_timer_control(&(tid->thread_timer), 240 RT_TIMER_CTRL_SET_TIME, 241 &timeout); 242 rt_timer_start(&(tid->thread_timer)); 243 } 244 rt_hw_interrupt_enable(level); 245 246 /* do thread scheduling */ 247 rt_schedule(); 248 249 level = rt_hw_interrupt_disable(); 250 251 /* remove signal waiting flag */ 252 tid->stat &= ~RT_THREAD_STAT_SIGNAL_WAIT; 253 254 /* check errno of thread */ 255 if (tid->error == -RT_ETIMEOUT) 256 { 257 tid->error = RT_EOK; 258 rt_hw_interrupt_enable(level); 259 260 /* timer timeout */ 261 ret = -RT_ETIMEOUT; 262 goto __done_return; 263 } 264 265 __done: 266 /* to get the first matched pending signals */ 267 si_node = (struct siginfo_node *)tid->si_list; 268 while (si_node) 269 { 270 int signo; 271 272 signo = si_node->si.si_signo; 273 if (sig_mask(signo) & *set) 274 { 275 *si = si_node->si; 276 277 LOG_D("sigwait: %d sig raised!", signo); 278 if (si_prev) si_prev->list.next = si_node->list.next; 279 else tid->si_list = si_node->list.next; 280 281 /* clear pending */ 282 tid->sig_pending &= ~sig_mask(signo); 283 rt_mp_free(si_node); 284 break; 285 } 286 287 si_prev = si_node; 288 si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list); 289 } 290 291 __done_int: 292 rt_hw_interrupt_enable(level); 293 294 __done_return: 295 return ret; 296 } 297 298 void rt_thread_handle_sig(rt_bool_t clean_state) 299 { 300 rt_base_t level; 301 302 rt_thread_t tid = rt_thread_self(); 303 struct siginfo_node *si_node; 304 305 level = rt_hw_interrupt_disable(); 306 if (tid->sig_pending & tid->sig_mask) 307 { 308 /* if thread is not waiting for signal */ 309 if (!(tid->stat & RT_THREAD_STAT_SIGNAL_WAIT)) 310 { 311 while (tid->sig_pending & tid->sig_mask) 312 { 313 int signo, error; 314 rt_sighandler_t handler; 315 316 si_node = (struct siginfo_node *)tid->si_list; 317 if (!si_node) break; 318 319 /* remove this sig info node from list */ 320 if (si_node->list.next == RT_NULL) 321 tid->si_list = RT_NULL; 322 else 323 tid->si_list = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list); 324 325 signo = si_node->si.si_signo; 326 handler = tid->sig_vectors[signo]; 327 rt_hw_interrupt_enable(level); 328 329 LOG_D("handle signal: %d, handler 0x%08x", signo, handler); 330 if (handler) handler(signo); 331 332 level = rt_hw_interrupt_disable(); 333 tid->sig_pending &= ~sig_mask(signo); 334 error = -RT_EINTR; 335 336 rt_mp_free(si_node); /* release this siginfo node */ 337 /* set errno in thread tcb */ 338 tid->error = error; 339 } 340 341 /* whether clean signal status */ 342 if (clean_state == RT_TRUE) tid->stat &= ~RT_THREAD_STAT_SIGNAL; 343 } 344 } 345 346 rt_hw_interrupt_enable(level); 347 } 348 349 void rt_thread_alloc_sig(rt_thread_t tid) 350 { 351 int index; 352 rt_base_t level; 353 rt_sighandler_t *vectors; 354 355 vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX); 356 RT_ASSERT(vectors != RT_NULL); 357 358 for (index = 0; index < RT_SIG_MAX; index ++) 359 { 360 vectors[index] = _signal_default_handler; 361 } 362 363 level = rt_hw_interrupt_disable(); 364 tid->sig_vectors = vectors; 365 rt_hw_interrupt_enable(level); 366 } 367 368 void rt_thread_free_sig(rt_thread_t tid) 369 { 370 rt_base_t level; 371 struct siginfo_node *si_list; 372 rt_sighandler_t *sig_vectors; 373 374 level = rt_hw_interrupt_disable(); 375 si_list = (struct siginfo_node *)tid->si_list; 376 tid->si_list = RT_NULL; 377 378 sig_vectors = tid->sig_vectors; 379 tid->sig_vectors = RT_NULL; 380 rt_hw_interrupt_enable(level); 381 382 if (si_list) 383 { 384 struct rt_slist_node *node; 385 struct siginfo_node *si_node; 386 387 LOG_D("free signal info list"); 388 node = &(si_list->list); 389 do 390 { 391 si_node = rt_slist_entry(node, struct siginfo_node, list); 392 rt_mp_free(si_node); 393 394 node = node->next; 395 } while (node); 396 } 397 398 if (sig_vectors) 399 { 400 RT_KERNEL_FREE(sig_vectors); 401 } 402 } 403 404 int rt_thread_kill(rt_thread_t tid, int sig) 405 { 406 siginfo_t si; 407 rt_base_t level; 408 struct siginfo_node *si_node; 409 410 RT_ASSERT(tid != RT_NULL); 411 if (!sig_valid(sig)) return -RT_EINVAL; 412 413 LOG_I("send signal: %d", sig); 414 si.si_signo = sig; 415 si.si_code = SI_USER; 416 si.si_value.sival_ptr = RT_NULL; 417 418 level = rt_hw_interrupt_disable(); 419 if (tid->sig_pending & sig_mask(sig)) 420 { 421 /* whether already emits this signal? */ 422 struct rt_slist_node *node; 423 struct siginfo_node *entry; 424 425 node = (struct rt_slist_node *)tid->si_list; 426 rt_hw_interrupt_enable(level); 427 428 /* update sig info */ 429 rt_enter_critical(); 430 for (; (node) != RT_NULL; node = node->next) 431 { 432 entry = rt_slist_entry(node, struct siginfo_node, list); 433 if (entry->si.si_signo == sig) 434 { 435 memcpy(&(entry->si), &si, sizeof(siginfo_t)); 436 rt_exit_critical(); 437 return 0; 438 } 439 } 440 rt_exit_critical(); 441 442 /* disable interrupt to protect tcb */ 443 level = rt_hw_interrupt_disable(); 444 } 445 else 446 { 447 /* a new signal */ 448 tid->sig_pending |= sig_mask(sig); 449 } 450 rt_hw_interrupt_enable(level); 451 452 si_node = (struct siginfo_node *) rt_mp_alloc(_rt_siginfo_pool, 0); 453 if (si_node) 454 { 455 rt_slist_init(&(si_node->list)); 456 memcpy(&(si_node->si), &si, sizeof(siginfo_t)); 457 458 level = rt_hw_interrupt_disable(); 459 if (!tid->si_list) tid->si_list = si_node; 460 else 461 { 462 struct siginfo_node *si_list; 463 464 si_list = (struct siginfo_node *)tid->si_list; 465 rt_slist_append(&(si_list->list), &(si_node->list)); 466 } 467 rt_hw_interrupt_enable(level); 468 } 469 else 470 { 471 LOG_E("The allocation of signal info node failed."); 472 } 473 474 /* deliver signal to this thread */ 475 _signal_deliver(tid); 476 477 return RT_EOK; 478 } 479 480 int rt_system_signal_init(void) 481 { 482 _rt_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node)); 483 if (_rt_siginfo_pool == RT_NULL) 484 { 485 LOG_E("create memory pool for signal info failed."); 486 RT_ASSERT(0); 487 } 488 489 return 0; 490 } 491 492 #endif 493