FreeBSD/Linux Kernel Cross Reference
sys/kern/mach_timer.c
1 /*
2 * Mach Operating System
3 * Copyright (c) 1993,1992 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26 /*
27 * HISTORY
28 * $Log: mach_timer.c,v $
29 * Revision 2.2 93/11/17 17:15:58 dbg
30 * Use thread_deallocate_nowait to remove thread reference as early
31 * as possible. Move syscall_timer_sleep here and make it use
32 * continuations.
33 * [93/07/21 dbg]
34 *
35 * Changed timer_arm_internal to timer_sleep_periodic.
36 * Added BUSY bit to show that timer is either enqueued
37 * or in timeout routine.
38 * [93/07/15 dbg]
39 *
40 * If the expiration time is before the current time, do the
41 * timeout action immediately.
42 * [93/07/14 dbg]
43 *
44 * Return actual wakeup time for timer_sleep.
45 * [93/07/13 dbg]
46 *
47 * Added timer_start_time.
48 * [93/07/01 dbg]
49 *
50 * Changed timer_ipc_lock to timer_lock, timer_lock to
51 * timer_ref_lock. Added mach_timer_thread_reference,
52 * mach_timer_thread_deallocate, so that a timer that is
53 * in use by a thread will remain even if all send rights
54 * vanish.
55 * [93/06/24 dbg]
56 *
57 * Use clock_read to read clock without locking.
58 * [93/06/18 dbg]
59 *
60 * Add interval parameter to timer_arm_internal.
61 * [93/05/12 dbg]
62 *
63 * Increment overrun count in timer instead of queuing repeated
64 * messages to an overflowing expire port.
65 * [93/05/06 dbg]
66 *
67 * Use normal waiting mechanism to wait for timer expiration
68 * instead of thread_suspend.
69 * [93/04/02 dbg]
70 *
71 * Timer_lock does not need splhigh.
72 * [93/02/26 dbg]
73 *
74 * Merged into kernel mainline.
75 *
76 * Make sure time_specs are valid
77 * [92/09/11 savage]
78 * Fixed race with task_lock in task_timers.
79 * [92/07/31 savage]
80 * Added TIMER_THREAD_SUSPEND. Needs fix since there is a possible
81 * priority inversion when the thread associated with timer is
82 * uninterruptible.
83 * [92/07/30 savage]
84 * Changed time_values_t to time_spec_t to account for timer hardware skew.
85 * Changed clock parameter in timer_create to clock_t instead of constant.
86 * [92/07/18 savage]
87 * Added timer_info, and task_timers
88 * [92/07/02 savage]
89 * Added timer_cancel, cleaned up flags & types, and fixed bug with
90 * freeing event and send right resources. Turns out we do need
91 * the reference counting :-)
92 * [92/06/22 savage]
93 * Added timer_sleep and timer_get_evc for performance reasons.
94 * [92/06/17 savage]
95 * Changed all references to timer_t to mach_timer_t and renamed
96 * timer_init to mach_timer_init... timer_t really should be counter_t
97 * [92/06/13 savage]
98 * Exported to user (with significant "borrowing" from thread.c)
99 * Added timer_arm, timer_create, timer_terminate, and timer_deallocate
100 * I'm not sure about the reference counting... do we really need this?
101 * [92/06/11 savage]
102 * Created
103 * [92/05/01 savage]
104 *
105 */
106
107 /*
108 * Timer alarm object, to provide accurate alarm services.
109 * Depends on a clock object.
110 */
111
112 #include <mach/boolean.h>
113 #include <mach/kern_return.h>
114 #include <mach/time_spec.h>
115 #include <mach/notify.h>
116 #include <device/device_types.h>
117 #include <ipc/ipc_port.h>
118 #include <ipc/ipc_space.h>
119 #include <kern/clock.h>
120 #include <kern/eventcount.h>
121 #include <kern/kern_io.h>
122 #include <kern/queue.h>
123 #include <kern/lock.h>
124 #include <kern/mach_param.h>
125 #include <kern/mach_timer.h>
126 #include <kern/sched_prim.h>
127 #include <kern/task.h>
128 #include <kern/zalloc.h>
129 #include <kern/memory.h>
130 #include <machine/machspl.h> /* For def'n of splsched() */
131
132 struct zone *timer_zone;
133 struct mach_timer timer_template;
134
135 /*
136 * Expiration routines
137 */
138 void timer_expire_synch(void *); /* wakeup */
139 void timer_expire_event(void *); /* event signal */
140 void timer_expire_message(void *); /* message/suspend */
141
142 /*
143 * Routine: mach_timer_init
144 * Purpose: Initialize memory, queues and locks for timer package
145 * Conditions: None
146 */
147 void mach_timer_init(void)
148 {
149 timer_zone = zinit(
150 sizeof(mach_timer_data_t),
151 TIMER_MAX * sizeof(mach_timer_data_t),
152 TIMER_CHUNK * sizeof(mach_timer_data_t),
153 FALSE, "timers");
154
155 /* timer_template.tm_chain (later) */
156 timer_template.tm_expire_time.seconds = 0;
157 timer_template.tm_expire_time.nanoseconds = 0;
158 /* timer_template.tm_fcn (later) */
159 /* timer_template.tm_param (later) */
160 timer_template.tm_flags = TELT_UNSET;
161 /* timer_template.tm_clock (later) */
162 timer_template.tm_period.seconds = 0;
163 timer_template.tm_period.nanoseconds = 0;
164
165 /* timer_template.tm_self (later) */
166 /* timer_template.tm_lock (later) */
167 /* timer_template.tm_ref_lock (later) */
168
169 /* one ref for being alive, the other for the caller */
170 timer_template.tm_ref_count = 2;
171
172 timer_template.tm_misc = TM_ACTIVE;
173 timer_template.tm_expire = IP_NULL;
174
175 /* No event allocated yet... */
176 timer_template.tm_event.ev_id = 0;
177 timer_template.tm_event.sanity = ((struct evc *)0);
178
179 timer_template.tm_thread = THREAD_NULL;
180 timer_template.tm_overruns = 0;
181 }
182
183 /*
184 * Routine: mach_timer_deallocate
185 * Purpose: Decrement timer reference count; if it's zero then
186 * free up the kernel resources associated with the
187 * timer.
188 * Conditions: None
189 */
190 void mach_timer_deallocate(
191 mach_timer_t timer)
192 {
193 if (timer == TIMER_NULL)
194 return;
195
196 timer_ref_lock(timer);
197 if (--timer->tm_ref_count > 0) {
198 timer_ref_unlock(timer);
199 return;
200 }
201 timer_ref_unlock(timer);
202
203 assert(!(timer->tm_misc & TM_ACTIVE));
204 assert(timer->tm_flags == TELT_UNSET);
205
206 /* give up kernel memory */
207 zfree(timer_zone, (vm_offset_t) timer);
208 }
209
210
211 /*
212 * Routine: timer_reference
213 * Purpose: Increment timer reference count
214 * Conditions: None
215 */
216 void mach_timer_reference(
217 mach_timer_t timer)
218 {
219 if (timer == TIMER_NULL)
220 return;
221
222 timer_ref_lock(timer);
223 timer->tm_ref_count++;
224 timer_ref_unlock(timer);
225 }
226
227 /*
228 * Routine: convert_port_to_timer
229 * Purpose: Convert from a port to a timer. If the port isn't
230 * valid, or doesn't represent a timer then return
231 * TIMER_NULL. Produces a timer reference.
232 * Conditions: None
233 */
234 mach_timer_t convert_port_to_timer(
235 ipc_port_t port)
236 {
237 mach_timer_t timer = TIMER_NULL;
238
239 if (IP_VALID(port)) {
240 ip_lock(port);
241 if (ip_active(port) &&
242 (ip_kotype(port) == IKOT_TIMER)) {
243 timer = (mach_timer_t) port->ip_kobject;
244 mach_timer_reference(timer);
245 }
246 ip_unlock(port);
247 }
248
249 return timer;
250 }
251
252
253 /*
254 * Routine: convert_timer_to_port
255 * Purpose: Convert from a port to a timer. Consumes a timer
256 * reference.
257 * Conditions: None
258 */
259 ipc_port_t convert_timer_to_port(
260 mach_timer_t timer)
261 {
262 ipc_port_t port;
263
264 timer_lock(timer);
265
266 if (timer->tm_misc & TM_ACTIVE) {
267 assert(timer->tm_self != IP_NULL);
268 port = ipc_port_make_send(timer->tm_self);
269 } else {
270 port = IP_NULL;
271 }
272
273 timer_unlock(timer);
274
275 mach_timer_deallocate(timer);
276 return port;
277 }
278
279 /*
280 * Routine: timer_disarm
281 * Purpose: SAFELY take a timer off its queue, wakeup any
282 * threads waiting on it, and free any expired
283 * send rights.
284 * Conditions: Caller should hold the timer lock.
285 */
286 void timer_disarm(
287 mach_timer_t timer)
288 {
289 if (timer_elt_dequeue(&timer->te)) {
290 /*
291 * Was on queue. If a thread is waiting
292 * for the timer, wake it up.
293 */
294 if (timer->tm_fcn == timer_expire_synch)
295 timer_expire_synch(timer->tm_param);
296
297 /*
298 * Timer is no longer busy.
299 */
300 timer->tm_misc &= ~TM_BUSY;
301 }
302 }
303
304
305 /*
306 * Routine: timer_create
307 * Purpose: Allocate a new timer object in the kernel,
308 * and enable its ipc connection to the world.
309 *
310 * Conditions: None
311 */
312 kern_return_t timer_create(
313 register mach_clock_t clock,
314 register mach_timer_t *timer)
315 {
316 register mach_timer_t new_timer;
317 ipc_port_t kport;
318 ipc_port_t kport_send_once;
319 ipc_port_t nprev;
320
321 if (clock == CLOCK_NULL)
322 return KERN_INVALID_ARGUMENT;
323
324 new_timer = (mach_timer_t) zalloc(timer_zone);
325 if (new_timer == TIMER_NULL)
326 return KERN_RESOURCE_SHORTAGE;
327
328 *new_timer = timer_template;
329
330 /* timeout routine parameter is always timer itself */
331 new_timer->tm_param = new_timer;
332
333 /* which clock device is responsible for this timer */
334 new_timer->tm_clock = clock;
335
336 /* for serialization of access to a particular timer */
337 simple_lock_init(&new_timer->tm_lock);
338 simple_lock_init(&new_timer->tm_ref_lock);
339
340 /* setup IPC structures and let it talk to the world */
341
342 kport = ipc_port_alloc_kernel();
343 if (kport == IP_NULL) {
344 /*
345 * No resources
346 */
347 zfree(timer_zone, (vm_offset_t) new_timer);
348 return KERN_RESOURCE_SHORTAGE;
349 }
350
351 new_timer->tm_self = kport;
352 ipc_kobject_set(kport, (ipc_kobject_t) new_timer, IKOT_TIMER);
353
354 /*
355 * Add no-more-senders notify request, sent
356 * to timer port. Since current make-send count
357 * is 0, sync must be 1 to avoid an immediate
358 * notification. One send right will be created
359 * when the timer port is returned to caller.
360 */
361 kport_send_once = ipc_port_make_sonce(kport);
362 ip_lock(kport);
363 ipc_port_nsrequest(kport, 1, kport_send_once, &nprev);
364 /* unlocks kport */
365 assert(nprev == IP_NULL);
366
367 *timer = new_timer;
368 return KERN_SUCCESS;
369 }
370
371 /*
372 * Internal routine to terminate a timer, shared between
373 * timer_terminate and timer_no_more_senders.
374 *
375 * Timer is locked on entry; will be unlocked (and perhaps
376 * deallocated) on exit.
377 */
378 void timer_terminate_internal(
379 mach_timer_t timer)
380 {
381 ipc_port_t kport;
382 thread_t old_thread;
383
384 /*
385 * Remove timer from queue.
386 */
387 timer_disarm(timer);
388
389 /*
390 * Mark timer inactive, and remove its port.
391 */
392 timer->tm_misc &= ~TM_ACTIVE;
393 kport = timer->tm_self;
394 timer->tm_self = IP_NULL;
395 ipc_kobject_set(timer->tm_self, IKO_NULL, IKOT_NONE);
396
397 /*
398 * Free event resources
399 */
400 if (timer->tm_misc & TM_EVENT_ALLOC)
401 evc_destroy(&timer->tm_event);
402
403 old_thread = timer->tm_thread;
404 timer_unlock(timer);
405
406 /*
407 * Release reference and send right for expire port.
408 */
409 if (IP_VALID(timer->tm_expire))
410 ipc_port_release_send(timer->tm_expire);
411
412 /*
413 * Deallocate the timer`s thread reference
414 * if there was one.
415 */
416 if (old_thread != THREAD_NULL)
417 thread_deallocate(old_thread);
418
419 /*
420 * Deallocate the timer port, and the timer structure
421 * itself.
422 */
423 ipc_port_dealloc_kernel(kport);
424 mach_timer_deallocate(timer);
425 }
426
427
428 /*
429 * Routine: timer_terminate
430 * Purpose: Destroy a timer. If the timer is already being killed
431 * then we will return KERN_FAILURE
432 * Conditions: None
433 */
434 kern_return_t timer_terminate(
435 register mach_timer_t timer)
436 {
437 if (timer == TIMER_NULL)
438 return KERN_INVALID_ARGUMENT;
439
440 timer_lock(timer);
441
442 /*
443 * Is it already being killed? If not then mark it as such.
444 */
445 if (!(timer->tm_misc & TM_ACTIVE)) {
446 timer_unlock(timer);
447 return KERN_FAILURE;
448 }
449
450 /*
451 * Destroy the timer (code is shared with no-more-senders)
452 */
453 timer_terminate_internal(timer);
454
455 return KERN_SUCCESS;
456 }
457
458 /*
459 * Routine: timer_thread_reference
460 * Purpose:
461 * Create a internal reference to a timer from a thread,
462 * to use the timer as the deadline or wakeup timer for
463 * the thread. This reference must keep the timer alive
464 * just as a send-once reference would; further rights
465 * to the timer port can be obtained from the thread.
466 *
467 * Implementation:
468 * Make a new send right to the timer port.
469 */
470 boolean_t
471 mach_timer_thread_reference(
472 mach_timer_t timer)
473 {
474 if (timer == TIMER_NULL)
475 return TRUE;
476
477 timer_lock(timer);
478 if (!(timer->tm_misc & TM_ACTIVE)) {
479 timer_unlock(timer);
480 return FALSE; /* timer died suddenly */
481 }
482
483 /*
484 * Make a new send right to the timer port.
485 */
486 (void) ipc_port_make_send(timer->tm_self);
487 timer_unlock(timer);
488
489 return TRUE;
490 }
491
492 /*
493 * Routine: timer_thread_deallocate
494 * Purpose:
495 * Remove an internal reference to a timer from a thread.
496 * If the timer now has no more send rights to its kernel
497 * port, terminate the timer.
498 *
499 * Implementation:
500 * Release the corresponding send right for the timer
501 * port.
502 */
503 void mach_timer_thread_deallocate(
504 mach_timer_t timer)
505 {
506 ipc_port_t kport;
507
508 if (timer == TIMER_NULL)
509 return;
510
511 timer_lock(timer);
512 assert(timer->tm_misc & TM_ACTIVE);
513
514 /*
515 * Release a send right to the timer port.
516 * Since this may send a no-more-senders
517 * message, we must unlock the timer first.
518 * The port is safe, since it has an extra
519 * reference corresponding to the send right
520 * that we are releasing. ipc_port_release_send
521 * will also release the reference.
522 */
523 kport = timer->tm_self;
524 timer_unlock(timer);
525
526 ipc_port_release_send(kport);
527 }
528
529
530 /*
531 * Routine: timer_get_evc
532 * Purpose: Associate timer with event handle and pass it back to
533 * the user. This is a significant performance
534 * improvement over a message, but it conveys less
535 * information and is only quasi-supported.
536 * Conditions: None.
537 */
538 kern_return_t timer_get_evc(
539 register mach_timer_t timer,
540 natural_t *event)
541 {
542 if (timer == TIMER_NULL)
543 return KERN_INVALID_ARGUMENT;
544
545 timer_lock(timer);
546
547 /*
548 * Do we already have an event?
549 */
550 if (timer->tm_misc & TM_EVENT_ALLOC) {
551 timer_unlock(timer);
552 return KERN_SUCCESS;
553 }
554
555 if (evc_init(&timer->tm_event) != KERN_SUCCESS) {
556 timer_unlock(timer);
557 return KERN_RESOURCE_SHORTAGE;
558 }
559
560 *event = timer->tm_event.ev_id;
561
562 /* Timer now owns event */
563 timer->tm_misc |= TM_EVENT_ALLOC;
564
565 timer_unlock(timer);
566
567 return KERN_SUCCESS;
568 }
569
570
571 /*
572 * Routine: timer_arm
573 * Purpose: Attempts to set a timer and enqueue it in a clock
574 * queue. This call may fail if the timer is already
575 * enqueued, if it is being killed, if the port specified
576 * for timer expiration messages is invalid, if the user
577 * specifies an event timer without allocating an event
578 * first, or if the specified expiration time has
579 * already passed.
580 * Conditions: None
581 */
582 kern_return_t timer_arm(
583 register mach_timer_t timer,
584 time_spec_t expire_time,
585 time_spec_t interval_time,
586 register ipc_port_t expire_port,
587 thread_t thread,
588 register int flags)
589 {
590 if (timer == TIMER_NULL)
591 return KERN_INVALID_ARGUMENT;
592
593 if (!time_spec_valid(expire_time) ||
594 ((flags & TIMER_PERIODIC) && !time_spec_valid(interval_time)))
595 {
596 return KERN_INVALID_VALUE;
597 }
598
599 timer_lock(timer);
600
601 /*
602 * Is the timer currently being killed or is it enqueued?
603 */
604 if (!(timer->tm_misc & TM_ACTIVE) || (timer->tm_misc & TM_BUSY)) {
605 timer_unlock(timer);
606 return KERN_FAILURE;
607 }
608
609 /*
610 * If TIMER_PERIODIC flag is set then the interval_time argument
611 * must be more than the clock resolution.
612 */
613 if (flags & TIMER_PERIODIC) {
614 if (interval_time.seconds == 0 &&
615 interval_time.nanoseconds < timer->tm_clock->resolution)
616 {
617 timer_unlock(timer);
618 return KERN_INVALID_VALUE;
619 }
620 timer->tm_period = interval_time;
621 timer->tm_flags |= TELT_PERIODIC;
622 }
623 else {
624 timer->tm_flags &= ~TELT_PERIODIC;
625 }
626
627 /*
628 * If the user claims that this is an event timer then make
629 * sure he's allocated the event already.
630 */
631 if (flags & TIMER_EVENT) {
632 if (!(timer->tm_misc & TM_EVENT_ALLOC)) {
633 timer_unlock(timer);
634 return KERN_INVALID_ARGUMENT;
635 }
636 timer->tm_event.count = 0;
637 timer->tm_fcn = timer_expire_event;
638 }
639 else {
640 /*
641 * It's a Message timer... now check to see if
642 * the expire port is valid?
643 */
644 if (!IP_VALID(expire_port)) {
645 timer_unlock(timer);
646 return KERN_INVALID_CAPABILITY;
647 }
648 /*
649 * Donate expire port reference and send right to timer
650 */
651 assert(timer->tm_expire == IP_NULL);
652 timer->tm_expire = expire_port;
653 timer->tm_fcn = timer_expire_message;
654 }
655
656 /*
657 * Save the thread to be suspended
658 */
659 if (flags & TIMER_THREAD_SUSPEND) {
660 if (thread == THREAD_NULL) {
661 timer->tm_expire = IP_NULL; /* error return will dealloc */
662 timer_unlock(timer);
663 return KERN_INVALID_ARGUMENT;
664 }
665 assert(timer->tm_thread == THREAD_NULL);
666 timer->tm_thread = thread;
667 timer->tm_misc |= TM_SUSPEND;
668 }
669
670 /*
671 * Mark the timer busy, and put it on the
672 * clock`s timeout queue.
673 */
674 timer->tm_misc |= TM_BUSY;
675 if (!timer_elt_enqueue(&timer->te,
676 expire_time,
677 (flags & TIMER_ABSOLUTE) != 0))
678 {
679 /*
680 * Time value was less than current time.
681 * Make the timer expire immediately.
682 */
683 timer_unlock(timer);
684 (*timer->tm_fcn)(timer->tm_param); /* resets 'busy' */
685 }
686 else {
687 timer_unlock(timer);
688 }
689
690 return KERN_SUCCESS;
691 }
692
693
694 /*
695 * Routine: timer_sleep
696 * Purpose: Attempts to set a timer and enqueue it in a clock
697 * queue. Similar to timer_arm except that the calling
698 * thread is blocked on on the timer and will be awakened
699 * when the timer expires.
700 * Conditions: None
701 */
702 kern_return_t timer_sleep(
703 mach_timer_t timer,
704 time_spec_t expire_time,
705 int flags,
706 time_spec_t *wakeup_time)
707 {
708 kern_return_t kr;
709
710 if (timer == TIMER_NULL)
711 return KERN_INVALID_ARGUMENT;
712
713 if (!time_spec_valid(expire_time))
714 return KERN_INVALID_VALUE;
715
716 timer_lock(timer);
717
718 /*
719 * Is the timer currently enqueued or being killed?
720 */
721 if (!(timer->tm_misc & TM_ACTIVE) || (timer->tm_misc & TM_BUSY)) {
722 timer_unlock(timer);
723 return KERN_FAILURE;
724 }
725
726 assert(timer->tm_thread == THREAD_NULL);
727 timer->tm_thread = current_thread();
728 thread_reference(timer->tm_thread); /* for timer */
729 timer->tm_fcn = timer_expire_synch;
730
731 timer->tm_misc |= TM_BUSY;
732 if (!timer_elt_enqueue(&timer->te,
733 expire_time,
734 (flags & TIMER_ABSOLUTE) != 0))
735 {
736 /*
737 * Time has already passed - don`t sleep.
738 */
739 timer->tm_misc &= ~TM_BUSY;
740 timer_unlock(timer);
741 kr = KERN_SUCCESS;
742 }
743 else {
744 /*
745 * Wait for timer to expire.
746 */
747 thread_will_wait(current_thread());
748 timer_unlock(timer);
749 thread_block(0);
750 kr = (current_thread()->wait_result == THREAD_INTERRUPTED)
751 ? KERN_ABORTED
752 : KERN_SUCCESS;
753 }
754
755 clock_read(*wakeup_time, timer->tm_clock);
756
757 return kr;
758 }
759
760 /*
761 * Routine: syscall_timer_sleep
762 * Purpose: same as timer_sleep, but called as a trap.
763 * Uses a continuation.
764 */
765
766 struct timer_sleep_save {
767 mach_timer_t timer;
768 time_spec_t *wakeup_time;
769 };
770
771 #define SAVE(thread) ((struct timer_sleep_save *)&(thread)->saved)
772
773 no_return
774 timer_sleep_continue(void)
775 {
776 thread_t thread = current_thread();
777 mach_timer_t timer = SAVE(thread)->timer;
778 time_spec_t *wakeup_time = SAVE(thread)->wakeup_time;
779 kern_return_t kr = (thread->wait_result == THREAD_INTERRUPTED)
780 ? KERN_ABORTED
781 : KERN_SUCCESS;
782 time_spec_t wake_time;
783
784 clock_read(wake_time, timer->tm_clock);
785 mach_timer_deallocate(timer);
786
787 (void) copyout(&wake_time,
788 wakeup_time,
789 sizeof(time_spec_t));
790 thread_syscall_return(kr);
791 /*NOTREACHED*/
792 }
793
794 extern mach_timer_t port_name_to_timer(mach_port_t);
795
796 kern_return_t syscall_timer_sleep(
797 mach_port_t timer_port,
798 time_spec_t expire_time,
799 int flags,
800 time_spec_t *wakeup_time)
801 {
802 thread_t thread = current_thread();
803 mach_timer_t timer;
804
805 timer = port_name_to_timer(timer_port);
806 if (timer == TIMER_NULL)
807 return KERN_INVALID_ARGUMENT;
808
809 if (!time_spec_valid(expire_time)) {
810 mach_timer_deallocate(timer);
811 return KERN_INVALID_VALUE;
812 }
813
814 /*
815 * Save the values we will need when the
816 * continuation is called.
817 */
818 SAVE(thread)->timer = timer;
819 SAVE(thread)->wakeup_time = wakeup_time;
820
821 timer_lock(timer);
822
823 /*
824 * Is the timer currently enqueued or being killed?
825 */
826 if (!(timer->tm_misc & TM_ACTIVE) || (timer->tm_misc & TM_BUSY)) {
827 timer_unlock(timer);
828 mach_timer_deallocate(timer);
829 return KERN_FAILURE;
830 }
831
832 assert(timer->tm_thread == THREAD_NULL);
833 timer->tm_thread = thread;
834 thread_reference(thread); /* for timer */
835 timer->tm_fcn = timer_expire_synch;
836
837 timer->tm_misc |= TM_BUSY;
838 if (!timer_elt_enqueue(&timer->te,
839 expire_time,
840 (flags & TIMER_ABSOLUTE) != 0))
841 {
842 /*
843 * Time has already passed - don`t sleep.
844 */
845 timer->tm_misc &= ~TM_BUSY;
846 timer_unlock(timer);
847 timer_sleep_continue();
848 /*NOTREACHED*/
849 }
850 else {
851 /*
852 * Wait for timer to expire.
853 */
854 thread_will_wait(thread);
855 timer_unlock(timer);
856 thread_block_noreturn(timer_sleep_continue);
857 /*NOTREACHED*/
858 }
859 }
860
861
862
863 /*
864 * Routine: timer_cancel
865 * Purpose: Attempts to cancel a timer which has been enqueued.
866 * If the timer is periodic and flags specifies
867 * TIMER_PERIODIC then only the current iteration of
868 * the timer is canceled. If a thread is sleeping on
869 * the timer then it will wake up.
870 * Conditions: None
871 */
872 kern_return_t timer_cancel(
873 mach_timer_t timer,
874 int flags)
875 {
876 ipc_port_t expire_port = IP_NULL;
877 thread_t thread = THREAD_NULL;
878
879 if (timer == TIMER_NULL)
880 return KERN_INVALID_ARGUMENT;
881
882 timer_lock(timer);
883
884 if (!(timer->tm_misc & TM_ACTIVE) || !(timer->tm_misc & TM_BUSY)) {
885 timer_unlock(timer);
886 return KERN_FAILURE;
887 }
888
889 timer_disarm(timer);
890
891 /*
892 * If the timer is periodic and the user specified that
893 * we should only cancel one iteration of the timer
894 * then re-enqueue it.
895 */
896
897 if ((timer->tm_flags & TELT_PERIODIC) &&
898 (flags & TIMER_PERIODIC))
899 {
900 time_spec_add(timer->tm_expire_time, timer->tm_period);
901 timer->tm_misc |= TM_BUSY;
902 (void) timer_elt_enqueue(&timer->te, timer->tm_expire_time, TRUE);
903 }
904 else {
905 /*
906 * Mark timer non-periodic - we are cancelling all iterations.
907 */
908 timer->tm_flags &= ~TELT_PERIODIC;
909
910 /*
911 * Release reference and send right to the expire_port,
912 * after we unlock the timer.
913 */
914 expire_port = timer->tm_expire;
915 timer->tm_expire = IP_NULL;
916
917 /*
918 * Also release reference to the thread.
919 */
920 thread = timer->tm_thread;
921 timer->tm_thread = THREAD_NULL;
922 }
923
924 timer_unlock(timer);
925
926 if (IP_VALID(expire_port))
927 ipc_port_release_send(expire_port);
928 if (thread != THREAD_NULL)
929 thread_deallocate(thread);
930
931 return KERN_SUCCESS;
932 }
933
934 /*
935 * Routine: timer_info
936 * Purpose: Returns the current state of the specified timer.
937 * Conditions: None
938 */
939 kern_return_t timer_info(
940 register mach_timer_t timer,
941 int flavor,
942 mach_clock_t *clock, /* out */
943 ipc_port_t *expire_port, /* out */
944 thread_t *thread, /* out */
945 mach_timer_info_t timer_info_out, /* pointer to OUT array */
946 natural_t *timer_info_count) /*IN/OUT*/
947 {
948
949 if (timer == TIMER_NULL)
950 return KERN_INVALID_ARGUMENT;
951
952 if (flavor == MACH_TIMER_BASIC_INFO) {
953 register mach_timer_basic_info_t basic_info;
954
955 if (*timer_info_count < MACH_TIMER_BASIC_INFO_COUNT) {
956 return KERN_INVALID_ARGUMENT;
957 }
958
959 basic_info = (mach_timer_basic_info_t) timer_info_out;
960
961 timer_lock(timer);
962
963 /*
964 * Return ports
965 */
966 *clock = timer->tm_clock;
967
968 if (IP_VALID(timer->tm_expire)) {
969 *expire_port = ipc_port_copy_send(timer->tm_expire);
970 } else {
971 *expire_port = MACH_PORT_NULL;
972 }
973
974 if (timer->tm_thread != THREAD_NULL) {
975 thread_reference(timer->tm_thread);
976 *thread = timer->tm_thread;
977 } else {
978 *thread = MACH_PORT_NULL;
979 }
980
981 /*
982 * Fill in info
983 */
984 basic_info->type = 0;
985 if (timer->tm_flags & TELT_ABSOLUTE)
986 basic_info->type |= TIMER_ABSOLUTE;
987 if (timer->tm_flags & TELT_PERIODIC)
988 basic_info->type |= TIMER_PERIODIC;
989 if (timer->tm_fcn == timer_expire_event)
990 basic_info->type |= TIMER_EVENT;
991 if (timer->tm_misc & TM_SUSPEND)
992 basic_info->type |= TIMER_THREAD_SUSPEND;
993
994 basic_info->expire_time = timer->tm_expire_time;
995 basic_info->interval_time = timer->tm_period;
996 basic_info->event = timer->tm_event.ev_id;
997 basic_info->overruns = timer->tm_overruns;
998 basic_info->enqueued = (timer->tm_misc & TM_BUSY) != 0;
999
1000 timer_unlock(timer);
1001
1002 *timer_info_count = MACH_TIMER_BASIC_INFO_COUNT;
1003 return KERN_SUCCESS;
1004 }
1005 else {
1006 return KERN_INVALID_ARGUMENT;
1007 }
1008 }
1009
1010 /*
1011 * What to do when timer expires:
1012 */
1013
1014 /*
1015 * Wakeup thread.
1016 */
1017 void timer_expire_synch(void *param)
1018 {
1019 mach_timer_t timer = (mach_timer_t) param;
1020
1021 thread_go(timer->tm_thread);
1022
1023 timer_lock(timer);
1024 if (!(timer->tm_flags & TELT_PERIODIC)) {
1025 timer->tm_misc &= ~TM_BUSY;
1026 thread_deallocate_nowait(timer->tm_thread);
1027 timer->tm_thread = THREAD_NULL;
1028 }
1029 timer_unlock(timer);
1030 }
1031
1032 /*
1033 * Signal event.
1034 */
1035 void timer_expire_event(void *param)
1036 {
1037 mach_timer_t timer = (mach_timer_t) param;
1038
1039 evc_signal(&timer->tm_event);
1040
1041 timer_lock(timer);
1042 if (!(timer->tm_flags & TELT_PERIODIC))
1043 timer->tm_misc &= ~TM_BUSY;
1044 timer_unlock(timer);
1045 }
1046
1047 /*
1048 * Send message; optionally suspend the thread.
1049 */
1050 extern kern_return_t
1051 timer_expire(ipc_port_t expire_port, time_spec_t time);
1052
1053 void timer_expire_message(void *param)
1054 {
1055 mach_timer_t timer = (mach_timer_t) param;
1056 ipc_port_t expire_port;
1057 time_spec_t cur_time;
1058 thread_t thread;
1059
1060 thread = timer->tm_thread;
1061 if (timer->tm_misc & TM_SUSPEND)
1062 thread_suspend_nowait(thread);
1063
1064 timer_lock(timer);
1065 expire_port = timer->tm_expire;
1066
1067 if (!IP_VALID(expire_port)) {
1068 /*
1069 * Port expired on the previous iteration,
1070 * but we didn`t have the proper locking to
1071 * notice it then. Clear the timer.
1072 */
1073 timer->tm_expire = IP_NULL;
1074 timer->tm_thread = THREAD_NULL;
1075 timer->tm_flags &= ~TELT_PERIODIC;
1076 timer->tm_misc &= ~(TM_BUSY | TM_SUSPEND);
1077 timer_unlock(timer);
1078
1079 if (thread != THREAD_NULL)
1080 thread_deallocate_nowait(thread);
1081 }
1082
1083 else if (timer->tm_flags & TELT_PERIODIC) {
1084 /*
1085 * Check here for port overflow.
1086 * If the port is full, don`t send the
1087 * message, since that will eventually
1088 * fill the kernel with undelivered
1089 * timer expirations. Instead increment
1090 * the timer`s overrun counter.
1091 *
1092 * (should be detected by the return code
1093 * from timer_expire, but that requires
1094 * patching it to not use SEND_ALWAYS.)
1095 */
1096 ip_lock(expire_port);
1097 if (ip_active(expire_port) &&
1098 expire_port->ip_receiver != ipc_space_kernel &&
1099 expire_port->ip_msgcount >= expire_port->ip_qlimit)
1100 {
1101 /*
1102 * Port is over its queue limit.
1103 * Increment timer overrun counter.
1104 */
1105 ip_unlock(expire_port);
1106 timer->tm_overruns++;
1107 timer_unlock(timer);
1108 }
1109 else {
1110 /*
1111 * Port is OK.
1112 * Clone another send right for the expire
1113 * port (for the next expiration), and send
1114 * the message.
1115 */
1116 ip_unlock(expire_port);
1117 timer->tm_expire = ipc_port_copy_send(expire_port);
1118 timer_unlock(timer);
1119
1120 clock_read(cur_time, timer->tm_clock);
1121 (void) timer_expire(expire_port, cur_time);
1122 /* may NOT block */
1123 }
1124 }
1125 else {
1126 /*
1127 * Single expiration. Move send right
1128 * to message, and clear the timer.
1129 */
1130 timer->tm_expire = IP_NULL;
1131 timer->tm_thread = THREAD_NULL;
1132 timer->tm_misc &= ~(TM_BUSY | TM_SUSPEND);
1133 timer_unlock(timer);
1134
1135 if (thread != THREAD_NULL)
1136 thread_deallocate_nowait(thread);
1137
1138 clock_read(cur_time, timer->tm_clock);
1139 (void) timer_expire(expire_port, cur_time);
1140 /* may NOT block */
1141 }
1142 }
1143
1144 /*
1145 * Internal version of timer_sleep that allows
1146 * sleeping on a periodic timer.
1147 */
1148 kern_return_t timer_sleep_periodic(
1149 mach_timer_t timer,
1150 time_spec_t expire_time,
1151 time_spec_t interval_time,
1152 thread_t thread,
1153 int flags)
1154 {
1155 timer_lock(timer);
1156
1157 assert((timer->tm_misc & TM_ACTIVE) && !(timer->tm_misc & TM_BUSY));
1158 assert(timer->tm_expire == IP_NULL);
1159 assert(timer->tm_thread == THREAD_NULL);
1160
1161 timer->tm_thread = thread;
1162 timer->tm_fcn = timer_expire_synch;
1163
1164 if (flags & TIMER_PERIODIC) {
1165 timer->tm_period = interval_time;
1166 timer->tm_flags |= TELT_PERIODIC;
1167 }
1168 else {
1169 timer->tm_flags &= ~TELT_PERIODIC;
1170 }
1171
1172 timer->tm_misc |= TM_BUSY;
1173 if (!timer_elt_enqueue(&timer->te,
1174 expire_time,
1175 (flags & TIMER_ABSOLUTE) != 0))
1176 {
1177 /*
1178 * Time already passed. Don`t make thread sleep.
1179 */
1180 timer->tm_misc &= ~TM_BUSY;
1181 timer_unlock(timer);
1182 }
1183 else {
1184 /*
1185 * Make thread wait for timer.
1186 */
1187 thread_will_wait(thread);
1188 timer_unlock(timer);
1189 }
1190
1191 return KERN_SUCCESS;
1192 }
1193
1194 /*
1195 * Change the expiration time or period of a timer
1196 */
1197 kern_return_t
1198 timer_rearm(
1199 mach_timer_t timer,
1200 time_spec_t new_expire_time,
1201 time_spec_t new_interval,
1202 int flags)
1203 {
1204 timer_lock(timer);
1205
1206 (void) timer_elt_dequeue(&timer->te);
1207
1208 if (flags & TIMER_PERIODIC) {
1209 timer->tm_period = new_interval;
1210 timer->tm_flags |= TELT_PERIODIC;
1211 }
1212 else {
1213 timer->tm_flags &= ~TELT_PERIODIC;
1214 }
1215
1216 timer->tm_flags |= TM_BUSY;
1217 if (!timer_elt_enqueue(&timer->te,
1218 new_expire_time,
1219 (flags & TIMER_ABSOLUTE) != 0))
1220 {
1221 /*
1222 * Time already expired - do action.
1223 */
1224 timer_unlock(timer);
1225 (*timer->tm_fcn)(timer->tm_param); /* resets 'busy' */
1226 }
1227 else {
1228 timer_unlock(timer);
1229 }
1230 return KERN_SUCCESS;
1231 }
1232
1233 /*
1234 * Return the time that the *current* period of a periodic
1235 * timer started: expiration_time minus interval.
1236 *
1237 * Returns FALSE if the timer is not queued or not periodic.
1238 */
1239 boolean_t timer_start_time(
1240 mach_timer_t timer,
1241 time_spec_t *start_time) /* OUT */
1242 {
1243 spl_t s;
1244
1245 /*
1246 * Lock the timer, to check whether it is
1247 * enqueued.
1248 */
1249 timer_lock(timer);
1250 if (!(timer->tm_misc & TM_ACTIVE) ||
1251 ((timer->tm_flags & (TELT_SET|TELT_PERIODIC))
1252 != (TELT_SET|TELT_PERIODIC)))
1253 {
1254 timer_unlock(timer);
1255 return FALSE;
1256 }
1257
1258 /*
1259 * Lock the clock, to keep the timer from expiring
1260 * while we read it.
1261 */
1262 s = splsched();
1263 clock_queue_lock(timer->tm_clock);
1264
1265 /*
1266 * The start of the *current* period is
1267 * <expire_time> - <interval>.
1268 */
1269 *start_time = timer->tm_expire_time;
1270 time_spec_subtract(*start_time, timer->tm_period);
1271
1272 clock_queue_unlock(timer->tm_clock);
1273 splx(s);
1274
1275 return TRUE;
1276 }
1277
1278
1279 /*
1280 * Make a thread wait for the next expiration of
1281 * a periodic timer.
1282 */
1283 void timer_wait(
1284 mach_timer_t timer,
1285 thread_t thread)
1286 {
1287 timer_lock(timer);
1288
1289 thread_will_wait(thread); /* XXX doesn`t need timer! */
1290
1291 timer_unlock(timer);
1292 }
1293
1294 /*
1295 * Handle no_more_senders notification by terminating timer.
1296 */
1297 boolean_t
1298 timer_notify(
1299 mach_msg_header_t *msg)
1300 {
1301 if (msg->msgh_id != MACH_NOTIFY_NO_SENDERS) {
1302 printf("timer_notify: strange notification %d\n", msg->msgh_id);
1303 return FALSE;
1304 }
1305 {
1306 mach_no_senders_notification_t *n =
1307 (mach_no_senders_notification_t *) msg;
1308
1309 ipc_port_t port = (ipc_port_t) n->not_header.msgh_remote_port;
1310 int ms_count = n->not_count;
1311
1312 mach_timer_t timer;
1313
1314 /*
1315 * Get the timer from the port, and check that
1316 * the make-send count is still the same as
1317 * the count received in the no-more-senders
1318 * notification.
1319 */
1320
1321 assert(IP_VALID(port));
1322 timer = convert_port_to_timer(port);
1323 if (timer == TIMER_NULL)
1324 return TRUE; /* message was ours */
1325
1326 timer_lock(timer);
1327 ip_lock(port);
1328 if (ip_active(port) &&
1329 ip_kotype(port) == IKOT_TIMER &&
1330 port->ip_mscount == ms_count)
1331 {
1332 /*
1333 * Port is still active, no new send rights have been
1334 * created, and there are no internal references from
1335 * threads.
1336 *
1337 * Set timer inactive to prevent convert_timer_to_port
1338 * from making new send rights. Then terminate the
1339 * timer.
1340 */
1341 timer->tm_misc &= ~TM_ACTIVE;
1342 ip_unlock(port);
1343
1344 timer_terminate_internal(timer);
1345 /* timer is now unlocked */
1346 }
1347
1348 timer_unlock(timer);
1349
1350 /*
1351 * Deallocate reference from convert_port_to_timer.
1352 */
1353 mach_timer_deallocate(timer);
1354 }
1355
1356 return TRUE;
1357 }
1358
Cache object: dbacd2de42dcd7f5d06d73a5c2dcf67d
|