home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Acorn User 2
/
AUCD2.iso
/
program
/
vista.arc
/
c
/
thread
< prev
next >
Wrap
Text File
|
1996-02-01
|
19KB
|
751 lines
// **************************************************************************
// Copyright 1996 David Allison
//
// VV VV IIIIII SSSSS TTTTTT AA
// VV VV II SS TT AA AA
// VV VV II SSSS TT AA AA
// VV VV II SS TT AAAAAAAA
// VV IIIIII SSSS TT AA AA
//
// MULTI-THREADED C++ WIMP CLASS LIBRARY
// for RISC OS
// **************************************************************************
//
// P U B L I C D O M A I N L I C E N C E
// -------------------------------------------
//
// This library is copyright. You may not sell the library for
// profit, but you may sell products which use it providing
// those products are presented as executable code and are not
// libraries themselves. The library is supplied without any
// warranty and the copyright owner cannot be held responsible for
// damage resulting from failure of any part of this library.
//
// See the User Manual for details of the licence.
//
// *************************************************************************
//
// threads
//
#include "Vista:thread.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <swis.h>
#include <stdarg.h>
#include <string.h>
ThreadManager *ThreadManager::current_manager ;
extern void print (char*...) ;
extern void dprintf (char*...) ;
ThreadResource::ThreadResource()
{
available = 0 ;
}
ThreadTimer::ThreadTimer (ThreadManager *m, int delay)
{
manager = m ;
next = prev = NULL ;
_kernel_swi_regs r ;
_kernel_swi (OS_ReadMonotonicTime, &r, &r) ;
time_set = r.r[0] + delay ;
manager->insert_timer (this) ;
}
ThreadTimer::~ThreadTimer()
{
manager->delete_timer (this) ;
}
void ThreadTimer::check_time (int time)
{
if (time >= time_set)
available = 1 ;
}
ThreadPipe::ThreadPipe ()
{
data = NULL ;
size = 0 ;
max = 0 ;
}
ThreadPipe::~ThreadPipe()
{
}
void ThreadPipe::write (char *buffer, int nbytes)
{
if (nbytes > max) // enough space?
{
if (data == NULL) // any buffer
data = (char*)malloc (nbytes * 2) ; // no - allocate one
else
data = (char*)realloc (data, nbytes * 2) ; // yes, extend it
max = nbytes * 2 ;
}
memcpy (data, buffer, nbytes) ; // copy in the data
size = nbytes ;
available = 1 ; // data is now available
}
void ThreadPipe::read (char *buffer, int max, int &nbytes)
{
if (max >= size) // enough space supplied?
{
memcpy (buffer, data, size) ;
nbytes = size ;
available = 0 ; // no data available now
}
else // not enough space - partial read (data still available)
{
memcpy (buffer, data, max) ; // copy one buffer full
nbytes = max ; // say what size
memmove (buffer, buffer + max, size - max) ; // move the rest down
size -= max ; // size is now smaller
}
}
Thread::Thread(char *name, int pri, int newstack)
{
this->name = name ;
manager = ThreadManager::current_manager ;
next = prev = NULL ;
nextq = prevq = NULL ;
if (newstack)
stack = manager->new_stack() ;
else
stack = NULL ;
state = IDLE ;
priority = THREAD_STARTUP_PRIORITY ;
base_priority = pri ;
cputime = 0 ;
accumulated_cputime = 0 ;
manager->insert_thread (this) ;
}
Thread::~Thread()
{
kill() ;
manager->delete_thread (this) ;
manager->delete_stack (stack) ;
}
// start a thread running. This must return as soon as the thread has been placed
// in the run queue of the thread manager. The thread will self terminate when the
// run function returns.
//
// To start a thread we need to:
// 1. Insert it into the run queue at the end of the queue
// 2. Load the sp, sl, fp, lr and pc regs in the thread save area with:
// - sp: thread->stack + 4096
// - sl: thread->stack + 560
// - fp: 0
// - pc: address of code to call the run function
// 3. return from this function to the caller right away
void Thread::start()
{
thread_disable_ints() ;
if (state == READY)
manager->stop_thread (this) ; // remove from run queue if on it
thread_init_save_area (save_area) ;
#ifdef __EASY_C
memset (exception_state, 0, sizeof (exception_state)) ;
#endif
*(int*)&save_area[13*4] = (int)((char*)stack + 4096) ; // set sp
*(int*)&save_area[10*4] = (int)((char*)stack + 560) ; // set sl
*(int*)&save_area[11*4] = 0 ; // set fp
*(int*)&save_area[0] = (int)this ; // set R0
*(int*)&save_area[15*4] = (int)thread_call_start2 ; // set pc
manager->run_thread(this) ; // place on run queue
manager->num_running_threads++ ;
available = 0 ;
// printf ("starting thread %s\n",name) ;
thread_enable_ints() ;
}
void Thread::start2()
{
#ifdef DEBUG
printf ("start2 thread %s\n",name) ;
#endif
run() ; // run the thread body
thread_disable_ints() ;
state = IDLE ;
available = 1 ;
manager->num_running_threads-- ;
thread_enable_ints() ;
for (;;)
yield() ;
}
void Thread::exit (int status)
{
exit_status = status ;
thread_disable_ints() ;
state = DEAD ;
available = 1 ;
manager->num_running_threads-- ;
thread_enable_ints() ;
for(;;) // nothing to return to so just loop until interrupted
yield() ;
}
//
// kill a thread. This may be yourself or another thread. Cannot be restarted
//
void Thread::kill()
{
thread_disable_ints() ;
int running = manager->running == this ;
if (!running && state == READY)
manager->stop_thread (this) ;
if (state == READY)
manager->num_running_threads-- ;
state = DEAD ;
available = 1 ;
thread_enable_ints() ;
if (running)
for (;;) // cant get out of here
yield() ;
}
//
// stop a thread temporarily until resumed
//
void Thread::stop()
{
if (state == STOPPED)
return ;
thread_disable_ints() ;
int running = manager->running == this ;
if (!running)
manager->stop_thread (this) ;
state = STOPPED ;
thread_enable_ints() ;
if (running)
while (state == STOPPED)
yield() ;
}
//
// resume a stopped thread
//
void Thread::resume()
{
if (state != STOPPED)
return ;
manager->run_thread (this) ;
}
void Thread::yield()
{
thread_yield (manager) ;
}
void Thread::wakeup()
{
if (state != SLEEPING)
return ;
manager->wakeup_thread (this) ; // remove from sleep queue
manager->run_thread (this) ; // place on run queue
}
//
// cause a thread to sleep for a period of time
//
// This calculates the wakeup time and places the thread on the sleep queue
// It then schedules the next thread and enters a loop.
// the loop will be terminated when the quantum expires and restarted
// when the manager wakes the thred up by placing it in the run queue again
// and resetting the sleeping flag
void Thread::sleep (int time) // sleep for centiseconds
{
// printf ("sleep(time) %s\n",name) ;
if (manager->running != this)
throw ("Can't put another thread to sleep") ;
ThreadTimer *timer = new ThreadTimer (manager,time) ; // allocate a new timer
sleep (timer) ;
delete timer ;
}
//
// sleep until the given thread terminates
//
//
// This is the general sleep routine. You can put yourself or another thread to sleep waiting
// for a particular resource.
//
void Thread::sleep (ThreadResource *r, int pri)
{
thread_disable_ints() ;
int running = manager->running == this ;
resource = r ; // wait for this resource
if (!running)
manager->stop_thread(this) ;
base_priority = pri ;
#ifdef DEBUG
printf ("sleep(resource) %s\n",name) ;
#endif
manager->sleep_thread (this) ; // place myself on sleep queue
thread_enable_ints() ;
if (running)
while (state == SLEEPING) // will wake up when manager resets sleeping
yield() ;
}
void Thread::write (ThreadPipe *pipe, char *buffer, int nbytes)
{
while (pipe->available) // any data already in pipe?
sleep (pipe) ; // sleep until space available
pipe->write (buffer, nbytes) ; // write the data
}
void Thread::read (ThreadPipe *pipe, char *buffer, int max, int &nbytes)
{
while (!pipe->available) // any data available
sleep (pipe) ; // sleep until some is
pipe->read (buffer, max, nbytes) ; // read the data
}
void Thread::setpriority (int pri)
{
base_priority = pri ;
}
int Thread::resource_available()
{
return resource->available ;
}
MainThread::MainThread()
: Thread ("main", 50,0)
{
quantum_limit = 0 ;
}
MainThread::~MainThread()
{
}
void MainThread::set_limit(int quanta)
{
quantum_limit = quanta ;
}
void MainThread::run()
{
#ifdef DEBUG
printf ("main thread running for %d quanta\n",quantum_limit) ;
#endif
manager->quantum_count = 0 ;
while (manager->quantum_count < quantum_limit && manager->num_running_threads > 0)
yield() ;
}
ResourceThread::ResourceThread()
: Thread ("resource", 55,1)
{
}
void ResourceThread::run()
{
_kernel_swi_regs r ;
Thread *t, *nextt ;
//
// process all timers
//
for (;;)
{
if (manager->timers != NULL)
{
_kernel_swi (OS_ReadMonotonicTime, &r, &r) ;
for (ThreadTimer *timer = manager->timers ; timer != NULL ; timer = timer->next)
timer->check_time (r.r[0]) ;
}
//
// now process all sleeping threads
//
for (t = manager->sleepqueue ; t != NULL ; t = nextt) // process all sleeping threads
{
nextt = t->nextq ;
if (t->resource_available())
{
thread_disable_ints() ;
manager->wakeup_thread(t) ; // remove from sleep queue
manager->run_thread (t) ; // insert in run queue
thread_enable_ints() ;
}
}
yield() ;
}
}
ThreadManager::ThreadManager()
{
current_manager = this ;
stack_pool = NULL ;
// printf ("starting thread manager %x\n",this) ;
threads = last_thread = NULL ;
runqueue = end_runqueue = NULL ;
sleepqueue = end_sleepqueue = NULL ;
timers = last_timer = NULL ;
running = NULL ;
num_threads = 0 ;
main_thread = new MainThread ; // low priority
resource_thread = new ResourceThread ; // high priority
resource_thread->start() ; // start resource thread
num_running_threads = 0 ;
thread_poll_rate = 1 ;
}
ThreadManager::~ThreadManager()
{
thread_disable_ints() ;
thread_stop_ticker(this) ;
Thread *nextt ;
for (Thread *t = threads ; t != NULL ; t = nextt)
{
nextt = t->next ;
delete t ;
}
}
//
// run all the threads for the specified number of quanta
//
void ThreadManager::run (int quanta)
{
current_manager = this ;
main_thread->set_limit(quanta) ;
running = main_thread ;
#ifdef __EASY_C
__save_exception_state__ (main_thread->exception_state) ;
#endif
running->state = Thread::RUNNING ;
thread_callback_regs = main_thread->save_area ; // set callback save area
thread_start_ticker(this) ;
thread_enable_ints() ;
thread_system_running = 1 ;
main_thread->run() ;
thread_system_running = 0 ;
thread_disable_ints() ;
thread_stop_ticker(this) ;
#ifdef __EASY_C
__restore_exception_state__ (main_thread->exception_state) ;
#endif
}
//
// switch to the next available thread in the run queue. Called from the
// callback handler to switch to the next available thread
//
void ThreadManager::context_switch()
{
int time = thread_get_time() ;
#ifdef DEBUG
printf ("context switch %x\n",this) ;
#endif
quantum_count++ ;
running->cputime += (thread_poll_rate + 1) * 100 ;
running->accumulated_cputime += (time - running->time)*100 ;
if (running->state == Thread::RUNNING)
run_thread(running) ; // put current thread on run queue
next_thread() ; // get next thread running
}
// insert a thread onto the end of the run queue
void ThreadManager::run_thread (Thread *t)
{
#ifdef DEBUG
printf ("running thread %s\n",t->name) ;
#endif
if (t->state == Thread::DEAD)
throw ("Can't resurrect a dead thread") ;
if (runqueue == NULL)
runqueue = end_runqueue = t ;
else
{
end_runqueue->nextq = t ;
t->prevq = end_runqueue ;
end_runqueue = t ;
}
t->state = Thread::READY ;
}
//
// remove a thread from the run queue
//
void ThreadManager::stop_thread (Thread *t)
{
if (t->state != Thread::READY)
throw ("Thread is not ready to run") ;
if (t->prevq == NULL)
runqueue = t->nextq ;
else
t->prevq->nextq = t->nextq ;
if (t->nextq == NULL)
end_runqueue = t->prevq ;
else
t->nextq->prevq = t->prevq ;
t->nextq = t->prevq = NULL ;
t->state = Thread::IDLE ;
}
void ThreadManager::next_thread ()
{
Thread *t, *next ;
int highest_pri = 0x7fffffff ;
for (t = runqueue ; t != NULL ; t = t->nextq)
{
t->cputime = t->cputime / 2 ;
t->priority = (t->cputime / 2) + t->base_priority ;
#ifdef DEBUG
printf ("%x: thread %s, cpu: %d, pri = %d\n",t,t->name,t->cputime,t->priority) ;
#endif
if (t->priority < highest_pri)
{
highest_pri = t->priority ;
next = t ;
}
}
#ifdef __EASY_C
__save_exception_state__ (running->exception_state) ;
#endif
stop_thread (next) ; // remove from run queue
running = next ;
running->state = Thread::RUNNING ;
#ifdef __EASY_C
__restore_exception_state__ (running->exception_state) ;
#endif
#ifdef DEBUG
printf ("running %s, pc = %x\n",running->name,*(int*)&running->save_area[15*4]) ;
#endif
thread_callback_regs = running->save_area ; // set callback reg save area
running->time = thread_get_time() ; // read time gained CPU
}
void ThreadManager::sleep_thread (Thread *t)
{
#ifdef DEBUG
printf ("sleeping %s\n",t->name) ;
#endif
if (sleepqueue == NULL)
sleepqueue = end_sleepqueue = t ;
else
{
end_sleepqueue->nextq = t ;
t->prevq = end_sleepqueue ;
end_sleepqueue = t ;
}
t->state = Thread::SLEEPING ;
#ifdef DEBUG
printf ("now sleeping\n") ;
#endif
}
//
// remove a thread from the sleep queue
//
void ThreadManager::wakeup_thread (Thread *t)
{
#ifdef DEBUG
printf ("waking up %s\n",t->name) ;
#endif
if (t->prevq == NULL)
sleepqueue = t->nextq ;
else
t->prevq->nextq = t->nextq ;
if (t->nextq == NULL)
end_sleepqueue = t->prevq ;
else
t->nextq->prevq = t->prevq ;
t->nextq = t->prevq = NULL ;
t->state = Thread::IDLE ;
t->cputime = (t->cputime / 2) ;
t->priority = (t->cputime / 2) + t->base_priority ;
}
void ThreadManager::insert_thread (Thread *t)
{
// printf ("thread inserted\n") ;
if (threads == NULL)
threads = last_thread = t ;
else
{
last_thread->next = t ;
t->prev = last_thread ;
last_thread = t ;
}
num_threads++ ;
}
void ThreadManager::delete_thread (Thread *t)
{
if (t->prev == NULL)
threads = t->next ;
else
t->prev->next = t->next ;
if (t->next == NULL)
last_thread = t->prev ;
else
t->next->prev = t->prev ;
num_threads-- ;
}
void ThreadManager::insert_timer (ThreadTimer *t)
{
// printf ("thread inserted\n") ;
if (timers == NULL)
timers = last_timer = t ;
else
{
last_timer->next = t ;
t->prev = last_timer ;
last_timer = t ;
}
}
void ThreadManager::delete_timer (ThreadTimer *t)
{
if (t->prev == NULL)
timers = t->next ;
else
t->prev->next = t->next ;
if (t->next == NULL)
last_timer = t->prev ;
else
t->next->prev = t->prev ;
}
_kernel_stack_chunk *ThreadManager::new_stack()
{
Thread *t ;
_kernel_stack_chunk *stack ;
if (stack_pool == NULL)
{
_kernel_stack_chunk *s = _kernel_current_stack_chunk() ;
for (int i = 0 ; i < THREAD_STACK_BLOCK ; i++)
{
stack = (_kernel_stack_chunk*)malloc (THREAD_STACK_SIZE) ;
if (stack == NULL)
throw ("Out of memory - cant allocate stack for thread") ;
stack->sc_next = stack_pool ;
stack->sc_mark = 0xf60690ff ; // see PRM page 4-239
stack->sc_prev = NULL ;
stack->sc_size = THREAD_STACK_SIZE ;
stack->sc_deallocate = 0 ;
memcpy (stack+1, s+1, 7*4) ; // copy in 7 reserved words
stack_pool = stack ;
}
}
stack = stack_pool ;
stack_pool = stack_pool->sc_next ;
stack->sc_next = NULL ;
return stack ;
}
void ThreadManager::delete_stack (_kernel_stack_chunk *stack)
{
_kernel_stack_chunk *next ;
while (stack != NULL)
{
next = stack->sc_next ;
stack->sc_next = stack_pool ;
stack->sc_prev = NULL ;
stack_pool = stack ;
stack = next ;
}
}
void ThreadManager::exit()
{
thread_disable_ints() ;
thread_stop_ticker(this) ;
}
void ThreadManager::sleep (int time)
{
running->sleep (time) ;
}
void ThreadManager::sleep (ThreadResource *r, int pri)
{
running->sleep (r,pri) ;
}
void ThreadManager::yield ()
{
running->yield () ;
}
void ThreadManager::show_threads()
{
Thread *t ;
dprintf ("NAME STATE PRI BPRI CPU ACCCPU") ;
dprintf ("---- ----- --- ---- --- ------") ;
for (t = threads ; t != NULL ; t = t->next)
dprintf ("%-12s %-10s %-4d %-4d %-4d %-8d",t->name,
t->state == Thread::IDLE?"IDLE":
t->state == Thread::READY?"READY":
t->state == Thread::RUNNING?"RUNNING":
t->state == Thread::SLEEPING?"SLEEPING":
t->state == Thread::STOPPED?"STOPPED":
t->state == Thread::DEAD?"DEAD":"UNKNOWN",t->priority,t->base_priority,t->cputime,
t->accumulated_cputime) ;
dprintf ("") ;
}
void ThreadManager::stop_threads()
{
thread_system_running = 0 ;
thread_disable_ints() ;
thread_stop_ticker (this) ;
}
void ThreadManager::resume_threads()
{
thread_start_ticker (this) ;
thread_enable_ints() ;
thread_system_running = 1 ;
}