signal.c 14.5 KB
Newer Older
B
bernard 已提交
1
/*
mysterywolf's avatar
mysterywolf 已提交
2
 * Copyright (c) 2006-2021, RT-Thread Development Team
B
bernard 已提交
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
B
bernard 已提交
5 6 7 8
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017/10/5      Bernard      the first version
S
shaojinchun 已提交
9 10
 * 2018/09/17     Jesven       fix: in _signal_deliver RT_THREAD_STAT_MASK to RT_THREAD_STAT_SIGNAL_MASK
 * 2018/11/22     Jesven       in smp version rt_hw_context_switch_to add a param
B
bernard 已提交
11 12 13 14 15 16 17 18 19 20 21
 */

#include <stdint.h>
#include <string.h>

#include <rthw.h>
#include <rtthread.h>

#ifdef RT_USING_SIGNALS

#ifndef RT_SIG_INFO_MAX
E
emlslxl 已提交
22
#define RT_SIG_INFO_MAX 32
23
#endif /* RT_SIG_INFO_MAX */
B
bernard 已提交
24

B
Bernard Xiong 已提交
25 26
#define DBG_TAG     "SIGN"
#define DBG_LVL     DBG_WARNING
B
bernard 已提交
27 28 29 30 31 32 33
#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
{
B
Bernard Xiong 已提交
34
    siginfo_t si;
B
bernard 已提交
35 36 37
    struct rt_slist_node list;
};

mysterywolf's avatar
mysterywolf 已提交
38
static struct rt_mempool *_siginfo_pool;
B
bernard 已提交
39 40 41 42 43
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)
{
44
    LOG_I("handled signo[%d] with default action.", signo);
B
bernard 已提交
45 46 47
    return ;
}

E
emlslxl 已提交
48
static void _signal_entry(void *parameter)
B
bernard 已提交
49 50 51 52 53 54
{
    rt_thread_t tid = rt_thread_self();

    /* handle signal */
    rt_thread_handle_sig(RT_FALSE);

S
shaojinchun 已提交
55 56 57 58
#ifdef RT_USING_SMP
    {
        struct rt_cpu* pcpu = rt_cpu_self();

59 60
        pcpu->current_thread->cpus_lock_nest--;
        if (pcpu->current_thread->cpus_lock_nest == 0)
S
shaojinchun 已提交
61 62 63 64 65 66
        {
            pcpu->current_thread->scheduler_lock_nest--;
        }

    }
#else
B
bernard 已提交
67 68 69
    /* return to thread */
    tid->sp = tid->sig_ret;
    tid->sig_ret = RT_NULL;
70
#endif /* RT_USING_SMP */
B
bernard 已提交
71

S
shaojinchun 已提交
72
    LOG_D("switch back to: 0x%08x\n", tid->sp);
B
bernard 已提交
73 74
    tid->stat &= ~RT_THREAD_STAT_SIGNAL;

S
shaojinchun 已提交
75
#ifdef RT_USING_SMP
S
shaojinchun 已提交
76
    rt_hw_context_switch_to((rt_base_t)&parameter, tid);
S
shaojinchun 已提交
77 78
#else
    rt_hw_context_switch_to((rt_ubase_t)&(tid->sp));
79
#endif /* RT_USING_SMP */
B
bernard 已提交
80 81
}

82 83 84 85 86 87 88 89 90 91
/*
 * To deliver a signal to thread, there are cases:
 * 1. When thread is suspended, function resumes thread and
 * set signal stat;
 * 2. When thread is ready:
 *   - If function delivers a signal to self thread, just handle
 *    it.
 *   - If function delivers a signal to another ready thread, OS
 *    should build a slice context to handle it.
 */
B
bernard 已提交
92 93 94 95
static void _signal_deliver(rt_thread_t tid)
{
    rt_ubase_t level;

S
shaojinchun 已提交
96 97
    level = rt_hw_interrupt_disable();

B
bernard 已提交
98
    /* thread is not interested in pended signals */
S
shaojinchun 已提交
99 100 101 102 103
    if (!(tid->sig_pending & tid->sig_mask))
    {
        rt_hw_interrupt_enable(level);
        return;
    }
B
bernard 已提交
104

105
    if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)
B
bernard 已提交
106 107 108
    {
        /* resume thread to handle signal */
        rt_thread_resume(tid);
E
emlslxl 已提交
109
        /* add signal state */
S
shaojinchun 已提交
110
        tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);
