forked from jitesh1337/mythread_lib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mythread_yield.c
97 lines (83 loc) · 2.91 KB
/
mythread_yield.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* Single Author info:
* jhshah Jitesh H Shah
* Group info:
* jhshah Jitesh H Shah
* sskanitk Salil S Kanitkar
* ajalgao Aditya A Jalgaonkar
*/
#include <mythread.h>
#include <futex.h>
#include <mythread_q.h>
#include <string.h>
#include <sys/syscall.h>
/* Global futex. Please see the mythread_yield() function for more info */
struct futex gfutex;
/* This function is called from inside yield. It finds a next suitable thread
* which is in the READY state and wakes it up for execution.
*
* It starts looping from the next thread of the current one and hence, FIFO
* is ensured.
*/
int __mythread_dispatcher(mythread_private_t * node)
{
mythread_private_t *ptr = node->next;
/* Loop till we find a thread in READY state. This loop is guanrateed
* to end since idle thread is ALWAYS READY.
*/
while (ptr->state != READY)
ptr = ptr->next;
/* No other thread is READY. Nothing to do */
if (ptr == node)
return -1;
else {
DEBUG_PRINTF("Dispatcher: Wake-up:%ld Sleep:%ld %d %d\n",
(unsigned long)ptr->tid, (unsigned long)node->tid,
ptr->sched_futex.count, ptr->state);
/* Wake up the target thread. This won't cause any races because
* it is protected by a global futex.
*/
futex_up(&ptr->sched_futex);
DEBUG_PRINTF("Dispatcher: Woken up:%ld, to %d\n",
(unsigned long)ptr->tid, ptr->sched_futex.count);
return 0;
}
}
/* Yield: Yield the processor to another thread. Dispatcher selects the next
* appropriate thread and wakes it up. Then current thread sleeps.
*/
int mythread_yield()
{
mythread_private_t *self;
int retval;
self = __mythread_selfptr();
/* Take the global futex to avoid races in yield. There was a condition
* when after a wake-up the target thread races ahead and entered yield
* before the first thread finised. This caused deadlocks and ugly races
* when the original thread hadn't slept yet, but was tried to woken up.
* So, protect yield by a global futex and make sure the current thread
* atleast reduces its futex value to 0, before another one starts.
*/
futex_down(&gfutex);
retval = __mythread_dispatcher(self);
/* Only one thread. Nothing to do */
if (retval == -1) {
futex_up(&gfutex);
return 0;
}
DEBUG_PRINTF("Yield: Might sleep on first down %ld %d\n",
(unsigned long)self->tid, self->sched_futex.count);
/* The "if" condition was to fix a couple of race conditions. The purpose
* of two futex down's is to make the process sleep on the second. But
* sometimes the value of the futex is already 0, so do a conditional
* down. This, alongwith the global futex, seems to alleviate maximum
* races in yield.
*/
if (self->sched_futex.count > 0)
futex_down(&self->sched_futex);
futex_up(&gfutex);
DEBUG_PRINTF("Yield: Might sleep on second down %ld %d\n",
(unsigned long)self->tid, self->sched_futex.count);
/* Sleep till another process wakes us up */
futex_down(&self->sched_futex);
return 0;
}