home *** CD-ROM | disk | FTP | other *** search
- // **************************************************************************
- // 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 ;
- }
-
-