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