1 /* 2 * Copyright (c) 2006-2018, RT-Thread Development Team 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Change Logs: 7 * Date Author Notes 8 * 2018-01-26 Bernard Fix pthread_detach issue for a none-joinable 9 * thread. 10 */ 11 12 #include <pthread.h> 13 #include <sched.h> 14 #include "pthread_internal.h" 15 16 int pthread_system_init(void) 17 { 18 /* initialize key area */ 19 pthread_key_system_init(); 20 /* initialize posix mqueue */ 21 posix_mq_system_init(); 22 /* initialize posix semaphore */ 23 posix_sem_system_init(); 24 25 return 0; 26 } 27 INIT_COMPONENT_EXPORT(pthread_system_init); 28 29 static void _pthread_cleanup(rt_thread_t tid) 30 { 31 _pthread_data_t *ptd; 32 ptd = _pthread_get_data(tid); 33 34 /* clear cleanup function */ 35 tid->cleanup = RT_NULL; 36 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE) 37 { 38 rt_sem_release(ptd->joinable_sem); 39 } 40 else 41 { 42 /* release pthread resource */ 43 pthread_detach(tid); 44 } 45 } 46 47 static void pthread_entry_stub(void *parameter) 48 { 49 _pthread_data_t *ptd; 50 void *value; 51 52 ptd = (_pthread_data_t *)parameter; 53 54 /* execute pthread entry */ 55 value = ptd->thread_entry(ptd->thread_parameter); 56 /* set value */ 57 ptd->return_value = value; 58 } 59 60 int pthread_create(pthread_t *tid, 61 const pthread_attr_t *attr, 62 void *(*start)(void *), void *parameter) 63 { 64 int result; 65 void *stack; 66 char name[RT_NAME_MAX]; 67 static rt_uint16_t pthread_number = 0; 68 _pthread_data_t *ptd; 69 70 /* tid shall be provided */ 71 RT_ASSERT(tid != RT_NULL); 72 73 /* allocate posix thread data */ 74 ptd = (_pthread_data_t *)rt_malloc(sizeof(_pthread_data_t)); 75 if (ptd == RT_NULL) 76 return ENOMEM; 77 /* clean posix thread data memory */ 78 rt_memset(ptd, 0, sizeof(_pthread_data_t)); 79 ptd->canceled = 0; 80 ptd->cancelstate = PTHREAD_CANCEL_DISABLE; 81 ptd->canceltype = PTHREAD_CANCEL_DEFERRED; 82 ptd->magic = PTHREAD_MAGIC; 83 84 if (attr != RT_NULL) 85 { 86 ptd->attr = *attr; 87 } 88 else 89 { 90 /* use default attribute */ 91 pthread_attr_init(&ptd->attr); 92 } 93 94 rt_snprintf(name, sizeof(name), "pth%02d", pthread_number ++); 95 if (ptd->attr.stack_base == 0) 96 { 97 stack = (void *)rt_malloc(ptd->attr.stack_size); 98 } 99 else 100 { 101 stack = (void *)(ptd->attr.stack_base); 102 } 103 104 if (stack == RT_NULL) 105 { 106 rt_free(ptd); 107 108 return ENOMEM; 109 } 110 111 /* pthread is a static thread object */ 112 ptd->tid = (rt_thread_t) rt_malloc(sizeof(struct rt_thread)); 113 if (ptd->tid == RT_NULL) 114 { 115 if (ptd->attr.stack_base == 0) 116 rt_free(stack); 117 rt_free(ptd); 118 119 return ENOMEM; 120 } 121 122 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE) 123 { 124 ptd->joinable_sem = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO); 125 if (ptd->joinable_sem == RT_NULL) 126 { 127 if (ptd->attr.stack_base != 0) 128 rt_free(stack); 129 rt_free(ptd); 130 131 return ENOMEM; 132 } 133 } 134 else 135 { 136 ptd->joinable_sem = RT_NULL; 137 } 138 139 /* set parameter */ 140 ptd->thread_entry = start; 141 ptd->thread_parameter = parameter; 142 143 /* initial this pthread to system */ 144 if (rt_thread_init(ptd->tid, name, pthread_entry_stub, ptd, 145 stack, ptd->attr.stack_size, 146 ptd->attr.priority, 5) != RT_EOK) 147 { 148 if (ptd->attr.stack_base == 0) 149 rt_free(stack); 150 if (ptd->joinable_sem != RT_NULL) 151 rt_sem_delete(ptd->joinable_sem); 152 rt_free(ptd); 153 154 return EINVAL; 155 } 156 157 /* set pthread id */ 158 *tid = ptd->tid; 159 160 /* set pthread cleanup function and ptd data */ 161 (*tid)->cleanup = _pthread_cleanup; 162 (*tid)->user_data = (rt_uint32_t)ptd; 163 164 /* start thread */ 165 result = rt_thread_startup(*tid); 166 if (result == RT_EOK) 167 return 0; 168 169 /* start thread failed */ 170 rt_thread_detach(ptd->tid); 171 if (ptd->attr.stack_base == 0) 172 rt_free(stack); 173 if (ptd->joinable_sem != RT_NULL) 174 rt_sem_delete(ptd->joinable_sem); 175 176 rt_free(ptd); 177 178 return EINVAL; 179 } 180 RTM_EXPORT(pthread_create); 181 182 int pthread_detach(pthread_t thread) 183 { 184 _pthread_data_t *ptd; 185 186 ptd = _pthread_get_data(thread); 187 188 if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED) 189 { 190 /* The implementation has detected that the value specified by thread does not refer 191 * to a joinable thread. 192 */ 193 return EINVAL; 194 } 195 196 if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE) 197 { 198 /* delete joinable semaphore */ 199 if (ptd->joinable_sem != RT_NULL) 200 rt_sem_delete(ptd->joinable_sem); 201 /* detach thread object */ 202 rt_thread_detach(ptd->tid); 203 204 /* release thread resource */ 205 if (ptd->attr.stack_base == RT_NULL) 206 { 207 /* release thread allocated stack */ 208 rt_free(ptd->tid->stack_addr); 209 } 210 else 211 { 212 /* clean stack addr pointer */ 213 ptd->tid->stack_addr = RT_NULL; 214 } 215 216 /* 217 * if this thread create the local thread data, 218 * delete it 219 */ 220 if (ptd->tls != RT_NULL) 221 rt_free(ptd->tls); 222 rt_free(ptd->tid); 223 rt_free(ptd); 224 } 225 else 226 { 227 rt_enter_critical(); 228 /* change to detach state */ 229 ptd->attr.detachstate = PTHREAD_CREATE_DETACHED; 230 231 /* detach joinable semaphore */ 232 rt_sem_delete(ptd->joinable_sem); 233 ptd->joinable_sem = RT_NULL; 234 rt_exit_critical(); 235 } 236 237 return 0; 238 } 239 RTM_EXPORT(pthread_detach); 240 241 int pthread_join(pthread_t thread, void **value_ptr) 242 { 243 _pthread_data_t *ptd; 244 rt_err_t result; 245 246 if (thread == rt_thread_self()) 247 { 248 /* join self */ 249 return EDEADLK; 250 } 251 252 ptd = _pthread_get_data(thread); 253 if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED) 254 return EINVAL; /* join on a detached pthread */ 255 256 result = rt_sem_take(ptd->joinable_sem, RT_WAITING_FOREVER); 257 if (result == RT_EOK) 258 { 259 /* get return value */ 260 if (value_ptr != RT_NULL) 261 *value_ptr = ptd->return_value; 262 263 /* release resource */ 264 pthread_detach(thread); 265 } 266 else 267 { 268 return ESRCH; 269 } 270 271 return 0; 272 } 273 RTM_EXPORT(pthread_join); 274 275 void pthread_exit(void *value) 276 { 277 _pthread_data_t *ptd; 278 _pthread_cleanup_t *cleanup; 279 extern _pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX]; 280 281 ptd = _pthread_get_data(rt_thread_self()); 282 283 rt_enter_critical(); 284 /* disable cancel */ 285 ptd->cancelstate = PTHREAD_CANCEL_DISABLE; 286 /* set return value */ 287 ptd->return_value = value; 288 rt_exit_critical(); 289 290 /* invoke pushed cleanup */ 291 while (ptd->cleanup != RT_NULL) 292 { 293 cleanup = ptd->cleanup; 294 ptd->cleanup = cleanup->next; 295 296 cleanup->cleanup_func(cleanup->parameter); 297 /* release this cleanup function */ 298 rt_free(cleanup); 299 } 300 301 /* destruct thread local key */ 302 if (ptd->tls != RT_NULL) 303 { 304 void *data; 305 rt_uint32_t index; 306 307 for (index = 0; index < PTHREAD_KEY_MAX; index ++) 308 { 309 if (_thread_keys[index].is_used) 310 { 311 data = ptd->tls[index]; 312 if (data) 313 _thread_keys[index].destructor(data); 314 } 315 } 316 317 /* release tls area */ 318 rt_free(ptd->tls); 319 ptd->tls = RT_NULL; 320 } 321 322 if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE) 323 { 324 /* release the joinable pthread */ 325 rt_sem_release(ptd->joinable_sem); 326 } 327 328 /* detach thread */ 329 rt_thread_detach(ptd->tid); 330 /* reschedule thread */ 331 rt_schedule(); 332 } 333 RTM_EXPORT(pthread_exit); 334 335 int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) 336 { 337 RT_ASSERT(once_control != RT_NULL); 338 RT_ASSERT(init_routine != RT_NULL); 339 340 rt_enter_critical(); 341 if (!(*once_control)) 342 { 343 /* call routine once */ 344 *once_control = 1; 345 rt_exit_critical(); 346 347 init_routine(); 348 } 349 rt_exit_critical(); 350 351 return 0; 352 } 353 RTM_EXPORT(pthread_once); 354 355 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) 356 { 357 return EOPNOTSUPP; 358 } 359 RTM_EXPORT(pthread_atfork); 360 361 int pthread_kill(pthread_t thread, int sig) 362 { 363 #ifdef RT_USING_SIGNALS 364 return rt_thread_kill(thread, sig); 365 #else 366 return ENOSYS; 367 #endif 368 } 369 RTM_EXPORT(pthread_kill); 370 371 #ifdef RT_USING_SIGNALS 372 int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) 373 { 374 return sigprocmask(how, set, oset); 375 } 376 #endif 377 378 void pthread_cleanup_pop(int execute) 379 { 380 _pthread_data_t *ptd; 381 _pthread_cleanup_t *cleanup; 382 383 /* get posix thread data */ 384 ptd = _pthread_get_data(rt_thread_self()); 385 RT_ASSERT(ptd != RT_NULL); 386 387 if (execute) 388 { 389 rt_enter_critical(); 390 cleanup = ptd->cleanup; 391 if (cleanup) 392 ptd->cleanup = cleanup->next; 393 rt_exit_critical(); 394 395 if (cleanup) 396 { 397 cleanup->cleanup_func(cleanup->parameter); 398 399 rt_free(cleanup); 400 } 401 } 402 } 403 RTM_EXPORT(pthread_cleanup_pop); 404 405 void pthread_cleanup_push(void (*routine)(void *), void *arg) 406 { 407 _pthread_data_t *ptd; 408 _pthread_cleanup_t *cleanup; 409 410 /* get posix thread data */ 411 ptd = _pthread_get_data(rt_thread_self()); 412 RT_ASSERT(ptd != RT_NULL); 413 414 cleanup = (_pthread_cleanup_t *)rt_malloc(sizeof(_pthread_cleanup_t)); 415 if (cleanup != RT_NULL) 416 { 417 cleanup->cleanup_func = routine; 418 cleanup->parameter = arg; 419 420 rt_enter_critical(); 421 cleanup->next = ptd->cleanup; 422 ptd->cleanup = cleanup; 423 rt_exit_critical(); 424 } 425 } 426 RTM_EXPORT(pthread_cleanup_push); 427 428 /* 429 * According to IEEE Std 1003.1, 2004 Edition , following pthreads 430 * interface support cancellation point: 431 * mq_receive() 432 * mq_send() 433 * mq_timedreceive() 434 * mq_timedsend() 435 * msgrcv() 436 * msgsnd() 437 * msync() 438 * pthread_cond_timedwait() 439 * pthread_cond_wait() 440 * pthread_join() 441 * pthread_testcancel() 442 * sem_timedwait() 443 * sem_wait() 444 * 445 * A cancellation point may also occur when a thread is 446 * executing the following functions: 447 * pthread_rwlock_rdlock() 448 * pthread_rwlock_timedrdlock() 449 * pthread_rwlock_timedwrlock() 450 * pthread_rwlock_wrlock() 451 * 452 * The pthread_cancel(), pthread_setcancelstate(), and pthread_setcanceltype() 453 * functions are defined to be async-cancel safe. 454 */ 455 456 int pthread_setcancelstate(int state, int *oldstate) 457 { 458 _pthread_data_t *ptd; 459 460 /* get posix thread data */ 461 ptd = _pthread_get_data(rt_thread_self()); 462 RT_ASSERT(ptd != RT_NULL); 463 464 if ((state == PTHREAD_CANCEL_ENABLE) || (state == PTHREAD_CANCEL_DISABLE)) 465 { 466 if (oldstate) 467 *oldstate = ptd->cancelstate; 468 ptd->cancelstate = state; 469 470 return 0; 471 } 472 473 return EINVAL; 474 } 475 RTM_EXPORT(pthread_setcancelstate); 476 477 int pthread_setcanceltype(int type, int *oldtype) 478 { 479 _pthread_data_t *ptd; 480 481 /* get posix thread data */ 482 ptd = _pthread_get_data(rt_thread_self()); 483 RT_ASSERT(ptd != RT_NULL); 484 485 if ((type != PTHREAD_CANCEL_DEFERRED) && (type != PTHREAD_CANCEL_ASYNCHRONOUS)) 486 return EINVAL; 487 488 if (oldtype) 489 *oldtype = ptd->canceltype; 490 ptd->canceltype = type; 491 492 return 0; 493 } 494 RTM_EXPORT(pthread_setcanceltype); 495 496 void pthread_testcancel(void) 497 { 498 int cancel = 0; 499 _pthread_data_t *ptd; 500 501 /* get posix thread data */ 502 ptd = _pthread_get_data(rt_thread_self()); 503 RT_ASSERT(ptd != RT_NULL); 504 505 if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE) 506 cancel = ptd->canceled; 507 if (cancel) 508 pthread_exit((void *)PTHREAD_CANCELED); 509 } 510 RTM_EXPORT(pthread_testcancel); 511 512 int pthread_cancel(pthread_t thread) 513 { 514 _pthread_data_t *ptd; 515 516 /* cancel self */ 517 if (thread == rt_thread_self()) 518 return 0; 519 520 /* get posix thread data */ 521 ptd = _pthread_get_data(thread); 522 RT_ASSERT(ptd != RT_NULL); 523 524 /* set canceled */ 525 if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE) 526 { 527 ptd->canceled = 1; 528 if (ptd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) 529 { 530 /* 531 * to detach thread. 532 * this thread will be removed from scheduler list 533 * and because there is a cleanup function in the 534 * thread (pthread_cleanup), it will move to defunct 535 * thread list and wait for handling in idle thread. 536 */ 537 rt_thread_detach(thread); 538 } 539 } 540 541 return 0; 542 } 543 RTM_EXPORT(pthread_cancel); 544