source: svn/newcon3bcm2_21bu/nexus/base/src/nexus_base_scheduler.c

Last change on this file was 76, checked in by megakiss, 10 years ago

1W 대기전력을 만족시키기 위하여 POWEROFF시 튜너를 Standby 상태로 함

  • Property svn:executable set to *
File size: 55.8 KB
Line 
1/***************************************************************************
2*     (c)2008-2011 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: nexus_base_scheduler.c $
39* $brcm_Revision: 50 $
40* $brcm_Date: 10/6/11 10:26a $
41*
42* API Description:
43*
44* Revision History:
45*
46* $brcm_Log: /nexus/base/src/nexus_base_scheduler.c $
47*
48* 50   10/6/11 10:26a erickson
49* SW7335-1362: in NEXUS_StopCallbacks, only synchronize with schedulers
50*  where the stopping callback is current. prevents deadlock when closing
51*  one handle and in a callback for another handle that happens to be on
52*  the same scheduler.
53*
54* 49   3/30/11 6:56p vsilyaev
55* SW7335-1214: Serialize access to the field that holds a timer handle
56*
57* 48   3/29/11 3:04p vsilyaev
58* SW7335-1214: Masked extra debug output
59*
60* 47   3/29/11 2:46p vsilyaev
61* SW7335-1214: Validate thread info prior to dereferencing
62*
63* 46   3/28/11 3:45p vsilyaev
64* SW7335-1214: Added NEXUS_CallbackHandler framework
65*
66* 45   11/11/10 6:16p vsilyaev
67* SWNOOS-430: Use NO_OS_DIAGS to select DIAGS software
68*
69* 44   11/11/10 6:14p vsilyaev
70* SW7405-4995: Use BKNI_Memcmp to preinitialize structure used in
71*  GetDefaultSettings
72*
73* 43   11/13/09 4:26p erickson
74* SW7325-250: remove unnecessary loop and sleep in
75*  NEXUS_P_Scheduler_Destroy. if the scheduler doesn't have a thread, any
76*  external thread will have already been destroyed. the finished flag
77*  was never being set.
78*
79* 42   7/24/09 10:29a erickson
80* PR56109: fix IsrCallback race condition in
81*  NEXUS_P_Scheduler_IsrCallbacks. also, ensure NEXUS_CallbackDesc
82*  members are always used atomically.
83*
84* 41   4/19/09 7:21p agin
85* PR54339: Add nexus no-os scheduler support.
86*
87* 40   4/8/09 12:32p erickson
88* PR53862: reintroduce NEXUS_P_SchedulerGetInfo
89*
90* 39   4/6/09 12:08p vsilyaev
91* PR 53905: More aggressive use of the BDBG_OBJECT_XXX facilityies to
92*  detect corrupted scheduller state
93*
94* 38   4/6/09 11:50a vsilyaev
95* PR 50742: Resolve fairness/livelock problem introduced in /main/32 .
96*  Use separate collect and invoke phases to handle ISR callbacks
97*
98* 37   4/2/09 1:06p erickson
99* PR53813: fix memory leak
100*
101* 36   4/2/09 9:38a erickson
102* PR53739: protect task callbacks from NEXUS_CallbackDesc.callback = NULL
103*  set after NEXUS_TaskCallback_Fire is called (race condition)
104*
105* 35   2/25/09 11:10a erickson
106* PR52491: remove bad BLST_D_REMOVE from NEXUS_P_Scheduler_Destroy, also
107*  added code to detect TaskCallback leaks
108*
109* 34   1/27/09 4:03p erickson
110* PR50582: additional change needed in NEXUS_Module_CancelTimer to
111*  complete previous change
112*
113* 33   1/20/09 3:46p vsilyaev
114* PR 49285, PR 50582: Improved debugability of scheduller and fixed
115*  problem in the scheduller, where it goes to WaitForGroup to extended
116*  amount of time
117*
118* 32   1/14/09 5:20p erickson
119* PR50742: fix race condition in NEXUS_P_Scheduler_IsrCallbacks
120*
121* 31   12/30/08 6:51p vsilyaev
122* PR 50582: Improved use of filenames in the debug output
123*
124* 30   12/29/08 10:40a erickson
125* PR50742: add BKNI_ASSERT_ISR_CONTEXT
126*
127* 29   11/11/08 3:30a erickson
128* PR 35457: add stronger comment in a place where customers are likely to
129*  modify in order to relieve application deadlocks.
130*
131* 28   11/7/08 11:49a erickson
132* PR35457: update comments
133*
134* 27   7/31/08 11:59a vsilyaev
135* PR 45313: Decrement 'armed' count when destroying armed callbacks
136*
137* 26   7/1/08 5:30p vsilyaev
138* PR 44452: Fixed handling of timeout in NEXUS_P_Scheduler_Step
139*
140* 25   6/18/08 1:12p erickson
141* PR43758: fix warnings, misspellings
142*
143* 24   6/16/08 11:21a vsilyaev
144* PR 40352: Fixed typo in detection callback type
145*
146* 23   6/9/08 7:11p vsilyaev
147* PR 43184: Changed phase of detection for outstanding callbacks
148*
149* 22   6/5/08 2:19p vsilyaev
150* PR 42318: Rearranged scheduller to share the same context in user and
151*  kernel mode, this allows to serve callback from kernel to user with
152*  single transition kernel<->user
153*
154* 21   5/28/08 5:49p vsilyaev
155* PR 42318: Reworked scheduller to allow use of externally created thread
156*
157* 20   5/28/08 11:01a vsilyaev
158* PR 42929: Fixed skew in timeout selection
159*
160* 19   5/27/08 4:27p erickson
161* PR42929: don't pass negative value to BKNI_WaitForGroup. It could
162*  actually be BKNI_INFINITE, which leads to hang.
163*
164* 18   4/3/08 12:09p erickson
165* PR41156: add coverity hints for BLST usage
166*
167* 17   4/2/08 1:17p vsilyaev
168* PR 41075: Added more debug code
169*
170* 16   4/2/08 11:31a erickson
171* PR40198: fix DEBUG=n warning
172*
173* 15   4/1/08 4:55p vsilyaev
174* PR 41153: Better error handling
175*
176* 14   4/1/08 4:45p erickson
177* PR41153: fix static analysis warning. potential array overrun.
178*
179* 13   3/31/08 2:01p erickson
180* PR40421: allow callback Set with NULL to clear callback
181*
182* 12   3/31/08 1:11p erickson
183* PR41075: added BDBG_OBJECT
184*
185* 11   3/27/08 5:02p rjlewis
186* PR40352: callback type not being set.  Fix cut and paste error.
187*
188* 10   3/17/08 12:46p erickson
189* PR40103: remove NEXUS_QueueCallback
190*
191* 9   3/12/08 10:25a erickson
192* PR39584: check cur_event->deleted before calling event callback
193*
194* 8   3/12/08 9:15a erickson
195* PR40458: move NEXUS_UnlockModule to bottom of
196*  NEXUS_Module_IsrCallback_Destroy
197*
198* 7   3/5/08 2:39p erickson
199* PR40307: fix NEXUS_RegisterEvent
200*
201* 6   2/29/08 11:38a vsilyaev
202* PR 40103: Updates IsrCallback implementation to respond of Start/Stop
203*  Callbacks
204*
205* 5   2/29/08 10:06a vsilyaev
206* PR 40103: Fixed TaskCallback
207*
208* 4   2/28/08 10:41p vsilyaev
209* PR 40103: Added interfaceHandle and settings for the
210*  NEXUS_IsrCallbackCreate
211*
212* 3   2/28/08 9:39p vsilyaev
213* PR 40103: Added NEXUS_TaskCallback functions
214*
215* 2   1/30/08 11:49a vsilyaev
216* PR 38682: Added routing of callbacks into the user space
217*
218* 1   1/18/08 2:13p jgarrett
219* PR 38808: Merging to main branch
220*
221* Nexus_Devel/14   10/30/07 5:01p vsilyaev
222* PR 34419: Fixed handling of race condition when module cancels timer
223* pending on the module lock
224*
225* Nexus_Devel/13   10/3/07 2:51p vsilyaev
226* PR 34419: Renamed cntx to context
227*
228* Nexus_Devel/12   10/1/07 5:42p vsilyaev
229* PR 34419: Added IsrCallback_Set function
230*
231* Nexus_Devel/11   10/1/07 1:05p vsilyaev
232* PR 34419: Fixed callback_Fire function
233*
234* Nexus_Devel/10   9/28/07 6:47p vsilyaev
235* PR 34419: Added functions to fire callback from the interrupt context
236*
237* Nexus_Devel/9   9/28/07 1:03p vsilyaev
238* PR 34419: Added more debug output on exit and fixed typo in the debug
239* print
240*
241* Nexus_Devel/8   9/24/07 10:24p vsilyaev
242* PR 34419: Improved handling of empty callbacks
243*
244* Nexus_Devel/7   9/24/07 10:33a vsilyaev
245* PR 34419: Added tagged versions of Nexus_Module_Lock and NEXUS_atoi
246* function
247*
248* Nexus_Devel/6   9/17/07 6:46p vsilyaev
249* PR 34419: Added second parameter to callback
250*
251* Nexus_Devel/5   9/13/07 2:45p vsilyaev
252* PR 34419: Updated for new directory layout
253*
254* Nexus_Devel/4   9/10/07 4:32p vsilyaev
255* PR 34419: Added NEXUS_CallbackDesc
256*
257* Nexus_Devel/3   8/31/07 6:02p vsilyaev
258* PR 34419: Added configuration structure for NEXUS_Base
259*
260* Nexus_Devel/2   8/30/07 7:07p vsilyaev
261* PR 34419: More complete implementation
262*
263* Nexus_Devel/1   8/28/07 6:39p vsilyaev
264* PR 34419: Initial implementation
265*
266***************************************************************************/
267#include "nexus_base.h"
268#include "nexus_base_priv.h"
269#include "blst_slist.h"
270#include "blst_squeue.h"
271#include "blst_list.h"
272#include "bkni.h"
273
274#define BDBG_MSG_TRACE(x) /* BDBG_MSG(x) */
275BDBG_MODULE(nexus_base_scheduler);
276
277BDBG_OBJECT_ID(NEXUS_EventCallback);
278
279struct NEXUS_EventCallback {
280    BDBG_OBJECT(NEXUS_EventCallback)
281    /* list sorted by the event */
282    BLST_S_ENTRY(NEXUS_EventCallback) list;
283    BKNI_EventHandle event;
284    NEXUS_ModuleHandle module;
285    void (*pCallback)(void *);
286    void *pContext;
287    bool deleted;
288    const char *pFileName;
289    unsigned lineNumber;
290};
291
292BDBG_OBJECT_ID(NEXUS_Timer);
293
294struct NEXUS_Timer {
295    BDBG_OBJECT(NEXUS_Timer)
296    BLST_S_ENTRY(NEXUS_Timer) list;
297    NEXUS_Time time;
298    NEXUS_ModuleHandle module;
299    void (*pCallback)(void *);
300    void *pContext;
301    bool deleted;
302    const char *pFileName;
303    unsigned lineNumber;
304};
305
306typedef enum { NEXUS_P_CallbackType_eTask, NEXUS_P_CallbackType_eIsr } NEXUS_P_CallbackType;
307
308struct NEXUS_CallbackCommon {
309    NEXUS_P_CallbackType type;
310    NEXUS_P_Scheduler *scheduler;
311    bool armed; /* true if callback fired */
312    bool queued; /* true if callback queued, -> callaback in the scheduler list */
313    bool stopped; /* true if callback stopped */
314    bool deleted; /* true is callback deleted */
315    NEXUS_CallbackDesc desc;
316    BLST_D_ENTRY(NEXUS_CallbackCommon) global_list;
317    void *object; /* object associated with the callback */
318    const char *pFileName;
319    unsigned lineNumber;
320};
321
322BDBG_OBJECT_ID(NEXUS_TaskCallback);
323struct NEXUS_TaskCallback {
324    struct NEXUS_CallbackCommon  common; /* must be first member */
325    BLST_SQ_ENTRY(NEXUS_TaskCallback) scheduler_list;
326    BDBG_OBJECT(NEXUS_TaskCallback)
327};
328
329BDBG_OBJECT_ID(NEXUS_IsrCallback);
330
331struct NEXUS_IsrCallback {
332    struct NEXUS_CallbackCommon  common; /* must be first member */
333    BLST_S_ENTRY(NEXUS_IsrCallback) list_temp;
334    BLST_D_ENTRY(NEXUS_IsrCallback) list;
335    bool armed_save; /* saved 'armed' status */
336    BDBG_OBJECT(NEXUS_IsrCallback)
337};
338
339typedef struct NEXUS_P_SchedulerRequest {
340    int timeout;
341} NEXUS_P_SchedulerRequest;
342
343typedef struct NEXUS_P_SchedulerResponse {
344    BKNI_EventHandle events[5];
345    unsigned nevents;
346    BERR_Code result;
347} NEXUS_P_SchedulerResponse;
348
349
350BDBG_OBJECT_ID(NEXUS_P_Scheduler);
351
352static struct {
353    BLST_D_HEAD(NEXUS_CallbackCommonHead, NEXUS_CallbackCommon) callbacks;
354} NEXUS_P_Base_Scheduler_State;
355
356BLST_D_HEAD(NEXUS_P_HeadIsrCallbacks, NEXUS_IsrCallback); /* list of IsrCallbacks */
357BLST_S_HEAD(NEXUS_P_HeadIsrCallbacks_Temp, NEXUS_IsrCallback); /* temporary list of armed IsrCallbacks */
358
359struct NEXUS_P_Scheduler {
360    BKNI_EventGroupHandle group;
361    BKNI_EventHandle control; /* control event */
362    BKNI_MutexHandle callback_lock; /* callback that is acquired when callback active */
363    bool exit;
364    bool timerDirty;
365    bool finished;
366    uint8_t priority; /* used only for debugging */
367    BLST_S_HEAD(head_event, NEXUS_EventCallback) events; /* list of events */
368    BLST_S_HEAD(head_timer, NEXUS_Timer) timers; /* sorted list of timers */
369    BLST_SQ_HEAD(head_task_callbacks, NEXUS_TaskCallback) task_callbacks; /* list of callbacks */
370    struct {
371        struct NEXUS_P_HeadIsrCallbacks list; /* list of IsrCallbacks */
372        unsigned deleted;
373        unsigned armed;
374    } isr_callbacks;
375    NEXUS_ThreadHandle thread;
376    BDBG_OBJECT(NEXUS_P_Scheduler)
377    struct NEXUS_CallbackCommon *current_callback;
378};
379
380static const char *
381NEXUS_P_PrepareFileName(const char *pFileName)
382{
383    const char *s;
384    unsigned i;
385
386    if(pFileName==NULL) {
387        return "";
388    }
389    for(s=pFileName;*s != '\0';s++) { } /* search forward */
390
391    for(i=0;s!=pFileName;s--) { /* search backward */
392        if(*s=='/' || *s=='\\') {
393            i++;
394            if(i>2) {
395                return s+1;
396            }
397        }
398    }
399    return pFileName;
400}
401
402NEXUS_EventCallbackHandle
403NEXUS_Module_RegisterEvent(NEXUS_ModuleHandle module, BKNI_EventHandle event, void (*pCallback)(void *), void *pContext, const char *pFileName, unsigned lineNumber)
404{
405    BERR_Code rc=BERR_SUCCESS;
406    NEXUS_EventCallbackHandle callback;
407    NEXUS_EventCallbackHandle cur_event;
408    NEXUS_P_Scheduler *scheduler;
409    bool need_add_event = true;
410
411    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
412
413    callback = BKNI_Malloc(sizeof(*callback));
414    if(!callback) {
415        rc = BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY);
416        goto err_alloc;
417    }
418    BKNI_Memset(callback, 0, sizeof(*callback));
419    BDBG_OBJECT_SET(callback, NEXUS_EventCallback);
420
421    callback->event = event;
422    callback->pCallback = pCallback;
423    callback->pContext = pContext;
424    callback->pFileName = NEXUS_P_PrepareFileName(pFileName);
425    callback->lineNumber = lineNumber;
426    callback->module = module;
427    callback->deleted = false;
428    NEXUS_LockModule();
429    BDBG_ASSERT(module->scheduler);
430    scheduler = module->scheduler;
431
432    /* XXX same event could belong to multiple schedulers */
433    for(cur_event = BLST_S_FIRST(&(scheduler->events)); cur_event!=NULL ;cur_event = BLST_S_NEXT(cur_event, list)) {
434        if(cur_event->event == event) {
435            break;
436        }
437    }
438    if(cur_event==NULL) {
439        BLST_S_INSERT_HEAD(&scheduler->events, callback, list);
440    } else { /* tuck new event after existing one */
441        BLST_S_INSERT_AFTER(&scheduler->events, cur_event, callback, list);
442        /* test whether there are alive handlers */
443        for(;cur_event && cur_event->event==event; cur_event = BLST_S_NEXT(cur_event, list)) {
444            if(cur_event != callback && !cur_event->deleted) {
445                need_add_event = false;
446            }
447        }
448    }
449    if(need_add_event) {
450        rc = BKNI_AddEventGroup(scheduler->group, event);
451    }
452    NEXUS_UnlockModule();
453    if(rc!=BERR_SUCCESS) {
454        rc = BERR_TRACE(rc);
455        goto err_add;
456    }
457    return callback;
458
459err_add:
460    callback->deleted = true; /* don't free it here, just mark for deletion */
461err_alloc:
462    return NULL;
463}
464
465void
466NEXUS_Module_UnregisterEvent(NEXUS_ModuleHandle module, NEXUS_EventCallbackHandle event, const char *pFileName, unsigned lineNumber)
467{
468    NEXUS_EventCallbackHandle cur_event;
469    NEXUS_P_Scheduler *scheduler;
470
471    BSTD_UNUSED(pFileName);
472    BSTD_UNUSED(lineNumber);
473    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
474    BDBG_OBJECT_ASSERT(event, NEXUS_EventCallback);
475    BDBG_ASSERT(module->scheduler);
476    scheduler = module->scheduler;
477    /* XXX same event could belong to multiple schedulers */
478    NEXUS_LockModule();
479    for(cur_event = BLST_S_FIRST(&(scheduler->events)); cur_event!=NULL ;cur_event = BLST_S_NEXT(cur_event, list)) {
480        BDBG_OBJECT_ASSERT(cur_event, NEXUS_EventCallback);
481        if(cur_event == event) {
482            if(!event->deleted) {
483                BKNI_RemoveEventGroup(scheduler->group, event->event);
484                event->deleted = true; /* don't free it yet, just mark for deletion. We can't delete it since we could step into the thread that holds reference to this callback */
485            } else {
486                cur_event = NULL;
487            }
488            break;
489        }
490    }
491    NEXUS_UnlockModule();
492    if(cur_event==NULL) {
493        BDBG_ERR(("NEXUS_Module_UnregisterEvent: module %s unregistering stale event:%#lx at %s:%u", module->pModuleName, (unsigned long)event, NEXUS_P_PrepareFileName(pFileName), lineNumber));
494    }
495    BKNI_SetEvent(scheduler->control); /* wakeup thread to release resources */
496    return;
497}
498
499NEXUS_TimerHandle
500NEXUS_Module_ScheduleTimer(NEXUS_ModuleHandle module, unsigned delayMs, void (*pCallback)(void *),  void *pContext, const char *pFileName, unsigned lineNumber)
501{
502    BERR_Code rc;
503    NEXUS_TimerHandle timer, prev, cur;
504    NEXUS_P_Scheduler *scheduler;
505
506    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
507
508    timer = BKNI_Malloc(sizeof(*timer));
509    if(!timer) {
510        rc = BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY);
511        goto err_alloc;
512    }
513    BKNI_Memset(timer, 0, sizeof(*timer));
514    BDBG_OBJECT_SET(timer, NEXUS_Timer);
515
516    NEXUS_Time_Get(&timer->time);
517    NEXUS_Time_Add(&timer->time, delayMs);
518    timer->pCallback = pCallback;
519    timer->pContext = pContext;
520    timer->pFileName = NEXUS_P_PrepareFileName(pFileName);
521    timer->lineNumber = lineNumber;
522    timer->module = module;
523    timer->deleted = false;
524    NEXUS_LockModule();
525    BDBG_ASSERT(module->scheduler);
526    scheduler = module->scheduler;
527    for(prev=NULL,cur=BLST_S_FIRST(&scheduler->timers);cur;cur=BLST_S_NEXT(cur, list)) {
528        BDBG_OBJECT_ASSERT(cur, NEXUS_Timer);
529        if (NEXUS_Time_Diff(&cur->time, &timer->time) > 0) {
530            break;
531        }
532        prev = cur;
533    }
534    BDBG_MSG_TRACE(("NEXUS_Module_ScheduleTimer: %#lx timer %#lx(%#lx) %s:%u (%#lx:%s:%u)", (unsigned long)scheduler, (unsigned long)timer, (unsigned long)timer->pContext, timer->pFileName, timer->lineNumber, (unsigned long)prev, prev==NULL?"FIRST":prev->pFileName, prev?prev->lineNumber:0));
535    if (prev==NULL) {
536        scheduler->timerDirty = true;
537        BLST_S_INSERT_HEAD(&scheduler->timers, timer, list);
538        BKNI_SetEvent(scheduler->control); /* wakeup thread right the way */
539    } else {
540        BLST_S_INSERT_AFTER(&scheduler->timers, prev, timer, list); /* thread would be woken up on its own after timeout expires */
541    }
542    NEXUS_UnlockModule();
543    return timer;
544err_alloc:
545    return NULL;
546}
547
548void
549NEXUS_Module_CancelTimer(NEXUS_ModuleHandle module, NEXUS_TimerHandle timer, const char *pFileName, unsigned lineNumber)
550{
551    NEXUS_TimerHandle cur;
552    NEXUS_P_Scheduler *scheduler;
553
554    BSTD_UNUSED(pFileName);
555    BSTD_UNUSED(lineNumber);
556    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
557    BDBG_OBJECT_ASSERT(timer, NEXUS_Timer);
558    NEXUS_LockModule();
559    BDBG_ASSERT(module->scheduler);
560    scheduler = module->scheduler;
561    for(cur=BLST_S_FIRST(&scheduler->timers);cur;cur=BLST_S_NEXT(cur, list)) {
562        if(cur==timer && !cur->deleted) {
563            cur->deleted = true;/* don't free it yet, just mark for deletion. We can't delete it since we could step into the thread that holds reference to this callback */
564            break;
565        }
566    }
567    if(cur==NULL) {
568        if (!timer->deleted) {
569            timer->deleted = true;
570        }
571        else {
572            BDBG_ERR(("NEXUS_Module_CancelTimer: module %s canceling expired timer:%#lx at %s:%u", module->pModuleName, (unsigned long)timer, NEXUS_P_PrepareFileName(pFileName), lineNumber));
573        }
574    }
575    NEXUS_UnlockModule();
576    BKNI_SetEvent(scheduler->control); /* wakeup thread to release resources */
577    return;
578}
579
580static void
581NEXUS_P_CallbackCommon_Init(struct NEXUS_CallbackCommon *callback, NEXUS_ModuleHandle module, void *interfaceHandle, NEXUS_P_CallbackType type, const NEXUS_CallbackSettings *pSettings, const char *pFileName, unsigned lineNumber)
582{
583    BSTD_UNUSED(pSettings);
584
585    BDBG_ASSERT(module->scheduler);
586    NEXUS_CallbackDesc_Init(&callback->desc);
587    callback->type = type;
588    callback->armed = false;
589    callback->queued = false;
590    callback->stopped = false;
591    callback->deleted = false;
592    callback->object = interfaceHandle;
593    callback->pFileName = NEXUS_P_PrepareFileName(pFileName);
594    callback->lineNumber = lineNumber;
595    NEXUS_LockModule();
596    callback->scheduler = module->scheduler;
597    BLST_D_INSERT_HEAD(&NEXUS_P_Base_Scheduler_State.callbacks, callback, global_list);
598    NEXUS_UnlockModule();
599}
600
601
602NEXUS_IsrCallbackHandle
603NEXUS_Module_IsrCallback_Create(NEXUS_ModuleHandle module,  void *interfaceHandle, const NEXUS_CallbackSettings *pSettings, const char *pFileName, unsigned lineNumber)
604{
605    NEXUS_IsrCallbackHandle  callback;
606
607    BSTD_UNUSED(pSettings);
608    BSTD_UNUSED(interfaceHandle);
609
610    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
611
612    callback = BKNI_Malloc(sizeof(*callback));
613    if(!callback) {
614        BERR_Code rc = BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY);
615        BSTD_UNUSED(rc);
616        return NULL;
617    }
618    BDBG_OBJECT_INIT(callback, NEXUS_IsrCallback);
619    callback->armed_save = false;
620    NEXUS_P_CallbackCommon_Init(&callback->common, module, interfaceHandle, NEXUS_P_CallbackType_eIsr, pSettings, pFileName, lineNumber);
621
622    NEXUS_LockModule();
623    BKNI_EnterCriticalSection();
624    BLST_D_INSERT_HEAD(&module->scheduler->isr_callbacks.list, callback, list);
625    BKNI_LeaveCriticalSection();
626    NEXUS_UnlockModule();
627    return callback;
628}
629
630void
631NEXUS_Module_IsrCallback_Destroy(NEXUS_ModuleHandle module, NEXUS_IsrCallbackHandle callback)
632{
633    NEXUS_P_Scheduler *scheduler;
634    NEXUS_IsrCallbackHandle cur;
635    BDBG_OBJECT_ASSERT(callback, NEXUS_IsrCallback);
636    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
637    NEXUS_LockModule();
638    BDBG_ASSERT(module->scheduler);
639    scheduler = module->scheduler;
640    BKNI_EnterCriticalSection();
641    for(cur=BLST_D_FIRST(&scheduler->isr_callbacks.list);cur!=NULL;cur=BLST_D_NEXT(cur, list)) {
642        BDBG_OBJECT_ASSERT(cur, NEXUS_IsrCallback);
643        if(cur==callback && !callback->common.deleted) {
644            /* we can't neither free element or move it from the list, since scheduler thread could hold reference to the element with released lock.
645             * So just mark is as deleted, and let thread to find and free it */
646            callback->common.deleted = true;
647            if(callback->common.armed) {
648                BDBG_ASSERT(scheduler->isr_callbacks.armed>0);
649                callback->common.armed=false;
650                scheduler->isr_callbacks.armed--;
651            }
652            BLST_D_REMOVE(&scheduler->isr_callbacks.list, callback, list); /* move item to the front of list, it shall be safe, in worst case thread would pass through list multiple times */
653            BLST_D_INSERT_HEAD(&scheduler->isr_callbacks.list, callback, list);
654            scheduler->isr_callbacks.deleted++;
655            break;
656        }
657    }
658    BKNI_LeaveCriticalSection();
659    if(cur==NULL) {
660        BDBG_OBJECT_ASSERT(callback, NEXUS_IsrCallback);
661        BDBG_ERR(("NEXUS_IsrCallback_Destroy: module %s destroying stale IsrCallback:%#lx at %s:%u", module->pModuleName, (unsigned long)callback, callback->common.pFileName, callback->common.lineNumber));
662    } else {
663        BLST_D_REMOVE(&NEXUS_P_Base_Scheduler_State.callbacks, &callback->common, global_list); /* remove callback from the global list */
664    }
665    BKNI_SetEvent(scheduler->control); /* wakeup thread to release resources */
666    NEXUS_UnlockModule();
667}
668#define NEXUS_P_FILENAME(str) ((str)?(str):"")
669
670static void NEXUS_Module_P_Callback_Test(const NEXUS_CallbackDesc *pDesc)
671{
672    if(pDesc->callback) {
673        NEXUS_P_ThreadInfo *info = NEXUS_P_ThreadInfo_Get();
674        if(info) {
675            unsigned stack_depth = BLIFO_READ_PEEK(&info->stack);
676            if(stack_depth>2) { /*  One API sets callbacks for another API */
677                if(pDesc->callback != NEXUS_Base_P_CallbackHandler_Dispatch) {
678                    const NEXUS_P_LockEntry *entry;
679                    BDBG_ERR(("Detected callback handler set by the nexus code without using NEXUS_CallbackHandler (%u)", stack_depth));
680                    BDBG_ERR(("All callbacks handled by the nexus should use NEXUS_CallbackHandler, limited call stack follows"));
681                    entry = BLIFO_READ(&info->stack);
682                    while(stack_depth>0) {
683                        BDBG_ERR(("%s %u:%s(%s:%u)",info->pThreadName, stack_depth, entry->module->pModuleName, NEXUS_P_FILENAME(entry->pFileName), entry->lineNumber));
684                        entry--;
685                        stack_depth--;
686                    }
687                }
688            }
689        }
690    }
691    return;
692}
693
694void
695NEXUS_Module_IsrCallback_Set(NEXUS_IsrCallbackHandle callback, const NEXUS_CallbackDesc *pDesc)
696{
697    BDBG_OBJECT_ASSERT(callback, NEXUS_IsrCallback);
698    BKNI_EnterCriticalSection();
699    if (pDesc) {
700        NEXUS_Module_P_Callback_Test(pDesc);
701        callback->common.desc = *pDesc;
702    }
703    else {
704        callback->common.desc.callback = NULL;
705    }
706    BKNI_LeaveCriticalSection();
707    return;
708}
709
710void
711NEXUS_Module_IsrCallback_Fire_isr(NEXUS_ModuleHandle module, NEXUS_IsrCallbackHandle callback)
712{
713    NEXUS_P_Scheduler *scheduler;
714
715    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
716    BDBG_OBJECT_ASSERT(callback, NEXUS_IsrCallback);
717    BKNI_ASSERT_ISR_CONTEXT();
718
719    if(callback->common.deleted) {
720        BDBG_WRN(("NEXUS_IsrCallback_Fire_isr: %#lx using stale callback", (unsigned long)callback));
721        /* fall through */
722    }
723    if(callback->common.desc.callback==NULL) { /* short-circuit empty callbacks */
724        goto done;
725    }
726    BDBG_ASSERT(module->scheduler);
727    scheduler = module->scheduler;
728    if(!callback->common.armed) {
729        callback->common.armed=true;
730        scheduler->isr_callbacks.armed++;
731    }
732    BLST_D_REMOVE(&scheduler->isr_callbacks.list, callback, list); /* move item to the  front of list, it shall be safe, in worst case thread would pass through list multiple times */
733    BLST_D_INSERT_HEAD(&scheduler->isr_callbacks.list, callback, list);
734    BKNI_SetEvent_isr(scheduler->control); /* wakeup thread to release resources */
735
736done:
737    return;
738}
739
740void
741NEXUS_Callback_GetDefaultSettings( NEXUS_CallbackSettings *settings)
742{
743    BKNI_Memset(settings, 0, sizeof(*settings));
744    settings->priority = NEXUS_ModulePriority_eDefault;
745    return;
746}
747
748NEXUS_TaskCallbackHandle
749NEXUS_Module_TaskCallback_Create(NEXUS_ModuleHandle module, void *interfaceHandle, const NEXUS_CallbackSettings *pSettings, const char *pFileName, unsigned lineNumber)
750{
751    NEXUS_TaskCallbackHandle callback;
752
753    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
754    BDBG_ASSERT(interfaceHandle);
755
756    callback = BKNI_Malloc(sizeof(*callback));
757    if(!callback) {
758        goto err_alloc;
759    }
760    BDBG_OBJECT_INIT(callback, NEXUS_TaskCallback);
761    NEXUS_P_CallbackCommon_Init(&callback->common, module, interfaceHandle, NEXUS_P_CallbackType_eTask, pSettings, pFileName, lineNumber);
762    return callback;
763
764err_alloc:
765    return NULL;
766}
767
768void
769NEXUS_Module_TaskCallback_Destroy( NEXUS_ModuleHandle module, NEXUS_TaskCallbackHandle callback)
770{
771    NEXUS_P_Scheduler *scheduler;
772    BDBG_OBJECT_ASSERT(callback, NEXUS_TaskCallback);
773    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
774    BDBG_ASSERT(callback->common.scheduler == module->scheduler);
775    BDBG_ASSERT(!callback->common.deleted);
776    NEXUS_LockModule();
777    callback->common.deleted = true; /* mark callback as deleted */
778    BLST_D_REMOVE(&NEXUS_P_Base_Scheduler_State.callbacks, &callback->common, global_list); /* remove callback from the global list */
779    scheduler = module->scheduler;
780    if(!callback->common.queued) { /* add callback into the queue */
781        callback->common.queued = true;
782        BLST_SQ_INSERT_HEAD(&scheduler->task_callbacks, callback, scheduler_list);
783    }
784    BKNI_SetEvent(scheduler->control); /* wakeup thread to release resources */
785    NEXUS_UnlockModule();
786    return;
787}
788
789void
790NEXUS_Module_TaskCallback_Set(NEXUS_TaskCallbackHandle callback, const NEXUS_CallbackDesc *pDesc)
791{
792    BDBG_OBJECT_ASSERT(callback, NEXUS_TaskCallback);
793    NEXUS_LockModule();
794    if (pDesc) {
795        NEXUS_Module_P_Callback_Test(pDesc);
796        callback->common.desc = *pDesc;
797    }
798    else {
799        callback->common.desc.callback = NULL;
800    }
801    NEXUS_UnlockModule();
802    return;
803}
804
805void
806NEXUS_Module_TaskCallback_Fire(NEXUS_ModuleHandle module, NEXUS_TaskCallbackHandle callback)
807{
808    BDBG_OBJECT_ASSERT(callback, NEXUS_TaskCallback);
809    BDBG_OBJECT_ASSERT(module, NEXUS_Module);
810    BDBG_ASSERT(callback->common.scheduler == module->scheduler);
811    if(callback->common.deleted) {
812        BDBG_WRN(("NEXUS_TaskCallback_Fire: %#lx using stale callback", (unsigned long)callback));
813        /* fall through */
814    }
815    if(callback->common.desc.callback==NULL) {
816        return;
817    }
818    NEXUS_LockModule();
819    BDBG_ASSERT(module->scheduler);
820    callback->common.armed = true;
821    if(!callback->common.stopped && !callback->common.queued) {
822        NEXUS_P_Scheduler *scheduler;
823        callback->common.queued = true;
824        scheduler = callback->common.scheduler;
825        BLST_SQ_INSERT_HEAD(&scheduler->task_callbacks, callback, scheduler_list);
826        BKNI_SetEvent(scheduler->control); /* wakeup thread */
827    }
828    NEXUS_UnlockModule();
829    return;
830}
831
832static void
833NEXUS_P_Scheduler_IsrCallbacks(NEXUS_P_Scheduler *scheduler)
834{
835    NEXUS_IsrCallbackHandle callback, next_callback;
836    struct NEXUS_P_HeadIsrCallbacks_Temp deleted_list, armed_list;
837
838    BLST_S_INIT(&deleted_list);
839    BLST_S_INIT(&armed_list);
840
841    BKNI_EnterCriticalSection();
842    /* 1. Collect deleted and armed callbacks */
843    for(callback=BLST_D_FIRST(&scheduler->isr_callbacks.list);
844        callback && (scheduler->isr_callbacks.deleted!=0 || scheduler->isr_callbacks.armed!=0);
845    ) {
846        NEXUS_IsrCallbackHandle next_callback = BLST_D_NEXT(callback, list);
847        BDBG_OBJECT_ASSERT(callback, NEXUS_IsrCallback);
848        if(callback->common.deleted) {
849            /* move callback into the deleted list */
850            BLST_D_REMOVE(&scheduler->isr_callbacks.list, callback, list);
851            BLST_S_INSERT_HEAD(&deleted_list, callback, list_temp);
852            if(callback->common.armed) {
853                scheduler->isr_callbacks.armed--;
854            }
855            scheduler->isr_callbacks.deleted--;
856        } else if(callback->common.armed) {
857            /* add callback into the armed list */
858            callback->common.armed = false;
859            scheduler->isr_callbacks.armed--;
860            BLST_S_INSERT_HEAD(&armed_list, callback, list_temp);
861        }
862        callback = next_callback;
863    }
864    if(scheduler->isr_callbacks.deleted+scheduler->isr_callbacks.armed>0) {
865        if(scheduler->isr_callbacks.deleted>0) {
866            BDBG_WRN(("NEXUS_P_Scheduler_IsrCallbacks: %#x stale deleted counter %u", (unsigned)scheduler, scheduler->isr_callbacks.deleted));
867        }
868        if(scheduler->isr_callbacks.armed>0) {
869            BDBG_WRN(("NEXUS_P_Scheduler_IsrCallbacks: %#x stale armed counter %u", (unsigned)scheduler, scheduler->isr_callbacks.armed));
870        }
871        BDBG_ASSERT(scheduler->isr_callbacks.deleted==0 && scheduler->isr_callbacks.armed==0); /* if counters aren't zero there is something wrong with either lists or ISR/TASK synchronization */
872        /* code below is only executed in the release version */
873        scheduler->isr_callbacks.deleted=0;
874        scheduler->isr_callbacks.armed=0;
875    }
876    BKNI_LeaveCriticalSection();
877
878    /* 2. Recycle deleted callbacks */
879    for(callback=BLST_S_FIRST(&deleted_list);callback;) {
880        next_callback=BLST_S_NEXT(callback, list_temp);
881        BDBG_OBJECT_DESTROY(callback, NEXUS_IsrCallback);
882        BKNI_Free(callback);
883        callback=next_callback;
884    }
885    /* 3. Call armed callbacks */
886    for(callback=BLST_S_FIRST(&armed_list);callback;callback=BLST_S_NEXT(callback, list_temp)) {
887        NEXUS_Callback callbackFunc = callback->common.desc.callback;
888        BDBG_OBJECT_ASSERT(callback, NEXUS_IsrCallback);
889        if(callbackFunc!=NULL) {  /* Cache data from the callback descriptor prior to releasing lock */
890            if(!callback->common.stopped) {
891                NEXUS_CallbackDesc desc;
892
893                /* ensure we get a coherent desc in case NEXUS_IsrCallback_Set is timesliced in here. */
894                BKNI_EnterCriticalSection();
895                desc = callback->common.desc;
896                BKNI_LeaveCriticalSection();
897
898                callback->armed_save = false;
899                scheduler->current_callback = &callback->common;
900                NEXUS_UnlockModule();
901               
902                /* It's possible that NEXUS_StopCallbacks or NEXUS_IsrCallback_Destroy is run here, so retest the common flags. */
903                BKNI_AcquireMutex(scheduler->callback_lock); /* this expected to always succeed, the only exception if NEXUS_StopCallbacks is running */
904                if (!callback->common.stopped && !callback->common.deleted && desc.callback) {
905                    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_IsrCallbacks: %#lx callback: %#lx (%#lx, %u)", (unsigned long)scheduler,
906                        (unsigned long)desc.callback, (unsigned long)desc.context, (unsigned)desc.param));
907                    desc.callback(desc.context, desc.param);
908                }
909                BKNI_ReleaseMutex(scheduler->callback_lock);
910               
911                NEXUS_LockModule();
912                scheduler->current_callback = NULL;
913            } else {
914                callback->armed_save = true;
915            }
916        }
917    }
918    return;
919}
920
921void
922NEXUS_P_SchedulerGetInfo(NEXUS_P_Scheduler *scheduler, NEXUS_P_SchedulerInfo *info)
923{
924    BDBG_OBJECT_ASSERT(scheduler, NEXUS_P_Scheduler);
925    info->callback_lock = scheduler->callback_lock;
926    return;
927}
928
929static bool
930NEXUS_P_SchedulerGetRequest(NEXUS_P_Scheduler *scheduler, NEXUS_P_SchedulerRequest *request)
931{
932    unsigned i;
933    long timeout = 0;
934
935    BDBG_OBJECT_ASSERT(scheduler, NEXUS_P_Scheduler);
936    NEXUS_LockModule();
937    request->timeout = 0;
938    if(scheduler->exit) {
939        goto done;
940    }
941    for(i=0;i<4;) {
942        NEXUS_Time curtime;
943        /* coverity[use_after_free] */
944        NEXUS_TimerHandle first_timer = BLST_S_FIRST(&scheduler->timers);
945        if(!first_timer) {
946            timeout = BKNI_INFINITE;
947            break;
948        }
949        BDBG_OBJECT_ASSERT(first_timer, NEXUS_Timer);
950        NEXUS_Time_Get(&curtime);
951        timeout = NEXUS_Time_Diff(&first_timer->time, &curtime);
952        if(timeout>0 && !first_timer->deleted) {
953             break;
954        }
955        BLST_S_REMOVE_HEAD(&scheduler->timers, list);
956        if(!first_timer->deleted) {
957            NEXUS_UnlockModule();
958            NEXUS_Module_Lock(first_timer->module);
959            if(!first_timer->deleted) {
960                BDBG_MSG_TRACE(("NEXUS_P_SchedulerGetRequest: %#lx timer %#lx(%#lx) %s:%u", (unsigned long)scheduler, (unsigned long)first_timer, (unsigned long)first_timer->pContext, first_timer->pFileName, first_timer->lineNumber));
961                i++;
962                first_timer->pCallback(first_timer->pContext);
963            }
964            NEXUS_Module_Unlock(first_timer->module);
965            NEXUS_LockModule();
966        }
967        if(!scheduler->timerDirty) {
968            scheduler->timerDirty = false;
969        }
970        BDBG_OBJECT_DESTROY(first_timer, NEXUS_Timer);
971        BKNI_Free(first_timer);
972    }
973    if(i==4) {
974        timeout = 0; /* Clear timeout if maxed out number of timer callouts */
975    }
976    for(i=0;i<4;) {
977        NEXUS_TaskCallbackHandle callback;
978        NEXUS_CallbackDesc desc;
979
980        callback = BLST_SQ_FIRST(&scheduler->task_callbacks);
981        if(!callback) {
982            break;
983        }
984        BDBG_OBJECT_ASSERT(callback, NEXUS_TaskCallback);
985        BDBG_ASSERT(callback->common.queued);
986        callback->common.queued = false;
987        BLST_SQ_REMOVE_HEAD(&scheduler->task_callbacks, scheduler_list);
988        if(callback->common.deleted) {
989            BDBG_OBJECT_DESTROY(callback, NEXUS_TaskCallback);
990            BKNI_Free(callback);
991            continue;
992        }
993
994        /* ensure we get a coherent desc in case NEXUS_TaskCallback_Set is timesliced in after the NEXUS_UnlockModule(). */
995        desc = callback->common.desc;
996        scheduler->current_callback = &callback->common;
997        NEXUS_UnlockModule(); /* we release our mutex, before grabbing other */
998
999        /* It's possible that NEXUS_StopCallbacks or NEXUS_TaskCallback_Destroy is run here, so test the common flags. */
1000
1001        BKNI_AcquireMutex(scheduler->callback_lock); /* this expected to always succeed, the only exception if NEXUS_StopCallbacks is running */
1002        if(callback->common.armed && !callback->common.stopped && !callback->common.deleted) {
1003            i++;
1004            callback->common.armed = false;
1005            if (desc.callback) {
1006                desc.callback(desc.context, desc.param);
1007                BDBG_MSG_TRACE(("NEXUS_P_SchedulerGetRequest: %#lx timer %#lx(%#lx, %u)", (unsigned long)scheduler, (unsigned long)desc.callback, (unsigned long)desc.context, (unsigned)desc.param));
1008            }
1009        }
1010        BKNI_ReleaseMutex(scheduler->callback_lock);
1011        NEXUS_LockModule();
1012        scheduler->current_callback = NULL;
1013    }
1014    if(i==4) {
1015        timeout = 0; /* Clear timeout if maxed out number of callback callouts */
1016    }
1017    if(scheduler->isr_callbacks.armed!=0 || scheduler->isr_callbacks.deleted!=0) {
1018        NEXUS_P_Scheduler_IsrCallbacks(scheduler);
1019    }
1020    request->timeout = timeout;
1021    NEXUS_UnlockModule();
1022    return true;
1023
1024done:
1025    scheduler->finished = true;
1026    NEXUS_UnlockModule();
1027    return false;
1028}
1029
1030
1031static bool
1032NEXUS_P_SchedulerProcessResponse(NEXUS_P_Scheduler *scheduler, const NEXUS_P_SchedulerResponse *response)
1033{
1034    unsigned i;
1035    unsigned events_left;
1036    NEXUS_EventCallbackHandle cur_event;
1037
1038    BDBG_OBJECT_ASSERT(scheduler, NEXUS_P_Scheduler);
1039    NEXUS_LockModule();
1040    if (scheduler->exit) {
1041        goto done;
1042    }
1043    if (response->result==BERR_TIMEOUT) {
1044        goto timeout;
1045    } else if (response->result!=BERR_SUCCESS) {
1046        goto done;
1047    }
1048    /* coverity[use_after_free] */
1049    for(events_left=response->nevents, cur_event = BLST_S_FIRST(&scheduler->events); cur_event!=NULL && events_left>0; ) {
1050        if(cur_event->deleted) { /* recycle deleted event handlers */
1051            NEXUS_EventCallbackHandle deleted_event = cur_event;
1052            cur_event = BLST_S_NEXT(cur_event, list);
1053            BLST_S_REMOVE(&scheduler->events, deleted_event, NEXUS_EventCallback, list);
1054            BDBG_OBJECT_DESTROY(deleted_event, NEXUS_EventCallback);
1055            BKNI_Free(deleted_event);
1056            continue;
1057        }
1058        for(i=0;i<response->nevents;i++) {
1059            if(response->events[i]==cur_event->event) {
1060                do {
1061                    /* XXX move "hot" entries to head of the list */
1062                    NEXUS_UnlockModule();
1063                    NEXUS_Module_Lock(cur_event->module);
1064                    if (!cur_event->deleted) {
1065                        BDBG_MSG_TRACE(("NEXUS_P_SchedulerProcessResponse: %#lx event:%#lx(%#lx)", (unsigned long)scheduler, (unsigned long)cur_event->pCallback, (unsigned long)cur_event->pContext));
1066                        cur_event->pCallback(cur_event->pContext);
1067                    }
1068                    NEXUS_Module_Unlock(cur_event->module);
1069                    NEXUS_LockModule();
1070                    cur_event = BLST_S_NEXT(cur_event, list);
1071                } while(cur_event && response->events[i]==cur_event->event); /* traverse through all callback that share the same event */
1072                events_left--; /* we decrement events_left only for callback event, it has consequence that if control event signalled then we would walk and recycle all deleted events */
1073                break;
1074            }
1075        }
1076        if(i==response->nevents) {
1077            cur_event = BLST_S_NEXT(cur_event, list);
1078        }
1079    }
1080timeout:
1081    NEXUS_UnlockModule();
1082    return true;
1083done:
1084    scheduler->finished = true;
1085    NEXUS_UnlockModule();
1086    return false;
1087}
1088
1089NEXUS_Error
1090NEXUS_P_Scheduler_Step(NEXUS_P_Scheduler *scheduler, unsigned timeout, NEXUS_P_Base_Scheduler_Status *status, bool (*complete)(void *context), void *context)
1091{
1092    NEXUS_P_SchedulerRequest request;
1093    NEXUS_P_SchedulerResponse response;
1094    NEXUS_Time curTime, targetTime;
1095    int timeDiff;
1096
1097    BDBG_ASSERT(status);
1098
1099    NEXUS_Time_Get(&targetTime);
1100    timeDiff = (int)timeout;
1101    if(timeDiff<0) {
1102        timeDiff = 0;
1103    }
1104    NEXUS_Time_Add(&targetTime, timeDiff);
1105    for(;;) {
1106        bool success;
1107
1108        success = NEXUS_P_SchedulerGetRequest(scheduler, &request);
1109        if(!success) {
1110            goto done;
1111        }
1112        status->timeout = request.timeout;
1113        if(complete && complete(context)) {
1114            request.timeout = 0;
1115        } else if((int)timeout != BKNI_INFINITE) {
1116            NEXUS_Time_Get(&curTime);
1117            timeDiff = NEXUS_Time_Diff(&targetTime, &curTime);
1118            if(timeDiff>(int)timeout) {
1119                BDBG_WRN(("NEXUS_P_Scheduler_Step:%#lx: time wrap %d:%d", (unsigned long)scheduler, (int)timeDiff, (int)timeout));
1120                request.timeout = 0; /* force exit from the loop */
1121            } else if(timeDiff<=0) {
1122                request.timeout = 0;
1123            } else if(request.timeout==BKNI_INFINITE || timeDiff < request.timeout) {
1124                request.timeout = timeDiff;
1125            }
1126        }
1127        BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Step:%#lx: %u(%u)", (unsigned long)scheduler, (unsigned)request.timeout, (unsigned)timeout));
1128        response.result = BKNI_WaitForGroup(scheduler->group, request.timeout, response.events, sizeof(response.events)/sizeof(*response.events), &response.nevents);
1129        success = NEXUS_P_SchedulerProcessResponse(scheduler, &response);
1130        if(!success) {
1131            goto done;
1132        }
1133        if(request.timeout==0) {
1134            break;
1135        }
1136    }
1137    status->idle = BLST_S_FIRST(&scheduler->events) == NULL && BLST_S_FIRST(&scheduler->timers) == NULL && BLST_SQ_FIRST(&scheduler->task_callbacks) == NULL && BLST_D_FIRST(&scheduler->isr_callbacks.list) == NULL;
1138    status->exit = false;
1139    return NEXUS_SUCCESS;
1140done:
1141    status->timeout = 0;
1142    status->idle = false;
1143    status->exit = true;
1144    return NEXUS_SUCCESS;
1145}
1146
1147#ifdef NO_OS_DIAGS
1148NEXUS_Error
1149NEXUS_P_NO_OS_Scheduler_Step(NEXUS_P_Scheduler *scheduler, unsigned timeout, NEXUS_P_Base_Scheduler_Status *status, bool (*complete)(void *context), void *context)
1150{
1151    NEXUS_P_SchedulerRequest request;
1152    NEXUS_P_SchedulerResponse response;
1153
1154    BDBG_ASSERT(status);
1155
1156    {
1157        bool success;
1158
1159        success = NEXUS_P_SchedulerGetRequest(scheduler, &request);
1160        if(!success) {
1161            goto done;
1162        }
1163        response.result = BKNI_WaitForGroup(scheduler->group, request.timeout, response.events, sizeof(response.events)/sizeof(*response.events), &response.nevents);
1164        success = NEXUS_P_SchedulerProcessResponse(scheduler, &response);
1165        if(!success) {
1166            goto done;
1167        }
1168    }
1169done:
1170    status->timeout = 0;
1171    status->idle = false;
1172    status->exit = true;
1173    return NEXUS_SUCCESS;
1174}
1175
1176void
1177NEXUS_P_NO_OS_Scheduler_Thread(void *s)
1178{
1179    NEXUS_P_Scheduler *scheduler = s;
1180    NEXUS_P_Base_Scheduler_Status status;
1181    NEXUS_P_NO_OS_Scheduler_Step(scheduler, BKNI_INFINITE, &status, NULL, NULL);
1182}
1183
1184#endif /* NO_OS_DIAGS */
1185
1186static void
1187NEXUS_P_Scheduler_Thread(void *s)
1188{
1189    NEXUS_P_Scheduler *scheduler = s;
1190    NEXUS_P_Base_Scheduler_Status status;
1191
1192    for(;;) {
1193        NEXUS_P_Scheduler_Step(scheduler, BKNI_INFINITE, &status, NULL, NULL);
1194        if(status.exit==true) {break;}
1195    }
1196    return;
1197}
1198
1199
1200NEXUS_P_Scheduler *
1201NEXUS_P_Scheduler_Init(unsigned priority, const char *name, const NEXUS_ThreadSettings *pSettings)
1202{
1203    NEXUS_P_Scheduler *scheduler;
1204    BERR_Code rc;
1205
1206    BSTD_UNUSED(name);
1207    BSTD_UNUSED(pSettings);
1208    NEXUS_ASSERT_MODULE(); /* we need lock held to task wouldn't start in middle of initialization */
1209
1210    scheduler = BKNI_Malloc(sizeof(*scheduler));
1211    if(!scheduler) {
1212        rc = BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY);
1213        goto err_alloc;
1214    }
1215    BDBG_OBJECT_INIT(scheduler, NEXUS_P_Scheduler);
1216    scheduler->priority = priority;
1217    scheduler->exit = false;
1218    scheduler->timerDirty = true;
1219    scheduler->finished = false;
1220    scheduler->thread = NULL;
1221    BLST_S_INIT(&scheduler->events);
1222    BLST_S_INIT(&scheduler->timers);
1223    BLST_SQ_INIT(&scheduler->task_callbacks);
1224    BLST_D_INIT(&scheduler->isr_callbacks.list);
1225    scheduler->isr_callbacks.deleted = 0;
1226    scheduler->isr_callbacks.armed = 0;
1227    rc = BKNI_CreateEvent(&scheduler->control);
1228    if(rc!=BERR_SUCCESS) {
1229        rc = BERR_TRACE(rc);
1230        goto err_event;
1231    }
1232    rc = BKNI_CreateMutex(&scheduler->callback_lock);
1233    if(rc!=BERR_SUCCESS) {
1234        rc = BERR_TRACE(rc);
1235        goto err_mutex;
1236    }
1237    rc = BKNI_CreateEventGroup(&scheduler->group);
1238    if(rc!=BERR_SUCCESS) {
1239        rc = BERR_TRACE(rc);
1240        goto err_group;
1241    }
1242    rc = BKNI_AddEventGroup(scheduler->group, scheduler->control);
1243    if(rc!=BERR_SUCCESS) {
1244        rc = BERR_TRACE(rc);
1245        goto err_addevent;
1246    }
1247    return scheduler;
1248
1249err_addevent:
1250    BKNI_DestroyEventGroup(scheduler->group);
1251err_group:
1252    BKNI_DestroyMutex(scheduler->callback_lock);
1253err_mutex:
1254    BKNI_DestroyEvent(scheduler->control);
1255err_event:
1256    BKNI_Free(scheduler);
1257err_alloc:
1258    return NULL;
1259
1260}
1261
1262NEXUS_P_Scheduler *
1263NEXUS_P_Scheduler_Create(const char *name, const NEXUS_ThreadSettings *pSettings)
1264{
1265    NEXUS_P_Scheduler *scheduler;
1266    BERR_Code rc;
1267
1268    NEXUS_ASSERT_MODULE(); /* we need lock held to task wouldn't start in middle of initialization */
1269
1270    scheduler = NEXUS_P_Scheduler_Init(0, name, pSettings);
1271    if(!scheduler) {
1272        rc = BERR_TRACE(NEXUS_OS_ERROR);
1273        goto err_scheduller;
1274    }
1275    scheduler->thread = NEXUS_Thread_Create(name, NEXUS_P_Scheduler_Thread, scheduler, pSettings);
1276    if(!scheduler->thread) {
1277        rc = BERR_TRACE(NEXUS_OS_ERROR);
1278        goto err_task;
1279    }
1280    return scheduler;
1281err_task:
1282    scheduler->finished = true;
1283    NEXUS_P_Scheduler_Destroy(scheduler);
1284err_scheduller:
1285    return NULL;
1286}
1287
1288void
1289NEXUS_P_Scheduler_Destroy(NEXUS_P_Scheduler *scheduler)
1290{
1291    NEXUS_TimerHandle timer;
1292    NEXUS_EventCallbackHandle event;
1293    NEXUS_IsrCallbackHandle isr_callback;
1294    NEXUS_TaskCallbackHandle task_callback;
1295
1296    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy:> %#lx", (unsigned long)scheduler));
1297
1298    BDBG_OBJECT_ASSERT(scheduler, NEXUS_P_Scheduler);
1299    NEXUS_ASSERT_MODULE();
1300    scheduler->exit=true;
1301    NEXUS_UnlockModule();
1302    BKNI_SetEvent(scheduler->control); /* wakeup thread to release resources */
1303    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy:NEXUS_Thread_Destroy"));
1304    if(scheduler->thread) {
1305        NEXUS_Thread_Destroy(scheduler->thread);
1306        scheduler->thread = NULL;
1307    }
1308    NEXUS_LockModule();
1309
1310    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: Free timers"));
1311    while(NULL!=(timer=BLST_S_FIRST(&scheduler->timers))) {
1312        if(!timer->deleted) {
1313            BDBG_WRN(("NEXUS_P_Scheduler_Destroy: %#lx lost timer %#lx (%s:%u)", (unsigned long)scheduler, (unsigned long)timer, timer->pFileName, timer->lineNumber));
1314        }
1315        BLST_S_REMOVE_HEAD(&scheduler->timers, list);
1316        BDBG_OBJECT_DESTROY(timer, NEXUS_Timer);
1317        BKNI_Free(timer);
1318    }
1319    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: Free events"));
1320    while(NULL!=(event=BLST_S_FIRST(&scheduler->events))) {
1321        BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: %#lx Free event(%#lx)", (unsigned long)scheduler, (unsigned long)event));
1322        if(!event->deleted) {
1323            BDBG_WRN(("NEXUS_P_Scheduler_Destroy: %#lx lost event %#lx (%s:%u)", (unsigned long)scheduler, (unsigned long)event, event->pFileName, event->lineNumber));
1324        }
1325        BLST_S_REMOVE_HEAD(&scheduler->events, list);
1326        BDBG_OBJECT_DESTROY(event, NEXUS_EventCallback);
1327        BKNI_Free(event);
1328    }
1329    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: Free IsrCallback"));
1330    while(NULL!=(isr_callback=BLST_D_FIRST(&scheduler->isr_callbacks.list))) {
1331        BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: %#lx Free IsrCallback(%#lx)", (unsigned long)scheduler, (unsigned long)isr_callback));
1332        BLST_D_REMOVE_HEAD(&scheduler->isr_callbacks.list, list);
1333        if(!isr_callback->common.deleted) {
1334            BDBG_ERR(("NEXUS_P_Scheduler_Destroy: %#lx lost IsrCallback %#lx (%s:%u)", (unsigned long)scheduler, (unsigned long)isr_callback, isr_callback->common.pFileName, isr_callback->common.lineNumber));
1335            /* do not free. nexus code must be fixed. */
1336        }
1337        else {
1338            BDBG_OBJECT_DESTROY(isr_callback, NEXUS_IsrCallback);
1339            BKNI_Free(isr_callback);
1340        }
1341    }
1342    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: Free TaskCallback"));
1343    while(NULL!=(task_callback=BLST_SQ_FIRST(&scheduler->task_callbacks))) {
1344        BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: %#lx Free TaskCallback(%#lx)", (unsigned long)scheduler, (unsigned long)task_callback));
1345        if(!task_callback->common.deleted) {
1346            BDBG_ERR(("NEXUS_P_Scheduler_Destroy: %#lx lost TaskCallback %#lx (%s:%u)", (unsigned long)scheduler, (unsigned long)task_callback, task_callback->common.pFileName, task_callback->common.lineNumber));
1347        }
1348        BLST_SQ_REMOVE_HEAD(&scheduler->task_callbacks, scheduler_list);
1349        if(!task_callback->common.deleted) {
1350            BDBG_ERR(("NEXUS_P_Scheduler_Destroy: %#lx lost TaskCallback %#lx (%s:%u)", (unsigned long)scheduler, (unsigned long)task_callback, task_callback->common.pFileName, task_callback->common.lineNumber));
1351            /* do not free. nexus code must be fixed. */
1352        }
1353        else {
1354            BDBG_OBJECT_DESTROY(task_callback, NEXUS_TaskCallback);
1355            BKNI_Free(task_callback);
1356        }
1357    }
1358
1359    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: BKNI_RemoveEventGroup"));
1360    BKNI_RemoveEventGroup(scheduler->group, scheduler->control);
1361    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: BKNI_DestroyEventGroup"));
1362    BKNI_DestroyEventGroup(scheduler->group);
1363    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: BKNI_DestroyMutex"));
1364    BKNI_DestroyMutex(scheduler->callback_lock);
1365    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: BKNI_DestroyEvent"));
1366    BKNI_DestroyEvent(scheduler->control);
1367    BDBG_OBJECT_DESTROY(scheduler, NEXUS_P_Scheduler);
1368    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy: BKNI_Free"));
1369    BKNI_Free(scheduler);
1370    BDBG_MSG_TRACE(("NEXUS_P_Scheduler_Destroy:< %#lx", (unsigned long)scheduler));
1371    return;
1372}
1373
1374void
1375NEXUS_P_Base_Scheduler_Init(void)
1376{
1377    BLST_D_INIT(&NEXUS_P_Base_Scheduler_State.callbacks);
1378    return;
1379}
1380
1381void
1382NEXUS_P_Base_Scheduler_Uninit(void)
1383{
1384    struct NEXUS_CallbackCommon *callback;
1385
1386    while(NULL!=(callback=BLST_D_FIRST(&NEXUS_P_Base_Scheduler_State.callbacks)))  {
1387        BDBG_WRN(("leaked task callback: %#lx", callback));
1388        if(callback->type == NEXUS_P_CallbackType_eTask) {
1389            BDBG_OBJECT_ASSERT((NEXUS_TaskCallbackHandle)callback, NEXUS_TaskCallback);
1390        } else if(callback->type == NEXUS_P_CallbackType_eIsr) {
1391            BDBG_OBJECT_ASSERT((NEXUS_IsrCallbackHandle)callback, NEXUS_IsrCallback);
1392        } else {
1393            BDBG_ERR(("illegal scheduler callback type = %x\n", callback->type));
1394            BDBG_ASSERT(0);
1395        }
1396        BLST_D_REMOVE_HEAD(&NEXUS_P_Base_Scheduler_State.callbacks, global_list);
1397        BDBG_WRN(("leaked callback: %#lx scheduler:%#lx object:%#lx allocated at:%s:%u", (unsigned long)callback, (unsigned long)callback->scheduler, (unsigned long)callback->object, callback->pFileName, callback->lineNumber));
1398        if(callback->type == NEXUS_P_CallbackType_eTask) {
1399            BDBG_OBJECT_DESTROY((NEXUS_TaskCallbackHandle)callback, NEXUS_TaskCallback);
1400        } else if(callback->type == NEXUS_P_CallbackType_eIsr) {
1401            BDBG_OBJECT_DESTROY((NEXUS_IsrCallbackHandle)callback, NEXUS_IsrCallback);
1402        }
1403        BKNI_Free(callback);
1404    }
1405    return;
1406}
1407
1408void
1409NEXUS_Base_P_StopCallbacks(void *interfaceHandle)
1410{
1411    NEXUS_P_Scheduler *schedulers[NEXUS_ModulePriority_eMax]; /* list of schedulers */
1412    unsigned nschedulers;
1413    unsigned i;
1414    NEXUS_P_Scheduler *scheduler;
1415    struct NEXUS_CallbackCommon *callback;
1416
1417    NEXUS_LockModule();
1418    for(nschedulers=0, callback=BLST_D_FIRST(&NEXUS_P_Base_Scheduler_State.callbacks); callback; callback=BLST_D_NEXT(callback, global_list)) {
1419
1420        if(callback->object != interfaceHandle) {
1421            continue;
1422        }
1423        callback->stopped = true;
1424       
1425        scheduler = callback->scheduler;
1426        if (callback != scheduler->current_callback) {
1427            /* if this callback isn't current in the scheduler, we don't have to synchronize */
1428            continue;
1429        }
1430       
1431        for(i=0;i<nschedulers;i++) {
1432            if(schedulers[i]==scheduler) {
1433                goto duplicate;
1434            }
1435        }
1436        if(nschedulers==sizeof(schedulers)/sizeof(*schedulers)) {
1437            BDBG_ERR(("NEXUS_Base_P_StopCallbacks: overflow of scheduler array:%u", nschedulers));
1438            /* This should never happen, but if it does don't overflow array. */
1439            break;
1440        }
1441        schedulers[nschedulers] = scheduler;
1442        nschedulers++;
1443duplicate:
1444        ;
1445    }
1446    NEXUS_UnlockModule(); /* release module lock */
1447
1448    /* here all relevant callbacks are marked as 'stopped'. now, we must synchronize with every scheduler
1449    where the callback was current. this could be zero, one or more than one.
1450    to ensure this function returns with no stopped callback still running on any scheduler. */
1451    for(i=0;i<nschedulers;i++) {
1452        scheduler = schedulers[i];
1453        /* a simple acquire/release pair is enough to ensure that any active callback (which may include a stopped callback) completes.
1454        if you find that your application has deadlocked here, please debug your application and don't relax this code.
1455        for instance, you may be trying to close an interface inside a callback, or your close function might be protected with the same
1456        mutex which is being acquired inside a callback. both of these cases will result in a deadlock here. */
1457        BKNI_AcquireMutex(scheduler->callback_lock);
1458        BKNI_ReleaseMutex(scheduler->callback_lock);
1459    }
1460    /* now we know that no stopped callbacks are running on any scheduler. */
1461    return;
1462}
1463
1464void
1465NEXUS_Base_P_StartCallbacks(void *interfaceHandle)
1466{
1467    NEXUS_P_Scheduler *scheduler;
1468    struct NEXUS_CallbackCommon *callback;
1469
1470    NEXUS_LockModule();
1471    for(callback=BLST_D_FIRST(&NEXUS_P_Base_Scheduler_State.callbacks); callback; callback=BLST_D_NEXT(callback, global_list)) {
1472
1473        if(callback->object != interfaceHandle) {
1474            continue;
1475        }
1476        if(!callback->stopped) {
1477            continue;
1478        }
1479        callback->stopped = false;
1480        scheduler = callback->scheduler;
1481        if(callback->type == NEXUS_P_CallbackType_eTask) {
1482            BDBG_OBJECT_ASSERT((NEXUS_TaskCallbackHandle)callback, NEXUS_TaskCallback);
1483            if(callback->armed && !callback->queued) {
1484                callback->queued = true;
1485                BDBG_OBJECT_ASSERT((NEXUS_TaskCallbackHandle)callback, NEXUS_TaskCallback);
1486                BLST_SQ_INSERT_HEAD(&scheduler->task_callbacks, (NEXUS_TaskCallbackHandle)callback, scheduler_list);
1487                BKNI_SetEvent(scheduler->control); /* wakeup thread */
1488            }
1489        } else if(callback->type == NEXUS_P_CallbackType_eIsr) {
1490            BDBG_OBJECT_ASSERT((NEXUS_IsrCallbackHandle)callback, NEXUS_IsrCallback);
1491            if(((NEXUS_IsrCallbackHandle)callback)->armed_save) {
1492                ((NEXUS_IsrCallbackHandle)callback)->armed_save = false;
1493                BKNI_EnterCriticalSection();
1494                if(!callback->armed) {
1495                    callback->armed=true;
1496                    scheduler->isr_callbacks.armed++;
1497                }
1498                BLST_D_REMOVE(&scheduler->isr_callbacks.list, (NEXUS_IsrCallbackHandle)callback, list); /* move item to the front of list, it shall be safe, in worst case thread would pass through list multiple times */
1499                BLST_D_INSERT_HEAD(&scheduler->isr_callbacks.list, (NEXUS_IsrCallbackHandle)callback, list);
1500                BKNI_LeaveCriticalSection();
1501                BKNI_SetEvent(scheduler->control); /* wakeup thread to release resources */
1502            }
1503        }
1504    }
1505    NEXUS_UnlockModule();
1506    return;
1507}
1508
1509
Note: See TracBrowser for help on using the repository browser.