12
12
#include <sys/task.h>
13
13
14
14
#include "private/error.h"
15
+ #include "private/utils.h"
15
16
16
17
/* Semaphore Control Block structure. */
17
18
struct sem_t {
@@ -26,36 +27,47 @@ struct sem_t {
26
27
27
28
static inline bool sem_is_valid (const sem_t * s )
28
29
{
29
- return (s && s -> magic == SEM_MAGIC && s -> wait_q );
30
+ return s && s -> magic == SEM_MAGIC && s -> wait_q && s -> max_waiters > 0 &&
31
+ s -> count >= 0 && s -> count <= SEM_MAX_COUNT ;
32
+ }
33
+
34
+ static inline void sem_invalidate (sem_t * s )
35
+ {
36
+ if (s ) {
37
+ s -> magic = 0xDEADBEEF ; /* Clear magic to prevent reuse */
38
+ s -> count = -1 ;
39
+ s -> max_waiters = 0 ;
40
+ }
30
41
}
31
42
32
43
sem_t * mo_sem_create (uint16_t max_waiters , int32_t initial_count )
33
44
{
34
45
/* Enhanced input validation */
35
- if (!max_waiters || initial_count < 0 || initial_count > SEM_MAX_COUNT )
46
+ if (unlikely (!max_waiters || initial_count < 0 ||
47
+ initial_count > SEM_MAX_COUNT ))
36
48
return NULL ;
37
49
38
50
sem_t * sem = malloc (sizeof (sem_t ));
39
- if (!sem )
51
+ if (unlikely ( !sem ) )
40
52
return NULL ;
41
53
42
- /* Initialize structure to known state */
54
+ /* Initialize structure to known safe state */
43
55
sem -> wait_q = NULL ;
44
56
sem -> count = 0 ;
45
57
sem -> max_waiters = 0 ;
46
58
sem -> magic = 0 ;
47
59
48
60
/* Create wait queue */
49
61
sem -> wait_q = queue_create (max_waiters );
50
- if (!sem -> wait_q ) {
62
+ if (unlikely ( !sem -> wait_q ) ) {
51
63
free (sem );
52
64
return NULL ;
53
65
}
54
66
55
67
/* Initialize remaining fields atomically */
56
68
sem -> count = initial_count ;
57
69
sem -> max_waiters = max_waiters ;
58
- sem -> magic = SEM_MAGIC ; /* Mark as valid last */
70
+ sem -> magic = SEM_MAGIC ; /* Mark as valid last to prevent races */
59
71
60
72
return sem ;
61
73
}
@@ -65,19 +77,19 @@ int32_t mo_sem_destroy(sem_t *s)
65
77
if (!s )
66
78
return ERR_OK ; /* Destroying NULL is a no-op, not an error */
67
79
68
- if (!sem_is_valid (s ))
80
+ if (unlikely ( !sem_is_valid (s ) ))
69
81
return ERR_FAIL ;
70
82
71
83
NOSCHED_ENTER ();
72
84
73
85
/* Check if any tasks are waiting - unsafe to destroy if so */
74
- if (queue_count (s -> wait_q ) > 0 ) {
86
+ if (unlikely ( queue_count (s -> wait_q ) > 0 ) ) {
75
87
NOSCHED_LEAVE ();
76
88
return ERR_TASK_BUSY ;
77
89
}
78
90
79
- /* Invalidate the semaphore to prevent further use */
80
- s -> magic = 0 ;
91
+ /* Atomically invalidate the semaphore to prevent further use */
92
+ sem_invalidate ( s ) ;
81
93
queue_t * wait_q = s -> wait_q ;
82
94
s -> wait_q = NULL ;
83
95
@@ -91,23 +103,23 @@ int32_t mo_sem_destroy(sem_t *s)
91
103
92
104
void mo_sem_wait (sem_t * s )
93
105
{
94
- if (!sem_is_valid (s )) {
106
+ if (unlikely ( !sem_is_valid (s ) )) {
95
107
/* Invalid semaphore - this is a programming error */
96
108
panic (ERR_SEM_OPERATION );
97
109
}
98
110
99
111
NOSCHED_ENTER ();
100
112
101
- /* Fast path: resource available and no waiters (preserves FIFO) */
102
- if (s -> count > 0 && queue_count (s -> wait_q ) == 0 ) {
113
+ /* Fast path: resource available and no waiters (preserves FIFO ordering ) */
114
+ if (likely ( s -> count > 0 && queue_count (s -> wait_q ) == 0 ) ) {
103
115
s -> count -- ;
104
116
NOSCHED_LEAVE ();
105
117
return ;
106
118
}
107
119
108
120
/* Slow path: must wait for resource */
109
- /* Verify wait queue has capacity (should never fail for valid semaphore) */
110
- if (queue_count (s -> wait_q ) >= s -> max_waiters ) {
121
+ /* Verify wait queue has capacity before attempting to block */
122
+ if (unlikely ( queue_count (s -> wait_q ) >= s -> max_waiters ) ) {
111
123
NOSCHED_LEAVE ();
112
124
panic (ERR_SEM_OPERATION ); /* Queue overflow - system error */
113
125
}
@@ -120,22 +132,22 @@ void mo_sem_wait(sem_t *s)
120
132
*/
121
133
_sched_block (s -> wait_q );
122
134
123
- /* When we return here, we have been awakened and have acquired the
124
- * semaphore. The task that signaled us did NOT increment the count - the
125
- * "token" was passed directly to us , so no further action is needed.
135
+ /* When we return here, we have been awakened and acquired the semaphore.
136
+ * The signaling task passed the "token" directly to us without incrementing
137
+ * the count , so no further action is needed.
126
138
*/
127
139
}
128
140
129
141
int32_t mo_sem_trywait (sem_t * s )
130
142
{
131
- if (!sem_is_valid (s ))
143
+ if (unlikely ( !sem_is_valid (s ) ))
132
144
return ERR_FAIL ;
133
145
134
146
int32_t result = ERR_FAIL ;
135
147
136
148
NOSCHED_ENTER ();
137
149
138
- /* Only succeed if resource is available AND no waiters (preserves FIFO) */
150
+ /* Only succeed if resource available AND no waiters (preserves FIFO) */
139
151
if (s -> count > 0 && queue_count (s -> wait_q ) == 0 ) {
140
152
s -> count -- ;
141
153
result = ERR_OK ;
@@ -147,7 +159,7 @@ int32_t mo_sem_trywait(sem_t *s)
147
159
148
160
void mo_sem_signal (sem_t * s )
149
161
{
150
- if (!sem_is_valid (s )) {
162
+ if (unlikely ( !sem_is_valid (s ) )) {
151
163
/* Invalid semaphore - this is a programming error */
152
164
panic (ERR_SEM_OPERATION );
153
165
}
@@ -157,13 +169,13 @@ void mo_sem_signal(sem_t *s)
157
169
158
170
NOSCHED_ENTER ();
159
171
160
- /* Check if any tasks are waiting */
172
+ /* Check if any tasks are waiting for resources */
161
173
if (queue_count (s -> wait_q ) > 0 ) {
162
174
/* Wake up the oldest waiting task (FIFO order) */
163
175
awakened_task = queue_dequeue (s -> wait_q );
164
- if (awakened_task ) {
165
- /* Validate the awakened task before changing its state */
166
- if (awakened_task -> state == TASK_BLOCKED ) {
176
+ if (likely ( awakened_task ) ) {
177
+ /* Validate awakened task state consistency */
178
+ if (likely ( awakened_task -> state == TASK_BLOCKED ) ) {
167
179
awakened_task -> state = TASK_READY ;
168
180
should_yield = true;
169
181
} else {
@@ -172,39 +184,46 @@ void mo_sem_signal(sem_t *s)
172
184
}
173
185
}
174
186
/* Note: count is NOT incremented - the "token" is passed directly to
175
- * the awakened task to prevent race conditions.
187
+ * the awakened task to prevent race conditions where the count could
188
+ * be decremented by another task between our increment and the
189
+ * awakened task's execution.
176
190
*/
177
191
} else {
178
- /* No waiting tasks - increment available count */
179
- if (s -> count < SEM_MAX_COUNT )
192
+ /* No waiting tasks - increment available resource count */
193
+ if (likely ( s -> count < SEM_MAX_COUNT ) )
180
194
s -> count ++ ;
181
- /* Silently ignore overflow - semaphore remains at max count */
195
+
196
+ /* Silently ignore overflow - semaphore remains at max count.
197
+ * This prevents wraparound while maintaining system stability.
198
+ */
182
199
}
183
200
184
201
NOSCHED_LEAVE ();
185
202
186
- /* Yield outside critical section to allow awakened task to run.
187
- * This improves responsiveness if the awakened task has higher priority.
203
+ /* Yield outside critical section if we awakened a task.
204
+ * This improves system responsiveness by allowing the awakened task to run
205
+ * immediately if it has higher priority.
188
206
*/
189
207
if (should_yield )
190
208
mo_task_yield ();
191
209
}
192
210
193
211
int32_t mo_sem_getvalue (sem_t * s )
194
212
{
195
- if (!sem_is_valid (s ))
213
+ if (unlikely ( !sem_is_valid (s ) ))
196
214
return -1 ;
197
215
198
- /* This is inherently racy - the value may change immediately after being
199
- * read. The volatile keyword ensures we read the current value, but does
200
- * not provide atomicity across multiple operations.
216
+ /* This read is inherently racy - the value may change immediately after
217
+ * being read. The volatile keyword ensures we read the current value from
218
+ * memory, but does not provide atomicity across multiple operations.
219
+ * Callers should not rely on this value for synchronization decisions.
201
220
*/
202
221
return s -> count ;
203
222
}
204
223
205
224
int32_t mo_sem_waiting_count (sem_t * s )
206
225
{
207
- if (!sem_is_valid (s ))
226
+ if (unlikely ( !sem_is_valid (s ) ))
208
227
return -1 ;
209
228
210
229
int32_t count ;
0 commit comments