B
bernard 已提交
111 112 113 114 115 116 117 118 119 120

        rt_hw_interrupt_enable(level);

        /* re-schedule */
        rt_schedule();
    }
    else
    {
        if (tid == rt_thread_self())
        {
E
emlslxl 已提交
121 122
            /* add signal state */
            tid->stat |= RT_THREAD_STAT_SIGNAL;
123

B
bernard 已提交
124 125 126
            rt_hw_interrupt_enable(level);

            /* do signal action in self thread context */
S
shaojinchun 已提交
127 128 129 130
            if (rt_interrupt_get_nest() == 0)
            {
                rt_thread_handle_sig(RT_TRUE);
            }
B
bernard 已提交
131
        }
S
shaojinchun 已提交
132
        else if (!((tid->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL))
B
bernard 已提交
133
        {
134
            /* add signal state */
S
shaojinchun 已提交
135
            tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);
136

S
shaojinchun 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150
#ifdef RT_USING_SMP
            {
                int cpu_id;

                cpu_id = tid->oncpu;
                if ((cpu_id != RT_CPU_DETACHED) && (cpu_id != rt_hw_cpu_id()))
                {
                    rt_uint32_t cpu_mask;

                    cpu_mask = RT_CPU_MASK ^ (1 << cpu_id);
                    rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);
                }
            }
#else
B
bernard 已提交
151
            /* point to the signal handle entry */
S
shaojinchun 已提交
152
            tid->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
B
bernard 已提交
153
            tid->sig_ret = tid->sp;
E
emlslxl 已提交
154 155
            tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL,
                                       (void *)((char *)tid->sig_ret - 32), RT_NULL);
156
#endif /* RT_USING_SMP */
B
bernard 已提交
157 158

            rt_hw_interrupt_enable(level);
159
            LOG_D("signal stack pointer @ 0x%08x", tid->sp);
B
bernard 已提交
160 161 162 163 164 165 166 167 168 169 170

            /* re-schedule */
            rt_schedule();
        }
        else
        {
            rt_hw_interrupt_enable(level);
        }
    }
}

S
shaojinchun 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
#ifdef RT_USING_SMP
void *rt_signal_check(void* context)
{
    rt_base_t level;
    int cpu_id;
    struct rt_cpu* pcpu;
    struct rt_thread *current_thread;

    level = rt_hw_interrupt_disable();
    cpu_id = rt_hw_cpu_id();
    pcpu   = rt_cpu_index(cpu_id);
    current_thread = pcpu->current_thread;

    if (pcpu->irq_nest)
    {
        rt_hw_interrupt_enable(level);
        return context;
    }

    if (current_thread->cpus_lock_nest == 1)
    {
        if (current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING)
        {
            void *sig_context;

            current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;

            rt_hw_interrupt_enable(level);
            sig_context = rt_hw_stack_init((void *)_signal_entry, context,
                    (void *)(context - 32), RT_NULL);
            return sig_context;
        }
    }
    rt_hw_interrupt_enable(level);
    return context;
}
207
#endif /* RT_USING_SMP */
S
shaojinchun 已提交
208

B
bernard 已提交
209 210
rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler)
{
S
shaojinchun 已提交
211
    rt_base_t level;
212
    rt_sighandler_t old = RT_NULL;
B
bernard 已提交
213 214
    rt_thread_t tid = rt_thread_self();

E
emlslxl 已提交
215
    if (!sig_valid(signo)) return SIG_ERR;
B
bernard 已提交
216

S
shaojinchun 已提交
217
    level = rt_hw_interrupt_disable();
B
bernard 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230
    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;
    }
S
shaojinchun 已提交
231
    rt_hw_interrupt_enable(level);
B
bernard 已提交
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262

    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);
    }
E
emlslxl 已提交
263
    else
