Changeset 1871118 in mainline
- Timestamp:
- 2023-02-10T22:59:11Z (22 months ago)
- Branches:
- master, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 11d2c983
- Parents:
- daadfa6
- git-author:
- Jiří Zárevúcky <zarevucky.jiri@…> (2023-02-10 22:53:12)
- git-committer:
- Jiří Zárevúcky <zarevucky.jiri@…> (2023-02-10 22:59:11)
- Location:
- kernel
- Files:
-
- 20 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/include/lib/refcount.h
rdaadfa6 r1871118 76 76 } 77 77 78 /** 79 * Try to upgrade a weak reference. 80 * Naturally, this is contingent on another form of synchronization being used 81 * to ensure that the object continues to exist while the weak reference is in 82 * use. 83 */ 84 static inline bool refcount_try_up(atomic_refcount_t *rc) 85 { 86 int cnt = atomic_load_explicit(&rc->__cnt, memory_order_relaxed); 87 88 while (cnt >= 0) { 89 if (atomic_compare_exchange_weak_explicit(&rc->__cnt, &cnt, cnt + 1, 90 memory_order_relaxed, memory_order_relaxed)) { 91 return true; 92 } 93 } 94 95 return false; 96 } 97 78 98 static inline bool refcount_unique(atomic_refcount_t *rc) 79 99 { -
kernel/generic/include/proc/thread.h
rdaadfa6 r1871118 70 70 /** Thread structure. There is one per thread. */ 71 71 typedef struct thread { 72 atomic_refcount_t refcount; 73 72 74 link_t rq_link; /**< Run queue link. */ 73 75 link_t wq_link; /**< Wait queue link. */ … … 77 79 odlink_t lthreads; 78 80 81 /** 82 * If true, the thread is terminating. 83 * It will not go to sleep in interruptible synchronization functions 84 * and will call thread_exit() before returning to userspace. 85 */ 86 volatile bool interrupted; 87 88 /** Waitq for thread_join_timeout(). */ 89 waitq_t join_wq; 90 79 91 /** Lock protecting thread structure. 80 92 * 81 * Protects the whole thread structure except list linksabove.93 * Protects the whole thread structure except fields listed above. 82 94 */ 83 95 IRQ_SPINLOCK_DECLARE(lock); … … 133 145 */ 134 146 bool in_copy_to_uspace; 135 136 /**137 * If true, the thread will not go to sleep at all and will call138 * thread_exit() before returning to userspace.139 */140 bool interrupted;141 142 /** If true, thread_join_timeout() cannot be used on this thread. */143 bool detached;144 /** Waitq for thread_join_timeout(). */145 waitq_t join_wq;146 /** Link used in the joiner_head list. */147 link_t joiner_link;148 147 149 148 #ifdef CONFIG_FPU … … 219 218 extern void thread_interrupt(thread_t *, bool); 220 219 220 static inline thread_t *thread_ref(thread_t *thread) 221 { 222 refcount_up(&thread->refcount); 223 return thread; 224 } 225 226 static inline thread_t *thread_try_ref(thread_t *thread) 227 { 228 if (refcount_try_up(&thread->refcount)) 229 return thread; 230 else 231 return NULL; 232 } 233 234 extern void thread_put(thread_t *); 235 221 236 #ifndef thread_create_arch 222 237 extern errno_t thread_create_arch(thread_t *, thread_flags_t); … … 236 251 extern errno_t thread_join(thread_t *); 237 252 extern errno_t thread_join_timeout(thread_t *, uint32_t, unsigned int); 238 extern void thread_detach(thread_t *);239 253 240 254 extern void thread_print_list(bool); 241 extern void thread_destroy(thread_t *, bool);242 255 extern thread_t *thread_find_by_id(thread_id_t); 243 256 extern size_t thread_count(void); … … 245 258 extern thread_t *thread_next(thread_t *); 246 259 extern void thread_update_accounting(bool); 247 extern bool thread_exists(thread_t *);260 extern thread_t *thread_try_get(thread_t *); 248 261 249 262 extern void thread_migration_disable(void); -
kernel/generic/include/synch/waitq.h
rdaadfa6 r1871118 43 43 typedef enum { 44 44 WAKEUP_FIRST = 0, 45 WAKEUP_ALL 45 WAKEUP_ALL, 46 WAKEUP_CLOSE, 46 47 } wakeup_mode_t; 47 48 -
kernel/generic/src/console/cmd.c
rdaadfa6 r1871118 993 993 printf("cpu%u: ", i); 994 994 thread_wire(thread, &cpus[i]); 995 thread_ready(thread );995 thread_ready(thread_ref(thread)); 996 996 thread_join(thread); 997 thread_ detach(thread);997 thread_put(thread); 998 998 } else 999 999 printf("Unable to create thread for cpu%u\n", i); -
kernel/generic/src/ipc/kbox.c
rdaadfa6 r1871118 90 90 LOG("Join kb.thread."); 91 91 thread_join(TASK->kb.thread); 92 thread_ detach(TASK->kb.thread);92 thread_put(TASK->kb.thread); 93 93 LOG("...join done."); 94 94 TASK->kb.thread = NULL; … … 136 136 /* Only detach kbox thread unless already terminating. */ 137 137 if (TASK->kb.finished == false) { 138 /* Detachkbox thread so it gets freed from memory. */139 thread_ detach(TASK->kb.thread);138 /* Release kbox thread so it gets freed from memory. */ 139 thread_put(TASK->kb.thread); 140 140 TASK->kb.thread = NULL; 141 141 } … … 247 247 } 248 248 249 task->kb.thread = kb_thread;249 task->kb.thread = thread_ref(kb_thread); 250 250 thread_ready(kb_thread); 251 251 } -
kernel/generic/src/main/kinit.c
rdaadfa6 r1871118 102 102 thread_t *thread; 103 103 104 /*105 * Detach kinit as nobody will call thread_join_timeout() on it.106 */107 thread_detach(THREAD);108 109 104 interrupts_disable(); 110 105 … … 125 120 126 121 thread_wire(thread, &cpus[0]); 127 thread_ready(thread );122 thread_ready(thread_ref(thread)); 128 123 thread_join(thread); 129 thread_ detach(thread);124 thread_put(thread); 130 125 131 126 /* -
kernel/generic/src/main/uinit.c
rdaadfa6 r1871118 56 56 void uinit(void *arg) 57 57 { 58 /*59 * So far, we don't have a use for joining userspace threads so we60 * immediately detach each uinit thread. If joining of userspace threads61 * is required, some userspace API based on the kernel mechanism will62 * have to be implemented. Moreover, garbage collecting of threads that63 * didn't detach themselves and nobody else joined them will have to be64 * deployed for the event of forceful task termination.65 */66 thread_detach(THREAD);67 68 58 #ifdef CONFIG_UDEBUG 69 59 udebug_stoppable_end(); -
kernel/generic/src/proc/program.c
rdaadfa6 r1871118 204 204 { 205 205 thread_ready(prg->main_thread); 206 prg->main_thread = NULL; 206 207 } 207 208 -
kernel/generic/src/proc/scheduler.c
rdaadfa6 r1871118 389 389 void scheduler_separated_stack(void) 390 390 { 391 DEADLOCK_PROBE_INIT(p_joinwq);392 391 task_t *old_task = TASK; 393 392 as_t *old_as = AS; … … 419 418 420 419 case Exiting: 421 repeat: 422 if (THREAD->detached) { 423 thread_destroy(THREAD, false); 424 } else { 425 /* 426 * The thread structure is kept allocated until 427 * somebody calls thread_detach() on it. 428 */ 429 if (!irq_spinlock_trylock(&THREAD->join_wq.lock)) { 430 /* 431 * Avoid deadlock. 432 */ 433 irq_spinlock_unlock(&THREAD->lock, false); 434 delay(HZ); 435 irq_spinlock_lock(&THREAD->lock, false); 436 DEADLOCK_PROBE(p_joinwq, 437 DEADLOCK_THRESHOLD); 438 goto repeat; 439 } 440 _waitq_wakeup_unsafe(&THREAD->join_wq, 441 WAKEUP_FIRST); 442 irq_spinlock_unlock(&THREAD->join_wq.lock, false); 443 444 THREAD->state = Lingering; 445 irq_spinlock_unlock(&THREAD->lock, false); 446 } 420 irq_spinlock_unlock(&THREAD->lock, false); 421 waitq_wakeup(&THREAD->join_wq, WAKEUP_CLOSE); 422 423 /* 424 * Release the reference CPU has for the thread. 425 * If there are no other references (e.g. threads calling join), 426 * the thread structure is deallocated. 427 */ 428 thread_put(THREAD); 447 429 break; 448 430 … … 556 538 size_t average; 557 539 size_t rdy; 558 559 /*560 * Detach kcpulb as nobody will call thread_join_timeout() on it.561 */562 thread_detach(THREAD);563 540 564 541 loop: -
kernel/generic/src/proc/task.c
rdaadfa6 r1871118 385 385 irq_spinlock_lock(&tasks_lock, true); 386 386 irq_spinlock_lock(&TASK->lock, false); 387 irq_spinlock_lock(&threads_lock, false);388 387 389 388 /* Set task name */ 390 389 str_cpy(TASK->name, TASK_NAME_BUFLEN, namebuf); 391 390 392 irq_spinlock_unlock(&threads_lock, false);393 391 irq_spinlock_unlock(&TASK->lock, false); 394 392 irq_spinlock_unlock(&tasks_lock, true); … … 529 527 { 530 528 irq_spinlock_lock(&task->lock, false); 531 irq_spinlock_lock(&threads_lock, false);532 529 533 530 /* … … 536 533 537 534 list_foreach(task->threads, th_link, thread_t, thread) { 538 thread_interrupt(thread, false); 535 thread_t *thr = thread_try_ref(thread); 536 if (thr) 537 thread_interrupt(thr, false); 538 539 // If NULL, the thread is already getting destroyed concurrently with this. 539 540 } 540 541 541 irq_spinlock_unlock(&threads_lock, false);542 542 irq_spinlock_unlock(&task->lock, false); 543 543 } -
kernel/generic/src/proc/thread.c
rdaadfa6 r1871118 94 94 * 95 95 * Members are of type thread_t. 96 * 97 * This structure contains weak references. Any reference from it must not leave 98 * threads_lock critical section unless strengthened via thread_try_ref(). 96 99 */ 97 100 odict_t threads; … … 249 252 /** Make thread ready 250 253 * 251 * Switch thread to the ready state. 254 * Switch thread to the ready state. Consumes reference passed by the caller. 252 255 * 253 256 * @param thread Thread to make ready. … … 320 323 return NULL; 321 324 325 refcount_init(&thread->refcount); 326 322 327 if (thread_create_arch(thread, flags) != EOK) { 323 328 slab_free(thread_cache, thread); … … 368 373 369 374 thread->interrupted = false; 370 thread->detached = false;371 375 waitq_initialize(&thread->join_wq); 372 376 … … 399 403 * 400 404 */ 401 void thread_destroy(thread_t *thread, bool irq_res) 402 { 403 assert(irq_spinlock_locked(&thread->lock)); 405 static void thread_destroy(void *obj) 406 { 407 thread_t *thread = (thread_t *) obj; 408 409 irq_spinlock_lock(&thread->lock, true); 404 410 assert((thread->state == Exiting) || (thread->state == Lingering)); 405 411 assert(thread->task); … … 421 427 */ 422 428 list_remove(&thread->th_link); 423 irq_spinlock_unlock(&thread->task->lock, irq_res);429 irq_spinlock_unlock(&thread->task->lock, true); 424 430 425 431 /* … … 430 436 } 431 437 438 void thread_put(thread_t *thread) 439 { 440 if (refcount_down(&thread->refcount)) { 441 thread_destroy(thread); 442 } 443 } 444 432 445 /** Make the thread visible to the system. 433 446 * … … 441 454 void thread_attach(thread_t *thread, task_t *task) 442 455 { 456 ipl_t ipl = interrupts_disable(); 457 443 458 /* 444 459 * Attach to the specified task. 445 460 */ 446 irq_spinlock_lock(&task->lock, true);461 irq_spinlock_lock(&task->lock, false); 447 462 448 463 /* Hold a reference to the task. */ … … 455 470 list_append(&thread->th_link, &task->threads); 456 471 457 irq_spinlock_ pass(&task->lock, &threads_lock);472 irq_spinlock_unlock(&task->lock, false); 458 473 459 474 /* 460 475 * Register this thread in the system-wide dictionary. 461 476 */ 477 irq_spinlock_lock(&threads_lock, false); 462 478 odict_insert(&thread->lthreads, &threads, NULL); 463 irq_spinlock_unlock(&threads_lock, true); 479 irq_spinlock_unlock(&threads_lock, false); 480 481 interrupts_restore(ipl); 464 482 } 465 483 … … 513 531 * blocking call was interruptable. See waitq_sleep_timeout(). 514 532 * 515 * The caller must guarantee the thread object is valid during the entire516 * function, eg by holding the threads_lock lock.517 *518 533 * Interrupted threads automatically exit when returning back to user space. 519 534 * 520 * @param thread A valid thread object. The caller must guarantee it 521 * will remain valid until thread_interrupt() exits. 535 * @param thread A valid thread object. 522 536 */ 523 537 void thread_interrupt(thread_t *thread, bool irq_dis) … … 534 548 if (sleeping) 535 549 waitq_interrupt_sleep(thread); 550 551 thread_put(thread); 536 552 } 537 553 … … 581 597 582 598 /** Wait for another thread to exit. 599 * This function does not destroy the thread. Reference counting handles that. 583 600 * 584 601 * @param thread Thread to join on exit. … … 594 611 return EINVAL; 595 612 596 /*597 * Since thread join can only be called once on an undetached thread,598 * the thread pointer is guaranteed to be still valid.599 */600 601 613 irq_spinlock_lock(&thread->lock, true); 602 assert(!thread->detached);614 state_t state = thread->state; 603 615 irq_spinlock_unlock(&thread->lock, true); 604 616 605 return waitq_sleep_timeout(&thread->join_wq, usec, flags, NULL); 606 607 // FIXME: join should deallocate the thread. 608 // Current code calls detach after join, that's contrary to how 609 // join is used in other threading APIs. 610 } 611 612 /** Detach thread. 613 * 614 * Mark the thread as detached. If the thread is already 615 * in the Lingering state, deallocate its resources. 616 * 617 * @param thread Thread to be detached. 618 * 619 */ 620 void thread_detach(thread_t *thread) 621 { 622 /* 623 * Since the thread is expected not to be already detached, 624 * pointer to it must be still valid. 625 */ 626 irq_spinlock_lock(&thread->lock, true); 627 assert(!thread->detached); 628 629 if (thread->state == Lingering) { 630 /* 631 * Unlock &thread->lock and restore 632 * interrupts in thread_destroy(). 633 */ 634 thread_destroy(thread, true); 635 return; 617 if (state == Exiting) { 618 return EOK; 636 619 } else { 637 thread->detached = true; 638 } 639 640 irq_spinlock_unlock(&thread->lock, true); 620 return waitq_sleep_timeout(&thread->join_wq, usec, 621 SYNCH_FLAGS_NON_BLOCKING, NULL); 622 } 641 623 } 642 624 … … 702 684 thread_t *thread; 703 685 704 /* Messing with thread structures, avoid deadlock*/686 /* Accessing system-wide threads list through thread_first()/thread_next(). */ 705 687 irq_spinlock_lock(&threads_lock, true); 706 688 … … 730 712 } 731 713 732 /** Check whether thread exists. 733 * 734 * Note that threads_lock must be already held and 735 * interrupts must be already disabled. 736 * 737 * @param thread Pointer to thread. 738 * 739 * @return True if thread t is known to the system, false otherwise. 740 * 741 */ 742 bool thread_exists(thread_t *thread) 743 { 744 assert(interrupts_disabled()); 745 assert(irq_spinlock_locked(&threads_lock)); 746 714 static bool thread_exists(thread_t *thread) 715 { 747 716 odlink_t *odlink = odict_find_eq(&threads, thread, NULL); 748 717 return odlink != NULL; 718 } 719 720 /** Check whether the thread exists, and if so, return a reference to it. 721 */ 722 thread_t *thread_try_get(thread_t *thread) 723 { 724 irq_spinlock_lock(&threads_lock, true); 725 726 if (thread_exists(thread)) { 727 /* Try to strengthen the reference. */ 728 thread = thread_try_ref(thread); 729 } else { 730 thread = NULL; 731 } 732 733 irq_spinlock_unlock(&threads_lock, true); 734 735 return thread; 749 736 } 750 737 … … 777 764 * interrupts must be disabled. 778 765 * 766 * The returned reference is weak. 767 * If the caller needs to keep it, thread_try_ref() must be used to upgrade 768 * to a strong reference _before_ threads_lock is released. 769 * 779 770 * @param id Thread ID. 780 771 * … … 854 845 { 855 846 irq_spinlock_lock(&threads_lock, true); 856 857 thread_t *thread = thread_find_by_id(thread_id); 847 thread_t *thread = thread_try_ref(thread_find_by_id(thread_id)); 848 irq_spinlock_unlock(&threads_lock, true); 849 858 850 if (thread == NULL) { 859 851 printf("No such thread.\n"); 860 irq_spinlock_unlock(&threads_lock, true);861 852 return; 862 853 } 863 864 irq_spinlock_lock(&thread->lock, false);865 854 866 855 /* … … 876 865 */ 877 866 867 irq_spinlock_lock(&thread->lock, true); 868 878 869 bool sleeping = false; 879 870 istate_t *istate = thread->udebug.uspace_state; … … 886 877 printf("Thread interrupt state not available.\n"); 887 878 888 irq_spinlock_unlock(&thread->lock, false);879 irq_spinlock_unlock(&thread->lock, true); 889 880 890 881 if (sleeping) 891 882 waitq_interrupt_sleep(thread); 892 883 893 irq_spinlock_unlock(&threads_lock, true);884 thread_put(thread); 894 885 } 895 886 -
kernel/generic/src/synch/waitq.c
rdaadfa6 r1871118 513 513 loop: 514 514 if (list_empty(&wq->sleepers)) { 515 if (mode != WAKEUP_ALL) { 515 if (mode == WAKEUP_CLOSE) { 516 // FIXME: this can technically fail if we get two billion sleeps after the wakeup call. 517 wq->missed_wakeups = INT_MAX; 518 } else if (mode != WAKEUP_ALL) { 516 519 wq->missed_wakeups++; 517 520 } -
kernel/generic/src/sysinfo/stats.c
rdaadfa6 r1871118 332 332 bool dry_run, void *data) 333 333 { 334 /* Messing with threads structures , avoid deadlock*/334 /* Messing with threads structures */ 335 335 irq_spinlock_lock(&threads_lock, true); 336 336 … … 597 597 return ret; 598 598 599 /* Messing with threads structures , avoid deadlock*/599 /* Messing with threads structures */ 600 600 irq_spinlock_lock(&threads_lock, true); 601 601 … … 627 627 ret.data.size = sizeof(stats_thread_t); 628 628 629 /* Hand-over-hand locking */ 630 irq_spinlock_exchange(&threads_lock, &thread->lock); 631 629 /* 630 * Replaced hand-over-hand locking with regular nested sections 631 * to avoid weak reference leak issues. 632 */ 633 irq_spinlock_lock(&thread->lock, false); 632 634 produce_stats_thread(thread, stats_thread); 633 634 irq_spinlock_unlock(&thread->lock, true); 635 irq_spinlock_unlock(&thread->lock, false); 636 637 irq_spinlock_unlock(&threads_lock, true); 635 638 } 636 639 … … 847 850 void kload(void *arg) 848 851 { 849 thread_detach(THREAD);850 851 852 while (true) { 852 853 size_t ready = atomic_load(&nrdy); -
kernel/generic/src/udebug/udebug_ops.c
rdaadfa6 r1871118 83 83 mutex_lock(&TASK->udebug.lock); 84 84 85 /* thread_exists() must be called with threads_lock held */ 86 irq_spinlock_lock(&threads_lock, true); 87 88 if (!thread_exists(thread)) { 89 irq_spinlock_unlock(&threads_lock, true); 85 thread = thread_try_get(thread); 86 87 if (!thread) { 90 88 mutex_unlock(&TASK->udebug.lock); 91 89 return ENOENT; 92 90 } 93 91 94 /* thread->lock is enough to ensure the thread's existence */ 95 irq_spinlock_exchange(&threads_lock, &thread->lock); 92 irq_spinlock_lock(&thread->lock, true); 96 93 97 94 /* Verify that 'thread' is a userspace thread. */ … … 111 108 } 112 109 113 /*114 * Since the thread has active == true, TASK->udebug.lock115 * is enough to ensure its existence and that active remains116 * true.117 *118 */119 irq_spinlock_unlock(&thread->lock, true);120 121 /* Only mutex TASK->udebug.lock left. */122 123 110 /* Now verify that the thread belongs to the current task. */ 124 111 if (thread->task != TASK) { 125 112 /* No such thread belonging this task */ 113 irq_spinlock_unlock(&thread->lock, true); 126 114 mutex_unlock(&TASK->udebug.lock); 127 115 return ENOENT; 128 116 } 117 118 irq_spinlock_unlock(&thread->lock, true); 119 120 /* Only mutex TASK->udebug.lock left. */ 129 121 130 122 /* … … 153 145 { 154 146 mutex_unlock(&thread->udebug.lock); 147 148 /* Drop reference from _thread_op_begin() */ 149 thread_put(thread); 155 150 } 156 151 -
kernel/test/mm/falloc2.c
rdaadfa6 r1871118 59 59 return; 60 60 } 61 62 thread_detach(THREAD);63 61 64 62 for (unsigned int run = 0; run < THREAD_RUNS; run++) { -
kernel/test/mm/slab1.c
rdaadfa6 r1871118 128 128 int i, j; 129 129 130 thread_detach(THREAD);131 132 130 TPRINTF("Starting thread #%" PRIu64 "...\n", THREAD->tid); 133 131 -
kernel/test/mm/slab2.c
rdaadfa6 r1871118 137 137 void *data = NULL, *new; 138 138 139 thread_detach(THREAD);140 141 139 mutex_lock(&starter_mutex); 142 140 condvar_wait(&thread_starter, &starter_mutex); -
kernel/test/synch/semaphore1.c
rdaadfa6 r1871118 46 46 static void producer(void *arg) 47 47 { 48 thread_detach(THREAD);49 50 48 waitq_sleep(&can_start); 51 49 … … 58 56 static void consumer(void *arg) 59 57 { 60 thread_detach(THREAD);61 62 58 waitq_sleep(&can_start); 63 59 -
kernel/test/synch/semaphore2.c
rdaadfa6 r1871118 61 61 int to; 62 62 63 thread_detach(THREAD);64 65 63 waitq_sleep(&can_start); 66 64 -
kernel/test/thread/thread1.c
rdaadfa6 r1871118 42 42 static void threadtest(void *data) 43 43 { 44 thread_detach(THREAD);45 46 44 while (atomic_load(&finish)) { 47 45 TPRINTF("%" PRIu64 " ", THREAD->tid);
Note:
See TracChangeset
for help on using the changeset viewer.