10 #ifndef InitializeListHead
11 #define InitializeListHead(PLH__) ((PLH__)->Flink = (PLH__)->Blink = (PLH__))
15 #define IsListEmpty(PLH__) ((PLH__)->Flink == (PVOID)(PLH__))
18 #ifndef RemoveEntryList
20 #define RemoveEntryList(PLE__) \
22 PLIST_ENTRY pleBack__ = (PLIST_ENTRY)((PLE__)->Blink); \
23 PLIST_ENTRY pleForward__ = (PLIST_ENTRY)((PLE__)->Flink); \
25 pleBack__->Flink = pleForward__; \
26 pleForward__->Blink = pleBack__; \
31 #ifndef InsertTailList
33 #define InsertTailList(PLH__, PLE__) \
35 PLIST_ENTRY pleListHead__ = (PLH__); \
36 PLIST_ENTRY pleBlink__ = (PLIST_ENTRY)((PLH__)->Blink); \
38 (PLE__)->Flink = pleListHead__; \
39 (PLE__)->Blink = pleBlink__; \
40 pleBlink__->Flink = (PLE__); \
41 pleListHead__->Blink = (PLE__); \
46 #ifndef RemoveHeadList
48 #define RemoveHeadList(PLH__) \
49 (PLIST_ENTRY)((PLH__)->Flink); \
50 RemoveEntryList((PLIST_ENTRY)((PLH__)->Flink));
54 #define FIBERTEST_COUNT 500
66 struct FiberData
* pfdPrev
;
70 static LIST_ENTRY a_leQueues
[32];
71 static unsigned nQuantum
= 0;
72 static struct FiberData
* pfdLastStarveScan
= NULL
;
78 struct FiberData
* Fbt_GetCurrent(void);
79 unsigned Fbt_GetCurrentId(void);
80 VOID CALLBACK
Fbt_Startup(PVOID
);
81 void Fbt_Dispatch(struct FiberData
*, int);
82 void Fbt_AfterSwitch(struct FiberData
*);
86 struct FiberData
* Fbt_GetCurrent(VOID
)
88 return GetFiberData();
91 unsigned Fbt_GetCurrentId(VOID
)
93 return Fbt_GetCurrent()->nId
;
98 struct FiberData
* pfdCur
;
100 pfdCur
= Fbt_GetCurrent();
107 pfdCur
->nPrio
= pfdCur
->nRealPrio
;
109 else if((rand() % 100) > 50 - (45 * pfdCur
->nPrio
) / 32)
110 Fbt_Dispatch(pfdCur
, 0);
113 void Fbt_AfterSwitch(struct FiberData
* pfdCur
)
115 struct FiberData
* pfdPrev
;
117 pfdPrev
= pfdCur
->pfdPrev
;
119 /* The previous fiber left some homework for us */
122 /* Kill the predecessor */
123 if(pfdCur
->bExitPrev
)
125 if(pfdLastStarveScan
== pfdPrev
)
126 pfdLastStarveScan
= 0;
128 DeleteFiber(pfdPrev
->pFiber
);
131 /* Enqueue the previous fiber in the correct ready queue */
134 /* Remember the quantum in which the previous fiber was queued */
135 pfdPrev
->nQuantumQueued
= nQuantum
;
137 /* Disable the anti-starvation boost */
141 pfdPrev
->nPrio
= pfdPrev
->nRealPrio
;
144 /* Enqueue the previous fiber */
147 &a_leQueues
[pfdPrev
->nPrio
],
154 VOID CALLBACK
Fbt_Startup(PVOID pParam
)
156 assert(pParam
== GetFiberData());
157 Fbt_AfterSwitch(pParam
);
162 void Fbt_Dispatch(struct FiberData
* pfdCur
, int bExit
)
166 struct FiberData
* pfdNext
;
168 assert(pfdCur
== GetFiberData());
172 /* Every ten quantums check for starving threads */
173 /* FIXME: this implementation of starvation prevention isn't that great */
174 if(nQuantum
% 10 == 0)
180 PLIST_ENTRY ple
= NULL
;
185 /* Pick up from where we left last time */
186 if(pfdLastStarveScan
)
190 nPrio
= pfdLastStarveScan
->nPrio
;
192 /* The last fiber we scanned for starvation isn't queued anymore */
193 if(IsListEmpty(&pfdLastStarveScan
->leQueue
))
194 /* Scan the ready queue for its priority */
196 /* Last fiber for its priority level */
197 else if(pfdLastStarveScan
->leQueue
.Flink
== &a_leQueues
[nPrio
])
198 /* Scan the ready queue for the next priority level */
200 /* Scan the next fiber in the ready queue */
204 ple
= pfdLastStarveScan
->leQueue
.Flink
;
208 /* Priority levels 15-31 are never checked for starvation */
219 Scan at most 16 threads, in the priority range 0-14, applying in total at
220 most 10 boosts. This loop scales O(1)
222 for(j
= 0, k
= 0, b
= 0; j
< 16 && k
< 15 && b
< 10; ++ j
)
226 /* No previous state to resume from */
231 /* Get the first element in the current queue */
232 nQueue
= (k
+ i
) % 15;
234 if(IsListEmpty(&a_leQueues
[nQueue
]))
240 ple
= (PLIST_ENTRY
)a_leQueues
[nQueue
].Flink
;
245 /* Get the current fiber */
246 pfdLastStarveScan
= CONTAINING_RECORD(ple
, struct FiberData
, leQueue
);
247 assert(pfdLastStarveScan
->nMagic
== 0x12345678);
248 assert(pfdLastStarveScan
!= pfdCur
);
250 /* Calculate the number of quantums the fiber has been in the queue */
251 if(nQuantum
> pfdLastStarveScan
->nQuantumQueued
)
252 nDiff
= nQuantum
- pfdLastStarveScan
->nQuantumQueued
;
254 nDiff
= UINT_MAX
- pfdLastStarveScan
->nQuantumQueued
+ nQuantum
;
256 /* The fiber has been ready for more than 30 quantums: it's starving */
259 /* Plus one boost applied */
262 /* Apply the boost */
263 pfdLastStarveScan
->nBoost
= 1;
264 pfdLastStarveScan
->nRealPrio
= pfdLastStarveScan
->nPrio
;
265 pfdLastStarveScan
->nPrio
= 15;
267 /* Re-enqueue the fiber in the correct priority queue */
268 RemoveEntryList(&pfdLastStarveScan
->leQueue
);
269 InsertTailList(&a_leQueues
[15], &pfdLastStarveScan
->leQueue
);
276 /* This fiber is going to die: scan all ready queues */
280 Scan only ready queues for priorities greater than or equal to the priority of
281 the current thread (round-robin)
284 n
= pfdCur
->nPrio
+ 1;
286 /* This loop scales O(1) */
287 for(i
= 32; i
>= n
; -- i
)
291 /* No fiber ready for this priority level */
292 if(IsListEmpty(&a_leQueues
[i
- 1]))
295 /* Get the next ready fiber */
296 pleNext
= RemoveHeadList(&a_leQueues
[i
- 1]);
297 InitializeListHead(pleNext
);
298 pfdNext
= CONTAINING_RECORD(pleNext
, struct FiberData
, leQueue
);
299 assert(pfdNext
->pFiber
!= GetCurrentFiber());
300 assert(pfdNext
->nMagic
== 0x12345678);
304 /* Next fiber chosen */
307 /* Give some homework to the next fiber */
308 pfdNext
->pfdPrev
= pfdCur
;
309 pfdNext
->bExitPrev
= bExit
;
311 /* Switch to the next fiber */
312 SwitchToFiber(pfdNext
->pFiber
);
314 /* Complete the switch back to this fiber */
315 Fbt_AfterSwitch(pfdCur
);
317 /* No next fiber, and current fiber exiting */
322 /* Delete the current fiber. This kills the thread and stops the simulation */
323 if(pfdLastStarveScan
== pfdCur
)
324 pfdLastStarveScan
= NULL
;
326 pCurFiber
= pfdCur
->pFiber
;
328 DeleteFiber(pCurFiber
);
330 /* No next fiber: continue running the current one */
335 Fbt_Dispatch(GetFiberData(), 1);
338 void Fbt_CreateFiber(int bInitial
)
341 struct FiberData
* pData
;
342 static int s_bFiberPrioSeeded
= 0;
343 static LONG s_nFiberIdSeed
= 0;
345 pData
= malloc(sizeof(struct FiberData
));
350 pFiber
= ConvertThreadToFiber(pData
);
352 pFiber
= CreateFiber(0, Fbt_Startup
, pData
);
354 if(!s_bFiberPrioSeeded
)
356 unsigned nFiberPrioSeed
;
359 tCurTime
= time(NULL
);
360 memcpy(&nFiberPrioSeed
, &tCurTime
, sizeof(nFiberPrioSeed
));
361 srand(nFiberPrioSeed
);
362 s_bFiberPrioSeeded
= 1;
367 pData
->nMagic
= 0x12345678;
368 pData
->nId
= InterlockedIncrement(&s_nFiberIdSeed
);
369 pData
->nPrio
= rand() % 32;
370 pData
->pFiber
= pFiber
;
371 pData
->nQuantumQueued
= 0;
373 pData
->nRealPrio
= pData
->nPrio
;
374 pData
->pfdPrev
= NULL
;
375 pData
->bExitPrev
= 0;
379 InitializeListHead(&pData
->leQueue
);
385 &a_leQueues
[pData
->nPrio
],
398 nId
= Fbt_GetCurrentId();
400 _ftprintf(stderr
, _T("[%u] BEGIN\n"), nId
);
402 for(i
= 0; i
< n
; ++ i
)
407 _ftprintf(stderr
, _T("[%u] [%u/%u]\n"), nId
, i
+ 1, n
);
411 for(j
= 0; j
< m
; ++ j
)
417 _ftprintf(stderr
, _T("[%u] END\n"), nId
);
420 int _tmain(int argc
, _TCHAR
const * const * argv
)
426 nFibers
= _tcstoul(argv
[1], NULL
, 0);
428 nFibers
= FIBERTEST_COUNT
;
430 for(i
= 0; i
< 32; ++ i
)
432 InitializeListHead(&a_leQueues
[i
]);
435 for(i
= 0; i
< nFibers
; ++ i
)
436 Fbt_CreateFiber(i
== 0);
438 Fbt_Startup(GetFiberData());