B
bernard 已提交
264 265 266 267 268
    {
        rt_hw_interrupt_enable(level);
    }
}

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout)
{
    int ret = RT_EOK;
    rt_base_t   level;
    rt_thread_t tid = rt_thread_self();
    struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL;

    /* current context checking */
    RT_DEBUG_IN_THREAD_CONTEXT;

    /* parameters check */
    if (set == NULL || *set == 0 || si == NULL )
    {
        ret = -RT_EINVAL;
        goto __done_return;
    }

    /* clear siginfo to avoid unknown value */
    memset(si, 0x0, sizeof(rt_siginfo_t));

    level = rt_hw_interrupt_disable();

    /* already pending */
    if (tid->sig_pending & *set) goto __done;

    if (timeout == 0)
    {
        ret = -RT_ETIMEOUT;
        goto __done_int;
    }

    /* suspend self thread */
    rt_thread_suspend(tid);
    /* set thread stat as waiting for signal */
    tid->stat |= RT_THREAD_STAT_SIGNAL_WAIT;

    /* start timeout timer */
    if (timeout != RT_WAITING_FOREVER)
    {
        /* reset the timeout of thread timer and start it */
        rt_timer_control(&(tid->thread_timer),
                         RT_TIMER_CTRL_SET_TIME,
                         &timeout);
        rt_timer_start(&(tid->thread_timer));
    }
    rt_hw_interrupt_enable(level);

    /* do thread scheduling */
    rt_schedule();

    level = rt_hw_interrupt_disable();

    /* remove signal waiting flag */
    tid->stat &= ~RT_THREAD_STAT_SIGNAL_WAIT;

    /* check errno of thread */
    if (tid->error == -RT_ETIMEOUT)
    {
        tid->error = RT_EOK;
        rt_hw_interrupt_enable(level);

        /* timer timeout */
        ret = -RT_ETIMEOUT;
        goto __done_return;
    }

__done:
    /* to get the first matched pending signals */
    si_node = (struct siginfo_node *)tid->si_list;
    while (si_node)
    {
        int signo;

        signo = si_node->si.si_signo;
        if (sig_mask(signo) & *set)
        {
            *si  = si_node->si;

347
            LOG_D("sigwait: %d sig raised!", signo);
348
            if (si_prev) si_prev->list.next = si_node->list.next;
S
shaojinchun 已提交
349 350 351 352 353 354 355 356 357 358 359 360 361 362
            else
            {
                struct siginfo_node *node_next;

                if (si_node->list.next)
                {
                    node_next = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
                    tid->si_list = node_next;
                }
                else
                {
                    tid->si_list = RT_NULL;
                }
            }
363 364 365 366 367 368 369 370

            /* clear pending */
            tid->sig_pending &= ~sig_mask(signo);
            rt_mp_free(si_node);
            break;
        }

        si_prev = si_node;
S
shaojinchun 已提交
371 372 373 374 375 376 377 378
        if (si_node->list.next)
        {
            si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
        }
        else
        {
            si_node = RT_NULL;
        }
379 380 381 382 383 384 385 386 387
     }

__done_int:
    rt_hw_interrupt_enable(level);

__done_return:
    return ret;
}

B
bernard 已提交
388 389
void rt_thread_handle_sig(rt_bool_t clean_state)
{
E
emlslxl 已提交
390
    rt_base_t level;
B
bernard 已提交
391 392 393 394

    rt_thread_t tid = rt_thread_self();
    struct siginfo_node *si_node;

E
emlslxl 已提交
395
    level = rt_hw_interrupt_disable();
396
    if (tid->sig_pending & tid->sig_mask)
B
bernard 已提交
397
    {
398 399 400 401 402 403 404
        /* if thread is not waiting for signal */
        if (!(tid->stat & RT_THREAD_STAT_SIGNAL_WAIT))
        {
            while (tid->sig_pending & tid->sig_mask)
            {
                int signo, error;
                rt_sighandler_t handler;
B
bernard 已提交
405

406 407
                si_node = (struct siginfo_node *)tid->si_list;
                if (!si_node) break;
B
bernard 已提交
408

409 410 411 412 413
                /* 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);
B
bernard 已提交
414

415 416
                signo   = si_node->si.si_signo;
                handler = tid->sig_vectors[signo];
S
shaojinchun 已提交
417
                tid->sig_pending &= ~sig_mask(signo);
418
                rt_hw_interrupt_enable(level);
B
bernard 已提交
419

420
                LOG_D("handle signal: %d, handler 0x%08x", signo, handler);
421
                if (handler) handler(signo);
B
bernard 已提交
422

423
                level = rt_hw_interrupt_disable();
B
Bernard Xiong 已提交
424
                error = -RT_EINTR;
B
bernard 已提交
425

426 427 428 429
                rt_mp_free(si_node); /* release this siginfo node */
                /* set errno in thread tcb */
                tid->error = error;
            }
B
bernard 已提交
430

431
            /* whether clean signal status */
S
shaojinchun 已提交
432 433 434 435 436 437 438 439
            if (clean_state == RT_TRUE)
            {
                tid->stat &= ~RT_THREAD_STAT_SIGNAL;
            }
            else
            {
                return;
            }
440 441
        }
    }
E
emlslxl 已提交
442
    rt_hw_interrupt_enable(level);
B
bernard 已提交
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
}

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;
S
shaojinchun 已提交
467
    struct siginfo_node *si_node;
B
bernard 已提交
468 469 470
    rt_sighandler_t *sig_vectors;

    level = rt_hw_interrupt_disable();
