1 /*
2 * Copyright (c) 2025 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include "condition.h"
30 #include "random.h"
31
32 void
fibers_condition_wakeup_one(fibers_condition_t * cond)33 fibers_condition_wakeup_one(fibers_condition_t *cond)
34 {
35 fibers_condition_wakeup_some(cond, 1, NULL, NULL);
36 }
37
38 int
fibers_condition_wakeup_some(fibers_condition_t * cond,int num_fibers,void (* callback)(void *,fiber_t),void * arg)39 fibers_condition_wakeup_some(fibers_condition_t *cond, int num_fibers, void (*callback)(void *, fiber_t), void *arg)
40 {
41 fibers_may_yield_internal_with_reason(
42 FIBERS_YIELD_REASON_WAKEUP |
43 FIBERS_YIELD_REASON_ORDER_PRE);
44
45 if (num_fibers < 0 || num_fibers > cond->wait_queue.count) {
46 num_fibers = cond->wait_queue.count;
47 }
48
49 unsigned int num_awakened = 0;
50 while (num_fibers > 0) {
51 fiber_t target = fibers_queue_pop(&cond->wait_queue, random_below(cond->wait_queue.count));
52 FIBERS_ASSERT(target->state == FIBER_WAIT, "fibers_condition_wakeup_some: waking up %d that is not FIBER_WAIT", target->id);
53 FIBERS_LOG(FIBERS_LOG_DEBUG, "waking up %d waiting on condition %p", target->id, cond);
54 if (callback) {
55 callback(arg, target);
56 }
57 fibers_queue_push(&fibers_run_queue, target);
58 --num_fibers;
59 num_awakened++;
60 }
61
62 fibers_may_yield_internal_with_reason(
63 FIBERS_YIELD_REASON_WAKEUP |
64 FIBERS_YIELD_REASON_ORDER_POST |
65 FIBERS_YIELD_REASON_ERROR_IF(num_awakened == 0));
66
67 return num_fibers;
68 }
69
70 void
fibers_condition_wait(fibers_condition_t * cond)71 fibers_condition_wait(fibers_condition_t *cond)
72 {
73 FIBERS_LOG(FIBERS_LOG_DEBUG, "waiting on condition %p", cond);
74 FIBERS_ASSERT(fibers_current->may_yield_disabled == 0, "fibers_condition_wait: waiting on a condition with fibers_current->may_yield_disabled not 0");
75 //FIBERS_ASSERT(fibers_queue_count(&cond->wait_queue) == cond->wait_queue.count, "fibers_queue_count");
76
77 fibers_queue_push(&cond->wait_queue, fibers_current);
78 fibers_choose_next(FIBER_WAIT);
79 }
80
81 void
fibers_condition_destroy(fibers_condition_t * cond)82 fibers_condition_destroy(fibers_condition_t *cond)
83 {
84 FIBERS_LOG(FIBERS_LOG_DEBUG, "destroy condition %p", cond);
85 FIBERS_ASSERT(cond->wait_queue.count == 0, "fibers_mutex_destroy: tried to destroy condition with non empty wait queue");
86 }
87
88 fiber_t
fibers_condition_identify(fibers_condition_t * cond)89 fibers_condition_identify(fibers_condition_t *cond)
90 {
91 FIBERS_LOG(FIBERS_LOG_DEBUG, "identify from wait queue of %d fibers", cond->wait_queue.count);
92 //FIBERS_ASSERT(fibers_queue_count(&cond->wait_queue) == cond->wait_queue.count, "fibers_queue_count");
93 if (cond->wait_queue.count == 0) {
94 return NULL;
95 }
96 size_t index = random_below(cond->wait_queue.count);
97 fiber_t iter = cond->wait_queue.top;
98 while (iter != NULL) {
99 if (index == 0) {
100 return iter;
101 }
102 index--;
103 iter = iter->next;
104 }
105 FIBERS_ASSERT(false, "fibers_condition_identify: unreachable");
106 return NULL;
107 }
108
109 bool
fibers_condition_wakeup_identified(fibers_condition_t * cond,fiber_t target)110 fibers_condition_wakeup_identified(fibers_condition_t *cond, fiber_t target)
111 {
112 fibers_may_yield_internal_with_reason(
113 FIBERS_YIELD_REASON_WAKEUP |
114 FIBERS_YIELD_REASON_ORDER_PRE);
115
116 //FIBERS_ASSERT(fibers_queue_count(&cond->wait_queue) == cond->wait_queue.count, "fibers_queue_count");
117 //FIBERS_ASSERT(fibers_queue_remove(&cond->wait_queue, target), "fibers_condition_wakeup_identified: target not in wait queue");
118 if (!fibers_queue_remove(&cond->wait_queue, target)) {
119 return false;
120 }
121
122 FIBERS_ASSERT(target->state == FIBER_WAIT, "fibers_condition_wakeup_identified: waking up %d that is not FIBER_WAIT", target->id);
123 FIBERS_LOG(FIBERS_LOG_DEBUG, "waking up %d waiting on condition %p", target->id, cond);
124 fibers_queue_push(&fibers_run_queue, target);
125
126 fibers_may_yield_internal_with_reason(
127 FIBERS_YIELD_REASON_WAKEUP |
128 FIBERS_YIELD_REASON_ORDER_POST);
129
130 return true;
131 }
132