summaryrefslogtreecommitdiff
path: root/sos-code-article6.75/sos/sched.c
blob: 2788be0a132478836963c7755080ec45a998551a (plain) (blame)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/* Copyright (C) 2004 David Decotigny

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
   USA. 
*/

#include <sos/errno.h>
#include <sos/klibc.h>
#include <sos/assert.h>
#include <sos/list.h>

#include "sched.h"


/**
 * The definition of the scheduler queue. We could have used a normal
 * kwaitq here, it would have had the same properties (regarding
 * priority ordering mainly). But we don't bother with size
 * considerations here (in kwaitq, we had better make the kwaitq
 * structure as small as possible because there are a lot of kwaitq in
 * the system: at least 1 per opened file), so that we can implement a
 * much faster way of handling the prioritized jobs.
 */
struct sos_sched_queue
{
  unsigned int nr_threads;
  struct sos_thread *thread_list[SOS_SCHED_NUM_PRIO];
};


/**
 * We manage 2 queues: a queue being scanned for ready threads
 * (active_queue) and a queue to store the threads the threads having
 * expired their time quantuum.
 */
static struct sos_sched_queue *active_queue, *expired_queue;


/**
 * The instances for the active/expired queues
 */
static struct sos_sched_queue sched_queue[2];


sos_ret_t sos_sched_subsystem_setup()
{
  memset(sched_queue, 0x0, sizeof(sched_queue));
  active_queue  = & sched_queue[0];
  expired_queue = & sched_queue[1];

  return SOS_OK;
}


/**
 * Helper function to add a thread in a ready queue AND to change the
 * state of the given thread to "READY".
 *
 * @param insert_at_tail TRUE to tell to add the thread at the end of
 * the ready list. Otherwise it is added at the head of it.
 */
static sos_ret_t add_in_ready_queue(struct sos_sched_queue *q,
				    struct sos_thread *thr,
				    sos_bool_t insert_at_tail)
{
  sos_sched_priority_t prio;

  SOS_ASSERT_FATAL( (SOS_THR_CREATED == thr->state)
		    || (SOS_THR_RUNNING == thr->state) /* Yield */
		    || (SOS_THR_BLOCKED == thr->state) );

  /* Add the thread to the CPU queue */
  prio = sos_thread_get_priority(thr);
  if (insert_at_tail)
    list_add_tail_named(q->thread_list[prio], thr,
			ready.rdy_prev, ready.rdy_next);
  else
    list_add_head_named(q->thread_list[prio], thr,
			ready.rdy_prev, ready.rdy_next);
  thr->ready.rdy_queue = q;
  q->nr_threads ++;

  /* Ok, thread is now really ready to be (re)started */
  thr->state = SOS_THR_READY;

  return SOS_OK;
}


sos_ret_t sos_sched_set_ready(struct sos_thread *thr)
{
  sos_ret_t retval;

  /* Don't do anything for already ready threads */
  if (SOS_THR_READY == thr->state)
    return SOS_OK;

  if (SOS_SCHED_PRIO_IS_RT(sos_thread_get_priority(thr)))
    {
      /* Real-time thread: schedule it for the present turn */
      retval = add_in_ready_queue(active_queue, thr, TRUE);
    }
  else
    {
      /* Non real-time thread: schedule it for next turn */
      retval = add_in_ready_queue(expired_queue, thr, TRUE);
    }

  return retval;
}


sos_ret_t sos_sched_change_priority(struct sos_thread *thr,
				    sos_sched_priority_t priority)
{
  struct sos_thread *thread_list;
  SOS_ASSERT_FATAL(SOS_THR_READY == thr->state);

  /* Temp variable */
  thread_list
    = thr->ready.rdy_queue->thread_list[sos_thread_get_priority(thr)];

  list_delete_named(thread_list, thr, ready.rdy_prev, ready.rdy_next);

  /* Update lists */
  thread_list = thr->ready.rdy_queue->thread_list[priority];
  list_add_tail_named(thread_list, thr, ready.rdy_prev, ready.rdy_next);
  thr->ready.rdy_queue->thread_list[priority] = thread_list;

  return SOS_OK;
}


struct sos_thread * sos_reschedule(struct sos_thread *current_thread,
				   sos_bool_t do_yield)
{
  sos_sched_priority_t prio;

  if (SOS_THR_ZOMBIE == current_thread->state)
    {
      /* Don't think of returning to this thread since it is
	 terminated */
      /* Nop */
    }
  else if (SOS_THR_BLOCKED != current_thread->state)
    {
      /* Take into account the current executing thread unless it is
	 marked blocked */
      if (do_yield)
	{
	  /* Ok, reserve it for next turn */
	  if (SOS_SCHED_PRIO_IS_RT(sos_thread_get_priority(current_thread)))
	    add_in_ready_queue(active_queue, current_thread, TRUE);
	  else
	    add_in_ready_queue(expired_queue, current_thread, TRUE);
	}
      else
	{
	  /* Put it at the head of the active list */
	  add_in_ready_queue(active_queue, current_thread, FALSE);
	}
    }


  /* Active queue is empty ? */
  if (active_queue->nr_threads <= 0)
    {
      /* Yes: Exchange it with the expired queue */
      struct sos_sched_queue *q;
      q = active_queue;
      active_queue = expired_queue;
      expired_queue = q;
    }

  /* Now loop over the priorities in the active queue, looking for a
     non-empty queue */
  for (prio = SOS_SCHED_PRIO_HIGHEST ; prio <= SOS_SCHED_PRIO_LOWEST ; prio ++)
    {
      struct sos_thread *next_thr;

      if (list_is_empty_named(active_queue->thread_list[prio],
			      ready.rdy_prev, ready.rdy_next))
	continue;
      
      /* Queue is not empty: take the thread at its head */
      next_thr = list_pop_head_named(active_queue->thread_list[prio],
				     ready.rdy_prev, ready.rdy_next);
      active_queue->nr_threads --;

      return next_thr;
    }


  SOS_FATAL_ERROR("No kernel thread ready ?!");
  return NULL;
}