source: svn/trunk/newcon3bcm2_21bu/nexus/lib/os/src/b_os_scheduler.c

Last change on this file was 2, checked in by phkim, 11 years ago

1.phkim

  1. revision copy newcon3sk r27
  • Property svn:executable set to *
File size: 31.6 KB
Line 
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
106BDBG_MODULE(b_os_scheduler);
107
108typedef 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
118typedef 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
128typedef 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
143typedef enum B_SchedulerState
144{
145    B_Scheduler_eRunning,
146    B_Scheduler_eStopping,
147    B_Scheduler_eStopped
148} B_SchedulerState;
149
150/***************************************************************************
151Summary:
152Scheduler Handle
153
154Description:
155A Scheduler is a higher-level OS construct.  A scheduler is responsible for
156dispatching synchronized timer and event callbacks using a single thread.
157***************************************************************************/
158typedef 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/***************************************************************************
177Summary:
178Get Default Scheduler Settings
179***************************************************************************/
180void B_Scheduler_GetDefaultSettings(
181    B_SchedulerSettings *pSettings      /* [out] */
182    )
183{
184    BSTD_UNUSED(pSettings);     /* Empty Structure */
185}
186
187/***************************************************************************
188Summary:
189Craete a scheduler
190***************************************************************************/
191B_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
241err_group:
242    B_Event_Destroy(pScheduler->controlEvent);
243err_event:
244    B_Mutex_Destroy(pScheduler->callbackMutex);
245err_callback_mutex:
246    B_Mutex_Destroy(pScheduler->mutex);
247err_mutex:
248    B_Os_Free(pScheduler);
249err_malloc:
250    return NULL;
251}
252
253
254/***************************************************************************
255Summary:
256Destroy a scheduler
257***************************************************************************/
258void 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/***************************************************************************
309Summary:
310Register an event with a scheduler
311
312Description:
313This will cause the scheduler to call the sepecified EventCallback function
314when the specified event has been set.  Prior to calling the callback,
315the specified mutex will be automatically locked.  After the callback
316returns, the mutex will be unlocked.
317***************************************************************************/
318B_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/***************************************************************************
375Summary:
376Un-Register an event from the scheduler
377***************************************************************************/
378void 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/***************************************************************************
419Summary:
420Schedule a timer
421
422Description:
423This will start a timer that will lock the specified mutex and call the
424specified callback routine when the timer expires.  Timers are one-shot,
425and they must be rescheduled to repeat.  Re-scheduling the timer from the
426callback itself is allowed.  After the timer expires, the TimerId value
427returned becomes invalid and should be discarded.
428***************************************************************************/
429B_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/***************************************************************************
490Summary:
491Cancel a timer
492
493Description:
494This will cancel a timer that was previously started.  If the timer has
495already expired, this call is safe.  If the callback has actually been
496called, this will generate a warning message.  If the timer has expired and
497the callback is pending on the mutex, the timer will be discarded and no
498warning will occur.
499***************************************************************************/
500void 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/***************************************************************************
537Summary:
538Run a scheduler
539
540Description:
541This routine will drive the scheduler execution.  It will not return until
542B_Scheduler_Stop is called.
543***************************************************************************/
544void 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(&currentTime);
575            timeout = B_Time_Diff(&pFirstTimer->time, &currentTime);
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/***************************************************************************
731Summary:
732Stop a scheduler
733
734Description:
735This routine will stop the scheduler execution.  After this is called,
736B_Scheduler_Run will return.  This routine can be called by any thread,
737including the scheduler thread itself.  It is not synchronized, so this call
738may return before B_Scheduler_Run returns.
739***************************************************************************/
740void 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/***************************************************************************
750Summary:
751Get default settings for a scheduler callback instance
752***************************************************************************/
753void B_SchedulerCallback_GetDefaultSettings(
754    B_SchedulerCallbackSettings *pSettings  /* [out] */
755    )
756{
757    BSTD_UNUSED(pSettings); /* Empty for now */
758}
759
760/***************************************************************************
761Summary:
762Register an asynchronous callback
763
764Description:
765This will cancel a timer that was previously started.  If the timer has
766already expired, this call is safe.  If the callback has actually been
767called, this will generate a warning message.  If the timer has expired and
768the callback is pending on the mutex, the timer will be discarded and no
769warning will occur.
770***************************************************************************/
771B_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/***************************************************************************
805Summary:
806Set the function to be used in an asynchronous callback
807***************************************************************************/
808B_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/***************************************************************************
830Summary:
831Fire an asynchronous callback
832
833Description:
834This will schedule the callback to be executed when the scheduler becomes
835idle.  If the same callback is fired multiple times before it is able to
836be called, only a single application callback will be made.
837***************************************************************************/
838void 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/***************************************************************************
858Summary:
859Destroying an asynchronous callback
860***************************************************************************/
861void 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/***************************************************************************
880Summary:
881Cancel all pending callbacks for this object instance
882
883Description:
884Because of the asynchronous nature of scheduler callbacks, race conditions
885can occur when closing objects that have pending callbacks.  In order to
886guarantee that all pending callbacks have been stopped for an instance,
887this function should be called as part of closing the object instance.
888
889NOTE: It is important to call this routine before acquiring your instance's
890mutex to avoid a race condition where a callback into your module may still
891be pending due to contention on the module's mutex.  As an example:
892
893void 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***************************************************************************/
906void 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
Note: See TracBrowser for help on using the repository browser.