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->due < system_time()) { 56 struct mtx *mutex = c->c_mtx; 57 58 // execute timer 59 list_remove_item(&sTimers, c); 60 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->due < timeout) 83 timeout = 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->due = 0; 168 c->flags = 0; 169 170 c->c_arg = NULL; 171 c->c_func = NULL; 172 c->c_mtx = mtx; 173 c->c_flags = flags; 174 } 175 176 177 int 178 callout_reset(struct callout *c, int _ticks, void (*func)(void *), void *arg) 179 { 180 int canceled = callout_stop(c); 181 182 MutexLocker locker(sLock); 183 184 c->c_func = func; 185 c->c_arg = arg; 186 187 TRACE("callout_reset %p, func %p, arg %p\n", c, c->c_func, c->c_arg); 188 189 if (ticks >= 0) { 190 // reschedule or add this timer 191 if (c->due <= 0) 192 list_add_item(&sTimers, c); 193 194 c->due = system_time() + TICKS_2_USEC(_ticks); 195 196 // notify timer about the change if necessary 197 if (sTimeout > c->due) 198 release_sem(sWaitSem); 199 } 200 201 return canceled; 202 } 203 204 205 int 206 callout_schedule(struct callout *callout, int _ticks) 207 { 208 return callout_reset(callout, _ticks, callout->c_func, callout->c_arg); 209 } 210 211 212 int 213 _callout_stop_safe(struct callout *c, int safe) 214 { 215 MutexLocker locker(sLock); 216 217 if (c == NULL) { 218 printf("_callout_stop_safe called with NULL callout\n"); 219 return 0; 220 } 221 222 TRACE("_callout_stop_safe %p, func %p, arg %p\n", c, c->c_func, c->c_arg); 223 224 if (c->due <= 0) 225 return 0; 226 227 // this timer is scheduled, cancel it 228 list_remove_item(&sTimers, c); 229 c->due = 0; 230 return 1; 231 } 232 233 234 int 235 callout_pending(struct callout *c) 236 { 237 return c->due > 0; 238 } 239 240 241 int 242 callout_active(struct callout *c) 243 { 244 return c == sCurrentCallout; 245 } 246