From d0c7a7a2c497ef273045eab38cd3065ca5a3694a Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 13:49:12 +0200 Subject: [PATCH 01/32] Added TinyCThread to support directory. --- support/tinycthread.c | 588 ++++++++++++++++++++++++++++++++++++++++++ support/tinycthread.h | 404 +++++++++++++++++++++++++++++ 2 files changed, 992 insertions(+) create mode 100644 support/tinycthread.c create mode 100644 support/tinycthread.h diff --git a/support/tinycthread.c b/support/tinycthread.c new file mode 100644 index 00000000..f97d07cc --- /dev/null +++ b/support/tinycthread.c @@ -0,0 +1,588 @@ +/* +Copyright (c) 2011 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +/* Activate some POSIX functionality (e.g. recursive mutexes) */ +#define _GNU_SOURCE +#if !defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE < 500) + #undef _XOPEN_SOURCE + #define _XOPEN_SOURCE 500 +#endif + +#include "tinycthread.h" +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include + #include + #include + #include + #include +#elif defined(_TTHREAD_WIN32_) + #include + #include +#endif + +/* Standard, good-to-have defines */ +#ifndef NULL + #define NULL (void*)0 +#endif +#ifndef TRUE + #define TRUE 1 +#endif +#ifndef FALSE + #define FALSE 0 +#endif + +int mtx_init(mtx_t *mtx, int type) +{ +#if defined(_TTHREAD_WIN32_) + mtx->mAlreadyLocked = FALSE; + mtx->mRecursive = type & mtx_recursive; + InitializeCriticalSection(&mtx->mHandle); + return thrd_success; +#else + int ret; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + if (type & mtx_recursive) + { + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + } + ret = pthread_mutex_init(mtx, &attr); + pthread_mutexattr_destroy(&attr); + return ret == 0 ? thrd_success : thrd_error; +#endif +} + +void mtx_destroy(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mtx->mHandle); +#else + pthread_mutex_destroy(mtx); +#endif +} + +int mtx_lock(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mtx->mHandle); + if (!mtx->mRecursive) + { + while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */ + mtx->mAlreadyLocked = TRUE; + } + return thrd_success; +#else + return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error; +#endif +} + +int mtx_timedlock(mtx_t *mtx, const xtime *xt) +{ + /* FIXME! */ + return thrd_error; +} + +int mtx_trylock(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy; + if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked) + { + LeaveCriticalSection(&mtx->mHandle); + ret = thrd_busy; + } + return ret; +#else + return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; +#endif +} + +int mtx_unlock(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + mtx->mAlreadyLocked = FALSE; + LeaveCriticalSection(&mtx->mHandle); + return thrd_success; +#else + return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;; +#endif +} + +#if defined(_TTHREAD_WIN32_) +#define _CONDITION_EVENT_ONE 0 +#define _CONDITION_EVENT_ALL 1 +#endif + +int cnd_init(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + cond->mWaitersCount = 0; + + /* Init critical section */ + InitializeCriticalSection(&cond->mWaitersCountLock); + + /* Init events */ + cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); + if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL) + { + cond->mEvents[_CONDITION_EVENT_ALL] = NULL; + return thrd_error; + } + cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL) + { + CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); + cond->mEvents[_CONDITION_EVENT_ONE] = NULL; + return thrd_error; + } + + return thrd_success; +#else + return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error; +#endif +} + +void cnd_destroy(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL) + { + CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); + } + if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL) + { + CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]); + } + DeleteCriticalSection(&cond->mWaitersCountLock); +#else + pthread_cond_destroy(cond); +#endif +} + +int cnd_signal(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + int haveWaiters; + + /* Are there any waiters? */ + EnterCriticalSection(&cond->mWaitersCountLock); + haveWaiters = (cond->mWaitersCount > 0); + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* If we have any waiting threads, send them a signal */ + if(haveWaiters) + { + if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0) + { + return thrd_error; + } + } + + return thrd_success; +#else + return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; +#endif +} + +int cnd_broadcast(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + int haveWaiters; + + /* Are there any waiters? */ + EnterCriticalSection(&cond->mWaitersCountLock); + haveWaiters = (cond->mWaitersCount > 0); + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* If we have any waiting threads, send them a signal */ + if(haveWaiters) + { + if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) + { + return thrd_error; + } + } + + return thrd_success; +#else + return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; +#endif +} + +#if defined(_TTHREAD_WIN32_) +static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout) +{ + int result, lastWaiter; + + /* Increment number of waiters */ + EnterCriticalSection(&cond->mWaitersCountLock); + ++ cond->mWaitersCount; + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* Release the mutex while waiting for the condition (will decrease + the number of waiters when done)... */ + mtx_unlock(mtx); + + /* Wait for either event to become signaled due to cnd_signal() or + cnd_broadcast() being called */ + result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout); + if (result == WAIT_TIMEOUT) + { + return thrd_timeout; + } + else if (result == (int)WAIT_FAILED) + { + return thrd_error; + } + + /* Check if we are the last waiter */ + EnterCriticalSection(&cond->mWaitersCountLock); + -- cond->mWaitersCount; + lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && + (cond->mWaitersCount == 0); + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* If we are the last waiter to be notified to stop waiting, reset the event */ + if (lastWaiter) + { + if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) + { + return thrd_error; + } + } + + /* Re-acquire the mutex */ + mtx_lock(mtx); + + return thrd_success; +} +#endif + +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + return _cnd_timedwait_win32(cond, mtx, INFINITE); +#else + return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error; +#endif +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +{ +#if defined(_TTHREAD_WIN32_) + xtime now; + DWORD delta; + xtime_get(&now, TIME_UTC); + delta = (xt->sec - now.sec) * 1000 + + (xt->nsec - now.nsec + 500000) / 1000000; + return _cnd_timedwait_win32(cond, mtx, delta); +#else + struct timespec ts; + int ret; + ts.tv_sec = xt->sec; + ts.tv_nsec = xt->nsec; + ret = pthread_cond_timedwait(cond, mtx, &ts); + if (ret == ETIMEDOUT) + { + return thrd_timeout; + } + return ret == 0 ? thrd_success : thrd_error; +#endif +} + + +/** Information to pass to the new thread (what to run). */ +typedef struct { + thrd_start_t mFunction; /**< Pointer to the function to be executed. */ + void * mArg; /**< Function argument for the thread function. */ +} _thread_start_info; + +/* Thread wrapper function. */ +#if defined(_TTHREAD_WIN32_) +unsigned WINAPI _thrd_wrapper_function(void * aArg) +#elif defined(_TTHREAD_POSIX_) +void * _thrd_wrapper_function(void * aArg) +#endif +{ + thrd_start_t fun; + void *arg; + int res; +#if defined(_TTHREAD_POSIX_) + void *pres; +#endif + + /* Get thread startup information */ + _thread_start_info *ti = (_thread_start_info *) aArg; + fun = ti->mFunction; + arg = ti->mArg; + + /* The thread is responsible for freeing the startup information */ + free((void *)ti); + + /* Call the actual client thread function */ + res = fun(arg); + +#if defined(_TTHREAD_WIN32_) + return res; +#else + pres = malloc(sizeof(int)); + if (pres != NULL) + { + *(int*)pres = res; + } + return pres; +#endif +} + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + /* Fill out the thread startup information (passed to the thread wrapper, + which will eventually free it) */ + _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info)); + if (ti == NULL) + { + return thrd_nomem; + } + ti->mFunction = func; + ti->mArg = arg; + + /* Create the thread */ +#if defined(_TTHREAD_WIN32_) + *thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL); +#elif defined(_TTHREAD_POSIX_) + if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0) + { + *thr = 0; + } +#endif + + /* Did we fail to create the thread? */ + if(!*thr) + { + free(ti); + return thrd_error; + } + + return thrd_success; +} + +thrd_t thrd_current(void) +{ +#if defined(_TTHREAD_WIN32_) + return GetCurrentThread(); +#else + return pthread_self(); +#endif +} + +int thrd_detach(thrd_t thr) +{ + /* FIXME! */ + return thrd_error; +} + +int thrd_equal(thrd_t thr0, thrd_t thr1) +{ +#if defined(_TTHREAD_WIN32_) + return thr0 == thr1; +#else + return pthread_equal(thr0, thr1); +#endif +} + +void thrd_exit(int res) +{ +#if defined(_TTHREAD_WIN32_) + ExitThread(res); +#else + void *pres = malloc(sizeof(int)); + if (pres != NULL) + { + *(int*)pres = res; + } + pthread_exit(pres); +#endif +} + +int thrd_join(thrd_t thr, int *res) +{ +#if defined(_TTHREAD_WIN32_) + if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED) + { + return thrd_error; + } + if (res != NULL) + { + DWORD dwRes; + GetExitCodeThread(thr, &dwRes); + *res = dwRes; + } +#elif defined(_TTHREAD_POSIX_) + void *pres; + int ires = 0; + if (pthread_join(thr, &pres) != 0) + { + return thrd_error; + } + if (pres != NULL) + { + ires = *(int*)pres; + free(pres); + } + if (res != NULL) + { + *res = ires; + } +#endif + return thrd_success; +} + +void thrd_sleep(const xtime *xt) +{ + xtime now; +#if defined(_TTHREAD_WIN32_) + DWORD delta; +#else + long delta; +#endif + + /* Get the current time */ + xtime_get(&now, TIME_UTC); + +#if defined(_TTHREAD_WIN32_) + /* Delta in milliseconds */ + delta = (xt->sec - now.sec) * 1000 + + (xt->nsec - now.nsec + 500000) / 1000000; + if (delta > 0) + { + Sleep(delta); + } +#else + /* Delta in microseconds */ + delta = (xt->sec - now.sec) * 1000000L + + (xt->nsec - now.nsec + 500L) / 1000L; + + /* On some systems, the usleep argument must be < 1000000 */ + while (delta > 999999L) + { + usleep(999999); + delta -= 999999L; + } + if (delta > 0L) + { + usleep((useconds_t)delta); + } +#endif +} + +void thrd_yield(void) +{ +#if defined(_TTHREAD_WIN32_) + Sleep(0); +#else + sched_yield(); +#endif +} + +int tss_create(tss_t *key, tss_dtor_t dtor) +{ +#if defined(_TTHREAD_WIN32_) + /* FIXME: The destructor function is not supported yet... */ + if (dtor != NULL) + { + return thrd_error; + } + *key = TlsAlloc(); + if (*key == TLS_OUT_OF_INDEXES) + { + return thrd_error; + } +#else + if (pthread_key_create(key, dtor) != 0) + { + return thrd_error; + } +#endif + return thrd_success; +} + +void tss_delete(tss_t key) +{ +#if defined(_TTHREAD_WIN32_) + TlsFree(key); +#else + pthread_key_delete(key); +#endif +} + +void *tss_get(tss_t key) +{ +#if defined(_TTHREAD_WIN32_) + return TlsGetValue(key); +#else + return pthread_getspecific(key); +#endif +} + +int tss_set(tss_t key, void *val) +{ +#if defined(_TTHREAD_WIN32_) + if (TlsSetValue(key, val) == 0) + { + return thrd_error; + } +#else + if (pthread_setspecific(key, val) != 0) + { + return thrd_error; + } +#endif + return thrd_success; +} + +int xtime_get(xtime *xt, int base) +{ + if (base == TIME_UTC) + { +#if defined(_TTHREAD_WIN32_) + struct _timeb tb; + _ftime(&tb); + xt->sec = (time_t)tb.time; + xt->nsec = 1000000 * (long)tb.millitm; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + xt->sec = (time_t)tv.tv_sec; + xt->nsec = 1000 * (long)tv.tv_usec; +#endif + return base; + } + else + { + return 0; + } +} + diff --git a/support/tinycthread.h b/support/tinycthread.h new file mode 100644 index 00000000..89191223 --- /dev/null +++ b/support/tinycthread.h @@ -0,0 +1,404 @@ +/* +Copyright (c) 2011 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef _TINYCTHREAD_H_ +#define _TINYCTHREAD_H_ + +/** +* @file +* @mainpage TinyCThread API Reference +* +* @section intro_sec Introduction +* TinyCThread is a minimal, portable implementation of basic threading +* classes for C. +* +* They closely mimic the functionality and naming of the C1X standard, and +* should be easily replaceable with the corresponding standard variants. +* +* @section port_sec Portability +* The Win32 variant uses the native Win32 API for implementing the thread +* classes, while for other systems, the POSIX threads API (pthread) is used. +* +* @section misc_sec Miscellaneous +* The following special keywords are available: #_Thread_local. +* +* For more detailed information, browse the different sections of this +* documentation. A good place to start is: +* tinycthread.h. +*/ + +/* Which platform are we on? */ +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +/* Generic includes */ +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include +#elif defined(_TTHREAD_WIN32_) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #define __UNDEF_LEAN_AND_MEAN + #endif + #include + #ifdef __UNDEF_LEAN_AND_MEAN + #undef WIN32_LEAN_AND_MEAN + #undef __UNDEF_LEAN_AND_MEAN + #endif +#endif + +/** TinyCThread version (major number). */ +#define TINYCTHREAD_VERSION_MAJOR 1 +/** TinyCThread version (minor number). */ +#define TINYCTHREAD_VERSION_MINOR 0 +/** TinyCThread version (full version). */ +#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR) + +/** +* @def _Thread_local +* Thread local storage keyword. +* A variable that is declared with the @c _Thread_local keyword makes the +* value of the variable local to each thread (known as thread-local storage, +* or TLS). Example usage: +* @code +* // This variable is local to each thread. +* _Thread_local int variable; +* @endcode +* @note The @c _Thread_local keyword is a macro that maps to the corresponding +* compiler directive (e.g. @c __declspec(thread)). +* @note This directive is currently not supported on Mac OS X (it will give +* a compiler error), since compile-time TLS is not supported in the Mac OS X +* executable format. Also, some older versions of MinGW (before GCC 4.x) do +* not support this directive. +* @hideinitializer +*/ + +/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C1X or + not (the spec is still draft)... */ +#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local) + #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) + #define _Thread_local __thread + #else + #define _Thread_local __declspec(thread) + #endif +#endif + +/* Macros */ +#define TSS_DTOR_ITERATIONS 0 + +/* Function return values */ +#define thrd_error 0 /**< The requested operation failed */ +#define thrd_success 1 /**< The requested operation succeeded */ +#define thrd_timeout 2 /**< The time specified in the call was reached without acquiring the requested resource */ +#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */ +#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */ + +/* Mutex types */ +#define mtx_plain 1 +#define mtx_timed 2 +#define mtx_try 4 +#define mtx_recursive 8 + +/** Time specification */ +typedef struct { + time_t sec; /**< Seconds */ + long nsec; /**< Nanoseconds */ +} xtime; + +/* Mutex */ +#if defined(_TTHREAD_WIN32_) +typedef struct { + CRITICAL_SECTION mHandle; /* Critical section handle */ + int mAlreadyLocked; /* TRUE if the mutex is already locked */ + int mRecursive; /* TRUE if the mutex is recursive */ +} mtx_t; +#else +typedef pthread_mutex_t mtx_t; +#endif + +/** Create a mutex object. +* @param mtx A mutex object. +* @param type Bit-mask that must have one of the following six values: +* @li @c mtx_plain for a simple non-recursive mutex +* @li @c mtx_timed for a non-recursive mutex that supports timeout +* @li @c mtx_try for a non-recursive mutex that supports test and return +* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive) +* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive) +* @li @c mtx_try | @c mtx_recursive (same as @c mtx_try, but recursive) +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_init(mtx_t *mtx, int type); + +/** Release any resources used by the given mutex. +* @param mtx A mutex object. +*/ +void mtx_destroy(mtx_t *mtx); + +/** Lock the given mutex. +* Blocks until the given mutex can be locked. If the mutex is non-recursive, and +* the calling thread already has a lock on the mutex, this call will block +* forever. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_lock(mtx_t *mtx); + +/** NOT YET IMPLEMENTED. +*/ +int mtx_timedlock(mtx_t *mtx, const xtime *xt); + +/** Try to lock the given mutex. +* The specified mutex shall support either test and return or timeout. If the +* mutex is already locked, the function returns without blocking. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_busy if the resource +* requested is already in use, or @ref thrd_error if the request could not be +* honored. +*/ +int mtx_trylock(mtx_t *mtx); + +/** Unlock the given mutex. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_unlock(mtx_t *mtx); + +/* Condition variable */ +#if defined(_TTHREAD_WIN32_) +typedef struct { + HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */ + unsigned int mWaitersCount; /* Count of the number of waiters. */ + CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */ +} cnd_t; +#else +typedef pthread_cond_t cnd_t; +#endif + +/** Create a condition variable object. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_init(cnd_t *cond); + +/** Release any resources used by the given condition variable. +* @param cond A condition variable object. +*/ +void cnd_destroy(cnd_t *cond); + +/** Signal a condition variable. +* Unblocks one of the threads that are blocked on the given condition variable +* at the time of the call. If no threads are blocked on the condition variable +* at the time of the call, the function does nothing and return success. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_signal(cnd_t *cond); + +/** Broadcast a condition variable. +* Unblocks all of the threads that are blocked on the given condition variable +* at the time of the call. If no threads are blocked on the condition variable +* at the time of the call, the function does nothing and return success. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_broadcast(cnd_t *cond); + +/** Wait for a condition variable to become signaled. +* The function atomically unlocks the given mutex and endeavors to block until +* the given condition variable is signaled by a call to cnd_signal or to +* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex +* before it returns. +* @param cond A condition variable object. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_wait(cnd_t *cond, mtx_t *mtx); + +/** Wait for a condition variable to become signaled. +* The function atomically unlocks the given mutex and endeavors to block until +* the given condition variable is signaled by a call to cnd_signal or to +* cnd_broadcast, or until after the specified time. When the calling thread +* becomes unblocked it locks the mutex before it returns. +* @param cond A condition variable object. +* @param mtx A mutex object. +* @param xt A point in time at which the request will time out (absolute time). +* @return @ref thrd_success upon success, or @ref thrd_timeout if the time +* specified in the call was reached without acquiring the requested resource, or +* @ref thrd_error if the request could not be honored. +*/ +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt); + +/* Thread */ +#if defined(_TTHREAD_WIN32_) +typedef HANDLE thrd_t; +#else +typedef pthread_t thrd_t; +#endif + +/** Thread start function. +* Any thread that is started with the @ref thrd_create() function must be +* started through a function of this type. +* @param arg The thread argument (the @c arg argument of the corresponding +* @ref thrd_create() call). +* @return The thread return value, which can be obtained by another thread +* by using the @ref thrd_join() function. +*/ +typedef int (*thrd_start_t)(void *arg); + +/** Create a new thread. +* @param thr Identifier of the newly created thread. +* @param func A function pointer to the function that will be executed in +* the new thread. +* @param arg An argument to the thread function. +* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could +* be allocated for the thread requested, or @ref thrd_error if the request +* could not be honored. +* @note A thread’s identifier may be reused for a different thread once the +* original thread has exited and either been detached or joined to another +* thread. +*/ +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + +/** Identify the calling thread. +* @return The identifier of the calling thread. +*/ +thrd_t thrd_current(void); + +/** NOT YET IMPLEMENTED. +*/ +int thrd_detach(thrd_t thr); + +/** Compare two thread identifiers. +* The function determines if two thread identifiers refer to the same thread. +* @return Zero if the two thread identifiers refer to different threads. +* Otherwise a nonzero value is returned. +*/ +int thrd_equal(thrd_t thr0, thrd_t thr1); + +/** Terminate execution of the calling thread. +* @param res Result code of the calling thread. +*/ +void thrd_exit(int res); + +/** Wait for a thread to terminate. +* The function joins the given thread with the current thread by blocking +* until the other thread has terminated. +* @param thr The thread to join with. +* @param res If this pointer is not NULL, the function will store the result +* code of the given thread in the integer pointed to by @c res. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int thrd_join(thrd_t thr, int *res); + +/** Put the calling thread to sleep. +* Suspend execution of the calling thread until after the time specified by the +* xtime object. +* @param xt A point in time at which the thread will resume (absolute time). +*/ +void thrd_sleep(const xtime *xt); + +/** Yield execution to another thread. +* Permit other threads to run, even if the current thread would ordinarily +* continue to run. +*/ +void thrd_yield(void); + +/* Thread local storage */ +#if defined(_TTHREAD_WIN32_) +typedef DWORD tss_t; +#else +typedef pthread_key_t tss_t; +#endif + +/** Destructor function for a thread-specific storage. +* @param val The value of the destructed thread-specific storage. +*/ +typedef void (*tss_dtor_t)(void *val); + +/** Create a thread-specific storage. +* @param key The unique key identifier that will be set if the function is +* successful. +* @param dtor Destructor function. This can be NULL. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +* @note The destructor function is not supported under Windows. If @c dtor is +* not NULL when calling this function under Windows, the function will fail +* and return @ref thrd_error. +*/ +int tss_create(tss_t *key, tss_dtor_t dtor); + +/** Delete a thread-specific storage. +* The function releases any resources used by the given thread-specific +* storage. +* @param key The key that shall be deleted. +*/ +void tss_delete(tss_t key); + +/** Get the value for a thread-specific storage. +* @param key The thread-specific storage identifier. +* @return The value for the current thread held in the given thread-specific +* storage. +*/ +void *tss_get(tss_t key); + +/** Set the value for a thread-specific storage. +* @param key The thread-specific storage identifier. +* @param val The value of the thread-specific storage to set for the current +* thread. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int tss_set(tss_t key, void *val); + +/* Timing */ +enum +{ + TIME_UTC = 1 +}; + +/** Get the current time. +* Set the xtime object to hold the current time based on the given time base. +* @param xt Will be filled out with the current time. +* @param base Time base (must be @c TIME_UTC). +* @return The non-zero value @c base if the function is successful, otherwise +* it returns zero. +*/ +int xtime_get(xtime *xt, int base); + +#endif /* _TINYTHREAD_H_ */ From 9e4bc36dd84e3d84e29f7de56e8cd7e40f0c5305 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 14:13:18 +0200 Subject: [PATCH 02/32] Initial TLS implementation (Cocoa broken). --- src/cocoa_opengl.m | 21 ++++++++++++++++++++- src/internal.h | 2 +- src/opengl.c | 19 +++++++++---------- src/win32_opengl.c | 28 +++++++++++++++++++++++----- src/window.c | 5 +++-- src/x11_opengl.c | 23 ++++++++++++++++++++++- 6 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/cocoa_opengl.m b/src/cocoa_opengl.m index 2ffd774c..1f1f0f23 100644 --- a/src/cocoa_opengl.m +++ b/src/cocoa_opengl.m @@ -30,6 +30,13 @@ #include "internal.h" +//======================================================================== +// The per-thread current context/window pointer +// TODO: Implement pthreads TLS +//======================================================================== +_GLFWwindow* _glfwCurrentWindow = NULL; + + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// @@ -44,6 +51,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) [window->NSGL.context makeCurrentContext]; else [NSOpenGLContext clearCurrentContext]; + + _glfwCurrentWindow = window; +} + + +//======================================================================== +// Return the window object whose context is current +//======================================================================== + +_GLFWwindow* _glfwPlatformGetCurrentContext(void) +{ + return _glfwCurrentWindow; } @@ -64,7 +83,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window) void _glfwPlatformSwapInterval(int interval) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwCurrentWindow; GLint sync = interval; [window->NSGL.context setValues:&sync forParameter:NSOpenGLCPSwapInterval]; diff --git a/src/internal.h b/src/internal.h index 89ac48b2..5ea2a815 100644 --- a/src/internal.h +++ b/src/internal.h @@ -216,7 +216,6 @@ struct _GLFWlibrary _GLFWhints hints; _GLFWwindow* windowListHead; - _GLFWwindow* currentWindow; _GLFWwindow* activeWindow; GLFWwindowsizefun windowSizeCallback; @@ -309,6 +308,7 @@ void _glfwPlatformWaitEvents(void); // OpenGL context management void _glfwPlatformMakeContextCurrent(_GLFWwindow* window); +_GLFWwindow* _glfwPlatformGetCurrentContext(void); void _glfwPlatformSwapBuffers(_GLFWwindow* window); void _glfwPlatformSwapInterval(int interval); void _glfwPlatformRefreshWindowParams(_GLFWwindow* window); diff --git a/src/opengl.c b/src/opengl.c index 67c6f414..dea2e387 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -350,7 +350,7 @@ GLboolean _glfwIsValidContextConfig(_GLFWwndconfig* wndconfig) GLboolean _glfwRefreshContextParams(void) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); if (!parseGLVersion(&window->glMajor, &window->glMinor, @@ -417,7 +417,7 @@ GLboolean _glfwRefreshContextParams(void) GLboolean _glfwIsValidContext(_GLFWwndconfig* wndconfig) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); if (window->glMajor < wndconfig->glMajor || (window->glMajor == wndconfig->glMajor && @@ -492,16 +492,15 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow handle) return; } - if (_glfwLibrary.currentWindow == window) + if (_glfwPlatformGetCurrentContext() == window) return; _glfwPlatformMakeContextCurrent(window); - _glfwLibrary.currentWindow = window; } //======================================================================== -// Returns the window whose OpenGL context is current +// Return the window object whose context is current //======================================================================== GLFWAPI GLFWwindow glfwGetCurrentContext(void) @@ -512,7 +511,7 @@ GLFWAPI GLFWwindow glfwGetCurrentContext(void) return GL_FALSE; } - return _glfwLibrary.currentWindow; + return _glfwPlatformGetCurrentContext(); } @@ -546,7 +545,7 @@ GLFWAPI void glfwSwapInterval(int interval) return; } - if (!_glfwLibrary.currentWindow) + if (!_glfwPlatformGetCurrentContext()) { _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL); return; @@ -571,7 +570,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension) return GL_FALSE; } - window = _glfwLibrary.currentWindow; + window = _glfwPlatformGetCurrentContext(); if (!window) { _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL); @@ -632,7 +631,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) return NULL; } - if (!_glfwLibrary.currentWindow) + if (!_glfwPlatformGetCurrentContext()) { _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL); return NULL; @@ -660,7 +659,7 @@ GLFWAPI void glfwCopyContext(GLFWwindow hsrc, GLFWwindow hdst, unsigned long mas src = (_GLFWwindow*) hsrc; dst = (_GLFWwindow*) hdst; - if (_glfwLibrary.currentWindow == dst) + if (_glfwPlatformGetCurrentContext() == dst) { _glfwSetError(GLFW_INVALID_VALUE, "glfwCopyContext: Cannot copy OpenGL state to a current context"); diff --git a/src/win32_opengl.c b/src/win32_opengl.c index fe1898e7..3b30179f 100644 --- a/src/win32_opengl.c +++ b/src/win32_opengl.c @@ -31,6 +31,12 @@ #include "internal.h" +//======================================================================== +// The per-thread current context/window pointer +//======================================================================== +__declspec(thread) _GLFWwindow* _glfwCurrentWindow = NULL; + + //======================================================================== // Initialize WGL-specific extensions // This function is called once before initial context creation, i.e. before @@ -438,7 +444,7 @@ static GLboolean createContext(_GLFWwindow* window, } } - glfwMakeContextCurrent(window); + _glfwPlatformMakeContextCurrent(window); initWGLExtensions(window); return GL_TRUE; @@ -507,8 +513,8 @@ void _glfwDestroyContext(_GLFWwindow* window) { // This is duplicated from glfwDestroyWindow // TODO: Stop duplicating code - if (window == _glfwLibrary.currentWindow) - glfwMakeContextCurrent(NULL); + if (window == _glfwCurrentWindow) + _glfwPlatformMakeContextCurrent(NULL); if (window->WGL.context) { @@ -538,6 +544,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) wglMakeCurrent(window->WGL.DC, window->WGL.context); else wglMakeCurrent(NULL, NULL); + + _glfwCurrentWindow = window; +} + + +//======================================================================== +// Return the window object whose context is current +//======================================================================== + +_GLFWwindow* _glfwPlatformGetCurrentContext(void) +{ + return _glfwCurrentWindow; } @@ -557,7 +575,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window) void _glfwPlatformSwapInterval(int interval) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwCurrentWindow; if (window->WGL.EXT_swap_control) window->WGL.SwapIntervalEXT(interval); @@ -572,7 +590,7 @@ int _glfwPlatformExtensionSupported(const char* extension) { const GLubyte* extensions; - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwCurrentWindow; if (window->WGL.GetExtensionsStringEXT != NULL) { diff --git a/src/window.c b/src/window.c index df592751..352e3ad9 100644 --- a/src/window.c +++ b/src/window.c @@ -451,8 +451,9 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow handle) return; // Clear the current context if this window's context is current - if (window == _glfwLibrary.currentWindow) - glfwMakeContextCurrent(NULL); + // TODO: Re-examine this in light of multithreading + if (window == _glfwPlatformGetCurrentContext()) + _glfwPlatformMakeContextCurrent(NULL); // Clear the active window pointer if this is the active window if (window == _glfwLibrary.activeWindow) diff --git a/src/x11_opengl.c b/src/x11_opengl.c index 2a5cc451..d7f33999 100644 --- a/src/x11_opengl.c +++ b/src/x11_opengl.c @@ -37,6 +37,11 @@ // This is the only glXGetProcAddress variant not declared by glxext.h void (*glXGetProcAddressEXT(const GLubyte* procName))(); +//======================================================================== +// The per-thread current context/window pointer +//======================================================================== +__thread _GLFWwindow* _glfwCurrentWindow = NULL; + //======================================================================== // Returns the specified attribute of the specified GLXFBConfig @@ -621,6 +626,10 @@ XVisualInfo* _glfwGetContextVisual(_GLFWwindow* window) } +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + //======================================================================== // Make the OpenGL context associated with the specified window current //======================================================================== @@ -635,6 +644,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) } else glXMakeCurrent(_glfwLibrary.X11.display, None, NULL); + + _glfwCurrentWindow = window; +} + + +//======================================================================== +// Return the window object whose context is current +//======================================================================== + +_GLFWwindow* _glfwPlatformGetCurrentContext(void) +{ + return _glfwCurrentWindow; } @@ -654,7 +675,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window) void _glfwPlatformSwapInterval(int interval) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwCurrentWindow; if (_glfwLibrary.GLX.EXT_swap_control) { From 14abb3c15d0eb53906b14d2cb77cdc8c9cadd9fe Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 15:04:59 +0200 Subject: [PATCH 03/32] Updated TinyCThread. --- support/tinycthread.c | 4 ++-- support/tinycthread.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/support/tinycthread.c b/support/tinycthread.c index f97d07cc..9bee2cb5 100644 --- a/support/tinycthread.c +++ b/support/tinycthread.c @@ -1,5 +1,5 @@ -/* -Copyright (c) 2011 Marcus Geelnard +/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2012 Marcus Geelnard This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/support/tinycthread.h b/support/tinycthread.h index 89191223..8069e3f3 100644 --- a/support/tinycthread.h +++ b/support/tinycthread.h @@ -1,5 +1,5 @@ -/* -Copyright (c) 2011 Marcus Geelnard +/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2012 Marcus Geelnard This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -32,7 +32,7 @@ freely, subject to the following restrictions: * TinyCThread is a minimal, portable implementation of basic threading * classes for C. * -* They closely mimic the functionality and naming of the C1X standard, and +* They closely mimic the functionality and naming of the C11 standard, and * should be easily replaceable with the corresponding standard variants. * * @section port_sec Portability @@ -101,8 +101,7 @@ freely, subject to the following restrictions: * @hideinitializer */ -/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C1X or - not (the spec is still draft)... */ +/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C11 or */ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local) #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) #define _Thread_local __thread @@ -402,3 +401,4 @@ enum int xtime_get(xtime *xt, int base); #endif /* _TINYTHREAD_H_ */ + From 5b95715bb1dee204e603a008eaf4afd91ae4f4d2 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 15:05:18 +0200 Subject: [PATCH 04/32] Added simple silly stupid test. --- tests/CMakeLists.txt | 5 ++- tests/threads.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/threads.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bcad7ef5..9be1c1cd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,8 @@ include_directories(${GLFW_SOURCE_DIR}/include set(GETOPT ${GLFW_SOURCE_DIR}/support/getopt.h ${GLFW_SOURCE_DIR}/support/getopt.c) +set(TINYCTHREAD ${GLFW_SOURCE_DIR}/support/tinycthread.h + ${GLFW_SOURCE_DIR}/support/tinycthread.c) add_executable(clipboard clipboard.c ${GETOPT}) add_executable(defaults defaults.c) @@ -27,6 +29,7 @@ add_executable(joysticks joysticks.c) add_executable(modes modes.c ${GETOPT}) add_executable(peter peter.c) add_executable(reopen reopen.c) +add_executable(threads threads.c ${TINYCTHREAD}) add_executable(accuracy WIN32 MACOSX_BUNDLE accuracy.c) set_target_properties(accuracy PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Accuracy") @@ -45,7 +48,7 @@ set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") set(WINDOWS_BINARIES accuracy sharing tearing title windows) set(CONSOLE_BINARIES clipboard defaults events fsaa fsfocus gamma glfwinfo - iconify joysticks modes peter reopen) + iconify joysticks modes peter reopen threads) if (MSVC) # Tell MSVC to use main instead of WinMain for Windows subsystem executables diff --git a/tests/threads.c b/tests/threads.c new file mode 100644 index 00000000..ba844dd7 --- /dev/null +++ b/tests/threads.c @@ -0,0 +1,96 @@ +//======================================================================== +// Multithreading test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test is intended to verify whether the OpenGL context part of +// the GLFW API is able to be used from multiple threads +// +//======================================================================== + +#include + +#include +#include +#include + +#include "tinycthread.h" + +static GLboolean running = GL_TRUE; + +static int thread_start(void* data) +{ + GLFWwindow window = (GLFWwindow) data; + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + while (running) + { + const float red = (float) sin(glfwGetTime()) / 2.f + 0.5f; + + glClearColor(red, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + } + + glfwMakeContextCurrent(NULL); + return 0; +} + +int main(void) +{ + int result; + GLFWwindow window; + thrd_t thread; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW: %s\n", glfwErrorString(glfwGetError())); + exit(EXIT_FAILURE); + } + + window = glfwCreateWindow(640, 480, GLFW_WINDOWED, "Multithreading", NULL); + if (!window) + { + fprintf(stderr, "Failed to open GLFW window: %s\n", glfwErrorString(glfwGetError())); + exit(EXIT_FAILURE); + } + + if (thrd_create(&thread, thread_start, window) == thrd_success) + { + while (!glfwGetWindowParam(window, GLFW_CLOSE_REQUESTED)) + glfwPollEvents(); + + running = GL_FALSE; + thrd_join(thread, &result); + } + else + { + fprintf(stderr, "Failed to create secondary thread\n"); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +} + From 3d6221c490fab8bf70ba527430a2518c821c1027 Mon Sep 17 00:00:00 2001 From: Riku Salminen Date: Sun, 12 Aug 2012 15:57:52 +0200 Subject: [PATCH 05/32] Added support for multithreaded use of Xlib. --- src/x11_init.c | 2 + src/x11_window.c | 137 ++++++++++++++++++++++++----------------------- 2 files changed, 73 insertions(+), 66 deletions(-) diff --git a/src/x11_init.c b/src/x11_init.c index 372fc2dd..afa9de33 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -634,6 +634,8 @@ static void terminateDisplay(void) int _glfwPlatformInit(void) { + XInitThreads(); + if (!initDisplay()) return GL_FALSE; diff --git a/src/x11_window.c b/src/x11_window.c index 442e76c0..60a18287 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -468,19 +468,16 @@ static _GLFWwindow* findWindow(Window handle) // Get and process next X event (called by _glfwPlatformPollEvents) //======================================================================== -static void processSingleEvent(void) +static void processSingleEvent(XEvent *event) { _GLFWwindow* window; - XEvent event; - XNextEvent(_glfwLibrary.X11.display, &event); - - switch (event.type) + switch (event->type) { case KeyPress: { // A keyboard key was pressed - window = findWindow(event.xkey.window); + window = findWindow(event->xkey.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for KeyPress event\n"); @@ -488,10 +485,10 @@ static void processSingleEvent(void) } // Translate and report key press - _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_PRESS); + _glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_PRESS); // Translate and report character input - _glfwInputChar(window, translateChar(&event.xkey)); + _glfwInputChar(window, translateChar(&event->xkey)); break; } @@ -499,7 +496,7 @@ static void processSingleEvent(void) case KeyRelease: { // A keyboard key was released - window = findWindow(event.xkey.window); + window = findWindow(event->xkey.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for KeyRelease event\n"); @@ -516,15 +513,15 @@ static void processSingleEvent(void) XPeekEvent(_glfwLibrary.X11.display, &nextEvent); if (nextEvent.type == KeyPress && - nextEvent.xkey.window == event.xkey.window && - nextEvent.xkey.keycode == event.xkey.keycode) + nextEvent.xkey.window == event->xkey.window && + nextEvent.xkey.keycode == event->xkey.keycode) { // This last check is a hack to work around key repeats // leaking through due to some sort of time drift // Toshiyuki Takahashi can press a button 16 times per // second so it's fairly safe to assume that no human is // pressing the key 50 times per second (value is ms) - if ((nextEvent.xkey.time - event.xkey.time) < 20) + if ((nextEvent.xkey.time - event->xkey.time) < 20) { // Do not report anything for this event break; @@ -533,7 +530,7 @@ static void processSingleEvent(void) } // Translate and report key release - _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_RELEASE); + _glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_RELEASE); break; } @@ -541,30 +538,30 @@ static void processSingleEvent(void) case ButtonPress: { // A mouse button was pressed or a scrolling event occurred - window = findWindow(event.xbutton.window); + window = findWindow(event->xbutton.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for ButtonPress event\n"); return; } - if (event.xbutton.button == Button1) + if (event->xbutton.button == Button1) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS); - else if (event.xbutton.button == Button2) + else if (event->xbutton.button == Button2) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS); - else if (event.xbutton.button == Button3) + else if (event->xbutton.button == Button3) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS); // XFree86 3.3.2 and later translates mouse wheel up/down into // mouse button 4 & 5 presses - else if (event.xbutton.button == Button4) + else if (event->xbutton.button == Button4) _glfwInputScroll(window, 0.0, 1.0); - else if (event.xbutton.button == Button5) + else if (event->xbutton.button == Button5) _glfwInputScroll(window, 0.0, -1.0); - else if (event.xbutton.button == Button6) + else if (event->xbutton.button == Button6) _glfwInputScroll(window, -1.0, 0.0); - else if (event.xbutton.button == Button7) + else if (event->xbutton.button == Button7) _glfwInputScroll(window, 1.0, 0.0); break; @@ -573,26 +570,26 @@ static void processSingleEvent(void) case ButtonRelease: { // A mouse button was released - window = findWindow(event.xbutton.window); + window = findWindow(event->xbutton.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for ButtonRelease event\n"); return; } - if (event.xbutton.button == Button1) + if (event->xbutton.button == Button1) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE); } - else if (event.xbutton.button == Button2) + else if (event->xbutton.button == Button2) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE); } - else if (event.xbutton.button == Button3) + else if (event->xbutton.button == Button3) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, @@ -604,7 +601,7 @@ static void processSingleEvent(void) case EnterNotify: { // The cursor entered the window - window = findWindow(event.xcrossing.window); + window = findWindow(event->xcrossing.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for EnterNotify event\n"); @@ -621,7 +618,7 @@ static void processSingleEvent(void) case LeaveNotify: { // The cursor left the window - window = findWindow(event.xcrossing.window); + window = findWindow(event->xcrossing.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for LeaveNotify event\n"); @@ -638,15 +635,15 @@ static void processSingleEvent(void) case MotionNotify: { // The cursor was moved - window = findWindow(event.xmotion.window); + window = findWindow(event->xmotion.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for MotionNotify event\n"); return; } - if (event.xmotion.x != window->X11.cursorPosX || - event.xmotion.y != window->X11.cursorPosY) + if (event->xmotion.x != window->X11.cursorPosX || + event->xmotion.y != window->X11.cursorPosY) { // The cursor was moved and we didn't do it int x, y; @@ -656,17 +653,17 @@ static void processSingleEvent(void) if (_glfwLibrary.activeWindow != window) break; - x = event.xmotion.x - window->X11.cursorPosX; - y = event.xmotion.y - window->X11.cursorPosY; + x = event->xmotion.x - window->X11.cursorPosX; + y = event->xmotion.y - window->X11.cursorPosY; } else { - x = event.xmotion.x; - y = event.xmotion.y; + x = event->xmotion.x; + y = event->xmotion.y; } - window->X11.cursorPosX = event.xmotion.x; - window->X11.cursorPosY = event.xmotion.y; + window->X11.cursorPosX = event->xmotion.x; + window->X11.cursorPosY = event->xmotion.y; window->X11.cursorCentered = GL_FALSE; _glfwInputCursorMotion(window, x, y); @@ -678,7 +675,7 @@ static void processSingleEvent(void) case ConfigureNotify: { // The window configuration changed somehow - window = findWindow(event.xconfigure.window); + window = findWindow(event->xconfigure.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for ConfigureNotify event\n"); @@ -686,12 +683,12 @@ static void processSingleEvent(void) } _glfwInputWindowSize(window, - event.xconfigure.width, - event.xconfigure.height); + event->xconfigure.width, + event->xconfigure.height); _glfwInputWindowPos(window, - event.xconfigure.x, - event.xconfigure.y); + event->xconfigure.x, + event->xconfigure.y); break; } @@ -699,14 +696,14 @@ static void processSingleEvent(void) case ClientMessage: { // Custom client message, probably from the window manager - window = findWindow(event.xclient.window); + window = findWindow(event->xclient.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for ClientMessage event\n"); return; } - if ((Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow) + if ((Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow) { // The window manager was asked to close the window, for example by // the user pressing a 'close' window decoration button @@ -714,17 +711,17 @@ static void processSingleEvent(void) _glfwInputWindowCloseRequest(window); } else if (_glfwLibrary.X11.wmPing != None && - (Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmPing) + (Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmPing) { // The window manager is pinging us to make sure we are still // responding to events - event.xclient.window = _glfwLibrary.X11.root; + event->xclient.window = _glfwLibrary.X11.root; XSendEvent(_glfwLibrary.X11.display, - event.xclient.window, + event->xclient.window, False, SubstructureNotifyMask | SubstructureRedirectMask, - &event); + event); } break; @@ -733,7 +730,7 @@ static void processSingleEvent(void) case MapNotify: { // The window was mapped - window = findWindow(event.xmap.window); + window = findWindow(event->xmap.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for MapNotify event\n"); @@ -747,7 +744,7 @@ static void processSingleEvent(void) case UnmapNotify: { // The window was unmapped - window = findWindow(event.xmap.window); + window = findWindow(event->xmap.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for UnmapNotify event\n"); @@ -761,7 +758,7 @@ static void processSingleEvent(void) case FocusIn: { // The window gained focus - window = findWindow(event.xfocus.window); + window = findWindow(event->xfocus.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for FocusIn event\n"); @@ -779,7 +776,7 @@ static void processSingleEvent(void) case FocusOut: { // The window lost focus - window = findWindow(event.xfocus.window); + window = findWindow(event->xfocus.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for FocusOut event\n"); @@ -797,7 +794,7 @@ static void processSingleEvent(void) case Expose: { // The window's contents was damaged - window = findWindow(event.xexpose.window); + window = findWindow(event->xexpose.window); if (window == NULL) { fprintf(stderr, "Cannot find GLFW window structure for Expose event\n"); @@ -821,7 +818,7 @@ static void processSingleEvent(void) { // The selection conversion status is available - XSelectionEvent* request = &event.xselection; + XSelectionEvent* request = &event->xselection; if (_glfwReadSelection(request)) _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED; @@ -835,7 +832,7 @@ static void processSingleEvent(void) { // The contents of the selection was requested - XSelectionRequestEvent* request = &event.xselectionrequest; + XSelectionRequestEvent* request = &event->xselectionrequest; XEvent response; memset(&response, 0, sizeof(response)); @@ -860,12 +857,12 @@ static void processSingleEvent(void) default: { #if defined(_GLFW_HAS_XRANDR) - switch (event.type - _glfwLibrary.X11.RandR.eventBase) + switch (event->type - _glfwLibrary.X11.RandR.eventBase) { case RRScreenChangeNotify: { // Show XRandR that we really care - XRRUpdateConfiguration(&event); + XRRUpdateConfiguration(event); break; } } @@ -1146,13 +1143,14 @@ void _glfwPlatformRefreshWindowParams(_GLFWwindow* window) void _glfwPlatformPollEvents(void) { - _GLFWwindow* window; - - // Process all pending events - while (XPending(_glfwLibrary.X11.display)) - processSingleEvent(); + XEvent event; + while(XCheckMaskEvent(_glfwLibrary.X11.display, ~0, &event) || + XCheckTypedEvent(_glfwLibrary.X11.display, ClientMessage, &event)) + processSingleEvent(&event); +#if 0 // Did the cursor move in an active window that has captured the cursor + _GLFWwindow* window; window = _glfwLibrary.activeWindow; if (window) { @@ -1170,8 +1168,10 @@ void _glfwPlatformPollEvents(void) XFlush( _glfwLibrary.X11.display ); } } +#endif } +#include //======================================================================== // Wait for new window and input events @@ -1179,13 +1179,18 @@ void _glfwPlatformPollEvents(void) void _glfwPlatformWaitEvents(void) { - XEvent event; + fd_set set; + int fd; - // Block waiting for an event to arrive - XNextEvent(_glfwLibrary.X11.display, &event); - XPutBackEvent(_glfwLibrary.X11.display, &event); + fd = ConnectionNumber(_glfwLibrary.X11.display); - _glfwPlatformPollEvents(); + FD_ZERO(&set); + FD_SET(fd, &set); + + XFlush(_glfwLibrary.X11.display); + + if(select(fd+1, &set, NULL, NULL, NULL) > 0) + _glfwPlatformPollEvents(); } From 208b7f0bb8ffad428b2dd901f621da36af5559f8 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 16:04:03 +0200 Subject: [PATCH 06/32] Formatting. --- src/x11_window.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index 60a18287..2d85e041 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -468,7 +468,7 @@ static _GLFWwindow* findWindow(Window handle) // Get and process next X event (called by _glfwPlatformPollEvents) //======================================================================== -static void processSingleEvent(XEvent *event) +static void processEvent(XEvent *event) { _GLFWwindow* window; @@ -1144,9 +1144,12 @@ void _glfwPlatformRefreshWindowParams(_GLFWwindow* window) void _glfwPlatformPollEvents(void) { XEvent event; - while(XCheckMaskEvent(_glfwLibrary.X11.display, ~0, &event) || - XCheckTypedEvent(_glfwLibrary.X11.display, ClientMessage, &event)) - processSingleEvent(&event); + + while (XCheckMaskEvent(_glfwLibrary.X11.display, ~0, &event) || + XCheckTypedEvent(_glfwLibrary.X11.display, ClientMessage, &event)) + { + processEvent(&event); + } #if 0 // Did the cursor move in an active window that has captured the cursor From f66e6a6916c37b0459c46dedc36cd66f47f20cd3 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 16:05:43 +0200 Subject: [PATCH 07/32] Re-enabled cursor re-centering. --- src/x11_window.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index 2d85e041..4cf2ce5b 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1151,8 +1151,9 @@ void _glfwPlatformPollEvents(void) processEvent(&event); } -#if 0 - // Did the cursor move in an active window that has captured the cursor + // Check whether the cursor has moved inside an active window that has + // captured the cursor (because then it needs to be re-centered) + _GLFWwindow* window; window = _glfwLibrary.activeWindow; if (window) @@ -1171,7 +1172,6 @@ void _glfwPlatformPollEvents(void) XFlush( _glfwLibrary.X11.display ); } } -#endif } #include From fd125f7e54d9ed24d5a4fea55c2cb091bfa01229 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 16:06:22 +0200 Subject: [PATCH 08/32] Moved inclusion directive to proper location. --- src/x11_window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index 4cf2ce5b..0a3608cb 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -30,6 +30,8 @@ #include "internal.h" +#include + #include #include #include @@ -1174,8 +1176,6 @@ void _glfwPlatformPollEvents(void) } } -#include - //======================================================================== // Wait for new window and input events //======================================================================== From 98c1c2fbf2c483efd4a84b907d2195bcdf948bf6 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 16:18:59 +0200 Subject: [PATCH 09/32] Added threading support to CMake files. --- CMakeLists.txt | 6 ++++++ tests/CMakeLists.txt | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 27a609a6..842612e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,12 @@ option(BUILD_SHARED_LIBS "Build shared libraries" OFF) find_package(OpenGL REQUIRED) +set(CMAKE_THREAD_PREFER_PTHREADS YES) +find_package(Threads) +if (CMAKE_THREAD_LIBS_INIT) + list(APPEND glfw_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) +endif() + #-------------------------------------------------------------------- # Enable all warnings on GCC, regardless of OS #-------------------------------------------------------------------- diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9be1c1cd..e2d2a825 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,10 @@ add_executable(peter peter.c) add_executable(reopen reopen.c) add_executable(threads threads.c ${TINYCTHREAD}) +if (BUILD_SHARED_LIBS) + target_link_libraries(threads ${CMAKE_THREAD_LIBS_INIT}) +endif() + add_executable(accuracy WIN32 MACOSX_BUNDLE accuracy.c) set_target_properties(accuracy PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Accuracy") From 6c449e6e8f65ade80a7628acaaf255889e6864d6 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 16:21:06 +0200 Subject: [PATCH 10/32] Formatting. --- src/x11_window.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index 0a3608cb..d2f471d0 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -467,7 +467,7 @@ static _GLFWwindow* findWindow(Window handle) //======================================================================== -// Get and process next X event (called by _glfwPlatformPollEvents) +// Process the specified X event //======================================================================== static void processEvent(XEvent *event) @@ -1176,23 +1176,24 @@ void _glfwPlatformPollEvents(void) } } + //======================================================================== // Wait for new window and input events //======================================================================== void _glfwPlatformWaitEvents(void) { - fd_set set; int fd; + fd_set fds; fd = ConnectionNumber(_glfwLibrary.X11.display); - FD_ZERO(&set); - FD_SET(fd, &set); + FD_ZERO(&fds); + FD_SET(fd, &fds); XFlush(_glfwLibrary.X11.display); - if(select(fd+1, &set, NULL, NULL, NULL) > 0) + if (select(fd + 1, &fds, NULL, NULL, NULL) > 0) _glfwPlatformPollEvents(); } From ac3a5e28c9eca16696df93c5f5ba87e4bcdec22e Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 16:24:37 +0200 Subject: [PATCH 11/32] Made the leap to glfwWaitEvents. --- tests/threads.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/threads.c b/tests/threads.c index ba844dd7..f19fdf88 100644 --- a/tests/threads.c +++ b/tests/threads.c @@ -80,7 +80,7 @@ int main(void) if (thrd_create(&thread, thread_start, window) == thrd_success) { while (!glfwGetWindowParam(window, GLFW_CLOSE_REQUESTED)) - glfwPollEvents(); + glfwWaitEvents(); running = GL_FALSE; thrd_join(thread, &result); From 4057885ac3c82ed8726e32251b31c6f10fd98d69 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 16:32:54 +0200 Subject: [PATCH 12/32] Decreased nesting in threads test. --- tests/threads.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/threads.c b/tests/threads.c index f19fdf88..de656e01 100644 --- a/tests/threads.c +++ b/tests/threads.c @@ -77,20 +77,18 @@ int main(void) exit(EXIT_FAILURE); } - if (thrd_create(&thread, thread_start, window) == thrd_success) - { - while (!glfwGetWindowParam(window, GLFW_CLOSE_REQUESTED)) - glfwWaitEvents(); - - running = GL_FALSE; - thrd_join(thread, &result); - } - else + if (thrd_create(&thread, thread_start, window) != thrd_success) { fprintf(stderr, "Failed to create secondary thread\n"); exit(EXIT_FAILURE); } + while (!glfwGetWindowParam(window, GLFW_CLOSE_REQUESTED)) + glfwWaitEvents(); + + running = GL_FALSE; + thrd_join(thread, &result); + exit(EXIT_SUCCESS); } From c594bb468901fd196c72e0848152601aca6df7b3 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 12 Aug 2012 22:36:10 +0200 Subject: [PATCH 13/32] Made loop boolean volatile. --- tests/threads.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/threads.c b/tests/threads.c index de656e01..d7843106 100644 --- a/tests/threads.c +++ b/tests/threads.c @@ -36,7 +36,7 @@ #include "tinycthread.h" -static GLboolean running = GL_TRUE; +static volatile GLboolean running = GL_TRUE; static int thread_start(void* data) { From 18a5aba8f1bf4c7b3bf722fc036b31b7223ccfcb Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 13 Aug 2012 16:03:44 +0200 Subject: [PATCH 14/32] Implemented TLS for all platforms. --- src/cocoa_init.m | 5 +++++ src/cocoa_opengl.m | 38 +++++++++++++++++++++++++++++++++----- src/cocoa_platform.h | 4 ++++ src/win32_opengl.c | 14 +++++++++++++- src/x11_opengl.c | 13 ++++++++++++- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 7c208b90..3bb61c18 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -103,6 +103,9 @@ int _glfwPlatformInit(void) _glfwInitJoysticks(); + if (!_glfwInitOpenGL()) + return GL_FALSE; + _glfwLibrary.NS.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); if (!_glfwLibrary.NS.eventSource) return GL_FALSE; @@ -143,6 +146,8 @@ int _glfwPlatformTerminate(void) _glfwTerminateJoysticks(); + _glfwTerminateOpenGL(); + return GL_TRUE; } diff --git a/src/cocoa_opengl.m b/src/cocoa_opengl.m index 1f1f0f23..4d4c4299 100644 --- a/src/cocoa_opengl.m +++ b/src/cocoa_opengl.m @@ -29,18 +29,46 @@ #include "internal.h" +#include + //======================================================================== // The per-thread current context/window pointer -// TODO: Implement pthreads TLS //======================================================================== -_GLFWwindow* _glfwCurrentWindow = NULL; +static pthread_key_t _glfwCurrentTLS; ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// +//======================================================================== +// Initialize OpenGL support +//======================================================================== + +int _glfwInitOpenGL(void) +{ + if (pthread_key_create(&_glfwCurrentTLS, NULL) != 0) + { + _glfwSetError(GLFW_PLATFORM_ERROR, + "Cocoa/NSGL: Failed to create context TLS"); + return GL_FALSE; + } + + return GL_TRUE; +} + + +//======================================================================== +// Terminate OpenGL support +//======================================================================== + +void _glfwTerminateOpenGL(void) +{ + pthread_key_delete(_glfwCurrentTLS); +} + + //======================================================================== // Make the OpenGL context associated with the specified window current //======================================================================== @@ -52,7 +80,7 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) else [NSOpenGLContext clearCurrentContext]; - _glfwCurrentWindow = window; + pthread_setspecific(_glfwCurrentTLS, window); } @@ -62,7 +90,7 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) _GLFWwindow* _glfwPlatformGetCurrentContext(void) { - return _glfwCurrentWindow; + return (_GLFWwindow*) pthread_getspecific(_glfwCurrentTLS); } @@ -83,7 +111,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window) void _glfwPlatformSwapInterval(int interval) { - _GLFWwindow* window = _glfwCurrentWindow; + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); GLint sync = interval; [window->NSGL.context setValues:&sync forParameter:NSOpenGLCPSwapInterval]; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 97e903d7..a202c25e 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -124,4 +124,8 @@ void _glfwTerminateJoysticks(void); GLboolean _glfwSetVideoMode(int* width, int* height, int* bpp, int* refreshRate); void _glfwRestoreVideoMode(void); +// OpenGL support +int _glfwInitOpenGL(void); +void _glfwTerminateOpenGL(void); + #endif // _platform_h_ diff --git a/src/win32_opengl.c b/src/win32_opengl.c index fb0ba47f..6801e144 100644 --- a/src/win32_opengl.c +++ b/src/win32_opengl.c @@ -33,10 +33,22 @@ #include +//======================================================================== +// Thread local storage attribute macro +//======================================================================== +#if defined(_MSC_VER) + #define _GLFW_TLS __declspec(thread) +#elif defined(__GNUC__) + #define _GLFW_TLS __thread +#else + #define _GLFW_TLS +#endif + + //======================================================================== // The per-thread current context/window pointer //======================================================================== -__declspec(thread) _GLFWwindow* _glfwCurrentWindow = NULL; +static _GLFW_TLS _GLFWwindow* _glfwCurrentWindow = NULL; //======================================================================== diff --git a/src/x11_opengl.c b/src/x11_opengl.c index d7f33999..38a43ff0 100644 --- a/src/x11_opengl.c +++ b/src/x11_opengl.c @@ -37,10 +37,21 @@ // This is the only glXGetProcAddress variant not declared by glxext.h void (*glXGetProcAddressEXT(const GLubyte* procName))(); + +//======================================================================== +// Thread local storage attribute macro +//======================================================================== +#if defined(__GNUC__) + #define _GLFW_TLS __thread +#else + #define _GLFW_TLS +#endif + + //======================================================================== // The per-thread current context/window pointer //======================================================================== -__thread _GLFWwindow* _glfwCurrentWindow = NULL; +static _GLFW_TLS _GLFWwindow* _glfwCurrentWindow = NULL; //======================================================================== From 86bcfb3e4ecd72fbd59a603ebecb97a39943399b Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 13 Aug 2012 19:37:39 +0200 Subject: [PATCH 15/32] Added multiple windows to threads test. --- tests/threads.c | 77 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/tests/threads.c b/tests/threads.c index d7843106..6e3a6eaf 100644 --- a/tests/threads.c +++ b/tests/threads.c @@ -33,25 +33,36 @@ #include #include #include +#include #include "tinycthread.h" +typedef struct +{ + GLFWwindow window; + const char* title; + float r, g, b; + thrd_t ID; +} Thread; + static volatile GLboolean running = GL_TRUE; static int thread_start(void* data) { - GLFWwindow window = (GLFWwindow) data; + const Thread* thread = (const Thread*) data; + + glfwMakeContextCurrent(thread->window); + assert(glfwGetCurrentContext() == thread->window); - glfwMakeContextCurrent(window); glfwSwapInterval(1); while (running) { - const float red = (float) sin(glfwGetTime()) / 2.f + 0.5f; + const float v = (float) fabs(sin(glfwGetTime() * 2.f)); + glClearColor(thread->r * v, thread->g * v, thread->b * v, 0.f); - glClearColor(red, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT); - glfwSwapBuffers(window); + glfwSwapBuffers(thread->window); } glfwMakeContextCurrent(NULL); @@ -60,34 +71,60 @@ static int thread_start(void* data) int main(void) { - int result; - GLFWwindow window; - thrd_t thread; + int i, result; + Thread threads[] = + { + { NULL, "Red", 1.f, 0.f, 0.f, 0 }, + { NULL, "Green", 0.f, 1.f, 0.f, 0 }, + { NULL, "Blue", 0.f, 0.f, 1.f, 0 } + }; + const int count = sizeof(threads) / sizeof(Thread); if (!glfwInit()) { - fprintf(stderr, "Failed to initialize GLFW: %s\n", glfwErrorString(glfwGetError())); + fprintf(stderr, "Failed to initialize GLFW: %s\n", + glfwErrorString(glfwGetError())); exit(EXIT_FAILURE); } - window = glfwCreateWindow(640, 480, GLFW_WINDOWED, "Multithreading", NULL); - if (!window) + for (i = 0; i < count; i++) { - fprintf(stderr, "Failed to open GLFW window: %s\n", glfwErrorString(glfwGetError())); - exit(EXIT_FAILURE); + threads[i].window = glfwCreateWindow(200, 200, + GLFW_WINDOWED, + threads[i].title, + NULL); + if (!threads[i].window) + { + fprintf(stderr, "Failed to open GLFW window: %s\n", + glfwErrorString(glfwGetError())); + exit(EXIT_FAILURE); + } + + glfwSetWindowPos(threads[i].window, 200 + 250 * i, 200); + + if (thrd_create(&threads[i].ID, thread_start, threads + i) != + thrd_success) + { + fprintf(stderr, "Failed to create secondary thread\n"); + exit(EXIT_FAILURE); + } } - if (thrd_create(&thread, thread_start, window) != thrd_success) + while (running) { - fprintf(stderr, "Failed to create secondary thread\n"); - exit(EXIT_FAILURE); - } + assert(glfwGetCurrentContext() == NULL); - while (!glfwGetWindowParam(window, GLFW_CLOSE_REQUESTED)) glfwWaitEvents(); - running = GL_FALSE; - thrd_join(thread, &result); + for (i = 0; i < count; i++) + { + if (glfwGetWindowParam(threads[i].window, GLFW_CLOSE_REQUESTED)) + running = GL_FALSE; + } + } + + for (i = 0; i < count; i++) + thrd_join(threads[i].ID, &result); exit(EXIT_SUCCESS); } From 2e789e17e698d5e8cda99a7554c0c9de59dc23b2 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 13 Aug 2012 19:52:49 +0200 Subject: [PATCH 16/32] Made threads test a GUI program. --- tests/CMakeLists.txt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e2d2a825..80a7b82f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,11 +29,6 @@ add_executable(joysticks joysticks.c) add_executable(modes modes.c ${GETOPT}) add_executable(peter peter.c) add_executable(reopen reopen.c) -add_executable(threads threads.c ${TINYCTHREAD}) - -if (BUILD_SHARED_LIBS) - target_link_libraries(threads ${CMAKE_THREAD_LIBS_INIT}) -endif() add_executable(accuracy WIN32 MACOSX_BUNDLE accuracy.c) set_target_properties(accuracy PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Accuracy") @@ -44,15 +39,22 @@ set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing") add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c) set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing") +add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD}) +set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads") + add_executable(title WIN32 MACOSX_BUNDLE title.c) set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title") add_executable(windows WIN32 MACOSX_BUNDLE windows.c) set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") -set(WINDOWS_BINARIES accuracy sharing tearing title windows) +if (BUILD_SHARED_LIBS) + target_link_libraries(threads ${CMAKE_THREAD_LIBS_INIT}) +endif() + +set(WINDOWS_BINARIES accuracy sharing tearing threads title windows) set(CONSOLE_BINARIES clipboard defaults events fsaa fsfocus gamma glfwinfo - iconify joysticks modes peter reopen threads) + iconify joysticks modes peter reopen) if (MSVC) # Tell MSVC to use main instead of WinMain for Windows subsystem executables From 12e00876d780ada3eeea476d8b575de1984e38d5 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 15 Aug 2012 20:26:09 +0200 Subject: [PATCH 17/32] Formatting. --- tests/threads.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/threads.c b/tests/threads.c index 6e3a6eaf..35c83716 100644 --- a/tests/threads.c +++ b/tests/threads.c @@ -42,12 +42,12 @@ typedef struct GLFWwindow window; const char* title; float r, g, b; - thrd_t ID; + thrd_t id; } Thread; static volatile GLboolean running = GL_TRUE; -static int thread_start(void* data) +static int thread_main(void* data) { const Thread* thread = (const Thread*) data; @@ -102,7 +102,7 @@ int main(void) glfwSetWindowPos(threads[i].window, 200 + 250 * i, 200); - if (thrd_create(&threads[i].ID, thread_start, threads + i) != + if (thrd_create(&threads[i].id, thread_main, threads + i) != thrd_success) { fprintf(stderr, "Failed to create secondary thread\n"); @@ -124,7 +124,7 @@ int main(void) } for (i = 0; i < count; i++) - thrd_join(threads[i].ID, &result); + thrd_join(threads[i].id, &result); exit(EXIT_SUCCESS); } From c03659e9c39f492f3cb876a1035331f4147e9803 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 6 Sep 2012 21:42:42 +0200 Subject: [PATCH 18/32] Updated change log and credit. --- readme.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.html b/readme.html index d330d1c1..5431eb9e 100644 --- a/readme.html +++ b/readme.html @@ -286,6 +286,7 @@ version of GLFW.

  • Added windows simple multi-window test program
  • Added sharing simple OpenGL object sharing test program
  • Added modes video mode enumeration and setting test program
  • +
  • Added threads simple multi-threaded rendering test program
  • Added glfw3native.h header and platform-specific functions for explicit access to native display, window and context handles
  • Added glfwSetGamma, glfwSetGammaRamp and glfwGetGammaRamp functions and GLFWgammaramp type for monitor gamma ramp control
  • Added window parameter to glfwSwapBuffers
  • @@ -926,6 +927,9 @@ their skills. Special thanks go out to:

  • Arturo J. Pérez, for a bug fix for cursor tracking on Mac OS X 10.6 Snow Leopard
  • +
  • Riku Salminen, for making the X11 event processing able to support + multi-threaded rendering
  • +
  • Douglas C. Schmidt and Irfan Pyarali, for their excellent article Strategies for Implementing POSIX Condition Variables on Win32
  • From ea7ab3e01b6ba2a518de314bf378595a4d951d53 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 6 Sep 2012 23:02:33 +0200 Subject: [PATCH 19/32] Fixed broken selector signature. --- src/cocoa_window.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5129a21f..43576ab0 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -686,7 +686,7 @@ static GLboolean createWindow(_GLFWwindow* window, [window->NS.object setAcceptsMouseMovedEvents:YES]; [window->NS.object center]; - if ([window->NS.object respondsToSelector:@selector(setRestorable)]) + if ([window->NS.object respondsToSelector:@selector(setRestorable:)]) [window->NS.object setRestorable:NO]; return GL_TRUE; From 1653541aca355917bb1b4c22c2b84278f4e80746 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 6 Sep 2012 23:03:54 +0200 Subject: [PATCH 20/32] Formatting. --- src/cocoa_window.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 43576ab0..14f1856c 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1093,7 +1093,7 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, int x, int y) CGPoint mainScreenOrigin = CGDisplayBounds(CGMainDisplayID()).origin; double mainScreenHeight = CGDisplayBounds(CGMainDisplayID()).size.height; CGPoint targetPoint = CGPointMake(globalPoint.x - mainScreenOrigin.x, - mainScreenHeight - globalPoint.y - + mainScreenHeight - globalPoint.y - mainScreenOrigin.y); CGDisplayMoveCursorToPoint(CGMainDisplayID(), targetPoint); } From 8f82e02a51c7049cd6246f995dcf28c8d39ce3b2 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 6 Sep 2012 23:05:19 +0200 Subject: [PATCH 21/32] Updated credit. --- readme.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.html b/readme.html index 5431eb9e..497546cd 100644 --- a/readme.html +++ b/readme.html @@ -907,7 +907,7 @@ their skills. Special thanks go out to:

    language
  • Shane Liesegang, for providing a bug fix relating to Cocoa window - restoration
  • + restoration and reporting a bug on 32-bit Cocoa builds
  • Tristam MacDonald, for his bug reports and feedback on the Cocoa port
  • From 8a948753df943a162dd2ef78a33d42ca644ef466 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 6 Sep 2012 23:55:23 +0200 Subject: [PATCH 22/32] Added printing of debug context window parameter. --- tests/glfwinfo.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index 52d6e0de..9d562400 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -288,6 +288,9 @@ int main(int argc, char** argv) get_glfw_profile_name(glfwGetWindowParam(window, GLFW_OPENGL_PROFILE))); } + printf("OpenGL context debug flag saved by GLFW: %s\n", + glfwGetWindowParam(window, GLFW_OPENGL_DEBUG_CONTEXT) ? "true" : "false"); + printf("OpenGL context renderer string: \"%s\"\n", glGetString(GL_RENDERER)); printf("OpenGL context vendor string: \"%s\"\n", glGetString(GL_VENDOR)); From 755c2a364b2b6521011aa2a544aa204809b3e154 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 6 Sep 2012 23:57:36 +0200 Subject: [PATCH 23/32] Updated .gitignore. --- .gitignore | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 0496a03c..da61d802 100644 --- a/.gitignore +++ b/.gitignore @@ -6,13 +6,13 @@ cmake_uninstall.cmake .DS_Store docs/Doxyfile src/config.h -src/libglfw.pc -src/libglfw.so -src/libglfw.a -src/libglfw.dylib -src/glfw.lib -src/glfw.dll -src/glfwdll.lib +src/glfw3.pc +src/libglfw3.so +src/libglfw3.a +src/libglfw3.dylib +src/glfw3.lib +src/glfw3.dll +src/glfw3dll.lib examples/boing examples/gears examples/heightmap @@ -22,8 +22,8 @@ examples/wave examples/*.app examples/*.exe tests/accuracy +tests/clipboard tests/defaults -tests/dynamic tests/events tests/fsaa tests/fsfocus @@ -31,11 +31,12 @@ tests/gamma tests/glfwinfo tests/iconify tests/joysticks -tests/listmodes +tests/modes tests/peter tests/reopen tests/sharing tests/tearing +tests/threads tests/title tests/windows tests/*.app From 8cf093a19dbae45b12d832aa542790773b34b04f Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Fri, 7 Sep 2012 01:01:17 +0200 Subject: [PATCH 24/32] Linux joystick fixes. --- src/x11_joystick.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x11_joystick.c b/src/x11_joystick.c index a5700c68..1895dc6a 100644 --- a/src/x11_joystick.c +++ b/src/x11_joystick.c @@ -53,7 +53,7 @@ static int openJoystickDevice(int joy, const char* path) char numAxes, numButtons; int fd, version; - fd = open(path, O_NONBLOCK); + fd = open(path, O_RDONLY | O_NONBLOCK); if (fd == -1) return GL_FALSE; @@ -127,7 +127,7 @@ static void pollJoystickEvents(void) if (errno == ENODEV) _glfwLibrary.X11.joystick[i].present = GL_FALSE; - if (result < sizeof(e)) + if (result == -1) break; // We don't care if it's an init event or not From e70ced5e05009b858956f6b04f62d1dc29f903d4 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Fri, 7 Sep 2012 02:15:54 +0200 Subject: [PATCH 25/32] Fixed bad editing in Cocoa joystick code. --- readme.html | 2 ++ src/cocoa_joystick.m | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/readme.html b/readme.html index 497546cd..314c392a 100644 --- a/readme.html +++ b/readme.html @@ -924,6 +924,8 @@ their skills. Special thanks go out to:

    Much of the Windows code of GLFW was originally based on Jeff's code +
  • Julian Møller, for reporting a bug in the Cocoa joystick code
  • +
  • Arturo J. Pérez, for a bug fix for cursor tracking on Mac OS X 10.6 Snow Leopard
  • diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 7eac7f91..04c9e972 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -550,15 +550,15 @@ int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numaxes) for (i = 0; i < numaxes; i++) { - _glfwJoystickElement* axes = + _glfwJoystickElement* elements = (_glfwJoystickElement*) CFArrayGetValueAtIndex(joystick.axes, i); - long readScale = axes->maxReport - axes->minReport; + long readScale = elements->maxReport - elements->minReport; if (readScale == 0) - axes[i] = axes->value; + axes[i] = elements->value; else - axes[i] = (2.0f * (axes->value - axes->minReport) / readScale) - 1.0f; + axes[i] = (2.0f * (elements->value - elements->minReport) / readScale) - 1.0f; if (i & 1) axes[i] = -axes[i]; From 4cb569b5b38dadfadfe503e7db33e852ab95165d Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Fri, 7 Sep 2012 15:27:41 +0200 Subject: [PATCH 26/32] Replaced guessing with dirent and regex. --- src/x11_init.c | 3 ++- src/x11_joystick.c | 49 ++++++++++++++++++++++++++++++++++------------ src/x11_platform.h | 2 +- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/x11_init.c b/src/x11_init.c index a5277c09..32719cbf 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -648,7 +648,8 @@ int _glfwPlatformInit(void) _glfwLibrary.X11.cursor = createNULLCursor(); - _glfwInitJoysticks(); + if (!_glfwInitJoysticks()) + return GL_FALSE; // Start the timer _glfwInitTimer(); diff --git a/src/x11_joystick.c b/src/x11_joystick.c index 1895dc6a..a383fa02 100644 --- a/src/x11_joystick.c +++ b/src/x11_joystick.c @@ -37,7 +37,8 @@ #include #include #include - +#include +#include #include #include #endif // _GLFW_USE_LINUX_JOYSTICKS @@ -171,30 +172,52 @@ static void pollJoystickEvents(void) // Initialize joystick interface //======================================================================== -void _glfwInitJoysticks(void) +int _glfwInitJoysticks(void) { #ifdef _GLFW_USE_LINUX_JOYSTICKS - int i, j, joy = 0; - char path[20]; - const char* bases[] = + int i, joy = 0; + regex_t regex; + DIR* dir; + const char* directories[] = { - "/dev/input/js", - "/dev/js" + "/dev/input", + "/dev" }; - for (i = 0; i < sizeof(bases) / sizeof(bases[0]); i++) + if (regcomp(®ex, "^js[0-9]\\+$", 0) != 0) { - for (j = 0; j < 50; j++) - { - if (joy > GLFW_JOYSTICK_LAST) - break; + _glfwSetError(GLFW_PLATFORM_ERROR, "X11: Failed to compile regex"); + return GL_FALSE; + } - sprintf(path, "%s%i", bases[i], j); + for (i = 0; i < sizeof(directories) / sizeof(directories[0]); i++) + { + struct dirent* entry; + + dir = opendir(directories[i]); + if (!dir) + continue; + + while ((entry = readdir(dir))) + { + char path[20]; + regmatch_t match; + + if (regexec(®ex, entry->d_name, 1, &match, 0) != 0) + continue; + + snprintf(path, sizeof(path), "%s/%s", directories[i], entry->d_name); if (openJoystickDevice(joy, path)) joy++; } + + closedir(dir); } + + regfree(®ex); #endif // _GLFW_USE_LINUX_JOYSTICKS + + return GL_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 75beb745..a6b576ed 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -305,7 +305,7 @@ void _glfwSetVideoMode(int* width, int* height, int* rate); void _glfwRestoreVideoMode(void); // Joystick input -void _glfwInitJoysticks(void); +int _glfwInitJoysticks(void); void _glfwTerminateJoysticks(void); // Unicode support From b4d0223faa6598b20e8e6da2153e38f1c8c32d43 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Fri, 7 Sep 2012 15:48:03 +0200 Subject: [PATCH 27/32] Formatting. --- src/x11_joystick.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/x11_joystick.c b/src/x11_joystick.c index a383fa02..c1fb62be 100644 --- a/src/x11_joystick.c +++ b/src/x11_joystick.c @@ -178,7 +178,7 @@ int _glfwInitJoysticks(void) int i, joy = 0; regex_t regex; DIR* dir; - const char* directories[] = + const char* dirs[] = { "/dev/input", "/dev" @@ -190,11 +190,11 @@ int _glfwInitJoysticks(void) return GL_FALSE; } - for (i = 0; i < sizeof(directories) / sizeof(directories[0]); i++) + for (i = 0; i < sizeof(dirs) / sizeof(dirs[0]); i++) { struct dirent* entry; - dir = opendir(directories[i]); + dir = opendir(dirs[i]); if (!dir) continue; @@ -206,7 +206,7 @@ int _glfwInitJoysticks(void) if (regexec(®ex, entry->d_name, 1, &match, 0) != 0) continue; - snprintf(path, sizeof(path), "%s/%s", directories[i], entry->d_name); + snprintf(path, sizeof(path), "%s/%s", dirs[i], entry->d_name); if (openJoystickDevice(joy, path)) joy++; } From a49c61f1a7a062e989a6ba970353e0b0540a0fee Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sat, 8 Sep 2012 20:43:08 +0200 Subject: [PATCH 28/32] Updated TinyCThread. --- support/tinycthread.c | 90 +++++++++++++++++++++---------------------- support/tinycthread.h | 89 ++++++++++++++++++++++++++++-------------- 2 files changed, 106 insertions(+), 73 deletions(-) diff --git a/support/tinycthread.c b/support/tinycthread.c index 9bee2cb5..ced7cf3f 100644 --- a/support/tinycthread.c +++ b/support/tinycthread.c @@ -21,13 +21,6 @@ freely, subject to the following restrictions: distribution. */ -/* Activate some POSIX functionality (e.g. recursive mutexes) */ -#define _GNU_SOURCE -#if !defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE < 500) - #undef _XOPEN_SOURCE - #define _XOPEN_SOURCE 500 -#endif - #include "tinycthread.h" #include @@ -99,9 +92,11 @@ int mtx_lock(mtx_t *mtx) #endif } -int mtx_timedlock(mtx_t *mtx, const xtime *xt) +int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) { /* FIXME! */ + (void)mtx; + (void)ts; return thrd_error; } @@ -290,21 +285,21 @@ int cnd_wait(cnd_t *cond, mtx_t *mtx) #endif } -int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) { #if defined(_TTHREAD_WIN32_) - xtime now; - DWORD delta; - xtime_get(&now, TIME_UTC); - delta = (xt->sec - now.sec) * 1000 + - (xt->nsec - now.nsec + 500000) / 1000000; - return _cnd_timedwait_win32(cond, mtx, delta); + struct timespec now; + if (clock_gettime(TIME_UTC, &now) == 0) + { + DWORD delta = (ts->tv_sec - now.tv_sec) * 1000 + + (ts->tv_nsec - now.tv_nsec + 500000) / 1000000; + return _cnd_timedwait_win32(cond, mtx, delta); + } + else + return thrd_error; #else - struct timespec ts; int ret; - ts.tv_sec = xt->sec; - ts.tv_nsec = xt->nsec; - ret = pthread_cond_timedwait(cond, mtx, &ts); + ret = pthread_cond_timedwait(cond, mtx, ts); if (ret == ETIMEDOUT) { return thrd_timeout; @@ -322,9 +317,9 @@ typedef struct { /* Thread wrapper function. */ #if defined(_TTHREAD_WIN32_) -unsigned WINAPI _thrd_wrapper_function(void * aArg) +static unsigned WINAPI _thrd_wrapper_function(void * aArg) #elif defined(_TTHREAD_POSIX_) -void * _thrd_wrapper_function(void * aArg) +static void * _thrd_wrapper_function(void * aArg) #endif { thrd_start_t fun; @@ -401,6 +396,7 @@ thrd_t thrd_current(void) int thrd_detach(thrd_t thr) { /* FIXME! */ + (void)thr; return thrd_error; } @@ -460,9 +456,9 @@ int thrd_join(thrd_t thr, int *res) return thrd_success; } -void thrd_sleep(const xtime *xt) +int thrd_sleep(const struct timespec *time_point, struct timespec *remaining) { - xtime now; + struct timespec now; #if defined(_TTHREAD_WIN32_) DWORD delta; #else @@ -470,20 +466,21 @@ void thrd_sleep(const xtime *xt) #endif /* Get the current time */ - xtime_get(&now, TIME_UTC); + if (clock_gettime(TIME_UTC, &now) != 0) + return -2; // FIXME: Some specific error code? #if defined(_TTHREAD_WIN32_) /* Delta in milliseconds */ - delta = (xt->sec - now.sec) * 1000 + - (xt->nsec - now.nsec + 500000) / 1000000; + delta = (time_point->tv_sec - now.tv_sec) * 1000 + + (time_point->tv_nsec - now.tv_nsec + 500000) / 1000000; if (delta > 0) { Sleep(delta); } #else /* Delta in microseconds */ - delta = (xt->sec - now.sec) * 1000000L + - (xt->nsec - now.nsec + 500L) / 1000L; + delta = (time_point->tv_sec - now.tv_sec) * 1000000L + + (time_point->tv_nsec - now.tv_nsec + 500L) / 1000L; /* On some systems, the usleep argument must be < 1000000 */ while (delta > 999999L) @@ -496,6 +493,14 @@ void thrd_sleep(const xtime *xt) usleep((useconds_t)delta); } #endif + + /* We don't support waking up prematurely (yet) */ + if (remaining) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return 0; } void thrd_yield(void) @@ -563,26 +568,21 @@ int tss_set(tss_t key, void *val) return thrd_success; } -int xtime_get(xtime *xt, int base) +#if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_) +int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts) { - if (base == TIME_UTC) - { #if defined(_TTHREAD_WIN32_) - struct _timeb tb; - _ftime(&tb); - xt->sec = (time_t)tb.time; - xt->nsec = 1000000 * (long)tb.millitm; + struct _timeb tb; + _ftime(&tb); + ts->tv_sec = (time_t)tb.time; + ts->tv_nsec = 1000000L * (long)tb.millitm; #else - struct timeval tv; - gettimeofday(&tv, NULL); - xt->sec = (time_t)tv.tv_sec; - xt->nsec = 1000 * (long)tv.tv_usec; + struct timeval tv; + gettimeofday(&tv, NULL); + ts->tv_sec = (time_t)tv.tv_sec; + ts->tv_nsec = 1000L * (long)tv.tv_usec; #endif - return base; - } - else - { - return 0; - } + return 0; } +#endif // _TTHREAD_EMULATE_CLOCK_GETTIME_ diff --git a/support/tinycthread.h b/support/tinycthread.h index 8069e3f3..1a9c805c 100644 --- a/support/tinycthread.h +++ b/support/tinycthread.h @@ -57,6 +57,22 @@ freely, subject to the following restrictions: #define _TTHREAD_PLATFORM_DEFINED_ #endif +/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */ +#if defined(_TTHREAD_POSIX_) + #undef _FEATURES_H + #if !defined(_GNU_SOURCE) + #define _GNU_SOURCE + #endif + #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L + #endif + #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500) + #undef _XOPEN_SOURCE + #define _XOPEN_SOURCE 500 + #endif +#endif + /* Generic includes */ #include @@ -75,10 +91,42 @@ freely, subject to the following restrictions: #endif #endif +/* Workaround for missing TIME_UTC: If time.h doesn't provide TIME_UTC, + it's quite likely that libc does not support it either. Hence, fall back to + the only other supported time specifier: CLOCK_REALTIME (and if that fails, + we're probably emulating clock_gettime anyway, so anything goes). */ +#ifndef TIME_UTC + #ifdef CLOCK_REALTIME + #define TIME_UTC CLOCK_REALTIME + #else + #define TIME_UTC 0 + #endif +#endif + +/* Workaround for missing clock_gettime (most Windows compilers, afaik) */ +#if defined(_TTHREAD_WIN32_) +#define _TTHREAD_EMULATE_CLOCK_GETTIME_ +/* Emulate struct timespec */ +struct _ttherad_timespec { + time_t tv_sec; + long tv_nsec; +}; +#define timespec _ttherad_timespec + +/* Emulate clockid_t */ +typedef int _tthread_clockid_t; +#define clockid_t _tthread_clockid_t + +/* Emulate clock_gettime */ +int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts); +#define clock_gettime _tthread_clock_gettime +#endif + + /** TinyCThread version (major number). */ #define TINYCTHREAD_VERSION_MAJOR 1 /** TinyCThread version (minor number). */ -#define TINYCTHREAD_VERSION_MINOR 0 +#define TINYCTHREAD_VERSION_MINOR 1 /** TinyCThread version (full version). */ #define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR) @@ -101,7 +149,7 @@ freely, subject to the following restrictions: * @hideinitializer */ -/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C11 or */ +/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C11 */ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local) #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) #define _Thread_local __thread @@ -126,12 +174,6 @@ freely, subject to the following restrictions: #define mtx_try 4 #define mtx_recursive 8 -/** Time specification */ -typedef struct { - time_t sec; /**< Seconds */ - long nsec; /**< Nanoseconds */ -} xtime; - /* Mutex */ #if defined(_TTHREAD_WIN32_) typedef struct { @@ -174,7 +216,7 @@ int mtx_lock(mtx_t *mtx); /** NOT YET IMPLEMENTED. */ -int mtx_timedlock(mtx_t *mtx, const xtime *xt); +int mtx_timedlock(mtx_t *mtx, const struct timespec *ts); /** Try to lock the given mutex. * The specified mutex shall support either test and return or timeout. If the @@ -260,7 +302,7 @@ int cnd_wait(cnd_t *cond, mtx_t *mtx); * specified in the call was reached without acquiring the requested resource, or * @ref thrd_error if the request could not be honored. */ -int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt); +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); /* Thread */ #if defined(_TTHREAD_WIN32_) @@ -326,11 +368,16 @@ void thrd_exit(int res); int thrd_join(thrd_t thr, int *res); /** Put the calling thread to sleep. -* Suspend execution of the calling thread until after the time specified by the -* xtime object. -* @param xt A point in time at which the thread will resume (absolute time). +* Suspend execution of the calling thread. +* @param time_point A point in time at which the thread will resume (absolute time). +* @param remaining If non-NULL, this parameter will hold the remaining time until +* time_point upon return. This will typically be zero, but if +* the thread was woken up by a signal that is not ignored before +* time_point was reached @c remaining will hold a positive +* time. +* @return 0 (zero) on successful sleep, or -1 if an interrupt occurred. */ -void thrd_sleep(const xtime *xt); +int thrd_sleep(const struct timespec *time_point, struct timespec *remaining); /** Yield execution to another thread. * Permit other threads to run, even if the current thread would ordinarily @@ -385,20 +432,6 @@ void *tss_get(tss_t key); */ int tss_set(tss_t key, void *val); -/* Timing */ -enum -{ - TIME_UTC = 1 -}; - -/** Get the current time. -* Set the xtime object to hold the current time based on the given time base. -* @param xt Will be filled out with the current time. -* @param base Time base (must be @c TIME_UTC). -* @return The non-zero value @c base if the function is successful, otherwise -* it returns zero. -*/ -int xtime_get(xtime *xt, int base); #endif /* _TINYTHREAD_H_ */ From bd70e5335207fc45e56f045f843d86bdd32601f3 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sat, 8 Sep 2012 21:08:39 +0200 Subject: [PATCH 29/32] Added missing flags for size hints. --- readme.html | 1 + src/x11_window.c | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/readme.html b/readme.html index 314c392a..31e9217e 100644 --- a/readme.html +++ b/readme.html @@ -349,6 +349,7 @@ version of GLFW.

  • [X11] Bugfix: Some window properties required by the ICCCM were not set
  • [X11] Bugfix: Calling glXCreateContextAttribsARB with an unavailable OpenGL version caused the application to terminate with a BadMatch Xlib error
  • [X11] Bugfix: A synchronization point necessary for jitter-free locked cursor mode was incorrectly removed
  • +
  • [X11] Bugfix: The window size hints were not updated when calling glfwSetWindowSize on a non-resizable window
  • [Win32] Changed port to use Unicode mode only
  • [Win32] Removed explicit support for versions of Windows older than Windows XP
  • [Win32] Bugfix: Window activation and iconification did not work as expected
  • diff --git a/src/x11_window.c b/src/x11_window.c index f219275c..cbc28dd0 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -956,7 +956,6 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { int mode = 0, rate, sizeChanged = GL_FALSE; - XSizeHints* sizehints; rate = window->refreshRate; @@ -970,14 +969,14 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { // Update window size restrictions to match new window size - sizehints = XAllocSizeHints(); - sizehints->flags = 0; + XSizeHints* hints = XAllocSizeHints(); - sizehints->min_width = sizehints->max_width = width; - sizehints->min_height = sizehints->max_height = height; + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; - XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, sizehints); - XFree(sizehints); + XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, hints); + XFree(hints); } // Change window size before changing fullscreen mode? From 2b946289d8922da768b317c3d9ff35e60a17b21d Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 9 Sep 2012 01:31:23 +0200 Subject: [PATCH 30/32] Added direct dependency on librt for threads test on X11. --- tests/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 80a7b82f..11bcfe9e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,11 @@ else() link_libraries(${glfw_LIBRARIES}) endif() +list(APPEND thread_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) +if (UNIX AND NOT APPLE) + list(APPEND thread_LIBRARIES ${RT_LIBRARY}) +endif() + include_directories(${GLFW_SOURCE_DIR}/include ${GLFW_SOURCE_DIR}/support ${OPENGL_INCLUDE_DIR}) @@ -49,7 +54,7 @@ add_executable(windows WIN32 MACOSX_BUNDLE windows.c) set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") if (BUILD_SHARED_LIBS) - target_link_libraries(threads ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(threads ${thread_LIBRARIES}) endif() set(WINDOWS_BINARIES accuracy sharing tearing threads title windows) From 50d68c83bbbfc5fe8fdc7d26e1025ff208f40b93 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 9 Sep 2012 13:56:57 +0200 Subject: [PATCH 31/32] Fixed TinyCThread declaring timespec on OS X. --- support/tinycthread.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/support/tinycthread.h b/support/tinycthread.h index 1a9c805c..18451ef9 100644 --- a/support/tinycthread.h +++ b/support/tinycthread.h @@ -104,14 +104,16 @@ freely, subject to the following restrictions: #endif /* Workaround for missing clock_gettime (most Windows compilers, afaik) */ -#if defined(_TTHREAD_WIN32_) +#if defined(_TTHREAD_WIN32_) || defined(__APPLE_CC__) #define _TTHREAD_EMULATE_CLOCK_GETTIME_ /* Emulate struct timespec */ +#if defined(_TTHREAD_WIN32_) struct _ttherad_timespec { time_t tv_sec; long tv_nsec; }; #define timespec _ttherad_timespec +#endif /* Emulate clockid_t */ typedef int _tthread_clockid_t; From feef05f839e67e86a08095f476805b389899a84a Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 9 Sep 2012 14:22:14 +0200 Subject: [PATCH 32/32] Removed superfluous inclusion. --- src/fullscreen.c | 6 ++---- src/window.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/fullscreen.c b/src/fullscreen.c index f632749f..4372d153 100644 --- a/src/fullscreen.c +++ b/src/fullscreen.c @@ -32,10 +32,8 @@ #include "internal.h" #include -#ifdef __APPLE__ -#include -#else -#include +#if _WIN32 + #include #endif diff --git a/src/window.c b/src/window.c index 6c7f5a59..c3944ead 100644 --- a/src/window.c +++ b/src/window.c @@ -33,10 +33,8 @@ #include #include -#ifdef __APPLE__ -#include -#else -#include +#if _WIN32 + #include #endif