1/* 2 * Copyright 2010, Axel D��rfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "device.h" 8 9#include <lock.h> 10#include <thread.h> 11 12extern "C" { 13# include <sys/callout.h> 14# include <sys/mutex.h> 15} 16 17#include <util/AutoLock.h> 18 19 20//#define TRACE_CALLOUT 21#ifdef TRACE_CALLOUT 22# define TRACE(x...) dprintf(x) 23#else 24# define TRACE(x...) ; 25#endif 26 27 28static struct list sTimers; 29static mutex sLock; 30static sem_id sWaitSem; 31static callout* sCurrentCallout; 32static thread_id sThread; 33static bigtime_t sTimeout; 34 35 36static status_t 37callout_thread(void* /*data*/) 38{ 39 status_t status = B_OK; 40 41 do { 42 bigtime_t timeout = B_INFINITE_TIMEOUT; 43 44 if (status == B_TIMED_OUT || status == B_OK) { 45 // scan timers for new timeout and/or execute a timer 46 mutex_lock(&sLock); 47 48 struct callout* c = NULL; 49 while (true) { 50 c = (callout*)list_get_next_item(&sTimers, c); 51 if (c == NULL) 52 break; 53 54 if (c->due < system_time()) { 55 struct mtx *mutex = c->c_mtx; 56 57 // execute timer 58 list_remove_item(&sTimers, c); 59 c->due = -1; 60 sCurrentCallout = c; 61 62 mutex_unlock(&sLock); 63 64 if (mutex != NULL) 65 mtx_lock(mutex); 66 67 c->c_func(c->c_arg); 68 69 if (mutex != NULL) 70 mtx_unlock(mutex); 71 72 mutex_lock(&sLock); 73 74 sCurrentCallout = NULL; 75 c = NULL; 76 // restart scanning as we unlocked the list 77 } else { 78 // calculate new timeout 79 if (c->due < timeout) 80 timeout = c->due; 81 } 82 } 83 84 sTimeout = timeout; 85 mutex_unlock(&sLock); 86 } 87 88 status = acquire_sem_etc(sWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout); 89 // the wait sem normally can't be acquired, so we 90 // have to look at the status value the call returns: 91 // 92 // B_OK - a new timer has been added or canceled 93 // B_TIMED_OUT - look for timers to be executed 94 // B_BAD_SEM_ID - we are asked to quit 95 } while (status != B_BAD_SEM_ID); 96 97 return B_OK; 98} 99 100 101// #pragma mark - private API 102 103 104status_t 105init_callout(void) 106{ 107 list_init(&sTimers); 108 sTimeout = B_INFINITE_TIMEOUT; 109 110 status_t status = B_OK; 111 mutex_init(&sLock, "fbsd callout"); 112 113 sWaitSem = create_sem(0, "fbsd callout wait"); 114 if (sWaitSem < 0) { 115 status = sWaitSem; 116 goto err1; 117 } 118 119 sThread = spawn_kernel_thread(callout_thread, "fbsd callout", 120 B_DISPLAY_PRIORITY, NULL); 121 if (sThread < 0) { 122 status = sThread; 123 goto err2; 124 } 125 126 return resume_thread(sThread); 127 128err1: 129 mutex_destroy(&sLock); 130err2: 131 delete_sem(sWaitSem); 132 return status; 133} 134 135 136void 137uninit_callout(void) 138{ 139 delete_sem(sWaitSem); 140 mutex_lock(&sLock); 141 142 mutex_destroy(&sLock); 143 144 status_t status; 145 wait_for_thread(sThread, &status); 146} 147 148 149// #pragma mark - public API 150 151 152void 153callout_init(struct callout *callout, int mpsafe) 154{ 155 if (mpsafe) 156 callout_init_mtx(callout, NULL, 0); 157 else 158 callout_init_mtx(callout, &Giant, 0); 159} 160 161 162void 163callout_init_mtx(struct callout *c, struct mtx *mtx, int flags) 164{ 165 c->due = 0; 166 c->flags = 0; 167 168 c->c_arg = NULL; 169 c->c_func = NULL; 170 c->c_mtx = mtx; 171 c->c_flags = flags; 172} 173 174 175int 176callout_reset(struct callout *c, int ticks, void (*func)(void *), void *arg) 177{ 178 int canceled = callout_stop(c); 179 180 MutexLocker locker(sLock); 181 182 c->c_func = func; 183 c->c_arg = arg; 184 185 TRACE("callout_reset %p, func %p, arg %p\n", c, c->c_func, c->c_arg); 186 187 if (ticks >= 0) { 188 // reschedule or add this timer 189 if (c->due <= 0) 190 list_add_item(&sTimers, c); 191 192 c->due = system_time() + ticks_to_usecs(ticks); 193 194 // notify timer about the change if necessary 195 if (sTimeout > c->due) 196 release_sem(sWaitSem); 197 } 198 199 return canceled; 200} 201 202 203int 204callout_schedule(struct callout *callout, int ticks) 205{ 206 return callout_reset(callout, ticks, callout->c_func, callout->c_arg); 207} 208 209 210int 211_callout_stop_safe(struct callout *c, int safe) 212{ 213 MutexLocker locker(sLock); 214 215 TRACE("_callout_stop_safe %p, func %p, arg %p\n", c, c->c_func, c->c_arg); 216 217 if (c->due <= 0) 218 return 0; 219 220 // this timer is scheduled, cancel it 221 list_remove_item(&sTimers, c); 222 c->due = 0; 223 return 1; 224} 225 226 227int 228callout_pending(struct callout *c) 229{ 230 return c->due > 0; 231} 232 233 234int 235callout_active(struct callout *c) 236{ 237 return c == sCurrentCallout; 238} 239