S
shaojinchun 已提交
471
    si_node = (struct siginfo_node *)tid->si_list;
B
bernard 已提交
472
    tid->si_list = RT_NULL;
E
emlslxl 已提交
473

B
bernard 已提交
474 475 476 477
    sig_vectors = tid->sig_vectors;
    tid->sig_vectors = RT_NULL;
    rt_hw_interrupt_enable(level);

S
shaojinchun 已提交
478
    if (si_node)
B
bernard 已提交
479 480
    {
        struct rt_slist_node *node;
S
shaojinchun 已提交
481
        struct rt_slist_node *node_to_free;
B
bernard 已提交
482

483
        LOG_D("free signal info list");
S
shaojinchun 已提交
484
        node = &(si_node->list);
E
emlslxl 已提交
485 486
        do
        {
S
shaojinchun 已提交
487
            node_to_free = node;
B
bernard 已提交
488
            node = node->next;
S
shaojinchun 已提交
489 490
            si_node = rt_slist_entry(node_to_free, struct siginfo_node, list);
            rt_mp_free(si_node);
B
bernard 已提交
491 492 493 494 495 496 497 498 499 500 501 502
        } while (node);
    }

    if (sig_vectors)
    {
        RT_KERNEL_FREE(sig_vectors);
    }
}

int rt_thread_kill(rt_thread_t tid, int sig)
{
    siginfo_t si;
E
emlslxl 已提交
503 504
    rt_base_t level;
    struct siginfo_node *si_node;
B
bernard 已提交
505 506 507 508

    RT_ASSERT(tid != RT_NULL);
    if (!sig_valid(sig)) return -RT_EINVAL;

509
    LOG_I("send signal: %d", sig);
B
bernard 已提交
510 511 512 513
    si.si_signo = sig;
    si.si_code  = SI_USER;
    si.si_value.sival_ptr = RT_NULL;

E
emlslxl 已提交
514
    level = rt_hw_interrupt_disable();
B
bernard 已提交
515 516 517 518 519 520
    if (tid->sig_pending & sig_mask(sig))
    {
        /* whether already emits this signal? */
        struct rt_slist_node *node;
        struct siginfo_node  *entry;

S
shaojinchun 已提交
521 522 523 524 525
        si_node = (struct siginfo_node *)tid->si_list;
        if (si_node)
            node = (struct rt_slist_node *)&si_node->list;
        else
            node = RT_NULL;
B
bernard 已提交
526

527
        /* update sig info */
B
bernard 已提交
528 529 530 531 532 533
        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));
S
shaojinchun 已提交
534
                rt_hw_interrupt_enable(level);
B
bernard 已提交
535 536 537 538
                return 0;
            }
        }
    }
E
emlslxl 已提交
539 540
    rt_hw_interrupt_enable(level);

mysterywolf's avatar
mysterywolf 已提交
541
    si_node = (struct siginfo_node *) rt_mp_alloc(_siginfo_pool, 0);
B
bernard 已提交
542 543 544 545 546
    if (si_node)
    {
        rt_slist_init(&(si_node->list));
        memcpy(&(si_node->si), &si, sizeof(siginfo_t));

E
emlslxl 已提交
547
        level = rt_hw_interrupt_disable();
S
shaojinchun 已提交
548 549

        if (tid->si_list)
B
bernard 已提交
550
        {
E
emlslxl 已提交
551
            struct siginfo_node *si_list;
B
bernard 已提交
552

E
emlslxl 已提交
553
            si_list = (struct siginfo_node *)tid->si_list;
B
bernard 已提交
554 555
            rt_slist_append(&(si_list->list), &(si_node->list));
        }
S
shaojinchun 已提交
556 557 558 559 560 561 562 563
        else
        {
            tid->si_list = si_node;
        }

        /* a new signal */
        tid->sig_pending |= sig_mask(sig);

E
emlslxl 已提交
564
        rt_hw_interrupt_enable(level);
B
bernard 已提交
565 566 567
    }
    else
    {
568
        LOG_E("The allocation of signal info node failed.");
B
bernard 已提交
569 570 571 572 573 574 575 576 577 578
    }

    /* deliver signal to this thread */
    _signal_deliver(tid);

    return RT_EOK;
}

int rt_system_signal_init(void)
{
mysterywolf's avatar
mysterywolf 已提交
579 580
    _siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node));
    if (_siginfo_pool == RT_NULL)
B
bernard 已提交
581
    {
582
        LOG_E("create memory pool for signal info failed.");
B
bernard 已提交
583 584 585 586 587 588
        RT_ASSERT(0);
    }

    return 0;
}

589
#endif /* RT_USING_SIGNALS */