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 while (true) { 49 struct callout* c = (callout*)list_get_next_item(&sTimers, c); 50 if (c == NULL) 51 break; 52 53 if (c->due < system_time()) { 54 struct mtx *mutex = c->c_mtx; 55 56 // execute timer 57 list_remove_item(&sTimers, c); 58 c->due = -1; 59 sCurrentCallout = c; 60 61 mutex_unlock(&sLock); 62 63 if (mutex != NULL) 64 mtx_lock(mutex); 65 66 c->c_func(c->c_arg); 67 68 if (mutex != NULL) 69 mtx_unlock(mutex); 70 71 mutex_lock(&sLock); 72 73 sCurrentCallout = NULL; 74 c = NULL; 75 // restart scanning as we unlocked the list 76 } else { 77 // calculate new timeout 78 if (c->due < timeout) 79 timeout = c->due; 80 } 81 } 82 83 sTimeout = timeout; 84 mutex_unlock(&sLock); 85 } 86 87 status = acquire_sem_etc(sWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout); 88 // the wait sem normally can't be acquired, so we 89 // have to look at the status value the call returns: 90 // 91 // B_OK - a new timer has been added or canceled 92 // B_TIMED_OUT - look for timers to be executed 93 // B_BAD_SEM_ID - we are asked to quit 94 } while (status != B_BAD_SEM_ID); 95 96 return B_OK; 97 } 98 99 100 // #pragma mark - private API 101 102 103 status_t 104 init_callout(void) 105 { 106 list_init(&sTimers); 107 sTimeout = B_INFINITE_TIMEOUT; 108 109 status_t status = B_OK; 110 mutex_init(&sLock, "fbsd callout"); 111 112 sWaitSem = create_sem(0, "fbsd callout wait"); 113 if (sWaitSem < 0) { 114 status = sWaitSem; 115 goto err1; 116 } 117 118 sThread = spawn_kernel_thread(callout_thread, "fbsd callout", 119 B_URGENT_DISPLAY_PRIORITY, NULL); 120 if (sThread < 0) { 121 status = sThread; 122 goto err2; 123 } 124 125 return resume_thread(sThread); 126 127 err1: 128 mutex_destroy(&sLock); 129 err2: 130 delete_sem(sWaitSem); 131 return status; 132 } 133 134 135 void 136 uninit_callout(void) 137 { 138 delete_sem(sWaitSem); 139 mutex_lock(&sLock); 140 141 mutex_destroy(&sLock); 142 143 status_t status; 144 wait_for_thread(sThread, &status); 145 } 146 147 148 // #pragma mark - public API 149 150 151 void 152 callout_init(struct callout *callout, int mpsafe) 153 { 154 if (mpsafe) 155 callout_init_mtx(callout, NULL, 0); 156 else 157 callout_init_mtx(callout, &Giant, 0); 158 } 159 160 161 void 162 callout_init_mtx(struct callout *c, struct mtx *mtx, int flags) 163 { 164 c->due = 0; 165 c->flags = 0; 166 167 c->c_arg = NULL; 168 c->c_func = NULL; 169 c->c_mtx = mtx; 170 c->c_flags = flags; 171 } 172 173 174 int 175 callout_reset(struct callout *c, int when, void (*func)(void *), void *arg) 176 { 177 int canceled = callout_stop(c); 178 179 MutexLocker locker(sLock); 180 181 c->c_func = func; 182 c->c_arg = arg; 183 184 TRACE("callout_reset %p, func %p, arg %p\n", c, c->c_func, c->c_arg); 185 186 if (when >= 0) { 187 // reschedule or add this timer 188 if (c->due <= 0) 189 list_add_item(&sTimers, c); 190 191 c->due = system_time() + when; 192 193 // notify timer about the change if necessary 194 if (sTimeout > c->due) 195 release_sem(sWaitSem); 196 } 197 198 return canceled; 199 } 200 201 202 int 203 callout_schedule(struct callout *callout, int toTicks) 204 { 205 return callout_reset(callout, toTicks, callout->c_func, callout->c_arg); 206 } 207 208 209 int 210 _callout_stop_safe(struct callout *c, int safe) 211 { 212 MutexLocker locker(sLock); 213 214 TRACE("_callout_stop_safe %p, func %p, arg %p\n", c, c->c_func, c->c_arg); 215 216 if (c->due <= 0) 217 return 0; 218 219 // this timer is scheduled, cancel it 220 list_remove_item(&sTimers, c); 221 c->due = 0; 222 return 1; 223 } 224 225 226 int 227 callout_pending(struct callout *c) 228 { 229 return c->due > 0; 230 } 231 232 233 int 234 callout_active(struct callout *c) 235 { 236 return c == sCurrentCallout; 237 } 238