signal.c 12.1 KB
Newer Older
B
bernard 已提交
1
/*
2
 * Copyright (c) 2006-2018, 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
B
bernard 已提交
23 24 25
#endif

// #define DBG_ENABLE
26
#define DBG_SECTION_NAME    "SIGN"
B
bernard 已提交
27 28 29 30 31 32 33 34 35
#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
{
B
Bernard Xiong 已提交
36
    siginfo_t si;
B
bernard 已提交
37 38 39 40 41 42 43 44 45
    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)
{
46
    LOG_I("handled signo[%d] with default action.", signo);
B
bernard 已提交
47 48 49
    return ;
}

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

    dbg_enter;

    /* handle signal */
    rt_thread_handle_sig(RT_FALSE);

E
emlslxl 已提交
59 60
    /* never come back... */
    rt_hw_interrupt_disable();
B
bernard 已提交
61 62 63 64
    /* return to thread */
    tid->sp = tid->sig_ret;
    tid->sig_ret = RT_NULL;

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

S
shaojinchun 已提交
68 69 70 71 72
#ifdef RT_USING_SMP
    rt_hw_context_switch_to((rt_ubase_t)&(tid->sp), tid);
#else
    rt_hw_context_switch_to((rt_ubase_t)&(tid->sp));
#endif /*RT_USING_SMP*/
B
bernard 已提交
73 74
}

75 76 77 78 79 80 81 82 83 84
/*
 * 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 已提交
85 86 87 88 89 90 91 92
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();
93
    if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)
B
bernard 已提交
94 95 96
    {
        /* resume thread to handle signal */
        rt_thread_resume(tid);
E
emlslxl 已提交
97 98
        /* add signal state */
        tid->stat |= RT_THREAD_STAT_SIGNAL;
B
bernard 已提交
99 100 101 102 103 104 105 106 107 108

        rt_hw_interrupt_enable(level);

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

B
bernard 已提交
112 113 114 115 116
            rt_hw_interrupt_enable(level);

            /* do signal action in self thread context */
            rt_thread_handle_sig(RT_TRUE);
        }
S
shaojinchun 已提交
117
        else if (!((tid->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL))
B
bernard 已提交
118
        {
119
            /* add signal state */
B
bernard 已提交
120
            tid->stat |= RT_THREAD_STAT_SIGNAL;
121

B
bernard 已提交
122 123
            /* point to the signal handle entry */
            tid->sig_ret = tid->sp;
E
emlslxl 已提交
124 125
            tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL,
                                       (void *)((char *)tid->sig_ret - 32), RT_NULL);
B
bernard 已提交
126 127

            rt_hw_interrupt_enable(level);
128
            LOG_D("signal stack pointer @ 0x%08x", tid->sp);
B
bernard 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141

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

rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler)
{
142
    rt_sighandler_t old = RT_NULL;
B
bernard 已提交
143 144
    rt_thread_t tid = rt_thread_self();

E
emlslxl 已提交
145
    if (!sig_valid(signo)) return SIG_ERR;
B
bernard 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192

    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);
    }
E
emlslxl 已提交
193
    else
B
bernard 已提交
194 195 196 197 198
    {
        rt_hw_interrupt_enable(level);
    }
}

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 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 263 264 265 266 267 268 269 270 271 272 273 274 275 276
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;

277
            LOG_D("sigwait: %d sig raised!", signo);
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
            if (si_prev) si_prev->list.next = si_node->list.next;
            else tid->si_list = si_node->list.next;

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

        si_prev = si_node;
        si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
     }

__done_int:
    rt_hw_interrupt_enable(level);

__done_return:
    return ret;
}

