aboutsummaryrefslogtreecommitdiff
path: root/src/sys/schedule.c
blob: bde12bc78fda70e2766a28a06ca77f752949c6bb (plain)
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <drivers/uart.h>
#include <sys/core.h>
#include <sys/schedule.h>
#include <util/mutex.h>

#define SYS_SCHEDULE_C
struct Scheduler scheduler = {
	.tlist = {
		{.prev = 0, .next = 0, .data = 0},
		{.prev = 0, .next = 0, .data = 0},
		{.prev = 0, .next = 0, .data = 0},
		{.prev = 0, .next = 0, .data = 0},
		{.prev = 0, .next = 0, .data = 0},
		{.prev = 0, .next = 0, .data = 0},
	},
	.rthread_ll = 0,
};

void init_scheduler(void)
{
	for(int i = 0; i < PRIORITIES; i++) {
		scheduler.tlist[i].prev = &scheduler.tlist[i];
		scheduler.tlist[i].next = &scheduler.tlist[i];
		scheduler.tlist[i].data = 0;
	}
	scheduler.rthread_ll = 0;
}

unsigned char stacks_table[MAX_THREADS] = {0, };

void* get_stack(void)
{
	for (int i = 0; i < MAX_THREADS; i++) {
		if (stacks_table[i] == 0) {
			stacks_table[i] = 1;
			return (void*)heap_end() - STACK_SIZE*i;
		}
	}
	return 0;
}

static unsigned long nextpid = 3;
void add_thread(void (*thread_fxn)(void), unsigned char priority)
{
	struct Thread* thread = (struct Thread*)malloc(sizeof(struct Thread));
	// Set the program counter to the entry
	thread->thread = thread_fxn;
	// Get a stack frame
	thread->stack = get_stack();
	thread->stack_base = thread->stack;
	// Put in error state for no stack
	if(thread->stack == 0)
		thread->data.status = THREAD_STACK_ERROR;
	else
		thread->data.status = THREAD_READY;
	// Doesn't wait for mutex at start
	thread->data.mutex_waiting = 0;
	// Set PID
	thread->data.pid = nextpid++;
	unsigned char p = priority;
	if (p >= PRIORITIES) {
		p = PRIORITIES - 1;
	}
	thread->data.priority = p;
	push_ll(&scheduler.tlist[p], thread);
}

struct LL* get_next_thread(void)
{
	for(unsigned long i = 0; i < PRIORITIES; i++) {
		struct LL* thread_ll = scheduler.tlist[i].next;
		if (thread_ll == &scheduler.tlist[i])
			continue;
		do {
			struct Thread* thread = thread_ll->data;
			if((thread->data.status == THREAD_RUNNING) || (thread->data.status == THREAD_READY))
				return thread_ll;
			thread_ll = thread_ll->next;
		} while(thread_ll != &scheduler.tlist[i]);
	}
	return 0;
}

unsigned long syssp = 0;
void schedule(void)
{
	struct LL* current_thread_ll = scheduler.rthread_ll;
	struct LL* next_thread_ll = get_next_thread();
	if (current_thread_ll) {
		if (current_thread_ll != next_thread_ll) {
			// Context switch
			struct Thread* current_thread = current_thread_ll->data;
			struct Thread* next_thread = next_thread_ll->data;
			//preserveregs(current_thread);
			preservestack(current_thread);
			preservepc(current_thread);
			restorestack(next_thread);
			//restoreregs(next_thread);
			scheduler.rthread_ll = next_thread_ll;
		}
	}
	else if (next_thread_ll) {
		struct Thread* next_thread = next_thread_ll->data;
		preservesysstack(&syssp);
		//preservesysregs(&regloc)
		restorestack(next_thread);
		//restoreregs(next_thread);
		scheduler.rthread_ll = next_thread_ll;
	}
	if (scheduler.rthread_ll) {
		struct Thread* rthread = scheduler.rthread_ll->data;
		// Run the thread - i.e. jump to the pc
		rthread->thread();
		// Remove the currently running thread after completion
		remove_running_thread();
		// Schedule the next thread
		schedule();
	} else {
		//restoresysregs(&regloc);
		restoresysstack(&syssp);
	}
}

void remove_running_thread(void)
{
	if (scheduler.rthread_ll != 0) {
		struct LL* ll = scheduler.rthread_ll;
		if ((ll->next == ll->prev) && (ll->next == ll)) {
			ll->data = 0;
		}
		else {
			struct LL* prev = ll->prev;
			struct LL* next = ll->next;
			prev->next = ll->next;
			next->prev = ll->prev;
			free(ll);
		}
		scheduler.rthread_ll = 0;
	}
}