| 1 | /*************************************************************************** |
|---|
| 2 | * (c)2004-2010 Broadcom Corporation |
|---|
| 3 | * |
|---|
| 4 | * This program is the proprietary software of Broadcom Corporation and/or its licensors, |
|---|
| 5 | * and may only be used, duplicated, modified or distributed pursuant to the terms and |
|---|
| 6 | * conditions of a separate, written license agreement executed between you and Broadcom |
|---|
| 7 | * (an "Authorized License"). Except as set forth in an Authorized License, Broadcom grants |
|---|
| 8 | * no license (express or implied), right to use, or waiver of any kind with respect to the |
|---|
| 9 | * Software, and Broadcom expressly reserves all rights in and to the Software and all |
|---|
| 10 | * intellectual property rights therein. IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU |
|---|
| 11 | * HAVE NO RIGHT TO USE THIS SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY |
|---|
| 12 | * NOTIFY BROADCOM AND DISCONTINUE ALL USE OF THE SOFTWARE. |
|---|
| 13 | * |
|---|
| 14 | * Except as expressly set forth in the Authorized License, |
|---|
| 15 | * |
|---|
| 16 | * 1. This program, including its structure, sequence and organization, constitutes the valuable trade |
|---|
| 17 | * secrets of Broadcom, and you shall use all reasonable efforts to protect the confidentiality thereof, |
|---|
| 18 | * and to use this information only in connection with your use of Broadcom integrated circuit products. |
|---|
| 19 | * |
|---|
| 20 | * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" |
|---|
| 21 | * AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, REPRESENTATIONS OR |
|---|
| 22 | * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO |
|---|
| 23 | * THE SOFTWARE. BROADCOM SPECIFICALLY DISCLAIMS ANY AND ALL IMPLIED WARRANTIES |
|---|
| 24 | * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, |
|---|
| 25 | * LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION |
|---|
| 26 | * OR CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING OUT OF |
|---|
| 27 | * USE OR PERFORMANCE OF THE SOFTWARE. |
|---|
| 28 | * |
|---|
| 29 | * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM OR ITS |
|---|
| 30 | * LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR |
|---|
| 31 | * EXEMPLARY DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO YOUR |
|---|
| 32 | * USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM HAS BEEN ADVISED OF |
|---|
| 33 | * THE POSSIBILITY OF SUCH DAMAGES; OR (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT |
|---|
| 34 | * ACTUALLY PAID FOR THE SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE |
|---|
| 35 | * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF |
|---|
| 36 | * ANY LIMITED REMEDY. |
|---|
| 37 | * |
|---|
| 38 | * $brcm_Workfile: b_os_scheduler.c $ |
|---|
| 39 | * $brcm_Revision: 15 $ |
|---|
| 40 | * $brcm_Date: 10/24/10 8:44p $ |
|---|
| 41 | * |
|---|
| 42 | * Description: |
|---|
| 43 | * API name: Scheduler |
|---|
| 44 | * Library routines for timer and thread event callback management |
|---|
| 45 | * |
|---|
| 46 | * Revision History: |
|---|
| 47 | * |
|---|
| 48 | * $brcm_Log: /nexus/lib/os/src/b_os_scheduler.c $ |
|---|
| 49 | * |
|---|
| 50 | * 15 10/24/10 8:44p agin |
|---|
| 51 | * SWNOOS-429: Use NO_OS_DIAGS define. |
|---|
| 52 | * |
|---|
| 53 | * 14 7/15/10 2:42p jtna |
|---|
| 54 | * SW7400-2711: Coverity Defect ID:19770 ATOMICITY |
|---|
| 55 | * |
|---|
| 56 | * 13 8/25/09 11:41a jgarrett |
|---|
| 57 | * SW7405-2624: Fixing callback list removal bug |
|---|
| 58 | * |
|---|
| 59 | * 12 8/18/09 6:46p katrep |
|---|
| 60 | * PR56109: Updated Fire_Callback api. |
|---|
| 61 | * |
|---|
| 62 | * 11 7/19/09 4:15p agin |
|---|
| 63 | * PR56924: Support B_Scheduler_Run for no-os. |
|---|
| 64 | * |
|---|
| 65 | * 10 4/16/09 12:29p jgarrett |
|---|
| 66 | * PR 41312: Fixing memory leak in event unregistration |
|---|
| 67 | * |
|---|
| 68 | * 9 8/13/08 3:56p tokushig |
|---|
| 69 | * PR42421: Fixed occasional hang in os scheduler during |
|---|
| 70 | * B_Scheduler_Destroy(). Make sure scheduler thread completely quits |
|---|
| 71 | * before destroy executes. |
|---|
| 72 | * |
|---|
| 73 | * 8 5/30/08 4:11p jgarrett |
|---|
| 74 | * PR 43201: Fixing continuous 0 timeouts |
|---|
| 75 | * |
|---|
| 76 | * 7 5/29/08 2:27p jgarrett |
|---|
| 77 | * PR 42334: Fixing back to back unregister/register of the same event |
|---|
| 78 | * |
|---|
| 79 | * 6 4/10/08 9:14a erickson |
|---|
| 80 | * PR41588: free correct pointer |
|---|
| 81 | * |
|---|
| 82 | * 5 4/10/08 9:13a erickson |
|---|
| 83 | * PR41587: added coverity hint |
|---|
| 84 | * |
|---|
| 85 | * 4 4/3/08 4:28p jgarrett |
|---|
| 86 | * PR 41312: Fixing event unregistration |
|---|
| 87 | * |
|---|
| 88 | * 3 3/31/08 1:13p jgarrett |
|---|
| 89 | * PR 41043: Making time routines public |
|---|
| 90 | * |
|---|
| 91 | * 2 3/10/08 8:54p jgarrett |
|---|
| 92 | * PR 40306: Fixing eventgroup registration |
|---|
| 93 | * |
|---|
| 94 | * 1 3/10/08 1:57p jgarrett |
|---|
| 95 | * PR 40306: Adding oslib |
|---|
| 96 | * |
|---|
| 97 | ***************************************************************************/ |
|---|
| 98 | |
|---|
| 99 | #include "b_os_lib.h" |
|---|
| 100 | #include "b_os_priv.h" |
|---|
| 101 | #include "blst_slist.h" |
|---|
| 102 | #include "blst_list.h" |
|---|
| 103 | #include "blst_squeue.h" |
|---|
| 104 | #include "blst_queue.h" |
|---|
| 105 | |
|---|
| 106 | BDBG_MODULE(b_os_scheduler); |
|---|
| 107 | |
|---|
| 108 | typedef struct B_SchedulerEvent |
|---|
| 109 | { |
|---|
| 110 | BLST_S_ENTRY(B_SchedulerEvent) node; /* Entry into event list */ |
|---|
| 111 | B_EventHandle event; /* Event Handle */ |
|---|
| 112 | B_MutexHandle mutex; /* Mutex to lock for callback */ |
|---|
| 113 | void (*pCallback)(void *); /* Callback routine */ |
|---|
| 114 | void *pContext; /* Callback parameter */ |
|---|
| 115 | bool deleted; /* Set to true when object is pending deletion */ |
|---|
| 116 | } B_SchedulerEvent; |
|---|
| 117 | |
|---|
| 118 | typedef struct B_SchedulerTimer |
|---|
| 119 | { |
|---|
| 120 | BLST_S_ENTRY(B_SchedulerTimer) node; /* Entry into timer list */ |
|---|
| 121 | B_Time time; /* Expiration time value */ |
|---|
| 122 | B_MutexHandle mutex; /* Mutex to lock for callback */ |
|---|
| 123 | void (*pCallback)(void *); /* Callback routine */ |
|---|
| 124 | void *pContext; /* Callback parameter */ |
|---|
| 125 | bool deleted; /* Set to true when object is pending deletion */ |
|---|
| 126 | } B_SchedulerTimer; |
|---|
| 127 | |
|---|
| 128 | typedef struct B_SchedulerCallback |
|---|
| 129 | { |
|---|
| 130 | struct B_Scheduler *pScheduler; /* Pointer back to parent */ |
|---|
| 131 | bool queued; /* true if callback is in the active list */ |
|---|
| 132 | bool armed; /* true if callback has been fired */ |
|---|
| 133 | bool stopped; /* true if callback has been stopped */ |
|---|
| 134 | void *pInstanceHandle; /* Object instance associated w/this callback */ |
|---|
| 135 | B_SchedulerCallbackFunction pFunction; /* Callback routine */ |
|---|
| 136 | void *pFuncParam1; /* First parameter to callback */ |
|---|
| 137 | int funcParam2; /* Second parameter to callback */ |
|---|
| 138 | BLST_S_ENTRY(B_SchedulerCallback) callbackNode; /* Node into scheduler's callback list */ |
|---|
| 139 | BLST_SQ_ENTRY(B_SchedulerCallback) activeCallbackNode; /* Node into scheduler's active callback list */ |
|---|
| 140 | bool deleted; /* Set to true when object is pending deletion */ |
|---|
| 141 | } B_SchedulerCallback; |
|---|
| 142 | |
|---|
| 143 | typedef enum B_SchedulerState |
|---|
| 144 | { |
|---|
| 145 | B_Scheduler_eRunning, |
|---|
| 146 | B_Scheduler_eStopping, |
|---|
| 147 | B_Scheduler_eStopped |
|---|
| 148 | } B_SchedulerState; |
|---|
| 149 | |
|---|
| 150 | /*************************************************************************** |
|---|
| 151 | Summary: |
|---|
| 152 | Scheduler Handle |
|---|
| 153 | |
|---|
| 154 | Description: |
|---|
| 155 | A Scheduler is a higher-level OS construct. A scheduler is responsible for |
|---|
| 156 | dispatching synchronized timer and event callbacks using a single thread. |
|---|
| 157 | ***************************************************************************/ |
|---|
| 158 | typedef struct B_Scheduler |
|---|
| 159 | { |
|---|
| 160 | B_MutexHandle mutex; |
|---|
| 161 | B_MutexHandle callbackMutex; |
|---|
| 162 | B_EventHandle controlEvent; |
|---|
| 163 | B_EventGroupHandle eventGroup; |
|---|
| 164 | |
|---|
| 165 | bool timerChanged; |
|---|
| 166 | bool callbackChanged; |
|---|
| 167 | |
|---|
| 168 | B_SchedulerState state; |
|---|
| 169 | |
|---|
| 170 | BLST_S_HEAD(eventList, B_SchedulerEvent) eventList; |
|---|
| 171 | BLST_S_HEAD(timerList, B_SchedulerTimer) timerList; |
|---|
| 172 | BLST_S_HEAD(callbackList, B_SchedulerCallback) callbackList; |
|---|
| 173 | BLST_SQ_HEAD(activeList, B_SchedulerCallback) activeCallbackList; |
|---|
| 174 | } B_Scheduler; |
|---|
| 175 | |
|---|
| 176 | /*************************************************************************** |
|---|
| 177 | Summary: |
|---|
| 178 | Get Default Scheduler Settings |
|---|
| 179 | ***************************************************************************/ |
|---|
| 180 | void B_Scheduler_GetDefaultSettings( |
|---|
| 181 | B_SchedulerSettings *pSettings /* [out] */ |
|---|
| 182 | ) |
|---|
| 183 | { |
|---|
| 184 | BSTD_UNUSED(pSettings); /* Empty Structure */ |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | /*************************************************************************** |
|---|
| 188 | Summary: |
|---|
| 189 | Craete a scheduler |
|---|
| 190 | ***************************************************************************/ |
|---|
| 191 | B_SchedulerHandle B_Scheduler_Create( |
|---|
| 192 | const B_SchedulerSettings *pSettings |
|---|
| 193 | ) |
|---|
| 194 | { |
|---|
| 195 | B_Scheduler *pScheduler; |
|---|
| 196 | B_Error errCode; |
|---|
| 197 | |
|---|
| 198 | BSTD_UNUSED(pSettings); /* Empty Structure */ |
|---|
| 199 | |
|---|
| 200 | pScheduler = B_Os_Calloc(1, sizeof(B_Scheduler)); |
|---|
| 201 | if ( NULL == pScheduler ) |
|---|
| 202 | { |
|---|
| 203 | errCode = BERR_TRACE(B_ERROR_OUT_OF_MEMORY); |
|---|
| 204 | goto err_malloc; |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | pScheduler->mutex = B_Mutex_Create(NULL); |
|---|
| 208 | if ( NULL == pScheduler->mutex ) |
|---|
| 209 | { |
|---|
| 210 | errCode = BERR_TRACE(B_ERROR_OS_ERROR); |
|---|
| 211 | goto err_mutex; |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | pScheduler->callbackMutex = B_Mutex_Create(NULL); |
|---|
| 215 | if ( NULL == pScheduler->callbackMutex ) |
|---|
| 216 | { |
|---|
| 217 | errCode = BERR_TRACE(B_ERROR_OS_ERROR); |
|---|
| 218 | goto err_callback_mutex; |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | pScheduler->controlEvent = B_Event_Create(NULL); |
|---|
| 222 | if ( NULL == pScheduler->controlEvent ) |
|---|
| 223 | { |
|---|
| 224 | errCode = BERR_TRACE(B_ERROR_OS_ERROR); |
|---|
| 225 | goto err_event; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | pScheduler->eventGroup = B_EventGroup_Create(NULL); |
|---|
| 229 | if ( NULL == pScheduler->eventGroup ) |
|---|
| 230 | { |
|---|
| 231 | errCode = BERR_TRACE(B_ERROR_OS_ERROR); |
|---|
| 232 | goto err_group; |
|---|
| 233 | } |
|---|
| 234 | |
|---|
| 235 | /* Add control event to group */ |
|---|
| 236 | B_EventGroup_AddEvent(pScheduler->eventGroup, pScheduler->controlEvent); |
|---|
| 237 | |
|---|
| 238 | /* Success */ |
|---|
| 239 | return pScheduler; |
|---|
| 240 | |
|---|
| 241 | err_group: |
|---|
| 242 | B_Event_Destroy(pScheduler->controlEvent); |
|---|
| 243 | err_event: |
|---|
| 244 | B_Mutex_Destroy(pScheduler->callbackMutex); |
|---|
| 245 | err_callback_mutex: |
|---|
| 246 | B_Mutex_Destroy(pScheduler->mutex); |
|---|
| 247 | err_mutex: |
|---|
| 248 | B_Os_Free(pScheduler); |
|---|
| 249 | err_malloc: |
|---|
| 250 | return NULL; |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | |
|---|
| 254 | /*************************************************************************** |
|---|
| 255 | Summary: |
|---|
| 256 | Destroy a scheduler |
|---|
| 257 | ***************************************************************************/ |
|---|
| 258 | void B_Scheduler_Destroy( |
|---|
| 259 | B_SchedulerHandle scheduler |
|---|
| 260 | ) |
|---|
| 261 | { |
|---|
| 262 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 263 | BDBG_ASSERT(B_Scheduler_eRunning != scheduler->state); /* App should always stop scheduler before destroying to avoid races */ |
|---|
| 264 | |
|---|
| 265 | while (B_Scheduler_eStopped != scheduler->state) |
|---|
| 266 | { |
|---|
| 267 | B_Thread_Sleep(10); /* wait for scheduler thread to stop gracefully */ |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 271 | { |
|---|
| 272 | B_SchedulerTimer *pTimer; |
|---|
| 273 | B_SchedulerEvent *pEvent; |
|---|
| 274 | B_SchedulerCallback *pCallback; |
|---|
| 275 | |
|---|
| 276 | while ( (pTimer = BLST_S_FIRST(&scheduler->timerList)) ) |
|---|
| 277 | { |
|---|
| 278 | BLST_S_REMOVE_HEAD(&scheduler->timerList, node); |
|---|
| 279 | BDBG_WRN(("Cleaning up timer %p", pTimer)); |
|---|
| 280 | B_Os_Free(pTimer); |
|---|
| 281 | } |
|---|
| 282 | while ( (pEvent = BLST_S_FIRST(&scheduler->eventList)) ) |
|---|
| 283 | { |
|---|
| 284 | BLST_S_REMOVE_HEAD(&scheduler->eventList, node); |
|---|
| 285 | BDBG_WRN(("Cleaning up event %p", pEvent)); |
|---|
| 286 | if ( !pEvent->deleted ) |
|---|
| 287 | { |
|---|
| 288 | B_EventGroup_RemoveEvent(scheduler->eventGroup, pEvent->event); |
|---|
| 289 | } |
|---|
| 290 | B_Os_Free(pEvent); |
|---|
| 291 | } |
|---|
| 292 | while ( (pCallback = BLST_S_FIRST(&scheduler->callbackList)) ) |
|---|
| 293 | { |
|---|
| 294 | BLST_S_REMOVE_HEAD(&scheduler->callbackList, callbackNode); |
|---|
| 295 | BDBG_WRN(("Cleaning up callback %p", pCallback)); |
|---|
| 296 | B_Os_Free(pCallback); |
|---|
| 297 | } |
|---|
| 298 | } |
|---|
| 299 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 300 | B_EventGroup_RemoveEvent(scheduler->eventGroup, scheduler->controlEvent); |
|---|
| 301 | B_EventGroup_Destroy(scheduler->eventGroup); |
|---|
| 302 | B_Event_Destroy(scheduler->controlEvent); |
|---|
| 303 | B_Mutex_Destroy(scheduler->callbackMutex); |
|---|
| 304 | B_Mutex_Destroy(scheduler->mutex); |
|---|
| 305 | B_Os_Free(scheduler); |
|---|
| 306 | } |
|---|
| 307 | |
|---|
| 308 | /*************************************************************************** |
|---|
| 309 | Summary: |
|---|
| 310 | Register an event with a scheduler |
|---|
| 311 | |
|---|
| 312 | Description: |
|---|
| 313 | This will cause the scheduler to call the sepecified EventCallback function |
|---|
| 314 | when the specified event has been set. Prior to calling the callback, |
|---|
| 315 | the specified mutex will be automatically locked. After the callback |
|---|
| 316 | returns, the mutex will be unlocked. |
|---|
| 317 | ***************************************************************************/ |
|---|
| 318 | B_SchedulerEventId B_Scheduler_RegisterEvent( |
|---|
| 319 | B_SchedulerHandle scheduler, |
|---|
| 320 | B_MutexHandle mutex, /* Mutex to lock prior to calling callback */ |
|---|
| 321 | B_EventHandle event, /* Event that will trigger the callback */ |
|---|
| 322 | B_EventCallback callback, /* Callback routine to execute when event is set */ |
|---|
| 323 | void *pContext /* Value passed to callback routine */ |
|---|
| 324 | ) |
|---|
| 325 | { |
|---|
| 326 | B_Error errCode; |
|---|
| 327 | B_SchedulerEvent *pEvent; |
|---|
| 328 | |
|---|
| 329 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 330 | BDBG_ASSERT(NULL != mutex); |
|---|
| 331 | BDBG_ASSERT(NULL != event); |
|---|
| 332 | BDBG_ASSERT(NULL != callback); |
|---|
| 333 | |
|---|
| 334 | pEvent = B_Os_Malloc(sizeof(B_SchedulerEvent)); |
|---|
| 335 | if ( NULL == pEvent ) |
|---|
| 336 | { |
|---|
| 337 | errCode = BERR_TRACE(B_ERROR_OUT_OF_MEMORY); |
|---|
| 338 | return NULL; |
|---|
| 339 | } |
|---|
| 340 | |
|---|
| 341 | pEvent->event = event; |
|---|
| 342 | pEvent->mutex = mutex; |
|---|
| 343 | pEvent->pCallback = callback; |
|---|
| 344 | pEvent->pContext = pContext; |
|---|
| 345 | pEvent->deleted = false; |
|---|
| 346 | |
|---|
| 347 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 348 | #if BDBG_DEBUG_BUILD |
|---|
| 349 | /* Scan for duplicates */ |
|---|
| 350 | /* TODO: Should this be supported as it was in nexus? */ |
|---|
| 351 | { |
|---|
| 352 | B_SchedulerEvent *pNode; |
|---|
| 353 | for ( pNode = BLST_S_FIRST(&scheduler->eventList); |
|---|
| 354 | NULL != pNode; |
|---|
| 355 | pNode = BLST_S_NEXT(pNode, node) ) |
|---|
| 356 | { |
|---|
| 357 | if ( pNode->event == event && !pNode->deleted ) |
|---|
| 358 | { |
|---|
| 359 | BDBG_ERR(("Duplicate event registration. An event can only be registered once per scheduler.")); |
|---|
| 360 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 361 | B_Os_Free(pEvent); |
|---|
| 362 | return NULL; |
|---|
| 363 | } |
|---|
| 364 | } |
|---|
| 365 | } |
|---|
| 366 | #endif |
|---|
| 367 | B_EventGroup_AddEvent(scheduler->eventGroup, event); |
|---|
| 368 | BLST_S_INSERT_HEAD(&scheduler->eventList, pEvent, node); |
|---|
| 369 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 370 | |
|---|
| 371 | return pEvent; |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | /*************************************************************************** |
|---|
| 375 | Summary: |
|---|
| 376 | Un-Register an event from the scheduler |
|---|
| 377 | ***************************************************************************/ |
|---|
| 378 | void B_Scheduler_UnregisterEvent( |
|---|
| 379 | B_SchedulerHandle scheduler, |
|---|
| 380 | B_SchedulerEventId eventId |
|---|
| 381 | ) |
|---|
| 382 | { |
|---|
| 383 | B_SchedulerEvent *pNode; |
|---|
| 384 | |
|---|
| 385 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 386 | BDBG_ASSERT(NULL != eventId); |
|---|
| 387 | |
|---|
| 388 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 389 | |
|---|
| 390 | for ( pNode = BLST_S_FIRST(&scheduler->eventList); |
|---|
| 391 | NULL != pNode; |
|---|
| 392 | pNode = BLST_S_NEXT(pNode, node) ) |
|---|
| 393 | { |
|---|
| 394 | if ( pNode == eventId ) |
|---|
| 395 | { |
|---|
| 396 | break; |
|---|
| 397 | } |
|---|
| 398 | } |
|---|
| 399 | |
|---|
| 400 | if ( NULL == pNode || pNode->deleted ) |
|---|
| 401 | { |
|---|
| 402 | BDBG_ERR(("Event not registered with this scheduler")); |
|---|
| 403 | BDBG_ASSERT(NULL != pNode); |
|---|
| 404 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 405 | return; |
|---|
| 406 | } |
|---|
| 407 | |
|---|
| 408 | /* Remove from group */ |
|---|
| 409 | /* TODO: Should duplicate event registration be supported? */ |
|---|
| 410 | B_EventGroup_RemoveEvent(scheduler->eventGroup, eventId->event); |
|---|
| 411 | |
|---|
| 412 | /* Notify scheduler of change */ |
|---|
| 413 | eventId->deleted = true; |
|---|
| 414 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 415 | B_Event_Set(scheduler->controlEvent); |
|---|
| 416 | } |
|---|
| 417 | |
|---|
| 418 | /*************************************************************************** |
|---|
| 419 | Summary: |
|---|
| 420 | Schedule a timer |
|---|
| 421 | |
|---|
| 422 | Description: |
|---|
| 423 | This will start a timer that will lock the specified mutex and call the |
|---|
| 424 | specified callback routine when the timer expires. Timers are one-shot, |
|---|
| 425 | and they must be rescheduled to repeat. Re-scheduling the timer from the |
|---|
| 426 | callback itself is allowed. After the timer expires, the TimerId value |
|---|
| 427 | returned becomes invalid and should be discarded. |
|---|
| 428 | ***************************************************************************/ |
|---|
| 429 | B_SchedulerTimerId B_Scheduler_StartTimer( |
|---|
| 430 | B_SchedulerHandle scheduler, |
|---|
| 431 | B_MutexHandle mutex, /* Mutex to lock prior to calling callback */ |
|---|
| 432 | int timeoutMsec, /* Timer expiration time in msec */ |
|---|
| 433 | B_TimerCallback callback, /* Callback to call when timer expires */ |
|---|
| 434 | void *pContext /* Value passed to callback routine */ |
|---|
| 435 | ) |
|---|
| 436 | { |
|---|
| 437 | B_Error errCode; |
|---|
| 438 | B_SchedulerTimer *pTimer, *pNode, *pPrev=NULL; |
|---|
| 439 | |
|---|
| 440 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 441 | BDBG_ASSERT(NULL != mutex); |
|---|
| 442 | BDBG_ASSERT(timeoutMsec > 0); |
|---|
| 443 | BDBG_ASSERT(NULL != callback); |
|---|
| 444 | |
|---|
| 445 | pTimer = B_Os_Malloc(sizeof(B_SchedulerTimer)); |
|---|
| 446 | if ( NULL == pTimer ) |
|---|
| 447 | { |
|---|
| 448 | errCode = BERR_TRACE(B_ERROR_OUT_OF_MEMORY); |
|---|
| 449 | return NULL; |
|---|
| 450 | } |
|---|
| 451 | |
|---|
| 452 | pTimer->mutex = mutex; |
|---|
| 453 | B_Time_Get(&pTimer->time); |
|---|
| 454 | B_Time_Add(&pTimer->time, timeoutMsec); |
|---|
| 455 | pTimer->pCallback = callback; |
|---|
| 456 | pTimer->pContext = pContext; |
|---|
| 457 | pTimer->deleted = false; |
|---|
| 458 | |
|---|
| 459 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 460 | { |
|---|
| 461 | scheduler->timerChanged = true; |
|---|
| 462 | |
|---|
| 463 | for ( pNode = BLST_S_FIRST(&scheduler->timerList); |
|---|
| 464 | NULL != pNode; |
|---|
| 465 | pNode = BLST_S_NEXT(pNode, node) ) |
|---|
| 466 | { |
|---|
| 467 | if ( B_Time_Diff(&pNode->time, &pTimer->time) > 0 ) |
|---|
| 468 | { |
|---|
| 469 | break; |
|---|
| 470 | } |
|---|
| 471 | pPrev = pNode; |
|---|
| 472 | } |
|---|
| 473 | |
|---|
| 474 | if ( NULL == pPrev ) |
|---|
| 475 | { |
|---|
| 476 | BLST_S_INSERT_HEAD(&scheduler->timerList, pTimer, node); |
|---|
| 477 | B_Event_Set(scheduler->controlEvent); /* Immediately wake thread, timeout will change */ |
|---|
| 478 | } |
|---|
| 479 | else |
|---|
| 480 | { |
|---|
| 481 | BLST_S_INSERT_AFTER(&scheduler->timerList, pPrev, pTimer, node); |
|---|
| 482 | } |
|---|
| 483 | } |
|---|
| 484 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 485 | |
|---|
| 486 | return pTimer; |
|---|
| 487 | } |
|---|
| 488 | |
|---|
| 489 | /*************************************************************************** |
|---|
| 490 | Summary: |
|---|
| 491 | Cancel a timer |
|---|
| 492 | |
|---|
| 493 | Description: |
|---|
| 494 | This will cancel a timer that was previously started. If the timer has |
|---|
| 495 | already expired, this call is safe. If the callback has actually been |
|---|
| 496 | called, this will generate a warning message. If the timer has expired and |
|---|
| 497 | the callback is pending on the mutex, the timer will be discarded and no |
|---|
| 498 | warning will occur. |
|---|
| 499 | ***************************************************************************/ |
|---|
| 500 | void B_Scheduler_CancelTimer( |
|---|
| 501 | B_SchedulerHandle scheduler, |
|---|
| 502 | B_SchedulerTimerId timerId |
|---|
| 503 | ) |
|---|
| 504 | { |
|---|
| 505 | B_SchedulerTimer *pNode, *pPrev=NULL; |
|---|
| 506 | |
|---|
| 507 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 508 | BDBG_ASSERT(NULL != timerId); |
|---|
| 509 | |
|---|
| 510 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 511 | { |
|---|
| 512 | for ( pNode = BLST_S_FIRST(&scheduler->timerList); |
|---|
| 513 | NULL != pNode; |
|---|
| 514 | pNode = BLST_S_NEXT(pNode, node) ) |
|---|
| 515 | { |
|---|
| 516 | if ( pNode == timerId ) |
|---|
| 517 | { |
|---|
| 518 | break; |
|---|
| 519 | } |
|---|
| 520 | pPrev = pNode; |
|---|
| 521 | } |
|---|
| 522 | if ( NULL == pNode || pNode->deleted == true ) |
|---|
| 523 | { |
|---|
| 524 | BDBG_ERR(("Cancelling stale timer %p", timerId)); |
|---|
| 525 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 526 | return; |
|---|
| 527 | } |
|---|
| 528 | |
|---|
| 529 | scheduler->timerChanged = true; |
|---|
| 530 | timerId->deleted = true; /* Actual work must be done by main thread to avoid races */ |
|---|
| 531 | } |
|---|
| 532 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 533 | B_Event_Set(scheduler->controlEvent); |
|---|
| 534 | } |
|---|
| 535 | |
|---|
| 536 | /*************************************************************************** |
|---|
| 537 | Summary: |
|---|
| 538 | Run a scheduler |
|---|
| 539 | |
|---|
| 540 | Description: |
|---|
| 541 | This routine will drive the scheduler execution. It will not return until |
|---|
| 542 | B_Scheduler_Stop is called. |
|---|
| 543 | ***************************************************************************/ |
|---|
| 544 | void B_Scheduler_Run( |
|---|
| 545 | B_SchedulerHandle scheduler |
|---|
| 546 | ) |
|---|
| 547 | { |
|---|
| 548 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 549 | |
|---|
| 550 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 551 | |
|---|
| 552 | while ( B_Scheduler_eRunning == scheduler->state ) |
|---|
| 553 | { |
|---|
| 554 | int i; |
|---|
| 555 | B_Error errCode; |
|---|
| 556 | B_EventHandle pEvents[5]; |
|---|
| 557 | B_SchedulerEvent *pEvent; |
|---|
| 558 | unsigned numEvents, eventsLeft; |
|---|
| 559 | const int maxTries=4; |
|---|
| 560 | long timeout = B_WAIT_FOREVER; |
|---|
| 561 | |
|---|
| 562 | /* First order of business. Handle Expired timers */ |
|---|
| 563 | /* Mutex is held */ |
|---|
| 564 | for ( i = 0; i < maxTries; i++ ) |
|---|
| 565 | { |
|---|
| 566 | B_Time currentTime; |
|---|
| 567 | /* coverity[use_after_free] */ |
|---|
| 568 | B_SchedulerTimer *pFirstTimer = BLST_S_FIRST(&scheduler->timerList); |
|---|
| 569 | if ( NULL == pFirstTimer ) |
|---|
| 570 | { |
|---|
| 571 | timeout = B_WAIT_FOREVER; |
|---|
| 572 | break; |
|---|
| 573 | } |
|---|
| 574 | B_Time_Get(¤tTime); |
|---|
| 575 | timeout = B_Time_Diff(&pFirstTimer->time, ¤tTime); |
|---|
| 576 | if ( timeout<=0 || pFirstTimer->deleted ) |
|---|
| 577 | { |
|---|
| 578 | if( !pFirstTimer->deleted ) |
|---|
| 579 | { |
|---|
| 580 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 581 | B_Mutex_Lock(pFirstTimer->mutex); |
|---|
| 582 | if ( !pFirstTimer->deleted ) |
|---|
| 583 | { |
|---|
| 584 | pFirstTimer->pCallback(pFirstTimer->pContext); |
|---|
| 585 | } |
|---|
| 586 | B_Mutex_Unlock(pFirstTimer->mutex); |
|---|
| 587 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 588 | } |
|---|
| 589 | if ( !scheduler->timerChanged ) |
|---|
| 590 | { |
|---|
| 591 | BDBG_ASSERT(pFirstTimer == BLST_S_FIRST(&scheduler->timerList)); |
|---|
| 592 | BLST_S_REMOVE_HEAD(&scheduler->timerList, node); |
|---|
| 593 | } |
|---|
| 594 | else |
|---|
| 595 | { |
|---|
| 596 | scheduler->timerChanged = false; |
|---|
| 597 | BLST_S_REMOVE(&scheduler->timerList, pFirstTimer, B_SchedulerTimer, node); |
|---|
| 598 | } |
|---|
| 599 | B_Os_Free(pFirstTimer); |
|---|
| 600 | } |
|---|
| 601 | else if ( timeout > 0 ) |
|---|
| 602 | { |
|---|
| 603 | break; |
|---|
| 604 | } |
|---|
| 605 | } |
|---|
| 606 | if ( i >= maxTries ) |
|---|
| 607 | { |
|---|
| 608 | /* Clear timeout if we exceeded the max number of timers */ |
|---|
| 609 | timeout = 0; |
|---|
| 610 | } |
|---|
| 611 | |
|---|
| 612 | /* Mutex still held here */ |
|---|
| 613 | for ( i = 0; i < maxTries; i++ ) |
|---|
| 614 | { |
|---|
| 615 | B_SchedulerCallback *pCallback; |
|---|
| 616 | pCallback = BLST_SQ_FIRST(&scheduler->activeCallbackList); |
|---|
| 617 | if( NULL == pCallback ) |
|---|
| 618 | { |
|---|
| 619 | break; |
|---|
| 620 | } |
|---|
| 621 | BDBG_ASSERT(pCallback->queued); |
|---|
| 622 | pCallback->queued = false; |
|---|
| 623 | BLST_SQ_REMOVE_HEAD(&scheduler->activeCallbackList, activeCallbackNode); |
|---|
| 624 | if ( pCallback->deleted ) |
|---|
| 625 | { |
|---|
| 626 | /* coverity[use] */ |
|---|
| 627 | BLST_S_REMOVE(&scheduler->callbackList, pCallback, B_SchedulerCallback, callbackNode); |
|---|
| 628 | B_Os_Free(pCallback); |
|---|
| 629 | continue; |
|---|
| 630 | } |
|---|
| 631 | B_Mutex_Unlock(scheduler->mutex); /* Unlock before acquiring other mutex */ |
|---|
| 632 | B_Mutex_Lock(scheduler->callbackMutex); /* This prevents racing with StopCallbacks */ |
|---|
| 633 | if ( pCallback->armed && !pCallback->stopped ) |
|---|
| 634 | { |
|---|
| 635 | pCallback->armed = false; |
|---|
| 636 | pCallback->pFunction(pCallback->pFuncParam1, pCallback->funcParam2); |
|---|
| 637 | } |
|---|
| 638 | B_Mutex_Unlock(scheduler->callbackMutex); |
|---|
| 639 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 640 | } |
|---|
| 641 | if( i == maxTries ) |
|---|
| 642 | { |
|---|
| 643 | timeout = 0; |
|---|
| 644 | } |
|---|
| 645 | |
|---|
| 646 | /* Mutex still held here -- need to yield before waiting on event group */ |
|---|
| 647 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 648 | |
|---|
| 649 | /* Wait for any event or a timeout */ |
|---|
| 650 | errCode = B_EventGroup_Wait(scheduler->eventGroup, timeout, pEvents, sizeof(pEvents)/sizeof(*pEvents), &numEvents); |
|---|
| 651 | |
|---|
| 652 | /* Acquire scheduler lock */ |
|---|
| 653 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 654 | if ( B_Scheduler_eRunning != scheduler->state ) |
|---|
| 655 | { |
|---|
| 656 | break; |
|---|
| 657 | } |
|---|
| 658 | if ( errCode == B_ERROR_TIMEOUT ) |
|---|
| 659 | { |
|---|
| 660 | #ifdef NO_OS_DIAGS |
|---|
| 661 | return; |
|---|
| 662 | #else |
|---|
| 663 | continue; |
|---|
| 664 | #endif |
|---|
| 665 | } |
|---|
| 666 | else if ( errCode != B_ERROR_SUCCESS ) |
|---|
| 667 | { |
|---|
| 668 | BDBG_ERR(("Unexpected event group error %u. Terminating.", errCode)); |
|---|
| 669 | break; |
|---|
| 670 | } |
|---|
| 671 | |
|---|
| 672 | /* Event Processing */ |
|---|
| 673 | for ( eventsLeft=numEvents, pEvent=BLST_S_FIRST(&scheduler->eventList); |
|---|
| 674 | NULL != pEvent && eventsLeft > 0; |
|---|
| 675 | ) |
|---|
| 676 | { |
|---|
| 677 | unsigned j; |
|---|
| 678 | |
|---|
| 679 | if ( pEvent->deleted ) |
|---|
| 680 | { |
|---|
| 681 | B_SchedulerEvent *pDeletedEvent = pEvent; |
|---|
| 682 | pEvent = BLST_S_NEXT(pEvent, node); |
|---|
| 683 | /* coverity[use_after_free] */ |
|---|
| 684 | BLST_S_REMOVE(&scheduler->eventList, pDeletedEvent, B_SchedulerEvent, node); /* TODO: This is inefficient but rarely called - consider saving prev */ |
|---|
| 685 | B_Os_Free(pDeletedEvent); |
|---|
| 686 | continue; |
|---|
| 687 | } |
|---|
| 688 | /* For each node in the list, test if any of the set events match */ |
|---|
| 689 | for ( j=0; j<numEvents; j++ ) |
|---|
| 690 | { |
|---|
| 691 | if ( pEvents[j] == pEvent->event ) |
|---|
| 692 | { |
|---|
| 693 | /* TODO: Consider allowing duplicate events as nexus did */ |
|---|
| 694 | /* TODO: move "hot" entries to head of the list */ |
|---|
| 695 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 696 | B_Mutex_Lock(pEvent->mutex); |
|---|
| 697 | if ( !pEvent->deleted ) |
|---|
| 698 | { |
|---|
| 699 | pEvent->pCallback(pEvent->pContext); |
|---|
| 700 | } |
|---|
| 701 | B_Mutex_Unlock(pEvent->mutex); |
|---|
| 702 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 703 | /* This value is decremented only for matching events. */ |
|---|
| 704 | /* As a side-effect, if another event (e.g. controlEvent) is set, the */ |
|---|
| 705 | /* entire list will be walked and deleted items will be purged */ |
|---|
| 706 | eventsLeft--; |
|---|
| 707 | /* Found a match, no need to continue searching this node. Advance to the next */ |
|---|
| 708 | break; |
|---|
| 709 | } |
|---|
| 710 | } |
|---|
| 711 | /* TODO: Why was this conditional? -- seems inefficient if we've already found a match */ |
|---|
| 712 | /*if ( j==numEvents ) */ |
|---|
| 713 | { |
|---|
| 714 | pEvent = BLST_S_NEXT(pEvent, node); |
|---|
| 715 | } |
|---|
| 716 | } |
|---|
| 717 | /* End event Processing */ |
|---|
| 718 | |
|---|
| 719 | /* Loop will continue. Mutex is still locked */ |
|---|
| 720 | #ifdef NO_OS_DIAGS |
|---|
| 721 | return; |
|---|
| 722 | #endif |
|---|
| 723 | } |
|---|
| 724 | |
|---|
| 725 | scheduler->state = B_Scheduler_eStopped; |
|---|
| 726 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 727 | /* Done */ |
|---|
| 728 | } |
|---|
| 729 | |
|---|
| 730 | /*************************************************************************** |
|---|
| 731 | Summary: |
|---|
| 732 | Stop a scheduler |
|---|
| 733 | |
|---|
| 734 | Description: |
|---|
| 735 | This routine will stop the scheduler execution. After this is called, |
|---|
| 736 | B_Scheduler_Run will return. This routine can be called by any thread, |
|---|
| 737 | including the scheduler thread itself. It is not synchronized, so this call |
|---|
| 738 | may return before B_Scheduler_Run returns. |
|---|
| 739 | ***************************************************************************/ |
|---|
| 740 | void B_Scheduler_Stop( |
|---|
| 741 | B_SchedulerHandle scheduler |
|---|
| 742 | ) |
|---|
| 743 | { |
|---|
| 744 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 745 | scheduler->state = B_Scheduler_eStopping; |
|---|
| 746 | B_Event_Set(scheduler->controlEvent); |
|---|
| 747 | } |
|---|
| 748 | |
|---|
| 749 | /*************************************************************************** |
|---|
| 750 | Summary: |
|---|
| 751 | Get default settings for a scheduler callback instance |
|---|
| 752 | ***************************************************************************/ |
|---|
| 753 | void B_SchedulerCallback_GetDefaultSettings( |
|---|
| 754 | B_SchedulerCallbackSettings *pSettings /* [out] */ |
|---|
| 755 | ) |
|---|
| 756 | { |
|---|
| 757 | BSTD_UNUSED(pSettings); /* Empty for now */ |
|---|
| 758 | } |
|---|
| 759 | |
|---|
| 760 | /*************************************************************************** |
|---|
| 761 | Summary: |
|---|
| 762 | Register an asynchronous callback |
|---|
| 763 | |
|---|
| 764 | Description: |
|---|
| 765 | This will cancel a timer that was previously started. If the timer has |
|---|
| 766 | already expired, this call is safe. If the callback has actually been |
|---|
| 767 | called, this will generate a warning message. If the timer has expired and |
|---|
| 768 | the callback is pending on the mutex, the timer will be discarded and no |
|---|
| 769 | warning will occur. |
|---|
| 770 | ***************************************************************************/ |
|---|
| 771 | B_SchedulerCallbackHandle B_SchedulerCallback_Create( |
|---|
| 772 | B_SchedulerHandle scheduler, |
|---|
| 773 | void *pInstanceHandle, /* Pass the handle to the application object's instance here */ |
|---|
| 774 | const B_SchedulerCallbackSettings *pSettings /* Pass NULL for defaults */ |
|---|
| 775 | ) |
|---|
| 776 | { |
|---|
| 777 | B_Error errCode; |
|---|
| 778 | B_SchedulerCallback *pCallback; |
|---|
| 779 | |
|---|
| 780 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 781 | BDBG_ASSERT(NULL != pInstanceHandle); |
|---|
| 782 | |
|---|
| 783 | BSTD_UNUSED(pSettings); /* Empty for now */ |
|---|
| 784 | |
|---|
| 785 | pCallback = B_Os_Calloc(1, sizeof(B_SchedulerCallback)); |
|---|
| 786 | if ( NULL == pCallback ) |
|---|
| 787 | { |
|---|
| 788 | errCode = BERR_TRACE(B_ERROR_OUT_OF_MEMORY); |
|---|
| 789 | return NULL; |
|---|
| 790 | } |
|---|
| 791 | |
|---|
| 792 | pCallback->pScheduler = scheduler; |
|---|
| 793 | pCallback->pInstanceHandle = pInstanceHandle; |
|---|
| 794 | |
|---|
| 795 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 796 | { |
|---|
| 797 | BLST_S_INSERT_HEAD(&scheduler->callbackList, pCallback, callbackNode); |
|---|
| 798 | } |
|---|
| 799 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 800 | |
|---|
| 801 | return pCallback; |
|---|
| 802 | } |
|---|
| 803 | |
|---|
| 804 | /*************************************************************************** |
|---|
| 805 | Summary: |
|---|
| 806 | Set the function to be used in an asynchronous callback |
|---|
| 807 | ***************************************************************************/ |
|---|
| 808 | B_Error B_SchedulerCallback_SetFunction( |
|---|
| 809 | B_SchedulerCallbackHandle callbackHandle, |
|---|
| 810 | B_SchedulerCallbackFunction callbackFunction, /* Function to be called */ |
|---|
| 811 | void *pParam1, /* First function parameter */ |
|---|
| 812 | int param2 /* Second function parameter */ |
|---|
| 813 | ) |
|---|
| 814 | { |
|---|
| 815 | BDBG_ASSERT(NULL != callbackHandle); |
|---|
| 816 | BDBG_ASSERT(NULL != callbackFunction); |
|---|
| 817 | |
|---|
| 818 | B_Mutex_Lock(callbackHandle->pScheduler->mutex); |
|---|
| 819 | { |
|---|
| 820 | callbackHandle->pFunction = callbackFunction; |
|---|
| 821 | callbackHandle->pFuncParam1 = pParam1; |
|---|
| 822 | callbackHandle->funcParam2 = param2; |
|---|
| 823 | } |
|---|
| 824 | B_Mutex_Unlock(callbackHandle->pScheduler->mutex); |
|---|
| 825 | |
|---|
| 826 | return B_ERROR_SUCCESS; |
|---|
| 827 | } |
|---|
| 828 | |
|---|
| 829 | /*************************************************************************** |
|---|
| 830 | Summary: |
|---|
| 831 | Fire an asynchronous callback |
|---|
| 832 | |
|---|
| 833 | Description: |
|---|
| 834 | This will schedule the callback to be executed when the scheduler becomes |
|---|
| 835 | idle. If the same callback is fired multiple times before it is able to |
|---|
| 836 | be called, only a single application callback will be made. |
|---|
| 837 | ***************************************************************************/ |
|---|
| 838 | void B_SchedulerCallback_Fire( |
|---|
| 839 | B_SchedulerCallbackHandle callback |
|---|
| 840 | ) |
|---|
| 841 | { |
|---|
| 842 | BDBG_ASSERT(NULL != callback); |
|---|
| 843 | |
|---|
| 844 | B_Mutex_Lock(callback->pScheduler->mutex); |
|---|
| 845 | { |
|---|
| 846 | if ( !callback->queued ) |
|---|
| 847 | { |
|---|
| 848 | BLST_SQ_INSERT_TAIL(&callback->pScheduler->activeCallbackList, callback, activeCallbackNode); |
|---|
| 849 | callback->queued = true; |
|---|
| 850 | } |
|---|
| 851 | callback->armed = true; |
|---|
| 852 | } |
|---|
| 853 | B_Mutex_Unlock(callback->pScheduler->mutex); |
|---|
| 854 | B_Event_Set(callback->pScheduler->controlEvent); |
|---|
| 855 | } |
|---|
| 856 | |
|---|
| 857 | /*************************************************************************** |
|---|
| 858 | Summary: |
|---|
| 859 | Destroying an asynchronous callback |
|---|
| 860 | ***************************************************************************/ |
|---|
| 861 | void B_SchedulerCallback_Destroy( |
|---|
| 862 | B_SchedulerCallbackHandle callback |
|---|
| 863 | ) |
|---|
| 864 | { |
|---|
| 865 | BDBG_ASSERT(NULL != callback); |
|---|
| 866 | |
|---|
| 867 | B_Mutex_Lock(callback->pScheduler->mutex); |
|---|
| 868 | { |
|---|
| 869 | if ( !callback->queued ) |
|---|
| 870 | { |
|---|
| 871 | BLST_SQ_INSERT_TAIL(&callback->pScheduler->activeCallbackList, callback, activeCallbackNode); |
|---|
| 872 | callback->queued = true; |
|---|
| 873 | } |
|---|
| 874 | callback->deleted = true; |
|---|
| 875 | } |
|---|
| 876 | B_Mutex_Unlock(callback->pScheduler->mutex); |
|---|
| 877 | } |
|---|
| 878 | |
|---|
| 879 | /*************************************************************************** |
|---|
| 880 | Summary: |
|---|
| 881 | Cancel all pending callbacks for this object instance |
|---|
| 882 | |
|---|
| 883 | Description: |
|---|
| 884 | Because of the asynchronous nature of scheduler callbacks, race conditions |
|---|
| 885 | can occur when closing objects that have pending callbacks. In order to |
|---|
| 886 | guarantee that all pending callbacks have been stopped for an instance, |
|---|
| 887 | this function should be called as part of closing the object instance. |
|---|
| 888 | |
|---|
| 889 | NOTE: It is important to call this routine before acquiring your instance's |
|---|
| 890 | mutex to avoid a race condition where a callback into your module may still |
|---|
| 891 | be pending due to contention on the module's mutex. As an example: |
|---|
| 892 | |
|---|
| 893 | void B_Object_Close(B_ObjectHandle handle) |
|---|
| 894 | { |
|---|
| 895 | BDBG_ASSERT(NULL != handle); |
|---|
| 896 | |
|---|
| 897 | B_Scheduler_StopCallbacks(handle->scheduler); |
|---|
| 898 | |
|---|
| 899 | // Now it's guaranteed that no callbacks are pending. |
|---|
| 900 | |
|---|
| 901 | B_Mutex_Lock(handle->mutex); |
|---|
| 902 | |
|---|
| 903 | // Actually clean up now |
|---|
| 904 | } |
|---|
| 905 | ***************************************************************************/ |
|---|
| 906 | void B_Scheduler_StopCallbacks( |
|---|
| 907 | B_SchedulerHandle scheduler, |
|---|
| 908 | void *pInstanceHandle |
|---|
| 909 | ) |
|---|
| 910 | { |
|---|
| 911 | B_SchedulerCallback *pCallback; |
|---|
| 912 | |
|---|
| 913 | BDBG_ASSERT(NULL != scheduler); |
|---|
| 914 | BDBG_ASSERT(NULL != pInstanceHandle); |
|---|
| 915 | |
|---|
| 916 | B_Mutex_Lock(scheduler->mutex); |
|---|
| 917 | { |
|---|
| 918 | /* Mark any callbacks matching the instance handle as stopped. */ |
|---|
| 919 | for ( pCallback = BLST_S_FIRST(&scheduler->callbackList); |
|---|
| 920 | NULL != pCallback; |
|---|
| 921 | pCallback = BLST_S_NEXT(pCallback, callbackNode) ) |
|---|
| 922 | { |
|---|
| 923 | if ( pCallback->pInstanceHandle == pInstanceHandle ) |
|---|
| 924 | { |
|---|
| 925 | pCallback->stopped = true; |
|---|
| 926 | } |
|---|
| 927 | } |
|---|
| 928 | } |
|---|
| 929 | B_Mutex_Unlock(scheduler->mutex); |
|---|
| 930 | |
|---|
| 931 | /* Sync with callback execution. After this barrier, no further callbacks will be issued */ |
|---|
| 932 | B_Mutex_Lock(scheduler->callbackMutex); |
|---|
| 933 | B_Mutex_Unlock(scheduler->callbackMutex); |
|---|
| 934 | } |
|---|
| 935 | |
|---|