forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsem.c
163 lines (130 loc) · 3.5 KB
/
sem.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
* Copyright (c) 2010-2016 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* @brief Kernel semaphore object.
*
* The semaphores are of the 'counting' type, i.e. each 'give' operation will
* increment the internal count by 1, if no fiber is pending on it. The 'init'
* call initializes the count to 0. Following multiple 'give' operations, the
* same number of 'take' operations can be performed without the calling fiber
* having to pend on the semaphore, or the calling task having to poll.
*/
#include <kernel.h>
#include <kernel_structs.h>
#include <debug/object_tracing_common.h>
#include <toolchain.h>
#include <linker/sections.h>
#include <wait_q.h>
#include <misc/dlist.h>
#include <ksched.h>
#include <init.h>
extern struct k_sem _k_sem_list_start[];
extern struct k_sem _k_sem_list_end[];
#ifdef CONFIG_OBJECT_TRACING
struct k_sem *_trace_list_k_sem;
/*
* Complete initialization of statically defined semaphores.
*/
static int init_sem_module(struct device *dev)
{
ARG_UNUSED(dev);
struct k_sem *sem;
for (sem = _k_sem_list_start; sem < _k_sem_list_end; sem++) {
SYS_TRACING_OBJ_INIT(k_sem, sem);
}
return 0;
}
SYS_INIT(init_sem_module, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
#endif /* CONFIG_OBJECT_TRACING */
void k_sem_init(struct k_sem *sem, unsigned int initial_count,
unsigned int limit)
{
__ASSERT(limit != 0, "limit cannot be zero");
sem->count = initial_count;
sem->limit = limit;
sys_dlist_init(&sem->wait_q);
#if defined(CONFIG_POLL)
sys_dlist_init(&sem->poll_events);
#endif
SYS_TRACING_OBJ_INIT(k_sem, sem);
}
/* returns 1 if a reschedule must take place, 0 otherwise */
static inline int handle_poll_events(struct k_sem *sem)
{
#ifdef CONFIG_POLL
u32_t state = K_POLL_STATE_SEM_AVAILABLE;
return _handle_obj_poll_events(&sem->poll_events, state);
#else
return 0;
#endif
}
static inline void increment_count_up_to_limit(struct k_sem *sem)
{
sem->count += (sem->count != sem->limit);
}
/* returns 1 if _Swap() will need to be invoked, 0 otherwise */
static int do_sem_give(struct k_sem *sem)
{
struct k_thread *thread = _unpend_first_thread(&sem->wait_q);
if (!thread) {
increment_count_up_to_limit(sem);
return handle_poll_events(sem);
}
(void)_abort_thread_timeout(thread);
_ready_thread(thread);
_set_thread_return_value(thread, 0);
return !_is_in_isr() && _must_switch_threads();
}
/*
* This function is meant to be called only by
* _sys_event_logger_put_non_preemptible(), which itself is really meant to be
* called only by _sys_k_event_logger_context_switch(), used within a context
* switch to log the event.
*
* WARNING:
* It must be called with interrupts already locked.
* It cannot be called for a sempahore part of a group.
*/
void _sem_give_non_preemptible(struct k_sem *sem)
{
struct k_thread *thread;
thread = _unpend_first_thread(&sem->wait_q);
if (!thread) {
increment_count_up_to_limit(sem);
return;
}
_abort_thread_timeout(thread);
_ready_thread(thread);
_set_thread_return_value(thread, 0);
}
void k_sem_give(struct k_sem *sem)
{
unsigned int key;
key = irq_lock();
if (do_sem_give(sem)) {
_Swap(key);
} else {
irq_unlock(key);
}
}
int k_sem_take(struct k_sem *sem, s32_t timeout)
{
__ASSERT(!_is_in_isr() || timeout == K_NO_WAIT, "");
unsigned int key = irq_lock();
if (likely(sem->count > 0)) {
sem->count--;
irq_unlock(key);
return 0;
}
if (timeout == K_NO_WAIT) {
irq_unlock(key);
return -EBUSY;
}
_pend_current_thread(&sem->wait_q, timeout);
return _Swap(key);
}