oskit/oskit-20020317/threads/cpuinherit/pthread_ratemono.c

500 lines
11 KiB
C
Executable File

/*
* Copyright (c) 1996, 1998, 1999 University of Utah and the Flux Group.
* All rights reserved.
*
* This file is part of the Flux OSKit. The OSKit is free software, also known
* as "open source;" you can redistribute it and/or modify it under the terms
* of the GNU General Public License (GPL), version 2, as published by the Free
* Software Foundation (FSF). To explore alternate licensing terms, contact
* the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
*
* The OSKit 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 GPL for more details. You should have
* received a copy of the GPL along with the OSKit; see the file COPYING. If
* not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
*/
#ifdef CPU_INHERIT
/*
* Simplified Rate Monotonic scheduler. Can be preemptive or not.
*/
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <oskit/threads/pthread.h>
#include <oskit/threads/cpuinherit.h>
#include <oskit/queue.h>
#include <malloc.h>
#include <assert.h>
#include "hash.h"
/*
* The scheduler structures are per-scheduler instantiation.
*
* The runq is a single doubly linked queue, ordered by schedule period.
* TIDs are mapped back to the state structure using a key value.
*/
typedef struct rmsched {
queue_head_t runq; /* Runq */
int runq_count; /* Number of ready threads */
short ready; /* Scheduler is ready to go */
short preemptible; /* Scheduler is preemptive */
pthread_t tid; /* Back pointer to thread */
hash_table_t *hashtable; /* Hash TID to pstate */
} rmsched_t;
/*
* Per-thread state information structure.
*/
typedef struct rm_state {
queue_chain_t runq; /* Queueing element */
pthread_t tid; /* TID of thread */
int period; /* In ms. */
int duration; /* In ms. */
} rm_state_t;
#define NULL_RMSTATE ((rm_state_t *) 0)
extern int ffs();
void *ratemono_schedloop(void *arg);
static void ratemono_canceled(void *arg);
static int ratemono_pedantic = 1;
/*
* Map TID to pstate structure with a hash table.
*/
static void
ratemono_setstate(rmsched_t *psched, pthread_t tid, rm_state_t *pstate)
{
if (tidhash_add(psched->hashtable, (void *) pstate, tid))
panic("ratemono_setstate: "
"hashtable add failed: tid:%d pstate:0x%x",
tid, (int) pstate);
}
static rm_state_t *
ratemono_getstate(rmsched_t *psched, pthread_t tid)
{
rm_state_t *pstate;
if ((pstate = (rm_state_t *)
tidhash_lookup(psched->hashtable, tid)) == NULL)
panic("ratemono_getstate: "
"hashtable lookup failed: tid:%d", tid);
return pstate;
}
static void
ratemono_remstate(rmsched_t *psched, pthread_t tid)
{
tidhash_rem(psched->hashtable, tid);
}
/*
* Create a Rate Monotonic scheduler. This creates the thread and makes sure
* it gets run so that it exists and is ready to handle scheduling
* messages.
*/
int
create_ratemono_scheduler(pthread_t *tid,
const pthread_attr_t *attr, int preemptible)
{
rmsched_t *psched;
if ((psched = (rmsched_t *) calloc(1, sizeof(rmsched_t))) == NULL)
panic("create_ratemono_scheduler: No more memory");
if (tidhash_create(&psched->hashtable, 0))
panic("create_ratemono_scheduler: Hash Table allocation");
psched->preemptible = preemptible;
queue_init(&psched->runq);
pthread_create(tid, attr, ratemono_schedloop, (void *) psched);
/*
* The scheduler has to run.
*/
while (! psched->ready)
oskit_pthread_sleep(1);
/*
* Back in this thread. Just return.
*/
return 0;
}
/*
* Initialize the per-thread scheduler state structure. This is called
* when a new thread is created. The state structure is then stored
* in the key table so that TIDs can be mapped back to the state structure.
*/
rm_state_t *
ratemono_thread_init(rmsched_t *psched, pthread_t tid, int period)
{
rm_state_t *pstate;
if ((pstate = (rm_state_t *) calloc(1, sizeof(rm_state_t))) == NULL)
panic("ratemono_thread_init: No more memory");
pstate->tid = tid;
pstate->period = period;
ratemono_setstate(psched, tid, pstate);
return pstate;
}
/*
* Are there any threads on the runq?
*/
static inline int
runq_empty(rmsched_t *psched)
{
return (psched->runq_count == 0);
}
/*
* Determine if a thread is on the runq. Use the runq.next pointer since
* its not doing much else.
*/
static inline int
runq_onrunq(rmsched_t *psched, rm_state_t *pstate)
{
return (int) pstate->runq.next;
}
/*
* Add and remove threads from the runq.
*/
/*
* Insert into the runq. The queue is ordered by the scheduling period,
* with lowest at the beginning.
*/
static void
runq_insert(rmsched_t *psched, rm_state_t *pstate)
{
queue_head_t *phdr = &(psched->runq);
rm_state_t *ptmp;
if (queue_empty(phdr)) {
queue_enter(phdr, pstate, rm_state_t *, runq);
goto done;
}
queue_iterate(&(psched->runq), ptmp, rm_state_t *, runq) {
if (pstate->period <= ptmp->period) {
queue_enter_before(phdr, ptmp,
pstate, rm_state_t *, runq);
goto done;
}
}
queue_enter(phdr, pstate, rm_state_t *, runq);
done:
psched->runq_count++;
}
/*
* Dequeue highest priority thread, which is actually the first thread
* on the runq.
*/
static rm_state_t *
runq_dequeue(rmsched_t *psched)
{
queue_head_t *phdr = &(psched->runq);
rm_state_t *pnext;
queue_remove_first(phdr, pnext, rm_state_t *, runq);
pnext->runq.next = (queue_entry_t) 0;
psched->runq_count--;
return pnext;
}
/*
* Remove an arbitrary thread from the runq.
*/
static inline void
runq_remove(rmsched_t *psched, rm_state_t *pstate)
{
queue_head_t *phdr = &(psched->runq);
queue_remove(phdr, pstate, rm_state_t *, runq);
pstate->runq.next = (queue_entry_t) 0;
psched->runq_count--;
}
/*
* Debug the runq.
*/
static void
runq_check(rmsched_t *psched)
{
int count;
rm_state_t *pstate;
count = 0;
queue_iterate(&(psched->runq), pstate, rm_state_t *, runq) {
count++;
if (count > psched->runq_count)
panic("ratemono scheduler: Bad runq(%d): 0x%x\n",
pthread_self(), psched);
}
}
/*
* Debug
*/
static void
rmono_debug(rmsched_t *psched)
{
rm_state_t *pstate;
printf("ratemono(%d):\n", (int) pthread_self());
queue_iterate(&(psched->runq), pstate, rm_state_t *, runq) {
printf("0x%x(%d) Period %d\n",
(int) pstate, (int) pstate->tid, pstate->period);
}
}
/*
* This is the scheduler loop.
*/
void *
ratemono_schedloop(void *arg)
{
rmsched_t *psched = (rmsched_t *) arg;
schedmsg_t msg;
rm_state_t *current_thread = 0, *pstate = 0;
sched_wakecond_t wakeup_cond = 0;
int pedantic, rc;
oskit_s32_t timeout;
psched->tid = pthread_self();
/*
* Must tell the main scheduling code ...
*/
pthread_sched_become_scheduler();
/*
* Preemption means donate with non-zero timeout.
*/
if (psched->preemptible)
timeout = PTHREAD_TICK;
else
timeout = 0;
/*
* Cancelation cleanup handler to cleanup resources at exit.
*/
pthread_cleanup_push(ratemono_canceled, (void *) psched);
pedantic = ratemono_pedantic;
psched->ready = 1;
while (1) {
pthread_testcancel();
CPUDEBUG(RATEMONO,
"ratemono_schedloop(%d): qcount(%d)\n",
psched->tid, psched->runq_count);
/*
* Consume any pending messages until there are no more.
*/
if (! pthread_sched_message_recv(&msg, 0))
goto consume;
if (CPUDEBUG_ISSET(RATEMONO))
rmono_debug(psched);
/*
* Find a thread to run.
*/
if (runq_empty(psched))
current_thread = NULL_RMSTATE;
else
current_thread = runq_dequeue(psched);
/*
* If we found a thread, switch into it and wait for
* a message. Otherwise, wait for messages to arrive
* that indicate something has changed.
*/
if (current_thread) {
CPUDEBUG(RATEMONO,
"ratemono_schedloop(%d): pstate 0x%x(%d)\n",
psched->tid, (int) current_thread,
current_thread->tid);
if (pedantic || !runq_empty(psched))
wakeup_cond = WAKEUP_ON_BLOCK;
else
wakeup_cond = WAKEUP_ON_SWITCH;
/*
* Donate and check the return condition.
*/
rc =
pthread_sched_donate_wait_recv(current_thread->tid,
wakeup_cond, &msg, timeout);
CPUDEBUG(RATEMONO,
"ratemono_schedloop(%d): Donated: %d %s\n",
psched->tid, current_thread->tid,
msg_sched_rcodes[rc & ~SCHEDULE_MSGRECV]);
switch (rc & ~SCHEDULE_MSGRECV) {
case SCHEDULE_NOTREADY:
/*
* Thread was not ready to recv donation.
* Forget about this thread.
*/
break;
case SCHEDULE_BLOCKED:
/* Thread blocked, so forget about it. */
break;
case SCHEDULE_PREEMPTED:
case SCHEDULE_YIELDED:
case SCHEDULE_TIMEDOUT:
assert(! runq_onrunq(psched, current_thread));
runq_insert(psched, current_thread);
break;
default:
if (rc == SCHEDULE_MSGRECV)
runq_insert(psched, current_thread);
else
panic("ratemono_schedloop: "
"Bad return code:%d", rc);
break;
}
/* Back to the top to try again. */
if (! (rc & SCHEDULE_MSGRECV))
continue;
}
else {
/*
* No threads to run so block waiting for a message.
*/
CPUDEBUG(RATEMONO,
"ratemono_schedloop(%d): Recv\n",psched->tid);
rc = pthread_sched_message_recv(&msg, -1);
if (rc == OSKIT_ECANCELED)
pthread_exit((void *) PTHREAD_CANCELED);
}
/*
* Process messages.
*/
consume:
/*
* Map tid in message to thread state structure. Avoid
* lookup if possible.
*/
assert(msg.tid);
if (msg.type != MSG_SCHED_NEWTHREAD) {
if (current_thread && msg.tid == current_thread->tid)
pstate = current_thread;
else
pstate = ratemono_getstate(psched, msg.tid);
}
CPUDEBUG(RATEMONO,
"ratemono_schedloop(%d): Message: %s 0x%x(%d)\n",
psched->tid,
msg_sched_typenames[msg.type], (int) pstate,
(pstate ? pstate->tid : 0));
switch (msg.type) {
case MSG_SCHED_NEWTHREAD:
/*
* New thread has joined us. Create a state structure
* and add it to the runq.
*/
pstate = ratemono_thread_init(psched, msg.tid,
msg.opaque);
/* and add it to the runq */
runq_insert(psched, pstate);
break;
case MSG_SCHED_UNBLOCK:
/*
* A thread wants to be rescheduled.
*/
if (! runq_onrunq(psched, pstate))
runq_insert(psched, pstate);
break;
case MSG_SCHED_SETSTATE:
/*
* Thread parameters have been changed. The opaque
* value is the thread period, which is the same
* as its priority in this scheduling model.
*/
if (runq_onrunq(psched, pstate)) {
runq_remove(psched, pstate);
pstate->period = (int) msg.opaque;
runq_insert(psched, pstate);
}
else
pstate->period = (int) msg.opaque;
break;
case MSG_SCHED_EXITED:
/*
* The thread has exited.
*/
CPUDEBUG(RATEMONO,
"ratemono_schedloop(%d): "
"Exit: Thread 0x%x(%d)\n",
pthread_self(), (int) pstate, pstate->tid);
ratemono_remstate(psched, pstate->tid);
free(pstate);
break;
default:
panic("ratemono_schedloop: Bad message: %d 0x%x\n",
msg.type, pstate);
break;
}
runq_check(psched);
}
/*
* Never reached.
*/
pthread_cleanup_pop(1);
}
/*
* Handle async cancel of the scheduler. Cleanup resources before the thread
* disappears completely.
*
* XXX NOT CLEANING UP THREADS!
*/
void
ratemono_canceled(void *arg)
{
rmsched_t *psched = (rmsched_t *) arg;
CPUDEBUG(RATEMONO,
"ratemono_terminate: Scheduler exiting:%d\n",
pthread_self());
free(psched);
}
#endif CPU_INHERIT