GCC Code Coverage Report


Directory: kernel/
File: kernel/src/pthread.c
Date: 2024-01-01 16:15:14
Exec Total Coverage
Lines: 70 81 86.4%
Functions: 11 11 100.0%
Branches: 15 26 57.7%

Line Branch Exec Source
1 /**
2 * @file pthread.c
3 * @author Henson
4 * @brief pthread api
5 * @version 0.1
6 * @date 2023-08-03
7 *
8 * @copyright Copyright (c) 2023
9 *
10 */
11 #include <puppy.h>
12 #include <puppy/kobj.h>
13 #include <puppy/posix/unistd.h>
14 #include <puppy/posix/pthread.h>
15 #include <string.h>
16
17 const pthread_attr_t pthread_default_attr =
18 {
19 P_SCHED_PRIO_DEFAULT,
20 0, 0,
21 PTHREAD_CREATE_JOINABLE, /* detach state */
22 0,
23 P_CONFIG_PTHREAD_STACK_DEFAULT,
24 };
25
26 struct _pthread_data
27 {
28 pthread_attr_t attr;
29 struct _thread_obj tid;
30 void *alloc_stack;
31
32 pthread_func_t thread_entry;
33 void *thread_parameter;
34
35 /* return value */
36 void *return_value;
37
38 /* semaphore for joinable thread */
39 struct _sem_obj joinable_sem;
40
41 /* cancel state and type */
42 uint8_t cancelstate;
43 volatile uint8_t canceltype;
44 volatile uint8_t canceled;
45
46 // _pthread_cleanup_t *cleanup;
47 void** tls; /* thread-local storage area */
48 };
49 typedef struct _pthread_data _pthread_data_t;
50
51 24 pthread_t _pthread_data_create(void)
52 {
53 24 _pthread_data_t *ptd = NULL;
54
55 24 ptd = (_pthread_data_t*)p_malloc(sizeof(_pthread_data_t));
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if (!ptd) return NULL;
57
58 24 memset(ptd, 0x0, sizeof(_pthread_data_t));
59 24 ptd->canceled = 0;
60 24 ptd->cancelstate = PTHREAD_CANCEL_DISABLE;
61 24 ptd->canceltype = PTHREAD_CANCEL_DEFERRED;
62
63 24 return ptd;
64 }
65 23 void _pthread_data_destroy(_pthread_data_t *ptd)
66 {
67 23 p_free(ptd);
68 23 }
69
70 23 static void _pthread_cleanup(p_obj_t obj)
71 {
72 _pthread_data_t *ptd;
73 23 ptd = p_container_of(obj, _pthread_data_t, tid);
74 23 p_sem_post(&ptd->joinable_sem);
75 23 }
76
77 1 int pthread_attr_init(pthread_attr_t *attr)
78 {
79 1 *attr = pthread_default_attr;
80 1 return 0;
81 }
82 1 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
83 {
84 1 attr->stacksize = stacksize;
85 1 return 0;
86 }
87 /**
88 * @brief Create a new thread
89 *
90 * The pthread_create() function starts a new thread in the calling
91 * process. The new thread starts execution by invoking
92 * start_routine(); arg is passed as the sole argument of
93 * start_routine().
94 *
95 * The new thread terminates in one of the following ways:
96 * • It calls pthread_exit(), specifying an exit status value that
97 * is available to another thread in the same process that calls
98 * pthread_join().
99 * • It returns from start_routine(). This is equivalent to
100 * calling pthread_exit() with the value supplied in the return
101 * statement.
102 *
103 * @param thread Buffer to store new thread ID, this identifier is used to refer
104 * to the thread in subsequent calls to other pthreads functions.
105 *
106 * @param attr The attr argument points to a pthread_attr_t structure whose
107 * contents are used at thread creation time to determine attributes
108 * for the new thread; this structure is initialized using
109 * pthread_attr_init() and related functions. If attr is NULL,
110 * then the thread is created with default attributes.
111 *
112 * @param start_routine Function to run when the new thread executes.
113 * @param arg arg is passed as the sole argument of start_routine().
114 * @return On success, pthread_create() returns 0; on error, it returns an
115 * error number, and the contents of *thread are undefined.
116 */
117 24 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
118 pthread_startroutine_t start_routine, pthread_addr_t arg)
119 {
120 24 int ret = 0;
121 _pthread_data_t *ptd;
122 void *stack;
123 /* allocate posix thread data */
124 24 ptd = _pthread_data_create();
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if (ptd == NULL)
126 {
127 ret = -P_ENOMEM;
128 goto _exit;
129 }
130
131
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 23 times.
24 if (attr != NULL)
132 {
133 1 ptd->attr = *attr;
134 }
135 else
136 {
137 /* use default attribute */
138 23 ptd->attr = pthread_default_attr;
139 }
140
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
141 {
142 24 p_sem_init(&ptd->joinable_sem, "sem", 0, 1);
143 }
144 /* set parameter */
145 24 ptd->thread_entry = start_routine;
146 24 ptd->thread_parameter = arg;
147
148 /* stack */
149
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if (ptd->attr.stackaddr == 0)
150 {
151 24 stack = (void *)p_malloc(ptd->attr.stacksize);
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if (stack == NULL)
153 {
154 ret = -P_ENOMEM;
155 goto _exit;
156 }
157
158 24 ptd->alloc_stack = stack;
159 }
160 else
161 {
162 stack = (void *)(ptd->attr.stackaddr);
163 }
164 /* initial this pthread to system */
165 24 p_thread_init(&ptd->tid, "pth", (void (*)(void *))start_routine, arg,
166 24 stack, ptd->attr.stacksize,
167 ptd->attr.priority, CPU_NA);
168 24 ptd->tid.cleanup = _pthread_cleanup;
169
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 1 times.
24 if(thread) *thread = ptd;
170 /* start thread */
171
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 if (p_thread_start(&ptd->tid) == 0)
172 24 return 0;
173 _exit:
174
175 return ret;
176 }
177 /**
178 * @brief join with a terminated thread
179 *
180 * The pthread_join() function waits for the thread specified by
181 * thread to terminate. If that thread has already terminated, then
182 * pthread_join() returns immediately. The thread specified by
183 * thread must be joinable.
184 *
185 * @param thread Thread ID is created by pthread_create().
186 * @param value the value passed to pthread_exit() by the terminating thread.
187 * @return On success, pthread_join() returns 0; on error, it returns a
188 * error number.
189 */
190 23 int pthread_join(pthread_t thread, void **value)
191 {
192 _pthread_data_t *ptd;
193 int result;
194
195 23 ptd = thread;
196
197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (ptd == NULL)
198 {
199 return -1; /* invalid pthread id */
200 }
201
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED)
203 {
204 return -1; /* join on a detached pthread */
205 }
206
207 23 result = p_sem_wait(&ptd->joinable_sem);
208
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (result == 0)
209 {
210 /* get return value */
211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (value != NULL)
212 *value = ptd->return_value;
213
214
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (ptd->alloc_stack)
215 {
216 23 p_free(ptd->alloc_stack);
217 }
218 23 p_obj_deinit(&ptd->tid);
219 23 p_obj_deinit(&ptd->joinable_sem);
220
221 /* destroy this pthread */
222 23 _pthread_data_destroy(ptd);
223 }
224 else
225 {
226 return -1;
227 }
228
229 23 return 0;
230 }
231
232 /**
233 * @brief terminate calling thread
234 *
235 * The pthread_exit() function terminates the calling thread and
236 * returns a value via value.
237 *
238 * @param value value passed to thread that calls pthread_join().
239 */
240 23 void pthread_exit(void *value)
241 {
242 _pthread_data_t *ptd;
243 23 ptd = p_container_of(p_thread_self(), _pthread_data_t, tid);
244 23 ptd->return_value = value;
245 23 }
246 /**
247 * @brief obtain ID of the calling thread
248 *
249 * The pthread_self() function returns the ID of the calling thread.
250 * This is the same value that is returned in *thread in the
251 * pthread_create() call that created this thread.
252 *
253 * @return the calling thread's ID.
254 */
255 1 pthread_t pthread_self(void)
256 {
257 _pthread_data_t *ptd;
258 1 ptd = p_container_of(p_thread_self(), _pthread_data_t, tid);
259 1 return ptd;
260 }
261
262 /**
263 * @brief set the name of a thread
264 *
265 * @param thread argument specifies the thread whose name is to be changed.
266 * @param name name specifies the new name.
267 * @return On success, these functions return 0; on error, they return a
268 * nonzero error number.
269 */
270 1 int pthread_setname_np(pthread_t thread, const char *name)
271 {
272 1 _pthread_data_t *ptd = thread;
273 1 ptd->tid.kobj.name = name;
274 1 return 0;
275 }
276
277 23 unsigned sleep(unsigned int __seconds)
278 {
279 23 p_thread_sleep(p_tick_persec() * __seconds);
280 23 return 0;
281 }
282