B
bernard 已提交
298 299
void rt_thread_handle_sig(rt_bool_t clean_state)
{
E
emlslxl 已提交
300
    rt_base_t level;
B
bernard 已提交
301 302 303 304

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

E
emlslxl 已提交
305
    level = rt_hw_interrupt_disable();
306
    if (tid->sig_pending & tid->sig_mask)
B
bernard 已提交
307
    {
308 309 310 311 312 313 314
        /* 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 已提交
315

316 317
                si_node = (struct siginfo_node *)tid->si_list;
                if (!si_node) break;
B
bernard 已提交
318

319 320 321 322 323
                /* 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 已提交
324

325 326 327
                signo   = si_node->si.si_signo;
                handler = tid->sig_vectors[signo];
                rt_hw_interrupt_enable(level);
B
bernard 已提交
328

329
                LOG_D("handle signal: %d, handler 0x%08x", signo, handler);
330
                if (handler) handler(signo);
B
bernard 已提交
331

332 333
                level = rt_hw_interrupt_disable();
                tid->sig_pending &= ~sig_mask(signo);
B
Bernard Xiong 已提交
334
                error = -RT_EINTR;
B
bernard 已提交
335

336 337 338 339
                rt_mp_free(si_node); /* release this siginfo node */
                /* set errno in thread tcb */
                tid->error = error;
            }
B
bernard 已提交
340

341 342 343 344
            /* whether clean signal status */
            if (clean_state == RT_TRUE) tid->stat &= ~RT_THREAD_STAT_SIGNAL;
        }
    }
B
bernard 已提交
345

E
emlslxl 已提交
346
    rt_hw_interrupt_enable(level);
B
bernard 已提交
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
}

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();
E
emlslxl 已提交
375
    si_list = (struct siginfo_node *)tid->si_list;
B
bernard 已提交
376
    tid->si_list = RT_NULL;
E
emlslxl 已提交
377

B
bernard 已提交
378 379 380 381 382 383 384 385 386
    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;

387
        LOG_D("free signal info list");
B
bernard 已提交
388
        node = &(si_list->list);
E
emlslxl 已提交
389 390
        do
        {
B
bernard 已提交
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
            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;
E
emlslxl 已提交
407 408
    rt_base_t level;
    struct siginfo_node *si_node;
B
bernard 已提交
409 410 411 412

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

413
    LOG_I("send signal: %d", sig);
B
bernard 已提交
414 415 416 417
    si.si_signo = sig;
    si.si_code  = SI_USER;
    si.si_value.sival_ptr = RT_NULL;

E
emlslxl 已提交
418
    level = rt_hw_interrupt_disable();
B
bernard 已提交
419 420 421 422 423 424
    if (tid->sig_pending & sig_mask(sig))
    {
        /* whether already emits this signal? */
        struct rt_slist_node *node;
        struct siginfo_node  *entry;

E
emlslxl 已提交
425 426
        node = (struct rt_slist_node *)tid->si_list;
        rt_hw_interrupt_enable(level);
B
bernard 已提交
427

428
        /* update sig info */
E
emlslxl 已提交
429
        rt_enter_critical();
B
bernard 已提交
430 431 432 433 434 435
        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));
E
emlslxl 已提交
436
                rt_exit_critical();
B
bernard 已提交
437 438 439
                return 0;
            }
        }
E
emlslxl 已提交
440
        rt_exit_critical();
B
bernard 已提交
441

E
emlslxl 已提交
442 443 444 445 446 447 448
        /* disable interrupt to protect tcb */
        level = rt_hw_interrupt_disable();
    }
    else
    {
        /* a new signal */
        tid->sig_pending |= sig_mask(sig);
B
bernard 已提交
449
    }
E
emlslxl 已提交
450 451 452
    rt_hw_interrupt_enable(level);

    si_node = (struct siginfo_node *) rt_mp_alloc(_rt_siginfo_pool, 0);
B
bernard 已提交
453 454 455 456 457
    if (si_node)
    {
        rt_slist_init(&(si_node->list));
        memcpy(&(si_node->si), &si, sizeof(siginfo_t));

E
emlslxl 已提交
458
        level = rt_hw_interrupt_disable();
B
bernard 已提交
459
        if (!tid->si_list) tid->si_list = si_node;
E
emlslxl 已提交
460
        else
B
bernard 已提交
461
        {
E
emlslxl 已提交
462
            struct siginfo_node *si_list;
B
bernard 已提交
463

E
emlslxl 已提交
464
            si_list = (struct siginfo_node *)tid->si_list;
B
bernard 已提交
465 466
            rt_slist_append(&(si_list->list), &(si_node->list));
        }
E
emlslxl 已提交
467
        rt_hw_interrupt_enable(level);
B
bernard 已提交
468 469 470
    }
    else
    {
471
        LOG_E("The allocation of signal info node failed.");
B
bernard 已提交
472 473 474 475 476 477 478 479 480 481 482 483 484
    }

    /* 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)
    {
485
        LOG_E("create memory pool for signal info failed.");
B
bernard 已提交
486 487 488 489 490 491 492
        RT_ASSERT(0);
    }

    return 0;
}

#endif