4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
33 #include <fmd_alloc.h>
35 #include <fmd_thread.h>
36 #include <fmd_timerq.h>
41 * Install a new timer to fire after at least 'delta' nanoseconds have elapsed.
42 * Timers are associated with persistent integer identifiers in some idspace.
43 * We allocate a new timer structure or re-use one from our freelist, and then
44 * place it on the queue's list in sorted order by expiration time. If the new
45 * timer is now the earliest to expire, we awaken the fmd_timerq_exec() thread.
48 fmd_timerq_install(fmd_timerq_t
*tmq
, fmd_idspace_t
*ids
,
49 fmd_timer_f
*func
, void *arg
, fmd_event_t
*ep
, hrtime_t delta
)
51 hrtime_t now
= fmd_time_gethrtime();
52 hrtime_t base
= ep
? fmd_event_hrtime(ep
) : now
;
58 (void) pthread_mutex_lock(&tmq
->tmq_lock
);
60 if ((tp
= fmd_list_next(&tmq
->tmq_free
)) == NULL
) {
61 tp
= fmd_zalloc(sizeof (fmd_timer_t
), FMD_SLEEP
);
62 (void) pthread_cond_init(&tp
->tmr_cv
, NULL
);
64 fmd_list_delete(&tmq
->tmq_free
, tp
);
66 if ((id
= fmd_idspace_alloc(ids
, tp
)) == -1) {
67 fmd_list_prepend(&tmq
->tmq_free
, tp
);
68 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
73 delta
= 0; /* ensure delta is at least 0ns from now */
75 if (base
+ delta
< base
)
76 hrt
= INT64_MAX
; /* if wrap-around, set timer for apocalypse */
87 * For now we use a simple insertion sort for tmq_list. If we have
88 * scaling problems here due to heavy use of our timer subsystem,
89 * then tmq_list can and should be replaced with a O(logN) heap.
91 for (up
= fmd_list_next(&tmq
->tmq_list
); up
; up
= fmd_list_next(up
)) {
92 if (tp
->tmr_hrt
< up
->tmr_hrt
)
97 fmd_list_insert_before(&tmq
->tmq_list
, up
, tp
);
99 fmd_list_insert_after(&tmq
->tmq_list
, up
, tp
);
101 if (up
!= NULL
&& fmd_list_next(&tmq
->tmq_list
) == tp
)
102 fmd_time_waitcancel(tmq
->tmq_thread
->thr_tid
);
103 else if (up
== NULL
&& fmd_list_next(&tmq
->tmq_list
) == tp
)
104 (void) pthread_cond_signal(&tmq
->tmq_cv
);
106 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
108 TRACE((FMD_DBG_TMR
, "timer %s:%ld insert +%lldns",
109 ids
->ids_name
, id
, delta
));
115 * Remove the specified timer. If the 'id' is invalid, we'll panic inside of
116 * fmd_idspace_free(). If the timer is still set, we move it to the freelist
117 * and update the timer thread as needed. If the timer 'id' is valid but
118 * tmr_id is not equal to id, then the timer callback is running: we wait for
119 * tmr_id to change to zero (indicating tmr_func is done) before returning.
122 fmd_timerq_remove(fmd_timerq_t
*tmq
, fmd_idspace_t
*ids
, id_t id
)
128 (void) pthread_mutex_lock(&tmq
->tmq_lock
);
129 tp
= fmd_idspace_free(ids
, id
);
130 ASSERT(tp
== NULL
|| tp
->tmr_ids
== ids
);
133 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
134 return (NULL
); /* timer is no longer active */
137 if (tp
->tmr_id
== id
) {
138 fmd_list_delete(&tmq
->tmq_list
, tp
);
139 delta
= tp
->tmr_hrt
- fmd_time_gethrtime();
142 fmd_list_append(&tmq
->tmq_free
, tp
);
145 * If tmq_list is now empty, we must awaken the exec thread so
146 * it will sleep on tmq_cv waiting for the list to change. We
147 * could also awaken the exec thread if we removed the head of
148 * tmq_list, but an early wakeup is harmless so we do nothing.
150 if (fmd_list_next(&tmq
->tmq_list
) == NULL
)
151 fmd_time_waitcancel(tmq
->tmq_thread
->thr_tid
);
154 * Wait until tmr_id is zero, indicating that tmr_func is done.
155 * This relies on expired fmd_timer_t's being returned to our
156 * free list rather than having the data structure deallocated.
158 while (tp
->tmr_id
!= 0)
159 (void) pthread_cond_wait(&tp
->tmr_cv
, &tmq
->tmq_lock
);
162 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
164 TRACE((FMD_DBG_TMR
, "timer %s:%ld remove -%lldns",
165 ids
->ids_name
, id
, delta
> 0 ? delta
: 0LL));
171 * fmd_timerq_exec() is the main loop of the thread that runs the timer queue.
172 * We sleep on tmq_cv waiting for timers to show up on tmq_list. When the list
173 * is non-empty, we execute the callback function for each expired timer. If
174 * timers remain that are not yet expired, we nanosleep() until the next expiry
175 * time. We awaken whenever nanosleep() expires or we are interrupted by a
176 * SIGALRM from fmd_timerq_install indicating that we need to rescan our list.
179 fmd_timerq_exec(fmd_timerq_t
*tmq
)
186 * fmd_thread_create() initializes threads with all signals blocked.
187 * We must unblock SIGALRM (whose disposition has been to set to call
188 * an empty function by fmd_timerq_init()) in order to permit directed
189 * signals to interrupt our nanosleep() and make it return EINTR.
190 * This SIGALRM mechanism is used by the native clock (see fmd_time.c).
192 (void) sigemptyset(&set
);
193 (void) sigaddset(&set
, SIGALRM
);
194 (void) pthread_sigmask(SIG_UNBLOCK
, &set
, NULL
);
195 (void) pthread_mutex_lock(&tmq
->tmq_lock
);
198 while (!tmq
->tmq_abort
&& fmd_list_next(&tmq
->tmq_list
) == NULL
)
199 (void) pthread_cond_wait(&tmq
->tmq_cv
, &tmq
->tmq_lock
);
201 if (tmq
->tmq_abort
) {
202 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
203 return; /* abort timerq thread */
206 for (now
= fmd_time_gethrtime(); (tp
= fmd_list_next(
207 &tmq
->tmq_list
)) != NULL
; now
= fmd_time_gethrtime()) {
209 if (now
== INT64_MAX
|| tp
->tmr_hrt
> now
)
210 break; /* no more timers left to expire */
212 tp
->tmr_id
= -tp
->tmr_id
;
213 fmd_list_delete(&tmq
->tmq_list
, tp
);
214 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
216 TRACE((FMD_DBG_TMR
, "tmr %s:%ld exec start (hrt=%llx)",
217 tp
->tmr_ids
->ids_name
, -tp
->tmr_id
, tp
->tmr_hrt
));
219 tp
->tmr_func(tp
->tmr_arg
, -tp
->tmr_id
, tp
->tmr_hrt
);
221 TRACE((FMD_DBG_TMR
, "tmr %s:%ld exec end",
222 tp
->tmr_ids
->ids_name
, -tp
->tmr_id
));
224 (void) pthread_mutex_lock(&tmq
->tmq_lock
);
225 (void) fmd_idspace_free(tp
->tmr_ids
, -tp
->tmr_id
);
226 fmd_list_append(&tmq
->tmq_free
, tp
);
227 tp
->tmr_id
= 0; /* for fmd_timer_remove() */
229 (void) pthread_cond_broadcast(&tp
->tmr_cv
);
233 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
234 fmd_time_waithrtime(tp
->tmr_hrt
- now
);
235 (void) pthread_mutex_lock(&tmq
->tmq_lock
);
241 fmd_timerq_alrm(int sig
)
243 TRACE((FMD_DBG_TMR
, "timer thread received alarm sig#%d", sig
));
247 fmd_timerq_create(void)
249 fmd_timerq_t
*tmq
= fmd_zalloc(sizeof (fmd_timerq_t
), FMD_SLEEP
);
250 struct sigaction act
;
252 (void) pthread_mutex_init(&tmq
->tmq_lock
, NULL
);
253 (void) pthread_cond_init(&tmq
->tmq_cv
, NULL
);
255 act
.sa_handler
= fmd_timerq_alrm
;
257 (void) sigemptyset(&act
.sa_mask
);
258 (void) sigaction(SIGALRM
, &act
, NULL
);
260 if ((tmq
->tmq_thread
= fmd_thread_create(fmd
.d_rmod
,
261 (fmd_thread_f
*)fmd_timerq_exec
, tmq
)) == NULL
)
262 fmd_panic("failed to create timer thread");
268 fmd_timerq_destroy(fmd_timerq_t
*tmq
)
270 struct sigaction act
;
273 (void) pthread_mutex_lock(&tmq
->tmq_lock
);
276 if (fmd_list_next(&tmq
->tmq_list
) != NULL
)
277 fmd_time_waitcancel(tmq
->tmq_thread
->thr_tid
);
279 (void) pthread_cond_signal(&tmq
->tmq_cv
);
281 (void) pthread_mutex_unlock(&tmq
->tmq_lock
);
282 fmd_thread_destroy(tmq
->tmq_thread
, FMD_THREAD_JOIN
);
283 (void) pthread_mutex_lock(&tmq
->tmq_lock
);
285 while ((tmr
= fmd_list_next(&tmq
->tmq_list
)) != NULL
) {
286 fmd_list_delete(&tmq
->tmq_list
, tmr
);
287 (void) fmd_idspace_free(tmr
->tmr_ids
, tmr
->tmr_id
);
288 fmd_free(tmr
, sizeof (fmd_timer_t
));
291 while ((tmr
= fmd_list_next(&tmq
->tmq_free
)) != NULL
) {
292 fmd_list_delete(&tmq
->tmq_free
, tmr
);
293 ASSERT(tmr
->tmr_id
== 0);
294 fmd_free(tmr
, sizeof (fmd_timer_t
));
297 act
.sa_handler
= SIG_DFL
;
299 (void) sigemptyset(&act
.sa_mask
);
300 (void) sigaction(SIGALRM
, &act
, NULL
);
302 fmd_free(tmq
, sizeof (fmd_timerq_t
));