2 * Copyright (c) 1993-1995, 1999-2000 Apple Computer, Inc.
5 * @APPLE_LICENSE_HEADER_START@
7 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
9 * This file contains Original Code and/or Modifications of Original Code
10 * as defined in and that are subject to the Apple Public Source License
11 * Version 2.0 (the 'License'). You may not use this file except in
12 * compliance with the License. Please obtain a copy of the License at
13 * http://www.opensource.apple.com/apsl/ and read it before using this
16 * The Original Code and all software distributed under the License are
17 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
18 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
19 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
21 * Please see the License for the specific language governing rights and
22 * limitations under the License.
24 * @APPLE_LICENSE_HEADER_END@
27 * Thread-based callout module.
32 * Pulled into Mac OS X (microkernel).
38 #include <mach/mach_types.h>
40 #include <kern/sched_prim.h>
41 #include <kern/clock.h>
42 #include <kern/task.h>
43 #include <kern/thread.h>
45 #include <kern/thread_call.h>
46 #include <kern/call_entry.h>
48 #include <kern/timer_call.h>
50 #include <sys/kdebug.h>
52 #define internal_call_num 768
54 #define thread_call_thread_min 4
58 internal_call_storage
[internal_call_num
];
60 decl_simple_lock_data(static,thread_call_lock
)
64 thread_call_delaytimer
;
68 thread_call_xxx_queue
,
69 thread_call_pending_queue
, thread_call_delayed_queue
;
73 call_thread_waitqueue
;
77 activate_thread_awake
;
93 static __inline__ thread_call_t
94 _internal_call_allocate(void);
96 static __inline__
void
97 _internal_call_release(
101 static __inline__
void
102 _pending_call_enqueue(
105 _pending_call_dequeue(
108 _delayed_call_enqueue(
111 _delayed_call_dequeue(
115 static void __inline__
116 _set_delayed_call_timer(
121 _remove_from_pending_queue(
122 thread_call_func_t func
,
123 thread_call_param_t param0
,
126 _remove_from_delayed_queue(
127 thread_call_func_t func
,
128 thread_call_param_t param0
,
132 static __inline__
void
133 _call_thread_wake(void);
137 _activate_thread(void);
141 timer_call_param_t p0
,
142 timer_call_param_t p1
145 #define qe(x) ((queue_entry_t)(x))
146 #define TC(x) ((thread_call_t)(x))
149 * Routine: thread_call_initialize [public]
151 * Description: Initialize this module, called
152 * early during system initialization.
154 * Preconditions: None.
156 * Postconditions: None.
160 thread_call_initialize(void)
165 simple_lock_init(&thread_call_lock
, ETAP_MISC_TIMER
);
168 simple_lock(&thread_call_lock
);
170 queue_init(&thread_call_pending_queue
);
171 queue_init(&thread_call_delayed_queue
);
173 queue_init(&thread_call_xxx_queue
);
175 call
= internal_call_storage
;
176 call
< &internal_call_storage
[internal_call_num
];
179 enqueue_tail(&thread_call_xxx_queue
, qe(call
));
182 timer_call_setup(&thread_call_delaytimer
, _delayed_call_timer
, NULL
);
184 wait_queue_init(&call_thread_waitqueue
, SYNC_POLICY_FIFO
);
185 thread_call_vars
.thread_lowat
= thread_call_thread_min
;
187 activate_thread_awake
= TRUE
;
189 simple_unlock(&thread_call_lock
);
192 kernel_thread_with_priority(_activate_thread
, MAXPRI_KERNEL
- 2);
198 thread_call_func_t func
,
199 thread_call_param_t param0
202 call_entry_setup(call
, func
, param0
);
206 * Routine: _internal_call_allocate [private, inline]
208 * Purpose: Allocate an internal callout entry.
210 * Preconditions: thread_call_lock held.
212 * Postconditions: None.
215 static __inline__ thread_call_t
216 _internal_call_allocate(void)
220 if (queue_empty(&thread_call_xxx_queue
))
221 panic("_internal_call_allocate");
223 call
= TC(dequeue_head(&thread_call_xxx_queue
));
229 * Routine: _internal_call_release [private, inline]
231 * Purpose: Release an internal callout entry which
232 * is no longer pending (or delayed).
234 * Preconditions: thread_call_lock held.
236 * Postconditions: None.
241 _internal_call_release(
245 if ( call
>= internal_call_storage
&&
246 call
< &internal_call_storage
[internal_call_num
] )
247 enqueue_head(&thread_call_xxx_queue
, qe(call
));
251 * Routine: _pending_call_enqueue [private, inline]
253 * Purpose: Place an entry at the end of the
254 * pending queue, to be executed soon.
256 * Preconditions: thread_call_lock held.
258 * Postconditions: None.
263 _pending_call_enqueue(
267 enqueue_tail(&thread_call_pending_queue
, qe(call
));
268 if (++thread_call_vars
.pending_num
> thread_call_vars
.pending_hiwat
)
269 thread_call_vars
.pending_hiwat
= thread_call_vars
.pending_num
;
271 call
->state
= PENDING
;
275 * Routine: _pending_call_dequeue [private, inline]
277 * Purpose: Remove an entry from the pending queue,
278 * effectively unscheduling it.
280 * Preconditions: thread_call_lock held.
282 * Postconditions: None.
287 _pending_call_dequeue(
291 (void)remque(qe(call
));
292 thread_call_vars
.pending_num
--;
298 * Routine: _delayed_call_enqueue [private, inline]
300 * Purpose: Place an entry on the delayed queue,
301 * after existing entries with an earlier
302 * (or identical) deadline.
304 * Preconditions: thread_call_lock held.
306 * Postconditions: None.
311 _delayed_call_enqueue(
315 thread_call_t current
;
317 current
= TC(queue_first(&thread_call_delayed_queue
));
320 if ( queue_end(&thread_call_delayed_queue
, qe(current
)) ||
321 call
->deadline
< current
->deadline
) {
322 current
= TC(queue_prev(qe(current
)));
326 current
= TC(queue_next(qe(current
)));
329 insque(qe(call
), qe(current
));
330 if (++thread_call_vars
.delayed_num
> thread_call_vars
.delayed_hiwat
)
331 thread_call_vars
.delayed_hiwat
= thread_call_vars
.delayed_num
;
333 call
->state
= DELAYED
;
337 * Routine: _delayed_call_dequeue [private, inline]
339 * Purpose: Remove an entry from the delayed queue,
340 * effectively unscheduling it.
342 * Preconditions: thread_call_lock held.
344 * Postconditions: None.
349 _delayed_call_dequeue(
353 (void)remque(qe(call
));
354 thread_call_vars
.delayed_num
--;
360 * Routine: _set_delayed_call_timer [private]
362 * Purpose: Reset the timer so that it
363 * next expires when the entry is due.
365 * Preconditions: thread_call_lock held.
367 * Postconditions: None.
370 static __inline__
void
371 _set_delayed_call_timer(
375 timer_call_enter(&thread_call_delaytimer
, call
->deadline
);
379 * Routine: _remove_from_pending_queue [private]
381 * Purpose: Remove the first (or all) matching
382 * entries from the pending queue,
383 * effectively unscheduling them.
384 * Returns whether any matching entries
387 * Preconditions: thread_call_lock held.
389 * Postconditions: None.
394 _remove_from_pending_queue(
395 thread_call_func_t func
,
396 thread_call_param_t param0
,
400 boolean_t call_removed
= FALSE
;
403 call
= TC(queue_first(&thread_call_pending_queue
));
405 while (!queue_end(&thread_call_pending_queue
, qe(call
))) {
406 if ( call
->func
== func
&&
407 call
->param0
== param0
) {
408 thread_call_t next
= TC(queue_next(qe(call
)));
410 _pending_call_dequeue(call
);
412 _internal_call_release(call
);
421 call
= TC(queue_next(qe(call
)));
424 return (call_removed
);
428 * Routine: _remove_from_delayed_queue [private]
430 * Purpose: Remove the first (or all) matching
431 * entries from the delayed queue,
432 * effectively unscheduling them.
433 * Returns whether any matching entries
436 * Preconditions: thread_call_lock held.
438 * Postconditions: None.
443 _remove_from_delayed_queue(
444 thread_call_func_t func
,
445 thread_call_param_t param0
,
449 boolean_t call_removed
= FALSE
;
452 call
= TC(queue_first(&thread_call_delayed_queue
));
454 while (!queue_end(&thread_call_delayed_queue
, qe(call
))) {
455 if ( call
->func
== func
&&
456 call
->param0
== param0
) {
457 thread_call_t next
= TC(queue_next(qe(call
)));
459 _delayed_call_dequeue(call
);
461 _internal_call_release(call
);
470 call
= TC(queue_next(qe(call
)));
473 return (call_removed
);
477 * Routine: thread_call_func [public]
479 * Purpose: Schedule a function callout.
480 * Guarantees { function, argument }
481 * uniqueness if unique_call is TRUE.
483 * Preconditions: Callable from an interrupt context
486 * Postconditions: None.
491 thread_call_func_t func
,
492 thread_call_param_t param
,
493 boolean_t unique_call
500 simple_lock(&thread_call_lock
);
502 call
= TC(queue_first(&thread_call_pending_queue
));
504 while (unique_call
&& !queue_end(&thread_call_pending_queue
, qe(call
))) {
505 if ( call
->func
== func
&&
506 call
->param0
== param
) {
510 call
= TC(queue_next(qe(call
)));
513 if (!unique_call
|| queue_end(&thread_call_pending_queue
, qe(call
))) {
514 call
= _internal_call_allocate();
516 call
->param0
= param
;
519 _pending_call_enqueue(call
);
521 if (thread_call_vars
.active_num
<= 0)
525 simple_unlock(&thread_call_lock
);
530 * Routine: thread_call_func_delayed [public]
532 * Purpose: Schedule a function callout to
533 * occur at the stated time.
535 * Preconditions: Callable from an interrupt context
538 * Postconditions: None.
542 thread_call_func_delayed(
543 thread_call_func_t func
,
544 thread_call_param_t param
,
552 simple_lock(&thread_call_lock
);
554 call
= _internal_call_allocate();
556 call
->param0
= param
;
558 call
->deadline
= deadline
;
560 _delayed_call_enqueue(call
);
562 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
563 _set_delayed_call_timer(call
);
565 simple_unlock(&thread_call_lock
);
570 * Routine: thread_call_func_cancel [public]
572 * Purpose: Unschedule a function callout.
573 * Removes one (or all)
574 * { function, argument }
575 * instance(s) from either (or both)
576 * the pending and the delayed queue,
577 * in that order. Returns a boolean
578 * indicating whether any calls were
581 * Preconditions: Callable from an interrupt context
584 * Postconditions: None.
588 thread_call_func_cancel(
589 thread_call_func_t func
,
590 thread_call_param_t param
,
598 simple_lock(&thread_call_lock
);
601 result
= _remove_from_pending_queue(func
, param
, cancel_all
) |
602 _remove_from_delayed_queue(func
, param
, cancel_all
);
604 result
= _remove_from_pending_queue(func
, param
, cancel_all
) ||
605 _remove_from_delayed_queue(func
, param
, cancel_all
);
607 simple_unlock(&thread_call_lock
);
614 * Routine: thread_call_allocate [public]
616 * Purpose: Allocate an external callout
619 * Preconditions: None.
621 * Postconditions: None.
625 thread_call_allocate(
626 thread_call_func_t func
,
627 thread_call_param_t param0
630 thread_call_t call
= (void *)kalloc(sizeof (thread_call_data_t
));
633 call
->param0
= param0
;
640 * Routine: thread_call_free [public]
642 * Purpose: Free an external callout
645 * Preconditions: None.
647 * Postconditions: None.
658 simple_lock(&thread_call_lock
);
660 if (call
->state
!= IDLE
) {
661 simple_unlock(&thread_call_lock
);
667 simple_unlock(&thread_call_lock
);
670 kfree((vm_offset_t
)call
, sizeof (thread_call_data_t
));
676 * Routine: thread_call_enter [public]
678 * Purpose: Schedule an external callout
679 * entry to occur "soon". Returns a
680 * boolean indicating whether the call
681 * had been already scheduled.
683 * Preconditions: Callable from an interrupt context
686 * Postconditions: None.
694 boolean_t result
= TRUE
;
698 simple_lock(&thread_call_lock
);
700 if (call
->state
!= PENDING
) {
701 if (call
->state
== DELAYED
)
702 _delayed_call_dequeue(call
);
703 else if (call
->state
== IDLE
)
706 _pending_call_enqueue(call
);
708 if (thread_call_vars
.active_num
<= 0)
714 simple_unlock(&thread_call_lock
);
723 thread_call_param_t param1
726 boolean_t result
= TRUE
;
730 simple_lock(&thread_call_lock
);
732 if (call
->state
!= PENDING
) {
733 if (call
->state
== DELAYED
)
734 _delayed_call_dequeue(call
);
735 else if (call
->state
== IDLE
)
738 _pending_call_enqueue(call
);
740 if (thread_call_vars
.active_num
<= 0)
744 call
->param1
= param1
;
746 simple_unlock(&thread_call_lock
);
753 * Routine: thread_call_enter_delayed [public]
755 * Purpose: Schedule an external callout
756 * entry to occur at the stated time.
757 * Returns a boolean indicating whether
758 * the call had been already scheduled.
760 * Preconditions: Callable from an interrupt context
763 * Postconditions: None.
767 thread_call_enter_delayed(
772 boolean_t result
= TRUE
;
776 simple_lock(&thread_call_lock
);
778 if (call
->state
== PENDING
)
779 _pending_call_dequeue(call
);
780 else if (call
->state
== DELAYED
)
781 _delayed_call_dequeue(call
);
782 else if (call
->state
== IDLE
)
786 call
->deadline
= deadline
;
788 _delayed_call_enqueue(call
);
790 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
791 _set_delayed_call_timer(call
);
793 simple_unlock(&thread_call_lock
);
800 thread_call_enter1_delayed(
802 thread_call_param_t param1
,
806 boolean_t result
= TRUE
;
810 simple_lock(&thread_call_lock
);
812 if (call
->state
== PENDING
)
813 _pending_call_dequeue(call
);
814 else if (call
->state
== DELAYED
)
815 _delayed_call_dequeue(call
);
816 else if (call
->state
== IDLE
)
819 call
->param1
= param1
;
820 call
->deadline
= deadline
;
822 _delayed_call_enqueue(call
);
824 if (queue_first(&thread_call_delayed_queue
) == qe(call
))
825 _set_delayed_call_timer(call
);
827 simple_unlock(&thread_call_lock
);
834 * Routine: thread_call_cancel [public]
836 * Purpose: Unschedule a callout entry.
837 * Returns a boolean indicating
838 * whether the call had actually
841 * Preconditions: Callable from an interrupt context
844 * Postconditions: None.
852 boolean_t result
= TRUE
;
856 simple_lock(&thread_call_lock
);
858 if (call
->state
== PENDING
)
859 _pending_call_dequeue(call
);
860 else if (call
->state
== DELAYED
)
861 _delayed_call_dequeue(call
);
865 simple_unlock(&thread_call_lock
);
872 * Routine: thread_call_is_delayed [public]
874 * Purpose: Returns a boolean indicating
875 * whether a call is currently scheduled
876 * to occur at a later time. Optionally
877 * returns the expiration time.
879 * Preconditions: Callable from an interrupt context
882 * Postconditions: None.
886 thread_call_is_delayed(
890 boolean_t result
= FALSE
;
894 simple_lock(&thread_call_lock
);
896 if (call
->state
== DELAYED
) {
897 if (deadline
!= NULL
)
898 *deadline
= call
->deadline
;
902 simple_unlock(&thread_call_lock
);
909 * Routine: _call_thread_wake [private, inline]
911 * Purpose: Wake a callout thread to service
912 * pending callout entries. May wake
913 * the activate thread in order to
914 * create additional callout threads.
916 * Preconditions: thread_call_lock held.
918 * Postconditions: None.
923 _call_thread_wake(void)
925 if (wait_queue_wakeup_one(
926 &call_thread_waitqueue
, &call_thread_waitqueue
,
927 THREAD_AWAKENED
) == KERN_SUCCESS
) {
928 thread_call_vars
.idle_thread_num
--;
930 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
931 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
934 if (!activate_thread_awake
) {
935 thread_wakeup_one(&activate_thread_awake
);
936 activate_thread_awake
= TRUE
;
941 * Routine: call_thread_block [private]
943 * Purpose: Hook via thread dispatch on
944 * the occasion of a callout blocking.
946 * Preconditions: splsched.
948 * Postconditions: None.
952 call_thread_block(void)
954 simple_lock(&thread_call_lock
);
956 if (--thread_call_vars
.active_num
< thread_call_vars
.active_lowat
)
957 thread_call_vars
.active_lowat
= thread_call_vars
.active_num
;
959 if ( thread_call_vars
.active_num
<= 0 &&
960 thread_call_vars
.pending_num
> 0 )
963 simple_unlock(&thread_call_lock
);
967 * Routine: call_thread_unblock [private]
969 * Purpose: Hook via thread wakeup on
970 * the occasion of a callout unblocking.
972 * Preconditions: splsched.
974 * Postconditions: None.
978 call_thread_unblock(void)
980 simple_lock(&thread_call_lock
);
982 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
983 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
985 simple_unlock(&thread_call_lock
);
989 * Routine: _call_thread [private]
991 * Purpose: Executed by a callout thread.
993 * Preconditions: None.
995 * Postconditions: None.
1000 _call_thread_continue(void)
1002 thread_t self
= current_thread();
1005 simple_lock(&thread_call_lock
);
1007 self
->active_callout
= TRUE
;
1009 while (thread_call_vars
.pending_num
> 0) {
1011 thread_call_func_t func
;
1012 thread_call_param_t param0
, param1
;
1014 call
= TC(dequeue_head(&thread_call_pending_queue
));
1015 thread_call_vars
.pending_num
--;
1018 param0
= call
->param0
;
1019 param1
= call
->param1
;
1023 _internal_call_release(call
);
1025 simple_unlock(&thread_call_lock
);
1028 KERNEL_DEBUG_CONSTANT(
1029 MACHDBG_CODE(DBG_MACH_SCHED
,MACH_CALLOUT
) | DBG_FUNC_NONE
,
1030 (int)func
, (int)param0
, (int)param1
, 0, 0);
1032 (*func
)(param0
, param1
);
1034 (void)thread_funnel_set(self
->funnel_lock
, FALSE
);
1037 simple_lock(&thread_call_lock
);
1040 self
->active_callout
= FALSE
;
1042 if (--thread_call_vars
.active_num
< thread_call_vars
.active_lowat
)
1043 thread_call_vars
.active_lowat
= thread_call_vars
.active_num
;
1045 if (thread_call_vars
.idle_thread_num
< thread_call_vars
.thread_lowat
) {
1046 thread_call_vars
.idle_thread_num
++;
1048 wait_queue_assert_wait(
1049 &call_thread_waitqueue
, &call_thread_waitqueue
,
1050 THREAD_INTERRUPTIBLE
);
1052 simple_unlock(&thread_call_lock
);
1055 thread_block(_call_thread_continue
);
1059 thread_call_vars
.thread_num
--;
1061 simple_unlock(&thread_call_lock
);
1064 (void) thread_terminate(self
->top_act
);
1072 _call_thread_continue();
1077 * Routine: _activate_thread [private]
1079 * Purpose: Executed by the activate thread.
1081 * Preconditions: None.
1083 * Postconditions: Never terminates.
1088 _activate_thread_continue(void)
1091 simple_lock(&thread_call_lock
);
1093 while ( thread_call_vars
.active_num
<= 0 &&
1094 thread_call_vars
.pending_num
> 0 ) {
1096 if (++thread_call_vars
.active_num
> thread_call_vars
.active_hiwat
)
1097 thread_call_vars
.active_hiwat
= thread_call_vars
.active_num
;
1099 if (++thread_call_vars
.thread_num
> thread_call_vars
.thread_hiwat
)
1100 thread_call_vars
.thread_hiwat
= thread_call_vars
.thread_num
;
1102 simple_unlock(&thread_call_lock
);
1105 kernel_thread_with_priority(_call_thread
, MAXPRI_KERNEL
- 1);
1108 simple_lock(&thread_call_lock
);
1111 assert_wait(&activate_thread_awake
, THREAD_INTERRUPTIBLE
);
1112 activate_thread_awake
= FALSE
;
1114 simple_unlock(&thread_call_lock
);
1117 thread_block(_activate_thread_continue
);
1123 _activate_thread(void)
1125 thread_t self
= current_thread();
1127 self
->vm_privilege
= TRUE
;
1128 vm_page_free_reserve(2); /* XXX */
1130 _activate_thread_continue();
1136 _delayed_call_timer(
1137 timer_call_param_t p0
,
1138 timer_call_param_t p1
1143 boolean_t new_pending
= FALSE
;
1147 simple_lock(&thread_call_lock
);
1149 clock_get_uptime(×tamp
);
1151 call
= TC(queue_first(&thread_call_delayed_queue
));
1153 while (!queue_end(&thread_call_delayed_queue
, qe(call
))) {
1154 if (call
->deadline
<= timestamp
) {
1155 _delayed_call_dequeue(call
);
1157 _pending_call_enqueue(call
);
1163 call
= TC(queue_first(&thread_call_delayed_queue
));
1166 if (!queue_end(&thread_call_delayed_queue
, qe(call
)))
1167 _set_delayed_call_timer(call
);
1169 if (new_pending
&& thread_call_vars
.active_num
<= 0)
1170 _call_thread_wake();
1172 simple_unlock(&thread_call_lock
);