1/* 2 * Copyright (c) 2000-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_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. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * timer.c 26 * - timer functions 27 */ 28 29/* 30 * Modification History 31 * 32 * May 8, 2000 Dieter Siegmund (dieter@apple) 33 * - created 34 */ 35 36#include <stdlib.h> 37#include <unistd.h> 38#include <string.h> 39#include <stdio.h> 40#include <sys/types.h> 41#include <sys/time.h> 42#include <math.h> 43#include <syslog.h> 44#include <notify.h> 45#include <notify_keys.h> 46#ifndef kNotifyClockSet 47#define kNotifyClockSet "com.apple.system.clock_set" 48#endif 49#include "globals.h" 50#include "util.h" 51#include "timer.h" 52 53#include <CoreFoundation/CFRunLoop.h> 54#include <CoreFoundation/CFDate.h> 55 56struct timer_callout { 57 timer_func_t * func; 58 void * arg1; 59 void * arg2; 60 void * arg3; 61 CFRunLoopTimerRef timer_source; 62 boolean_t enabled; 63 uint32_t time_generation; 64}; 65 66#ifdef TEST_ARP_SESSION 67#define my_log syslog 68#endif /* TEST_ARP_SESSION */ 69 70/* 71 * Use notify(3) APIs to detect date/time changes 72 */ 73 74static boolean_t S_time_change_registered; 75static int S_time_change_token; 76static uint32_t S_time_generation; 77 78static void 79timer_notify_check(void) 80{ 81 int check = 0; 82 int status; 83 84 if (S_time_change_registered == FALSE) { 85 return; 86 } 87 status = notify_check(S_time_change_token, &check); 88 if (status != NOTIFY_STATUS_OK) { 89 my_log(LOG_ERR, "timer: notify_check failed with %d", status); 90 } 91 else if (check != 0) { 92 S_time_generation++; 93 } 94 return; 95} 96 97static void 98timer_register_time_change(void) 99{ 100 int status; 101 102 if (S_time_change_registered) { 103 return; 104 } 105 status = notify_register_check(kNotifyClockSet, &S_time_change_token); 106 if (status != NOTIFY_STATUS_OK) { 107 my_log(LOG_ERR, "timer: notify_register_check(%s) failed, %d", 108 kNotifyClockSet, status); 109 return; 110 } 111 S_time_change_registered = TRUE; 112 113 /* throw away the first check, since it always says check=1 */ 114 timer_notify_check(); 115 return; 116} 117 118/** 119 ** Timer callout functions 120 **/ 121static void 122timer_callout_process(CFRunLoopTimerRef timer_source, void * info) 123{ 124 timer_callout_t * callout = (timer_callout_t *)info; 125 126 if (callout->func && callout->enabled) { 127 callout->enabled = FALSE; 128 (*callout->func)(callout->arg1, callout->arg2, callout->arg3); 129 } 130 return; 131} 132 133timer_callout_t * 134timer_callout_init() 135{ 136 timer_callout_t * callout; 137 138 callout = malloc(sizeof(*callout)); 139 if (callout == NULL) 140 return (NULL); 141 bzero(callout, sizeof(*callout)); 142 timer_register_time_change(); 143 callout->time_generation = S_time_generation; 144 return (callout); 145} 146 147void 148timer_callout_free(timer_callout_t * * callout_p) 149{ 150 timer_callout_t * callout = *callout_p; 151 152 if (callout == NULL) 153 return; 154 155 timer_cancel(callout); 156 free(callout); 157 *callout_p = NULL; 158 return; 159} 160 161int 162timer_callout_set(timer_callout_t * callout, 163 CFAbsoluteTime relative_time, timer_func_t * func, 164 void * arg1, void * arg2, void * arg3) 165{ 166 CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL }; 167 CFAbsoluteTime wakeup_time; 168 169 if (callout == NULL) { 170 return (0); 171 } 172 timer_cancel(callout); 173 if (func == NULL) { 174 return (0); 175 } 176 callout->func = func; 177 callout->arg1 = arg1; 178 callout->arg2 = arg2; 179 callout->arg3 = arg3; 180 callout->enabled = TRUE; 181 callout->time_generation = S_time_generation; 182 context.info = callout; 183 wakeup_time = CFAbsoluteTimeGetCurrent() + relative_time; 184 callout->timer_source 185 = CFRunLoopTimerCreate(NULL, wakeup_time, 186 0.0, 0, 0, 187 timer_callout_process, 188 &context); 189 my_log(LOG_DEBUG, "timer: wakeup time is (%0.09g) %0.09g", 190 relative_time, wakeup_time); 191 my_log(LOG_DEBUG, "timer: adding timer source"); 192 CFRunLoopAddTimer(CFRunLoopGetCurrent(), callout->timer_source, 193 kCFRunLoopDefaultMode); 194 return (1); 195} 196 197int 198timer_set_relative(timer_callout_t * callout, 199 struct timeval rel_time, timer_func_t * func, 200 void * arg1, void * arg2, void * arg3) 201{ 202 CFTimeInterval relative_time; 203 204 if (rel_time.tv_sec < 0) { 205 rel_time.tv_sec = 0; 206 rel_time.tv_usec = 1; 207 } 208 relative_time = rel_time.tv_sec 209 + ((double)rel_time.tv_usec / USECS_PER_SEC); 210 return (timer_callout_set(callout, relative_time, func, arg1, arg2, arg3)); 211} 212 213void 214timer_cancel(timer_callout_t * callout) 215{ 216 if (callout == NULL) { 217 return; 218 } 219 callout->enabled = FALSE; 220 callout->func = NULL; 221 callout->time_generation = S_time_generation; 222 if (callout->timer_source) { 223 my_log(LOG_DEBUG, "timer: freeing timer source"); 224 CFRunLoopTimerInvalidate(callout->timer_source); 225 CFRelease(callout->timer_source); 226 callout->timer_source = 0; 227 } 228 return; 229} 230 231/** 232 ** timer functions 233 **/ 234long 235timer_current_secs() 236{ 237 return ((long)CFAbsoluteTimeGetCurrent()); 238} 239 240struct timeval 241timer_current_time() 242{ 243 double t = CFAbsoluteTimeGetCurrent(); 244 struct timeval tv; 245 246 tv.tv_sec = (int32_t)t; 247 tv.tv_usec = (t - tv.tv_sec) * USECS_PER_SEC; 248 249 return (tv); 250} 251 252boolean_t 253timer_time_changed(timer_callout_t * entry) 254{ 255 timer_notify_check(); 256 return (entry->time_generation != S_time_generation); 257} 258 259boolean_t 260timer_still_pending(timer_callout_t * entry) 261{ 262 return (entry->enabled); 263} 264