1/* 2 * Copyright (C) 2001-2005 Chris Ross 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * o Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * o Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * o Neither the name of the ferite software nor the names of its contributors may 14 * be used to endorse or promote products derived from this software without 15 * specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30uses "thread.lib"; 31 32module-header 33{ 34 35#include "../../libs/aphex/include/aphex.h" 36 37#define SelfThread ((FeriteThread*)self->odata) 38#define SelfMutex ((AphexMutex*)self->odata) 39#define SelfEvent ((AphexEvent*)self->odata) 40 41 FeriteScript *ferite_thread_create_script( FeriteScript *script ); 42 void ferite_thread_destroy_script( FeriteScript *script, FeriteThread *ctx, int fd ); 43 void *ferite_thread_execute( void *ptr ); 44 45} 46 47/** 48 * @class Thread 49 * @brief A thread object allowing for multi-threaded scripts within the ferite environment 50 * @description This class provides the basic framework for threading. To create a thread you 51 have to inherit from this base class and create a run method. Once that is done 52 * You create an instance of the object and call the .start() method NOT the .run() 53 * method. This causes a thread to be spawned and the .run() method called. The 54 * thread will exit when the .run() method has completed. If you implement your 55 * own constructor is is VITAL that you call super() to make sure that the 56 * native side of the thread is instantiated correctly. 57 * @example <code> 58 <keyword>class</keyword> MyThread <keyword>extends</span> Thread {<nl/> 59 <tab/><keyword>function</keyword> run() {<nl/> 60 <tab/><tab/><keyword>while</keyword>( <type>true</type> ) {<nl/> 61 <tab/><tab/><tab/>Thread.sleep( 1000 ); <nl/> 62 <tab/><tab/><tab/>Console.println( "A<keyword>not</keyword>her Second Gone" );<nl/> 63 <tab/><tab/>}<nl/> 64 <tab/>}<tab/><nl/> 65 }<nl/> 66 <nl/> 67 <type>object</type> thread = <keyword>new</keyword> MyThread();<nl/> 68 thread.start( <keyword>true</keyword> );</code><nl/> 69 */ 70class Thread 71{ 72 /** 73 * @function constructor 74 * @declaration function constructor() 75 * @brief The constructor 76 * @warning You must make sure this is called within sub-classes. 77 */ 78 native function constructor() 79 { 80 /* create new script for this thread */ 81 self->odata = fmalloc( sizeof(FeriteThread) ); 82 SelfThread->ctxt = aphex_thread_create(); 83 SelfThread->script = ferite_thread_create_script( script ); 84 SelfThread->obj = self; 85 SelfThread->running = FE_FALSE; 86 SelfThread->pass_exceptions = FE_FALSE; 87 } 88 89 native function destructor() 90 { 91 if( SelfThread != NULL ) 92 { 93 if( !SelfThread->running ) 94 ferite_thread_destroy_script( script, SelfThread, FE_TRUE ); 95 self->odata = NULL; 96 } 97 } 98 99 /** 100 * @function start 101 * @declaration function start( number detach ) 102 * @brief Start the thread of execution 103 * @param number detach 'true' or 'false', if 'true' the thread will detach upon running 104 * @description When this function is run, it will return straight away. Pass true if you want the 105 * Thread to automatically clear itself up. Otherwise Thread.join() must be called on 106 * it. 107 */ 108 native function start( number detach ) 109 { 110 if( SelfThread != NULL ) 111 { 112 if( aphex_thread_start( SelfThread->ctxt, ferite_thread_execute, SelfThread, (int)detach ) != 0 ) 113 ferite_error( script, 0, "Unable to start thread! Not enough resources!\n" ); 114 } 115 else { 116 ferite_error( script, 0, "Unable to start thread! (this usually happens when the Thread constructor has not been called from the subclass.)\n" ); 117 } 118 } 119 120 /** 121 * @function start 122 * @declaration function start() 123 * @brief Start the thread of execution 124 * @description When this function is run, it will return straight away. The thread will clean itself 125 up when done. 126 */ 127 function start() 128 { 129 return .start( true ); 130 } 131 132 /** 133 * @function run 134 * @declaration function run() 135 * @brief The function that gets run. The stock run method exits immediatly. You need to implement your own function. 136 */ 137 native function run(); 138 139 /** 140 * @function sleep 141 * @declaration function sleep( number msecs ) 142 * @brief Causes the thread to sleep for an amount of time 143 * @param number msecs The amount of time to sleep in microseconds 144 */ 145 native function sleep( number msecs ) 146 { 147 aphex_thread_sleep( SelfThread->ctxt, (long)msecs ); 148 } 149 150 /** 151 * @function join 152 * @declaration static native function join( object thread ) 153 * @brief Cause the calling thread to wait for the thread passed to the method to complete 154 * @param object thread The thread to wait for 155 * @static 156 * @example <nl/><code> 157 <keyword>class</keyword> MyThread <keyword>extends</span> Thread {<nl/> 158 <tab/><keyword>function</keyword> run() {<nl/> 159 <tab/><tab/><keyword>while</keyword>( <type>true</type> ) {<nl/> 160 <tab/><tab/><tab/>Thread.sleep( 1000 ); <nl/> 161 <tab/><tab/><tab/>Console.println( "A<keyword>not</keyword>her Second Gone" );<nl/> 162 <tab/><tab/>}<nl/> 163 <tab/>}<tab/><nl/> 164 }<nl/> 165 <nl/> 166 <type>object</type> thread = <keyword>new</keyword> MyThread();<nl/> 167 thread.start(<type>false</type>);<nl/> 168 Thread.join(thread); </code><nl/> 169 */ 170 static native function join( object thread ) 171 { 172 if( thread != NULL && thread->odata != NULL ) 173 { 174 aphex_thread_join( ((FeriteThread*)thread->odata)->ctxt ); 175 } 176 } 177 178 /** 179 * @function setPassExceptions 180 * @declaration function setPassExceptions( number value ) 181 * @brief Set whether or not exceptions within a thread are passed onto the main process when the thread finishes executing. 182 * @param number value Either true or false 183 * @description If an exception is thrown within a thread, only that thread will suffer the exception. If you pass 184 true to the function, when a thread has an exception it will pass it onto the main program thread. 185 */ 186 native function setPassExceptions( number value ) 187 { 188 SelfThread->pass_exceptions = (long)value; 189 } 190 191 /** 192 * @function isRunning 193 * @declaration function isRunning() 194 * @brief Check to see if the thread is running 195 * @return true if it is running, false otherwise 196 */ 197 native function isRunning() 198 { 199 if( SelfThread->running ) 200 { 201 FE_RETURN_TRUE; 202 } 203 FE_RETURN_FALSE; 204 } 205} 206/** 207 * @end 208 */ 209 210/** 211 * @class Mutex 212 * @brief A mutex that can be used to lock sections of code 213 * @example <code> 214 <type>object</type> mutex = <keyword>new</keyword> Mutex();<nl/> 215 mutex.lock();<nl/> 216 // Critical section<nl/> 217 mutex.unlock();</code><nl/> 218 */ 219final class Mutex 220{ 221 native function constructor() 222 { 223 if( SelfMutex != NULL ) 224 self->odata = aphex_mutex_create(); 225 } 226 227 native function destructor() 228 { 229 if( SelfMutex != NULL ) 230 aphex_mutex_destroy( SelfMutex ); 231 self->odata = NULL; 232 } 233 234 /** 235 * @function lock 236 * @declaration function lock() 237 * @brief Cause the mutex to lock 238 * @description The mutex will lock. If it is already locked, the thread of execution will stop until 239 * it becomes unlocked. 240 */ 241 native function lock() 242 { 243 if( SelfMutex != NULL ) 244 aphex_mutex_lock( SelfMutex ); 245 } 246 247 /** 248 * @function unlock 249 * @declaration function unlock() 250 * @brief Cause the mutex to unlock 251 */ 252 native function unlock() 253 { 254 if( SelfMutex != NULL ) 255 aphex_mutex_unlock( SelfMutex ); 256 } 257} 258/** 259 * @end 260 */ 261 262/** 263 * @class Event 264 * @brief A thread safe way of signalling events between threads 265 * @example <code> 266 <keyword>global</keyword> {<nl/> 267 <tab/><type>object</type> event;<nl/> 268 <tab/><type>number</type> value;<nl/> 269 }<nl/> 270 <nl/> 271 <keyword>class</keyword> Producer <keyword>extends</span> Thread {<nl/> 272 <tab/><keyword>function</keyword> run() {<nl/> 273 <tab/><tab/><keyword>while</keyword>( <type>true</type> ) {<tab/><tab/><tab/><nl/> 274 <tab/><tab/><tab/>value = 10;<nl/> 275 <tab/><tab/><tab/>event.signal();<nl/> 276 <tab/><tab/>}<nl/> 277 <tab/>}<nl/> 278 }<nl/> 279 <keyword>class</keyword> Consumer <keyword>extends</span> Thread {<nl/> 280 <tab/><keyword>function</keyword> run() {<nl/> 281 <tab/><tab/><keyword>while</keyword>( <type>true</type> ) {<nl/> 282 <tab/><tab/><tab/>event.wait();<nl/> 283 <tab/><tab/><tab/>Console.println( "value: $value" );<nl/> 284 <tab/><tab/>}<nl/> 285 <tab/>}<nl/> 286 }<nl/> 287 <nl/> 288 <type>object</type> producer = <keyword>new</keyword> Producer();<nl/> 289 <type>object</type> consumer = <keyword>new</keyword> Consumer();<nl/> 290 event = <keyword>new</keyword> Event();<nl/> 291 <nl/> 292 producer.start();<nl/> 293 consumer.start();</code><nl/> 294 */ 295class Event 296{ 297 native function constructor() 298 { 299 self->odata = aphex_event_create(); 300 } 301 302 native function destructor() 303 { 304 if( SelfEvent != NULL ) 305 aphex_event_destroy( SelfEvent ); 306 self->odata = NULL; 307 } 308 309 /** 310 * @function signal 311 * @declaration function signal() 312 * @brief Cause an event to be broadcasted 313 * @description Signals any threads in wait or timedwait that it is okay to continue 314 * 315 */ 316 native function signal() 317 { 318 if( SelfEvent != NULL ) 319 aphex_event_signal( SelfEvent ); 320 } 321 322 /** 323 * @function wait 324 * @declaration function wait() 325 * @brief This thread will wait for a signal to be called 326 */ 327 native function wait() 328 { 329 if( SelfEvent != NULL ) 330 aphex_event_wait( SelfEvent ); 331 } 332 333 /** 334 * @function timedWait 335 * @declaration function timedWait( number seconds) 336 * @brief This thread will wait for a signal to be called 337 * @param number seconds The amount of time to wait in seconds 338 * @return true if a signal was caught, false otherwise 339 */ 340 native function timedWait( number seconds ) 341 { 342 if( SelfEvent != NULL ) 343 { 344 if( aphex_event_timedwait( SelfEvent, (long)seconds) == 0) 345 { 346 FE_RETURN_TRUE; 347 } 348 } 349 FE_RETURN_FALSE; 350 } 351} 352/** 353 * @end 354 */ 355 356