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 12 extern "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 28 static struct list sTimers; 29 static mutex sLock; 30 static sem_id sWaitSem; 31 static callout* sCurrentCallout; 32 static thread_id sThread; 33 static bigtime_t sTimeout; 34 35 36 static status_t 37 callout_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 104 status_t 105 init_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 128 err1: 129 mutex_destroy(&sLock); 130 err2: 131 delete_sem(sWaitSem); 132 return status; 133 } 134 135 136 void 137 uninit_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 152 void 153 callout_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 162 void 163 callout_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 175 int 176 callout_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 203 int 204 callout_schedule(struct callout *callout, int ticks) 205 { 206 return callout_reset(callout, ticks, callout->c_func, callout->c_arg); 207 } 208 209 210 int 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 227 int 228 callout_pending(struct callout *c) 229 { 230 return c->due > 0; 231 } 232 233 234 int 235 callout_active(struct callout *c) 236 { 237 return c == sCurrentCallout; 238 } 239