home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 5
/
DATAFILE_PDCD5.iso
/
utilities
/
v
/
vista
/
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 ru