1/* 2 * Copyright (c) 2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * Portions of this software have been released under the following terms: 31 * 32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. 33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY 34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION 35 * 36 * To anyone who acknowledges that this file is provided "AS IS" 37 * without any express or implied warranty: 38 * permission to use, copy, modify, and distribute this file for any 39 * purpose is hereby granted without fee, provided that the above 40 * copyright notices and this notice appears in all source code copies, 41 * and that none of the names of Open Software Foundation, Inc., Hewlett- 42 * Packard Company or Digital Equipment Corporation be used 43 * in advertising or publicity pertaining to distribution of the software 44 * without specific, written prior permission. Neither Open Software 45 * Foundation, Inc., Hewlett-Packard Company nor Digital 46 * Equipment Corporation makes any representations about the suitability 47 * of this software for any purpose. 48 * 49 * Copyright (c) 2007, Novell, Inc. All rights reserved. 50 * Redistribution and use in source and binary forms, with or without 51 * modification, are permitted provided that the following conditions 52 * are met: 53 * 54 * 1. Redistributions of source code must retain the above copyright 55 * notice, this list of conditions and the following disclaimer. 56 * 2. Redistributions in binary form must reproduce the above copyright 57 * notice, this list of conditions and the following disclaimer in the 58 * documentation and/or other materials provided with the distribution. 59 * 3. Neither the name of Novell Inc. nor the names of its contributors 60 * may be used to endorse or promote products derived from this 61 * this software without specific prior written permission. 62 * 63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY 67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 73 * 74 * @APPLE_LICENSE_HEADER_END@ 75 */ 76 77/* 78** 79** NAME 80** 81** rpctimer.c 82** 83** FACILITY: 84** 85** Remote Procedure Call (RPC) 86** 87** ABSTRACT: 88** 89** Definitions of types/constants internal to RPC facility and common 90** to all RPC components. 91** 92** 93*/ 94 95#include <commonp.h> 96 97 98/* ========================================================================= */ 99 100EXTERNAL rpc_clock_t rpc_g_clock_curr; 101GLOBAL boolean32 rpc_g_long_sleep; 102 103INTERNAL rpc_timer_p_t running_list; 104INTERNAL rpc_mutex_t rpc_g_timer_mutex; 105INTERNAL rpc_cond_t rpc_g_timer_cond; 106INTERNAL rpc_clock_t rpc_timer_high_trigger; 107INTERNAL rpc_clock_t rpc_timer_cur_trigger; 108INTERNAL rpc_timer_p_t rpc_timer_head, rpc_timer_tail; 109INTERNAL unsigned32 stop_timer; 110INTERNAL dcethread* timer_task; 111INTERNAL boolean timer_task_running = false; 112INTERNAL boolean timer_task_was_running = false; 113 114INTERNAL void timer_loop (void); 115 116INTERNAL void rpc__timer_set_int ( 117 rpc_timer_p_t /*t*/, 118 rpc_timer_proc_p_t /*proc*/, 119 dce_pointer_t /*parg*/, 120 rpc_clock_t /*freq*/ 121 ); 122 123/* 124 * Mutex lock macros 125 */ 126 127#define RPC_TIMER_LOCK_INIT(junk) RPC_MUTEX_INIT (rpc_g_timer_mutex) 128#define RPC_TIMER_LOCK(junk) RPC_MUTEX_LOCK (rpc_g_timer_mutex) 129#define RPC_TIMER_UNLOCK(junk) RPC_MUTEX_UNLOCK (rpc_g_timer_mutex) 130#define RPC_TIMER_LOCK_ASSERT(junk) RPC_MUTEX_LOCK_ASSERT(rpc_g_timer_mutex) 131#define RPC_TIMER_COND_INIT(junk) RPC_COND_INIT (rpc_g_timer_cond, rpc_g_timer_mutex) 132 133/* ========================================================================= */ 134 135 136/* 137 * T I M E R _ L O O P 138 * 139 * Periodically get the new clock time and process any ready timer events. 140 */ 141 142#ifndef NO_RPC_TIMER_THREAD 143 144INTERNAL void timer_loop(void) 145{ 146 RPC_TIMER_LOCK(0); 147 148 while (!stop_timer) 149 { 150 rpc_clock_t next; 151 struct timespec next_ts; 152 rpc_clock_t max_step; 153 154 /* 155 * It would be real nice if we could figure out a way to get 156 * the system time global variable mapped read-only into our 157 * address space to avoid the !@#$% gettimeofday syscall overhead. 158 */ 159 rpc__clock_update(); 160 next = rpc__timer_callout(); 161 rpc_g_long_sleep = ( next > RPC_CLOCK_SEC(1) ); 162 163 /* 164 * wake up at least once every 50 seconds, so we don't confuse 165 * the underlying rpc__clock_update() code. 166 */ 167 max_step = RPC_CLOCK_SEC(50); 168 if (next == 0 || next > max_step) 169 next = max_step; 170 171 if (next > 10) 172 { 173 RPC_DBG_PRINTF(rpc_e_dbg_timer, 5, 174 ("(timer_loop) next event in %ld seconds\n", next/RPC_C_CLOCK_HZ)); 175 } 176 rpc_timer_cur_trigger = rpc_g_clock_curr + next; 177 rpc__clock_timespec (rpc_timer_cur_trigger, &next_ts); 178 RPC_COND_TIMED_WAIT(rpc_g_timer_cond, rpc_g_timer_mutex, &next_ts); 179 } 180 181 RPC_TIMER_UNLOCK(0); 182} 183 184#endif /* NO_RPC_TIMER_THREAD */ 185 186INTERNAL void rpc__timer_prod 187( 188 rpc_clock_t trigger 189) 190{ 191 192 RPC_DBG_PRINTF(rpc_e_dbg_timer, 5, ( 193 "(rpc__timer_prod) timer backup; old %ld, new %ld\n", 194 rpc_timer_cur_trigger, trigger 195 )); 196 /* 197 * forestall "double pokes", which appear to 198 * happen occasionally. 199 */ 200 rpc_timer_cur_trigger = trigger; 201 RPC_COND_SIGNAL(rpc_g_timer_cond, rpc_g_timer_mutex); 202 203} 204 205 206/* 207 ** R P C _ _ T I M E R _ I N I T 208 ** 209 ** Initialize the timer package. 210 **/ 211 212PRIVATE void rpc__timer_init(void) 213{ 214 RPC_TIMER_LOCK_INIT (0); 215 RPC_TIMER_COND_INIT (0); 216 217 running_list = NULL; 218 rpc_timer_high_trigger = 0; 219 rpc_timer_head = NULL; 220 rpc_timer_tail = NULL; 221 rpc_g_clock_curr = 0; 222 rpc_g_long_sleep = 0; 223 timer_task_running = false; 224 timer_task_was_running = false; 225 226 /* 227 * Initialize the current time... 228 */ 229 rpc__clock_update(); 230 231#ifndef NO_RPC_TIMER_THREAD 232 stop_timer = 0; 233 timer_task_running = true; 234 dcethread_create_throw ( 235 &timer_task, /* new thread */ 236 NULL, /* attributes */ 237 (dcethread_startroutine)timer_loop, /* start routine */ 238 NULL); /* arguments */ 239 240#endif /* NO_RPC_TIMER_THREAD */ 241} 242 243#ifdef ATFORK_SUPPORTED 244/* 245 ** R P C _ _ T I M E R _ F O R K _ H A N D L E R 246 ** 247 ** Handle timer related fork issues. 248 **/ 249 250PRIVATE void rpc__timer_fork_handler 251( 252 rpc_fork_stage_id_t stage 253) 254{ 255#ifndef NO_RPC_TIMER_THREAD 256 switch ((int)stage) 257 { 258 case RPC_C_PREFORK: 259 RPC_TIMER_LOCK(0); 260 timer_task_was_running = false; 261 if (timer_task_running) 262 { 263 stop_timer = 1; 264 rpc__timer_prod(0); 265 RPC_TIMER_UNLOCK(0); 266 dcethread_join_throw(timer_task, NULL); 267 RPC_TIMER_LOCK(0); 268 /* Why was this ever done? It doesn't make any sense to 269 perform a detach right after a join 270 DCETHREAD_TRY 271 { 272 dcethread_detach_throw(timer_task); 273 } 274 DCETHREAD_CATCH(dcethread_use_error_e) 275 { 276 } 277 DCETHREAD_CATCH(dcethread_badparam_e) 278 { 279 } 280 DCETHREAD_ENDTRY; */ 281 timer_task_was_running = true; 282 timer_task_running = false; 283 } 284 break; 285 286 case RPC_C_POSTFORK_PARENT: 287 if (timer_task_was_running) 288 { 289 timer_task_was_running = false; 290 timer_task_running = true; 291 stop_timer = 0; 292 dcethread_create_throw ( 293 &timer_task, /* new thread */ 294 NULL, /* attributes */ 295 (dcethread_startroutine) timer_loop, /* start routine */ 296 NULL); /* arguments */ 297 } 298 RPC_TIMER_UNLOCK(0); 299 break; 300 301 case RPC_C_POSTFORK_CHILD: 302 timer_task_was_running = false; 303 timer_task_running = false; 304 stop_timer = 0; 305 RPC_TIMER_UNLOCK(0); 306 break; 307 } 308#endif /* NO_RPC_TIMER_THREAD */ 309} 310#endif /* ATFORK_SUPPORTED */ 311 312 313/* 314** R P C _ _ T I M E R _ S E T 315** 316** Lock timer lock, add timer to timer callout queue, and unlock timer lock. 317** 318*/ 319 320PRIVATE void rpc__timer_set 321( 322 rpc_timer_p_t t, 323 rpc_timer_proc_p_t proc, 324 dce_pointer_t parg, 325 rpc_clock_t freq 326) 327{ 328 RPC_TIMER_LOCK (0); 329 rpc__timer_set_int (t, proc, parg, freq); 330 RPC_TIMER_UNLOCK (0); 331} 332 333/* 334** R P C _ _ T I M E R _ S E T _ I N T 335** 336** Insert a routine to be called from the rpc_timer periodic callout queue. 337** The entry address, a single argument to be passed when the routine is 338** called, together with the frequency with which the routine is to be 339** called are inserted into a rpc_timer_t structure provided by the 340** caller and pointed to by 't', which is placed in the queue. 341*/ 342 343INTERNAL void rpc__timer_set_int 344( 345 rpc_timer_p_t t, 346 rpc_timer_proc_p_t proc, 347 dce_pointer_t parg, 348 rpc_clock_t freq 349) 350{ 351 rpc_timer_p_t list_ptr, prev_ptr; 352 353 /* 354 * Insert the routine calling address, the argument to be passed and 355 * the frequency into the rpc_timer_t structure. 356 */ 357 358 t->proc = proc; 359 t->parg = parg; 360 t->frequency = freq; 361 t->trigger = freq + rpc_g_clock_curr; 362 363 /* 364 * Insert the rpc_timer_t structure sent by the caller into the 365 * rpc_timer queue, by order of the frequency with which it is to be 366 * called. 367 */ 368 if (rpc_timer_head != NULL && t->trigger >= rpc_timer_high_trigger) 369 { 370 rpc_timer_tail->next = t; 371 rpc_timer_tail = t; 372 t->next = 0; 373 rpc_timer_high_trigger = t->trigger; 374 return; 375 } 376 377 /* 378 * Handle the case in which the element gets inserted somewhere 379 * into the middle of the list. 380 */ 381 382 prev_ptr = NULL; 383 for (list_ptr = rpc_timer_head; list_ptr; list_ptr = list_ptr->next ) 384 { 385 if (t->trigger < list_ptr->trigger) 386 { 387 if (list_ptr == rpc_timer_head) 388 { 389 t->next = rpc_timer_head; 390 rpc_timer_head = t; 391 /* 392 * If the next time the timer thread will wake up is 393 * in the future, prod it. 394 */ 395 if (rpc_timer_cur_trigger > t->trigger) 396 rpc__timer_prod(t->trigger); 397 } 398 else 399 { 400 assert(prev_ptr != NULL); 401 prev_ptr->next = t; 402 t->next = list_ptr; 403 } 404 return; 405 } 406 prev_ptr = list_ptr; 407 } 408 409 /* 410 * The only way to get this far is when the head pointer is NULL. Either 411 * we just started up, or possibly there's been no RPC activity and all the 412 * monitors have removed themselves. 413 */ 414 assert (rpc_timer_head == NULL); 415 416 rpc_timer_head = rpc_timer_tail = t; 417 t->next = NULL; 418 rpc_timer_high_trigger = t->trigger; 419 if (rpc_timer_cur_trigger > t->trigger) 420 rpc__timer_prod(t->trigger); 421} 422 423/* 424 ** R P C _ _ T I M E R _ A D J U S T 425 ** 426 ** Search the rpc_timer queue for the rpc_timer_t structure pointed to by 427 ** 't'. Then modify the 'frequency' attribute. 428 **/ 429 430PRIVATE void rpc__timer_adjust 431( 432 rpc_timer_p_t t, 433 rpc_clock_t frequency 434) 435{ 436 rpc_timer_p_t ptr; 437 438 /* 439 * First see if the monitor being adjusted is currently running. 440 * If so, just reset the frequency field, when the monitor is rescheduled 441 * it will be queued in the proper place. 442 */ 443 444 RPC_TIMER_LOCK (0); 445 for (ptr = running_list; ptr != NULL; ptr = ptr->next) 446 { 447 if (t == ptr) 448 { 449 t->frequency = frequency; 450 RPC_TIMER_UNLOCK (0); 451 return; 452 } 453 } 454 455 RPC_TIMER_UNLOCK (0); 456 /* 457 * Otherwise, we need to remove the monitor from its current position 458 * in the queue, adjust the frequency, and then replace it in the correct 459 * position. 460 */ 461 462 rpc__timer_clear (t); 463 t->frequency = frequency; 464 rpc__timer_set (t, t->proc, t->parg, t->frequency); 465} 466 467/* 468 ** R P C _ _ T I M E R _ C L E A R 469 ** 470 ** Remove an rpc_timer_t structure from the rpc_timer scheduling queue. 471 **/ 472 473PRIVATE void rpc__timer_clear 474( 475 rpc_timer_p_t t 476) 477{ 478 rpc_timer_p_t list_ptr, prev = NULL; 479 480 RPC_TIMER_LOCK (0); 481 482 /* 483 * First see if the monitor being cleared is currently running. 484 * If so, remove it from the running list. 485 */ 486 487 for (list_ptr = running_list; list_ptr; prev = list_ptr, list_ptr = list_ptr->next) 488 { 489 if (list_ptr == t) 490 { 491 if (prev == NULL) 492 running_list = running_list->next; 493 else 494 prev->next = list_ptr->next; 495 RPC_TIMER_UNLOCK (0); 496 return; 497 } 498 } 499 500 /* 501 * Next, see if the specified timer element is in the list. 502 */ 503 504 prev = NULL; 505 for (list_ptr = rpc_timer_head; list_ptr; 506 prev = list_ptr, list_ptr = list_ptr->next) 507 { 508 if (list_ptr == t) 509 { 510 /* 511 * If this element was at the head of the list... 512 */ 513 if (t == rpc_timer_head) 514 { 515 /* 516 * !!! perhaps need to inform timer thread that next event 517 * is a little further off? 518 */ 519 rpc_timer_head = t->next; 520 } 521 else 522 { 523 /* 524 * Unlink element from list. If the element was at the end 525 * of the list, reset the tail pointer and high trigger value. 526 */ 527 prev->next = t->next; 528 if (t->next == NULL) 529 { 530 rpc_timer_tail = prev; 531 rpc_timer_high_trigger = prev->trigger; 532 } 533 } 534 RPC_TIMER_UNLOCK (0); 535 return; 536 } 537 } 538 539 RPC_TIMER_UNLOCK (0); 540} 541 542/* 543** R P C _ _ T I M E R _ C A L L O U T 544** 545** Make a pass through the rpc_timer_t periodic callout queue. 546** Make a call to any routines which are ready to run at this time. 547** Return the amount of time until the next routine is scheduled 548** to be run. Return 0 if the queue is empty. 549** The mutex, RPC_TIMER_LOCK, is LOCKED while this routine is running 550** and while any routines which may be calld are running. 551** 552** the parameter NEXT is set to the "next" time to wake up, if any 553** (suitable for passing to RPC_COND_TIMED_WAIT). 554**/ 555 556PRIVATE rpc_clock_t rpc__timer_callout (void) 557{ 558 rpc_clock_t ret_val; 559 rpc_timer_p_t ptr, prev; 560 561#if 0 562 RPC_TIMER_LOCK (0); 563#endif 564 565 RPC_TIMER_LOCK_ASSERT(0); 566 567 /* 568 * Go through list, running routines which have trigger counts which 569 * have expired. 570 */ 571 572 while (rpc_timer_head && rpc_timer_head->trigger <= rpc_g_clock_curr) 573 { 574 ptr = rpc_timer_head; 575 rpc_timer_head = rpc_timer_head->next; 576 ptr->next = running_list; 577 running_list = ptr; 578 579 RPC_TIMER_UNLOCK (0); 580 (*ptr->proc) (ptr->parg); 581 RPC_TIMER_LOCK (0); 582 583 /* 584 * We need to handle two situations here depending on whether 585 * the monitor routine has deleted itself from the timer queue. 586 * If so, we'll know this from the fact that it will be gone 587 * from the running list. If not, we need to update the head 588 * pointer here, and reschedule the current monitor. 589 */ 590 591 if (ptr == running_list) 592 { 593 running_list = running_list->next; 594 rpc__timer_set_int (ptr, ptr->proc, ptr->parg, ptr->frequency); 595 } 596 else 597 { 598 for (prev = running_list; prev; prev = prev->next) 599 { 600 if (prev->next == ptr) 601 { 602 prev->next = ptr->next; 603 604 rpc__timer_set_int 605 (ptr, ptr->proc, ptr->parg, ptr->frequency); 606 607 } 608 } 609 } 610 } 611 612 ret_val = (rpc_timer_head == NULL) ? 0 613 : (rpc_timer_head->trigger - rpc_g_clock_curr); 614 615 RPC_TIMER_LOCK_ASSERT(0); 616 return (ret_val); 617} 618