xref: /xnu-12377.1.9/tests/unit/mocks/fibers/condition.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
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