1 /*
2 * Copyright (C) 1997-2005, R3vis Corporation.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
18 *
19 * Original Contributor:
20 * Wes Bethel, R3vis Corporation, Marin County, California
21 * Additional Contributor(s):
22 *
23 * The OpenRM project is located at http://openrm.sourceforge.net/.
24 */
25
26 /*
27 * $Id: rmthread.c,v 1.7 2005/02/19 16:41:34 wes Exp $
28 * Version: $Name: OpenRM-1-6-0-2-RC2 $
29 * $Revision: 1.7 $
30 * $Log: rmthread.c,v $
31 * Revision 1.7 2005/02/19 16:41:34 wes
32 * Distro sync and consolidation.
33 * Support for NO_PTHREADS build.
34 *
35 * Revision 1.6 2005/01/23 17:00:22 wes
36 * Copyright updated to 2005.
37 *
38 * Revision 1.5 2004/01/16 16:56:02 wes
39 *
40 * Rearranged calls within rendering routines so that:
41 * (1) the time synchronization for constant-rate rendering happens after
42 * the actual rendering, but right before SwapBuffers;
43 * (2) the user-assignable routines for grabbing frambuffer pixels are
44 * relocated to after the SwapBuffers - they were located before in
45 * the previous version.
46 *
47 * These changes are expected to have the following benefits:
48 * (1) frame sync is more stable when associated with SwapBuffers rather
49 * than having it placed immediately before when rendering starts;
50 * (2) We have removed an embedded glFinish() call; SwapBuffers always
51 * implies a glFlush(), and in some implementations, also implies
52 * a glFinish(). The oddball here in terms of execution behavior is
53 * software Mesa. The only detrimental impact will be on timing rendering
54 * as you must explicitly insert your own glFinish() to ensure that
55 * timings are accurate. We are looking at this for the next rev of OpenRM.
56 *
57 * Revision 1.4 2003/10/03 19:19:32 wes
58 * Use platform-independent interface to access the OpenGL context.
59 *
60 * Revision 1.3 2003/04/05 14:13:46 wes
61 * Renamed rmMutexDestroy to rmMutexDelete for API consistency.
62 *
63 * Revision 1.2 2003/02/02 02:07:16 wes
64 * Updated copyright to 2003.
65 *
66 * Revision 1.1.1.1 2003/01/28 02:15:23 wes
67 * Manual rebuild of rm150 repository.
68 *
69 * Revision 1.5 2003/01/16 22:21:17 wes
70 * Updated all source files to reflect new organization of header files:
71 * all header files formerly located in include/rmaux, include/rmi, include/rmv
72 * are now located in include/rm.
73 *
74 * Revision 1.4 2002/04/30 19:34:03 wes
75 * Updated copyright dates.
76 *
77 * Revision 1.3 2001/06/03 20:50:16 wes
78 * No significant differences.
79 *
80 * Revision 1.2 2001/03/31 17:12:39 wes
81 * v1.4.0-alpha-2 checkin.
82 *
83 * Revision 1.1 2000/12/03 22:33:24 wes
84 * Initial entry.
85 *
86 */
87
88 /*
89 * this file contains wrappers for MP code that is based upon a
90 * threaded programming model.
91 *
92 * We use Posix threads for mutex'es and threads. Win32 requires
93 * an add-on library that provides Posix threads functionality. Such
94 * a library is available for free download from:
95 * http://sources.redhat.com/pthreads-win32/
96 * See the OpenRM RELEASENOTES for more information about the
97 * installation and use of pthreads-win32.
98 */
99
100 #include <rm/rm.h>
101 #include "rmprivat.h"
102 #include "rmmultis.h"
103
104 #ifdef _NO_PTHREADS
105 #include "rmpthrd.h"
106 #endif
107
108 /*
109 * ----------------------------------------------------
110 * @Name rmThreadCreate
111 @pstart
112 RMenum rmThreadCreate(RMthread *threadID,
113 void * (*threadFunc)(void *),
114 void *args)
115 @pend
116
117 @astart
118 RMthread *threadID - a handle to an RMthread object (modified).
119 void * (*threadFunc)(void *) - a handle to a function that will be
120 executed by the new thread (input).
121 void *args - arguments to the threadFunc, cast to a void *.
122 @aend
123
124 @dstart
125 Use this routine to create a new execution thread. rmThreadCreate
126 is a threads-abstraction layer that creates new execution threads
127 in both Unix and Win32 environments. The Unix version is built using
128 POSIX threads - for more information, see pthread_create(3). On
129 Win32, see the MSDN documentation for _beginthread().
130
131 The new thread is detached, and begins immediate execution of the
132 code contained in the routine "threadFunc". Arguments to the threadFunc
133 may be passed through the parameter "args." Typically, args are
134 packaged into a struct, and the handle to the struct is cast to
135 a void *. The threadFunc then performs an inverse cast of the
136 void *args to a struct * to gain access to the arguments.
137
138 rmThreadCreate only creates a detached thread. Any synchronization
139 must be performed by the application using the appropriate
140 constructs. RMmutex's can be used (both Win32 and Unix) to
141 implement synchronization. Alternatively, developers who have
142 specialized knowledge of OS-specific features (e.g., semaphores,
143 condition variables, etc) may use those constructs.
144
145 Upon success, this routine will return RM_CHILL, and the RMthread
146 *threadID is modified to contain thread-specific identification
147 information. Upon failure, an error message is printed, and
148 a RM_WHACKED is returned.
149 @dend
150 * ----------------------------------------------------
151 */
152 RMenum
rmThreadCreate(RMthread * threadID,void * (* threadFunc)(void *),void * args)153 rmThreadCreate(RMthread *threadID,
154 void * (*threadFunc)(void *),
155 void *args)
156 {
157 int stat;
158 RMenum rval;
159 pthread_attr_t attr;
160
161 pthread_attr_init(&attr);
162 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
163 stat = pthread_create(threadID, &attr, threadFunc, (void *)args);
164
165 if (stat != 0)
166 {
167 rval = RM_WHACKED;
168 perror("rmThreadCreate/pthread_create error:");
169 }
170 else
171 rval = RM_CHILL;
172 return(rval);
173 }
174
175 /*
176 * ----------------------------------------------------
177 * @Name rmThreadJoin
178 @pstart
179 RMenum rmThreadJoin(RMthread *threadID,
180 void **threadReturn)
181 @pend
182
183 @astart
184 RMthread *threadID - a handle to an RMthread object (input).
185 void **threadReturn - a handle to a void * (modified).
186 @aend
187
188 @dstart
189 "Thread joining" means "wait for the thread to finish." Use
190 rmThreadJoin to wait for completion of a thread. Upon
191 success, this routine returns RM_CHILL, indicating the
192 thread in question has completed. Upon failure, an error
193 message is printed, and RM_WHACKED is returned (Unix only).
194
195 NOTE: this function is a no-op on Win32, as there is no equivalent
196 thread join routine in Win32. Developers must use explicit
197 synchronization mechanisms on Win32 to coordinate signaling
198 of completion between app and detached threads. The RMmutex
199 will work nicely for that purpose.
200 @dend
201 * ----------------------------------------------------
202 */
203 RMenum
rmThreadJoin(RMthread * threadID,void ** threadReturn)204 rmThreadJoin(RMthread *threadID,
205 void **threadReturn)
206 {
207 int stat;
208 RMenum rval;
209 stat = pthread_join(*threadID, threadReturn);
210 if (stat != 0)
211 {
212 rval = RM_WHACKED;
213 perror("rmThreadJoin/pthread_join error:");
214 }
215 else
216 rval = RM_CHILL;
217 return(rval);
218 }
219
220 /*
221 * ----------------------------------------------------
222 * @Name rmMutexNew
223 @pstart
224 RMmutex * rmMutexNew(RMenum initLockState)
225 @pend
226
227 @astart
228 RMenum initLockState - an RMenum value (input) specifying the
229 initial state of the RMmutex returned to the caller.
230 @aend
231
232 @dstart
233 Creates an initialized RMmutex object, and returns the handle of
234 the new RMmutex object to the caller upon success, or NULL upon
235 failure. The initial value of the RMmutex is set to the value
236 specified by the input parameter initLockState.
237
238 Valid values for initLockState are RM_MUTEX_LOCK or RM_MUTEX_UNLOCK.
239
240 The RMmutex object is a synchronization mechanism that can be
241 used to control access to critical resources, and may be used
242 across multiple threads or processes. A full description of
243 mutex usage and theory is beyond the scope of this document. Please
244 refer to literature for more details (eg, Programming With
245 Posix Threads, by Butenhof, Addison-Wesley).
246
247 Attempting to access (or lock) an already locked mutex using
248 rmMutexLock() will cause the caller to block until the mutex is
249 released (unlocked). Attempting to unlock and already-unlocked mutex
250 using rmMutexUnlock() will have no effect. Callers can use
251 check the status of a mutex with rmMutexTryLock(), which is
252 non-blocking.
253
254 OpenRM RMmutex objects (in Linux) use the "error checking" kind of
255 mutex - which means attempts to lock a mutex already owned and locked
256 by the calling thread will not result in multiple locks (like a semaphore).
257 Instead, an error is reported. In general, OpenRM applications developers
258 should use lots of programming discipline to avoid cases in which
259 code will apply multiple locks to a given mutex.
260
261 OpenRM mutex objects are intended to behave similarly in both
262 Unix and Win32 environments.
263 @dend
264 * ----------------------------------------------------
265 */
266 RMmutex *
rmMutexNew(RMenum initLockState)267 rmMutexNew(RMenum initLockState)
268 {
269 RMmutex *m;
270 pthread_mutexattr_t attr;
271 int stat;
272
273 m = (RMmutex *)malloc(sizeof(RMmutex));
274 pthread_mutex_init(m, NULL);
275
276 pthread_mutexattr_init(&attr);
277 #ifdef LINUX
278 pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
279 #endif
280
281 /* what is the initial state of a mutex after being initialized? */
282
283 if (initLockState == RM_MUTEX_LOCK)
284 stat = pthread_mutex_lock(m);
285 return(m);
286 }
287
288 /*
289 * ----------------------------------------------------
290 * @Name rmMutexDelete
291 @pstart
292 RMenum rmMutexDelete(RMmutex *toDelete)
293 @pend
294
295 @astart
296 RMmutex * toDelete - a handle to an RMmutex object (modified).
297 @aend
298
299 @dstart
300 Releases resources associated with an RMmutex object. Callers
301 should take care to ensure the RMmutex object is unlocked
302 prior to calling rmMutexDelete().
303
304 Returns RM_CHILL upon success, or RM_WHACKED upon failure.
305
306 The RMmutex object is a synchronization mechanism that can be
307 used to control access to critical resources, and may be used
308 across multiple threads or processes. A full description of
309 mutex usage and theory is beyond the scope of this document. Please
310 refer to literature for more details (eg, Programming With
311 Posix Threads, by Butenhof, Addison-Wesley).
312
313 Attempting to access (or lock) an already locked mutex using
314 rmMutexLock() will cause the caller to block until the mutex is
315 released (unlocked). Attempting to unlock and already-unlocked mutex
316 using rmMutexUnlock() will have no effect. Callers can use
317 check the status of a mutex with rmMutexTryLock(), which is
318 non-blocking.
319
320 OpenRM RMmutex objects (in Linux) use the "error checking" kind of
321 mutex - which means attempts to lock a mutex already owned and locked
322 by the calling thread will not result in multiple locks (like a semaphore).
323 Instead, an error is reported. In general, OpenRM applications developers
324 should use lots of programming discipline to avoid cases in which
325 code will apply multiple locks to a given mutex.
326
327 OpenRM mutex objects are intended to behave similarly in both
328 Unix and Win32 environments.
329 @dend
330 * ----------------------------------------------------
331 */
332 RMenum
rmMutexDelete(RMmutex * toDelete)333 rmMutexDelete(RMmutex *toDelete)
334 {
335 if (RM_ASSERT(toDelete,"rmMutexDelete error: the input RMmutex is NULL.")== RM_WHACKED)
336 return (RM_WHACKED);
337
338 if ((pthread_mutex_destroy(toDelete)) != 0)
339 {
340 perror("rmMutexDelete");
341 return(RM_WHACKED);
342 }
343
344 free((void *)toDelete);
345 return(RM_CHILL);
346 }
347
348 /*
349 * ----------------------------------------------------
350 * @Name rmMutexLock
351 @pstart
352 RMenum rmMutexLock(RMmutex *toLock)
353 @pend
354
355 @astart
356 RMmutex * toLock - a handle to an RMmutex object (modified).
357 @aend
358
359 @dstart
360
361 Performs a blocking wait until the RMmutex object toLock is
362 unlocked, then will apply a lock and return RM_CHILL to the caller.
363 A return status of RM_WHACKED indicates an error of some type.
364 When an error is detected, this routine will call perror() (on
365 Unix) or its equivalent (on Win32) to report the cause of the error.
366
367 The RMmutex object is a synchronization mechanism that can be
368 used to control access to critical resources, and may be used
369 across multiple threads or processes. A full description of
370 mutex usage and theory is beyond the scope of this document. Please
371 refer to literature for more details (eg, Programming With
372 Posix Threads, by Butenhof, Addison-Wesley).
373
374 Attempting to access (or lock) an already locked mutex using
375 rmMutexLock() will cause the caller to block until the mutex is
376 released (unlocked). Attempting to unlock and already-unlocked mutex
377 using rmMutexUnlock() will have no effect. Callers can use
378 check the status of a mutex with rmMutexTryLock(), which is
379 non-blocking.
380
381 OpenRM RMmutex objects (in Linux) use the "error checking" kind of
382 mutex - which means attempts to lock a mutex already owned and locked
383 by the calling thread will not result in multiple locks (like a semaphore).
384 Instead, an error is reported. In general, OpenRM applications developers
385 should use lots of programming discipline to avoid cases in which
386 code will apply multiple locks to a given mutex.
387
388 OpenRM mutex objects are intended to behave similarly in both
389 Unix and Win32 environments.
390 @dend
391 * ----------------------------------------------------
392 */
393 RMenum
rmMutexLock(RMmutex * toLock)394 rmMutexLock(RMmutex *toLock)
395 {
396 if (RM_ASSERT(toLock,"rmMutexLock error: the input RMmutex is NULL.")== RM_WHACKED)
397 return (RM_WHACKED);
398
399 if ((pthread_mutex_lock(toLock)) != 0)
400 {
401 perror("rmMutexLock");
402 return(RM_WHACKED);
403 }
404
405 return(RM_CHILL);
406 }
407
408 /*
409 * ----------------------------------------------------
410 * @Name rmMutexUnlock
411 @pstart
412 RMenum rmMutexUnlock(RMmutex *toUnlock)
413 @pend
414
415 @astart
416 RMmutex * toUnlock - a handle to an RMmutex object (modified).
417 @aend
418
419 @dstart
420
421 Unlocks an RMmutex object. This call is non-blocking. Upon
422 success, RM_CHILL is returned to the caller. Upon failure,
423 RM_WHACKED is returned. When an error is detected, this routine
424 will call perror() (on
425 Unix) or its equivalent (on Win32) to report the cause of the error.
426
427 The RMmutex object is a synchronization mechanism that can be
428 used to control access to critical resources, and may be used
429 across multiple threads or processes. A full description of
430 mutex usage and theory is beyond the scope of this document. Please
431 refer to literature for more details (eg, Programming With
432 Posix Threads, by Butenhof, Addison-Wesley).
433
434 Attempting to access (or lock) an already locked mutex using
435 rmMutexLock() will cause the caller to block until the mutex is
436 released (unlocked). Attempting to unlock and already-unlocked mutex
437 using rmMutexUnlock() will have no effect. Callers can use
438 check the status of a mutex with rmMutexTryLock(), which is
439 non-blocking.
440
441 OpenRM RMmutex objects (in Linux) use the "error checking" kind of
442 mutex - which means attempts to lock a mutex already owned and locked
443 by the calling thread will not result in multiple locks (like a semaphore).
444 Instead, an error is reported. In general, OpenRM applications developers
445 should use lots of programming discipline to avoid cases in which
446 code will apply multiple locks to a given mutex.
447
448 OpenRM mutex objects are intended to behave similarly in both
449 Unix and Win32 environments.
450 @dend
451 * ----------------------------------------------------
452 */
453 RMenum
rmMutexUnlock(RMmutex * toUnlock)454 rmMutexUnlock(RMmutex *toUnlock)
455 {
456 if (RM_ASSERT(toUnlock,"rmMutexUnlock error: the input RMmutex is NULL.")== RM_WHACKED)
457 return (RM_WHACKED);
458
459 if ((pthread_mutex_unlock(toUnlock)) != 0)
460 {
461 perror("rmMutexUnlock");
462 return(RM_WHACKED);
463 }
464
465 return(RM_CHILL);
466 }
467
468 /*
469 * ----------------------------------------------------
470 * @Name rmMutexTryLock
471 @pstart
472 RMenum rmMutexTryLock(RMmutex *toLock)
473 @pend
474
475 @astart
476 RMmutex * toLock - a handle to an RMmutex object (modified).
477 @aend
478
479 @dstart
480
481 Attempts to lock an RMmutex object - this call is non-blocking.
482
483 If the RMmutex object is locked, a value of RM_MUTEX_BUSY is
484 returned to the caller, and the input RMmutex object remains
485 unmodified.
486
487 If the RMmutex was unlocked, this routine will lock it, and
488 return RM_MUTEX_LOCK to the caller.
489
490 The RMmutex object is a synchronization mechanism that can be
491 used to control access to critical resources, and may be used
492 across multiple threads or processes. A full description of
493 mutex usage and theory is beyond the scope of this document. Please
494 refer to literature for more details (eg, Programming With
495 Posix Threads, by Butenhof, Addison-Wesley).
496
497 Attempting to access (or lock) an already locked mutex using
498 rmMutexLock() will cause the caller to block until the mutex is
499 released (unlocked). Attempting to unlock and already-unlocked mutex
500 using rmMutexUnlock() will have no effect. Callers can use
501 check the status of a mutex with rmMutexTryLock(), which is
502 non-blocking.
503
504 OpenRM RMmutex objects (in Linux) use the "error checking" kind of
505 mutex - which means attempts to lock a mutex already owned and locked
506 by the calling thread will not result in multiple locks (like a semaphore).
507 Instead, an error is reported. In general, OpenRM applications developers
508 should use lots of programming discipline to avoid cases in which
509 code will apply multiple locks to a given mutex.
510
511 OpenRM mutex objects are intended to behave similarly in both
512 Unix and Win32 environments.
513 @dend
514 * ----------------------------------------------------
515 */
516 RMenum
rmMutexTryLock(RMmutex * toQuery)517 rmMutexTryLock(RMmutex *toQuery)
518 {
519 int rstat;
520 if (RM_ASSERT(toQuery,"rmMutexTryLock error: the input RMmutex is NULL.")== RM_WHACKED)
521 return (RM_WHACKED);
522
523 rstat = pthread_mutex_trylock(toQuery);
524
525 if (rstat == 0)
526 return(RM_MUTEX_LOCK);
527 else
528 return(RM_MUTEX_BUSY);
529 }
530
531 /* #endif */
532
533
534 /* PRIVATE */
535 void *
private_rmViewThreadFunc(void * args)536 private_rmViewThreadFunc(void *args)
537 {
538 /*
539 * for use with blocking MULTISTAGE rendering (serial or parallel)
540 */
541 RMthreadArgs *ta;
542 int command=THREAD_WORK;
543 RMmatrix initModelMatrix, initViewMatrix, initProjectionMatrix;
544 RMmatrix initTextureMatrix;
545
546 rmMatrixIdentity(&initModelMatrix);
547 rmMatrixIdentity(&initViewMatrix);
548 rmMatrixIdentity(&initProjectionMatrix);
549 rmMatrixIdentity(&initTextureMatrix);
550
551 ta = (RMthreadArgs *)args;
552
553 #if (DEBUG_LEVEL & DEBUG_TRACE)
554 fprintf(stderr," view thread started. \n");
555 fflush(stderr);
556 #endif
557
558 for (;command!=THREAD_QUIT;)
559 {
560 barrier_wait(ta->one);
561
562 command = ta->commandOpcode;
563
564 #if (DEBUG_LEVEL & DEBUG_TRACE)
565 /* work goes here */
566 fprintf(stderr," view command %d, frame %d, buffer %d\n",command, ta->frameNumber, private_rmSelectEvenOddBuffer(ta->p->frameNumber));
567 fflush(stderr);
568 #endif
569
570 if (ta->initModel != NULL)
571 rmMatrixCopy(&initModelMatrix, ta->initModel);
572
573 if (ta->initView != NULL)
574 rmMatrixCopy(&initViewMatrix, ta->initView);
575
576 if (ta->initProjection != NULL)
577 rmMatrixCopy(&initProjectionMatrix, ta->initProjection);
578
579 if (ta->initTexture != NULL)
580 rmMatrixCopy(&initTextureMatrix, ta->initTexture);
581
582 private_rmView(ta->p, ta->n, ta->frameNumber,
583 &initModelMatrix, &initViewMatrix,
584 &initProjectionMatrix, &initTextureMatrix);
585
586 barrier_wait(ta->two);
587 }
588 #if (DEBUG_LEVEL & DEBUG_TRACE)
589 fprintf(stderr,"view thread exiting \n");
590 fflush(stderr);
591 #endif
592 return NULL;
593 }
594
595 /* PRIVATE */
596 void *
private_rmRenderThreadFunc(void * args)597 private_rmRenderThreadFunc(void *args)
598 {
599 /*
600 * for use with blocking MULTISTAGE rendering (serial or parallel)
601 */
602 int stat;
603 RMthreadArgs *ta;
604 int command=THREAD_WORK;
605
606 ta = (RMthreadArgs *)args;
607
608 #ifdef RM_X
609 /*
610 * make the OpenGL context current for this thread. Note that
611 * the caller has to have done a context release in order for
612 * this to work - a glXMakeCurrent apparently does not unbind
613 * a thread-context binding that might have been done elsewhere.
614 */
615 stat = glXMakeCurrent(rmxPipeGetDisplay(ta->p),
616 rmPipeGetWindow(ta->p),
617 rmPipeGetContext(ta->p));
618 #endif
619 #ifdef RM_WIN
620 stat = wglMakeCurrent(ta->p->hdc, ta->p->hRC);
621 #endif
622 private_rmSetBackBuffer(ta->p);
623
624 #if (DEBUG_LEVEL & DEBUG_GLERRORCHECK)
625 rmGLGetError("private_rmRenderThreadFunc start");
626 #endif
627
628 #if (DEBUG_LEVEL & DEBUG_TRACE)
629 fprintf(stderr," render thread started. \n");
630 fflush(stderr);
631 #endif
632
633 for (;command!=THREAD_QUIT;)
634 {
635 barrier_wait(ta->one);
636 command = ta->commandOpcode;
637
638 #if (DEBUG_LEVEL & DEBUG_TRACE)
639 fprintf(stderr," render command %d, frame %d, buffer=%d\n",command, ta->frameNumber, private_rmSelectEvenOddBuffer(ta->frameNumber));
640 fflush(stderr);
641 #endif
642 if (ta->frameNumber >= 0)
643 {
644
645 private_rmRender(ta->p, ta->frameNumber);
646
647 private_postRenderBarrierFunc(ta->p);
648
649 if (ta->p->timeSyncFunc != NULL)
650 (*(ta->p->timeSyncFunc))(ta->p);
651
652 private_postRenderSwapBuffersFunc(ta->p);
653 private_postRenderImageFuncs(ta->p, GL_FRONT);
654
655 /* glFlush(); this glFlush may not always be needed! Depends on
656 whether or not SwapBuffers includes a flush! */
657 }
658
659 barrier_wait(ta->two);
660 }
661
662 #if (DEBUG_LEVEL & DEBUG_TRACE)
663 fprintf(stderr,"render thread exiting \n");
664 fflush(stderr);
665 #endif
666 return NULL;
667 }
668 /* EOF */
669
670