diff --git a/CMakeLists.txt b/CMakeLists.txt index 49916a08..a82b0081 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,18 @@ string(STRIP ${versionFile} SIMGEAR_VERSION) #packaging SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING") SET(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simulation support libraries for FlightGear and related projects") +SET(CPACK_PACKAGE_VENDOR "The FlightGear project") +SET(CPACK_GENERATOR "TBZ2") +SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/) + +# split version string into components, note CMAKE_MATCH_0 is the entire regexp match +string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} ) +set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) +set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) +set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) + +message(STATUS "version is ${CPACK_PACKAGE_VERSION_MAJOR} dot ${CPACK_PACKAGE_VERSION_MINOR} dot ${CPACK_PACKAGE_VERSION_PATCH}") # We have some custom .cmake scripts not in the official distribution. set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}") @@ -145,11 +157,14 @@ if(WIN32) # SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}") # endforeach(warning) - set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS") + set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996") endif(MSVC) # assumed on Windows set(HAVE_GETLOCALTIME 1) + + set( WINSOCK_LIBRARY "ws2_32.lib" ) + set( RT_LIBRARY "winmm" ) endif(WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${MSVC_FLAGS}") diff --git a/configure.ac b/configure.ac index 81bae41e..09caad86 100644 --- a/configure.ac +++ b/configure.ac @@ -398,7 +398,7 @@ case "${host}" in esac -if test "$OPENAL_OK" == "no" -a "x$enable_headless" != "xyes"; then +if test "$OPENAL_OK" = "no" -a "x$enable_headless" != "xyes"; then echo echo "You *must* have the openal library installed on your system to build" echo "SimGear!" @@ -409,7 +409,7 @@ if test "$OPENAL_OK" == "no" -a "x$enable_headless" != "xyes"; then exit fi -if test "$ALUT_OK" == "no" -a "x$enable_headless" != "xyes"; then +if test "$ALUT_OK" = "no" -a "x$enable_headless" != "xyes"; then echo echo "You *must* have the alut library installed on your system to build" echo "SimGear!" diff --git a/projects/VC90/SimGear.vcproj b/projects/VC90/SimGear.vcproj index 34aa5752..077af7ae 100644 --- a/projects/VC90/SimGear.vcproj +++ b/projects/VC90/SimGear.vcproj @@ -1487,6 +1487,14 @@ RelativePath="..\..\simgear\threads\SGQueue.hxx" > + + + + #include +#include -#include // for STDOUT_FILENO #include #include -#include + #include #include @@ -133,10 +133,12 @@ int main(int argc, char* argv[]) cl.setProxy(proxyHost, proxyPort, proxyAuth); } +#ifndef WIN32 signal(SIGPIPE, SIG_IGN); +#endif if (!outFile) { - outFile = new SGFile(STDOUT_FILENO); + outFile = new SGFile(fileno(stdout)); } if (url.empty()) { diff --git a/simgear/io/raw_socket.cxx b/simgear/io/raw_socket.cxx index b849bddc..cf7b20f7 100644 --- a/simgear/io/raw_socket.cxx +++ b/simgear/io/raw_socket.cxx @@ -1,20 +1,20 @@ /* simgear::Socket, adapted from PLIB Socket by James Turner Copyright (C) 2010 James Turner - + PLIB - A Suite of Portable Game Libraries Copyright (C) 1998,2002 Steve Baker - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. - + You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -47,7 +47,7 @@ # include # include # include -# include +# include # include # include # include @@ -61,40 +61,43 @@ #include #include - -#include -#include -#include +#include namespace { -class Resolver : public OpenThreads::Thread +class Resolver : public SGThread { public: static Resolver* instance() { if (!static_instance) { - OpenThreads::Thread::Init(); - static_instance = new Resolver; atexit(&Resolver::cleanup); static_instance->start(); } - + return static_instance; } - + static void cleanup() { - static_instance->cancel(); + static_instance->shutdown(); + static_instance->join(); } - - Resolver() + + Resolver() : + _done(false) { - // take the lock initially, thread will wait upon it once running - _lock.lock(); } - + + void shutdown() + { + _lock.lock(); + _done = true; + _wait.signal(); + _lock.unlock(); + } + simgear::IPAddress* lookup(const string& host) { simgear::IPAddress* result = NULL; @@ -109,7 +112,7 @@ public: _lock.unlock(); return result; } - + simgear::IPAddress* lookupSync(const string& host) { simgear::IPAddress* result = NULL; @@ -140,31 +143,33 @@ protected: */ virtual void run() { - while (true) { - _wait.wait(&_lock); + _lock.lock(); + while (!_done) { AddressCache::iterator it; - + for (it = _cache.begin(); it != _cache.end(); ++it) { if (it->second == NULL) { string h = it->first; - + _lock.unlock(); simgear::IPAddress* addr = new simgear::IPAddress; // may take seconds or even minutes! lookupHost(h.c_str(), *addr); _lock.lock(); - + // cahce may have changed while we had the lock released - // so iterators may be invalid: restart the traversal it = _cache.begin(); _cache[h] = addr; } // of found un-resolved entry - } // of un-resolved address iteration + } // of un-resolved address iteration + _wait.wait(_lock); } // of thread run loop + _lock.unlock(); } private: static Resolver* static_instance; - + /** * The actual synchronous, blocking host lookup function * do *not* call this with any locks (mutexs) held, since depending @@ -177,7 +182,7 @@ private: memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; bool ok = false; - + struct addrinfo* result0 = NULL; int err = getaddrinfo(host, NULL, &hints, &result0); if (err) { @@ -205,21 +210,22 @@ private: freeaddrinfo(result0); return ok; } - - OpenThreads::Mutex _lock; - OpenThreads::Condition _wait; - + + SGMutex _lock; + SGWaitCondition _wait; + typedef std::map AddressCache; AddressCache _cache; + bool _done; }; Resolver* Resolver::static_instance = NULL; - + } // of anonymous namespace namespace simgear { - + IPAddress::IPAddress ( const char* host, int port ) { set ( host, port ) ; @@ -266,18 +272,18 @@ void IPAddress::set ( const char* host, int port ) addr->sin_addr.s_addr = INADDR_ANY; return; } - + if (strcmp(host, "") == 0) { addr->sin_addr.s_addr = INADDR_BROADCAST; return; } - + // check the cache IPAddress* cached = Resolver::instance()->lookupSync(host); if (cached) { memcpy(addr, cached->getAddr(), cached->getAddrLen()); } - + addr->sin_port = htons (port); // fix up port after getaddrinfo } @@ -289,12 +295,12 @@ IPAddress::~IPAddress() } bool IPAddress::lookupNonblocking(const char* host, IPAddress& addr) -{ +{ IPAddress* cached = Resolver::instance()->lookup(host); if (!cached) { return false; } - + addr = *cached; return true; } @@ -313,9 +319,9 @@ const char* IPAddress::getHost () const return buf; } -unsigned int IPAddress::getIP () const -{ - return addr->sin_addr.s_addr; +unsigned int IPAddress::getIP () const +{ + return addr->sin_addr.s_addr; } unsigned int IPAddress::getPort() const @@ -328,9 +334,9 @@ void IPAddress::setPort(int port) addr->sin_port = htons(port); } -unsigned int IPAddress::getFamily () const -{ - return addr->sin_family; +unsigned int IPAddress::getFamily () const +{ + return addr->sin_family; } const char* IPAddress::getLocalHost () @@ -371,7 +377,7 @@ struct sockaddr* IPAddress::getAddr() const addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in)); memset(addr, 0, sizeof(struct sockaddr_in)); } - + return (struct sockaddr*) addr; } @@ -456,7 +462,7 @@ void Socket::setBroadcast ( bool broadcast ) } else { result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, NULL, 0 ); } - + if ( result < 0 ) { throw sg_exception("Socket::setBroadcast failed"); } @@ -476,7 +482,7 @@ int Socket::bind ( const char* host, int port ) } #endif - // 224.0.0.0 - 239.255.255.255 are multicast + // 224.0.0.0 - 239.255.255.255 are multicast // Usage of 239.x.x.x is recommended for local scope // Reference: http://tools.ietf.org/html/rfc5771 if( ntohl(addr.getIP()) >= 0xe0000000 && ntohl(addr.getIP()) <= 0xefffffff ) { @@ -486,7 +492,7 @@ int Socket::bind ( const char* host, int port ) a.sin_addr.S_un.S_addr = INADDR_ANY; a.sin_family = AF_INET; a.sin_port = htons(port); - + if( (result = ::bind(handle,(const sockaddr*)&a,sizeof(a))) < 0 ) { SG_LOG(SG_IO, SG_ALERT, "bind(any:" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")"); return result; @@ -636,7 +642,7 @@ int Socket::select ( Socket** reads, Socket** writes, int timeout ) { fd_set r,w; int retval; - + FD_ZERO (&r); FD_ZERO (&w); @@ -674,7 +680,7 @@ int Socket::select ( Socket** reads, Socket** writes, int timeout ) // It bothers me that select()'s first argument does not appear to // work as advertised... [it hangs like this if called with // anything less than FD_SETSIZE, which seems wasteful?] - + // Note: we ignore the 'exception' fd_set - I have never had a // need to use it. The name is somewhat misleading - the only // thing I have ever seen it used for is to detect urgent data - diff --git a/simgear/math/SGMathTest.cxx b/simgear/math/SGMathTest.cxx index 415a4d59..ef1f5074 100644 --- a/simgear/math/SGMathTest.cxx +++ b/simgear/math/SGMathTest.cxx @@ -23,6 +23,7 @@ #include #include "SGMath.hxx" +#include "sg_random.h" template bool @@ -198,6 +199,34 @@ QuatTest(void) return true; } +template +bool +QuatDerivativeTest(void) +{ + for (unsigned i = 0; i < 100; ++i) { + // Generate the test case: + // Give a lower bound to the distance, so avoid testing cancelation + T dt = T(0.01) + sg_random(); + // Start with orientation o0, angular velocity av and a random stepsize + SGQuat o0 = SGQuat::fromEulerDeg(T(360)*sg_random(), T(360)*sg_random(), T(360)*sg_random()); + SGVec3 av(sg_random(), sg_random(), sg_random()); + // Do one euler step and renormalize + SGQuat o1 = normalize(o0 + dt*o0.derivative(av)); + + // Check if we can restore the angular velocity + SGVec3 av2 = SGQuat::forwardDifferenceVelocity(o0, o1, dt); + if (!equivalent(av, av2)) + return false; + + // Test with the equivalent orientation + o1 = -o1; + av2 = SGQuat::forwardDifferenceVelocity(o0, o1, dt); + if (!equivalent(av, av2)) + return false; + } + return true; +} + template bool MatrixTest(void) @@ -273,17 +302,17 @@ GeodesyTest(void) // uses examples from Williams aviation formulary SGGeoc lax = SGGeoc::fromRadM(-2.066470, 0.592539, 10.0); SGGeoc jfk = SGGeoc::fromRadM(-1.287762, 0.709186, 10.0); - + double distNm = SGGeodesy::distanceRad(lax, jfk) * SG_RAD_TO_NM; std::cout << "distance is " << distNm << std::endl; if (0.5 < fabs(distNm - 2144)) // 2144 nm return false; - + double crsDeg = SGGeodesy::courseRad(lax, jfk) * SG_RADIANS_TO_DEGREES; std::cout << "course is " << crsDeg << std::endl; if (0.5 < fabs(crsDeg - 66)) // 66 degrees return false; - + SGGeoc adv; SGGeodesy::advanceRadM(lax, crsDeg * SG_DEGREES_TO_RADIANS, 100 * SG_NM_TO_METER, adv); std::cout << "lon:" << adv.getLongitudeRad() << ", lat:" << adv.getLatitudeRad() << std::endl; @@ -298,6 +327,8 @@ GeodesyTest(void) int main(void) { + sg_srandom(17); + // Do vector tests if (!Vec3Test()) return EXIT_FAILURE; @@ -309,6 +340,10 @@ main(void) return EXIT_FAILURE; if (!QuatTest()) return EXIT_FAILURE; + if (!QuatDerivativeTest()) + return EXIT_FAILURE; + if (!QuatDerivativeTest()) + return EXIT_FAILURE; // Do matrix tests if (!MatrixTest()) diff --git a/simgear/math/SGQuat.hxx b/simgear/math/SGQuat.hxx index 256ddb28..9449c914 100644 --- a/simgear/math/SGQuat.hxx +++ b/simgear/math/SGQuat.hxx @@ -230,7 +230,7 @@ public: T absv1 = fabs(v(0)); T absv2 = fabs(v(1)); T absv3 = fabs(v(2)); - + SGVec3 axis; if (absv2 < absv1 && absv3 < absv1) { T quot = v(1)/v(0); @@ -279,7 +279,7 @@ public: xRad = 0; else xRad = atan2(num, den); - + T tmp = 2*(x()*z() - w()*y()); if (tmp <= -1) yRad = T(0.5)*SGMisc::pi(); @@ -287,8 +287,8 @@ public: yRad = -T(0.5)*SGMisc::pi(); else yRad = -asin(tmp); - - num = 2*(x()*y() + w()*z()); + + num = 2*(x()*y() + w()*z()); den = sqrQW + sqrQX - sqrQY - sqrQZ; if (fabs(den) <= SGLimits::min() && fabs(num) <= SGLimits::min()) @@ -323,7 +323,7 @@ public: T sAng = sin(angle); if (fabs(sAng) <= SGLimits::min()) axis = SGVec3(1, 0, 0); - else + else axis = (rNrm/sAng)*imag(*this); angle *= 2; } @@ -449,10 +449,46 @@ public: deriv.x() = T(0.5)*( w()*angVel(0) - z()*angVel(1) + y()*angVel(2)); deriv.y() = T(0.5)*( z()*angVel(0) + w()*angVel(1) - x()*angVel(2)); deriv.z() = T(0.5)*(-y()*angVel(0) + x()*angVel(1) + w()*angVel(2)); - + return deriv; } + /// Return the angular velocity w that makes q0 translate to q1 using + /// an explicit euler step with stepsize h. + /// That is, look for an w where + /// q1 = normalize(q0 + h*q0.derivative(w)) + static SGVec3 + forwardDifferenceVelocity(const SGQuat& q0, const SGQuat& q1, const T& h) + { + // Let D_q0*w = q0.derivative(w), D_q0 the above 4x3 matrix. + // Then D_q0^t*D_q0 = 0.25*Id and D_q0*q0 = 0. + // Let lambda be a nonzero normailzation factor, then + // q1 = normalize(q0 + h*q0.derivative(w)) + // can be rewritten + // lambda*q1 = q0 + h*D_q0*w. + // Multiply left by the transpose D_q0^t and reorder gives + // 4*lambda/h*D_q0^t*q1 = w. + // Now compute lambda by substitution of w into the original + // equation + // lambda*q1 = q0 + 4*lambda*D_q0*D_q0^t*q1, + // multiply by q1^t from the left + // lambda* = + 4*lambda* + // and solving for lambda gives + // lambda = /(1 - 4*). + + // The transpose of the derivative matrix + // the 0.5 factor is handled below + // also note that the initializer uses x, y, z, w instead of w, x, y, z + SGQuat d0(q0.w(), q0.z(), -q0.y(), -q0.x()); + SGQuat d1(-q0.z(), q0.w(), q0.x(), -q0.y()); + SGQuat d2(q0.y(), -q0.x(), q0.w(), -q0.z()); + // 2*D_q0^t*q1 + SGVec3 Dq(dot(d0, q1), dot(d1, q1), dot(d2, q1)); + // Like above, but take into account that Dq = 2*D_q0^t*q1 + T lambda = dot(q0, q1)/(T(1) - dot(Dq, Dq)); + return (2*lambda/h)*Dq; + } + private: // Private because it assumes normalized inputs. @@ -478,7 +514,7 @@ private: // Now our assumption of angles <= 90 deg comes in play. // For that reason, we know that cos05ang is not zero. - // It is even more, we can see from the above formula that + // It is even more, we can see from the above formula that // sqrt(0.5) < cos05ang. diff --git a/simgear/props/propertyObject_test.cxx b/simgear/props/propertyObject_test.cxx index b2c458ad..5bb4ae58 100644 --- a/simgear/props/propertyObject_test.cxx +++ b/simgear/props/propertyObject_test.cxx @@ -5,6 +5,9 @@ #include #include +// working around MSVC weirdness with props.hxx and SGMathFwd +#include + #include "propertyObject.hxx" #include diff --git a/simgear/props/props_test.cxx b/simgear/props/props_test.cxx index b3109821..cd54ec50 100644 --- a/simgear/props/props_test.cxx +++ b/simgear/props/props_test.cxx @@ -6,6 +6,10 @@ #include #include + +// working around MSVC weirdness with props.hxx and SGMathFwd +#include + #include "props.hxx" #include "props_io.hxx" diff --git a/simgear/structure/StringTable.cxx b/simgear/structure/StringTable.cxx index 81afc69b..5dba1848 100644 --- a/simgear/structure/StringTable.cxx +++ b/simgear/structure/StringTable.cxx @@ -1,6 +1,6 @@ #include "StringTable.hxx" -#include +#include namespace simgear { @@ -8,8 +8,7 @@ using namespace std; const string* StringTable::insert(const string& str) { - using namespace OpenThreads; - ScopedLock lock(_mutex); + SGGuard lock(_mutex); StringContainer::iterator it = _strings.insert(str).first; return &*it; } diff --git a/simgear/structure/StringTable.hxx b/simgear/structure/StringTable.hxx index 9e5700d3..8f5d010a 100644 --- a/simgear/structure/StringTable.hxx +++ b/simgear/structure/StringTable.hxx @@ -3,7 +3,7 @@ #include -#include +#include #include #include #include @@ -21,7 +21,7 @@ class StringTable { const std::string* insert(const std::string& str); private: - OpenThreads::Mutex _mutex; + SGMutex _mutex; StringContainer _strings; }; } diff --git a/simgear/structure/commands.cxx b/simgear/structure/commands.cxx index fd67ae4f..6b4b11ea 100644 --- a/simgear/structure/commands.cxx +++ b/simgear/structure/commands.cxx @@ -11,13 +11,12 @@ #include #include -#include -#include - #include "commands.hxx" #include #include +#include +#include #include @@ -36,7 +35,7 @@ SGCommandMgr::~SGCommandMgr () // no-op } -OpenThreads::Mutex SGCommandMgr::_instanceMutex; +SGMutex SGCommandMgr::_instanceMutex; SGCommandMgr* SGCommandMgr::instance() @@ -45,7 +44,7 @@ SGCommandMgr::instance() if (mgr.get()) return mgr.get(); - OpenThreads::ScopedLock lock(_instanceMutex); + SGGuard lock(_instanceMutex); if (mgr.get()) return mgr.get(); @@ -85,8 +84,8 @@ SGCommandMgr::execute (const std::string &name, const SGPropertyNode * arg) cons command_t command = getCommand(name); if (command == 0) return false; - - + + try { return (*command)(arg); } catch (sg_exception& e) { diff --git a/simgear/structure/commands.hxx b/simgear/structure/commands.hxx index 3f8a248f..09f1c1bc 100644 --- a/simgear/structure/commands.hxx +++ b/simgear/structure/commands.hxx @@ -17,8 +17,7 @@ #include #include -#include - +#include #include #include @@ -107,7 +106,7 @@ private: typedef std::map command_map; command_map _commands; - static OpenThreads::Mutex _instanceMutex; + static SGMutex _instanceMutex; }; diff --git a/simgear/threads/CMakeLists.txt b/simgear/threads/CMakeLists.txt index 9eb4d540..5258be8f 100644 --- a/simgear/threads/CMakeLists.txt +++ b/simgear/threads/CMakeLists.txt @@ -5,10 +5,5 @@ set(HEADERS SGQueue.hxx SGThread.hxx) - -if (MSVC) - install (FILES ${HEADERS} DESTINATION include/simgear/threads) -else (MSVC) - set(SOURCES SGThread.cxx) - simgear_component(threads threads "${SOURCES}" "${HEADERS}") -endif(MSVC) +set(SOURCES SGThread.cxx) +simgear_component(threads threads "${SOURCES}" "${HEADERS}") diff --git a/simgear/threads/SGQueue.hxx b/simgear/threads/SGQueue.hxx index bcdda1d3..37122c5a 100644 --- a/simgear/threads/SGQueue.hxx +++ b/simgear/threads/SGQueue.hxx @@ -5,9 +5,8 @@ #include #include -#include -#include -#include +#include "SGGuard.hxx" +#include "SGThread.hxx" /** * SGQueue defines an interface for a FIFO. @@ -66,7 +65,7 @@ public: protected: /** - * + * */ std::queue fifo; }; @@ -74,7 +73,7 @@ protected: /** * A simple thread safe queue. All access functions are guarded with a mutex. */ -template +template class SGLockedQueue : public SGQueue { public: @@ -95,7 +94,7 @@ public: * @return bool True if queue is empty, otherwisr false. */ virtual bool empty() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); return this->fifo.empty(); } @@ -105,7 +104,7 @@ public: * @param T object to add. */ virtual void push( const T& item ) { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); this->fifo.push( item ); } @@ -115,7 +114,7 @@ public: * @return T next available object. */ virtual T front() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); assert( ! this->fifo.empty() ); T item = this->fifo.front(); return item; @@ -127,7 +126,7 @@ public: * @return T next available object. */ virtual T pop() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); //if (fifo.empty()) throw NoSuchElementException(); assert( ! this->fifo.empty() ); // if (fifo.empty()) @@ -146,7 +145,7 @@ public: * @return size_t size of queue. */ virtual size_t size() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); return this->fifo.size(); } @@ -155,7 +154,7 @@ private: /** * Mutex to serialise access. */ - SGLOCK mutex; + SGMutex mutex; private: // Prevent copying. @@ -182,10 +181,10 @@ public: ~SGBlockingQueue() {} /** - * + * */ virtual bool empty() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); return this->fifo.empty(); } @@ -195,7 +194,7 @@ public: * @param T object to add. */ virtual void push( const T& item ) { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); this->fifo.push( item ); not_empty.signal(); } @@ -207,7 +206,7 @@ public: * @return T next available object. */ virtual T front() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); assert(this->fifo.empty() != true); //if (fifo.empty()) throw ?? @@ -223,10 +222,10 @@ public: * @return T next available object. */ virtual T pop() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); while (this->fifo.empty()) - not_empty.wait(&mutex); + not_empty.wait(mutex); assert(this->fifo.empty() != true); //if (fifo.empty()) throw ?? @@ -242,7 +241,7 @@ public: * @return size_t size of queue. */ virtual size_t size() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); return this->fifo.size(); } @@ -251,12 +250,12 @@ private: /** * Mutex to serialise access. */ - OpenThreads::Mutex mutex; + SGMutex mutex; /** * Condition to signal when queue not empty. */ - OpenThreads::Condition not_empty; + SGWaitCondition not_empty; private: // Prevent copying. @@ -284,18 +283,18 @@ public: ~SGBlockingDeque() {} /** - * + * */ virtual void clear() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); this->queue.clear(); } - + /** - * + * */ virtual bool empty() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); return this->queue.empty(); } @@ -305,7 +304,7 @@ public: * @param T object to add. */ virtual void push_front( const T& item ) { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); this->queue.push_front( item ); not_empty.signal(); } @@ -316,7 +315,7 @@ public: * @param T object to add. */ virtual void push_back( const T& item ) { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); this->queue.push_back( item ); not_empty.signal(); } @@ -328,7 +327,7 @@ public: * @return T next available object. */ virtual T front() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); assert(this->queue.empty() != true); //if (queue.empty()) throw ?? @@ -344,10 +343,10 @@ public: * @return T next available object. */ virtual T pop_front() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); while (this->queue.empty()) - not_empty.wait(&mutex); + not_empty.wait(mutex); assert(this->queue.empty() != true); //if (queue.empty()) throw ?? @@ -364,10 +363,10 @@ public: * @return T next available object. */ virtual T pop_back() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); while (this->queue.empty()) - not_empty.wait(&mutex); + not_empty.wait(mutex); assert(this->queue.empty() != true); //if (queue.empty()) throw ?? @@ -383,7 +382,7 @@ public: * @return size_t size of queue. */ virtual size_t size() { - OpenThreads::ScopedLock g(mutex); + SGGuard g(mutex); return this->queue.size(); } @@ -392,12 +391,12 @@ private: /** * Mutex to serialise access. */ - OpenThreads::Mutex mutex; + SGMutex mutex; /** * Condition to signal when queue not empty. */ - OpenThreads::Condition not_empty; + SGWaitCondition not_empty; private: // Prevent copying. diff --git a/simgear/threads/SGThread.cxx b/simgear/threads/SGThread.cxx index c7a841b0..8e5c3a36 100644 --- a/simgear/threads/SGThread.cxx +++ b/simgear/threads/SGThread.cxx @@ -1,107 +1,410 @@ -#include +// SGThread - Simple pthread class wrappers. +// +// Written by Bernie Bright, started April 2001. +// +// Copyright (C) 2001 Bernard Bright - bbright@bigpond.net.au +// Copyright (C) 2011 Mathias Froehlich +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// -#if defined(_MSC_VER) || defined(__MINGW32__) -# include -#else -# if defined ( sgi ) && !defined( __GNUC__ ) - // This works around a bug triggered when using MipsPro 7.4.1 - // and (at least) IRIX 6.5.20 -# include -# endif -# include -#endif -#if _MSC_VER >= 1300 -# include +#ifdef HAVE_CONFIG_H +# include #endif +#include + #include "SGThread.hxx" -void* -start_handler( void* arg ) +#ifdef _WIN32 + +///////////////////////////////////////////////////////////////////////////// +/// win32 threads +///////////////////////////////////////////////////////////////////////////// + +#include +#include + +struct SGThread::PrivateData { + PrivateData() : + _handle(INVALID_HANDLE_VALUE) + { + } + ~PrivateData() + { + if (_handle == INVALID_HANDLE_VALUE) + return; + CloseHandle(_handle); + _handle = INVALID_HANDLE_VALUE; + } + + static DWORD WINAPI start_routine(LPVOID data) + { + SGThread* thread = reinterpret_cast(data); + thread->run(); + return 0; + } + + bool start(SGThread& thread) + { + if (_handle != INVALID_HANDLE_VALUE) + return false; + _handle = CreateThread(0, 0, start_routine, &thread, 0, 0); + if (_handle == INVALID_HANDLE_VALUE) + return false; + return true; + } + + void join() + { + if (_handle == INVALID_HANDLE_VALUE) + return; + DWORD ret = WaitForSingleObject(_handle, INFINITE); + if (ret != WAIT_OBJECT_0) + return; + CloseHandle(_handle); + _handle = INVALID_HANDLE_VALUE; + } + + HANDLE _handle; +}; + +struct SGMutex::PrivateData { + PrivateData() + { + InitializeCriticalSection((LPCRITICAL_SECTION)&_criticalSection); + } + + ~PrivateData() + { + DeleteCriticalSection((LPCRITICAL_SECTION)&_criticalSection); + } + + void lock(void) + { + EnterCriticalSection((LPCRITICAL_SECTION)&_criticalSection); + } + + void unlock(void) + { + LeaveCriticalSection((LPCRITICAL_SECTION)&_criticalSection); + } + + CRITICAL_SECTION _criticalSection; +}; + +struct SGWaitCondition::PrivateData { + ~PrivateData(void) + { + // The waiters list should be empty anyway + _mutex.lock(); + while (!_pool.empty()) { + CloseHandle(_pool.front()); + _pool.pop_front(); + } + _mutex.unlock(); + } + + void signal(void) + { + _mutex.lock(); + if (!_waiters.empty()) + SetEvent(_waiters.back()); + _mutex.unlock(); + } + + void broadcast(void) + { + _mutex.lock(); + for (std::list::iterator i = _waiters.begin(); i != _waiters.end(); ++i) + SetEvent(*i); + _mutex.unlock(); + } + + bool wait(SGMutex::PrivateData& externalMutex, DWORD msec) + { + _mutex.lock(); + if (_pool.empty()) + _waiters.push_front(CreateEvent(NULL, FALSE, FALSE, NULL)); + else + _waiters.splice(_waiters.begin(), _pool, _pool.begin()); + std::list::iterator i = _waiters.begin(); + _mutex.unlock(); + + externalMutex.unlock(); + + DWORD result = WaitForSingleObject(*i, msec); + + externalMutex.lock(); + + _mutex.lock(); + if (result != WAIT_OBJECT_0) + result = WaitForSingleObject(*i, 0); + _pool.splice(_pool.begin(), _waiters, i); + _mutex.unlock(); + + return result == WAIT_OBJECT_0; + } + + void wait(SGMutex::PrivateData& externalMutex) + { + wait(externalMutex, INFINITE); + } + + // Protect the list of waiters + SGMutex::PrivateData _mutex; + + std::list _waiters; + std::list _pool; +}; + +#else +///////////////////////////////////////////////////////////////////////////// +/// posix threads +///////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +struct SGThread::PrivateData { + PrivateData() : + _started(false) + { + } + ~PrivateData() + { + // If we are still having a started thread and nobody waited, + // now detach ... + if (!_started) + return; + pthread_detach(_thread); + } + + static void *start_routine(void* data) + { + SGThread* thread = reinterpret_cast(data); + thread->run(); + return 0; + } + + bool start(SGThread& thread) + { + if (_started) + return false; + + int ret = pthread_create(&_thread, 0, start_routine, &thread); + if (0 != ret) + return false; + + _started = true; + return true; + } + + void join() + { + if (!_started) + return; + + pthread_join(_thread, 0); + _started = false; + } + + pthread_t _thread; + bool _started; +}; + +struct SGMutex::PrivateData { + PrivateData() + { + int err = pthread_mutex_init(&_mutex, 0); + assert(err == 0); + (void)err; + } + + ~PrivateData() + { + int err = pthread_mutex_destroy(&_mutex); + assert(err == 0); + (void)err; + } + + void lock(void) + { + int err = pthread_mutex_lock(&_mutex); + assert(err == 0); + (void)err; + } + + void unlock(void) + { + int err = pthread_mutex_unlock(&_mutex); + assert(err == 0); + (void)err; + } + + pthread_mutex_t _mutex; +}; + +struct SGWaitCondition::PrivateData { + PrivateData(void) + { + int err = pthread_cond_init(&_condition, NULL); + assert(err == 0); + (void)err; + } + ~PrivateData(void) + { + int err = pthread_cond_destroy(&_condition); + assert(err == 0); + (void)err; + } + + void signal(void) + { + int err = pthread_cond_signal(&_condition); + assert(err == 0); + (void)err; + } + + void broadcast(void) + { + int err = pthread_cond_broadcast(&_condition); + assert(err == 0); + (void)err; + } + + void wait(SGMutex::PrivateData& mutex) + { + int err = pthread_cond_wait(&_condition, &mutex._mutex); + assert(err == 0); + (void)err; + } + + bool wait(SGMutex::PrivateData& mutex, unsigned msec) + { + struct timespec ts; +#ifdef HAVE_CLOCK_GETTIME + if (0 != clock_gettime(CLOCK_REALTIME, &ts)) + return false; +#else + struct timeval tv; + if (0 != gettimeofday(&tv, NULL)) + return false; + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; +#endif + + ts.tv_nsec += 1000000*(msec % 1000); + if (1000000000 <= ts.tv_nsec) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + ts.tv_sec += msec / 1000; + + int evalue = pthread_cond_timedwait(&_condition, &mutex._mutex, &ts); + if (evalue == 0) + return true; + + assert(evalue == ETIMEDOUT); + return false; + } + + pthread_cond_t _condition; +}; + +#endif + +SGThread::SGThread() : + _privateData(new PrivateData) { - SGThread* thr = static_cast(arg); - thr->run(); - return 0; +} + +SGThread::~SGThread() +{ + delete _privateData; + _privateData = 0; +} + +bool +SGThread::start() +{ + return _privateData->start(*this); } void -SGThread::set_cancel( cancel_t mode ) +SGThread::join() { - switch (mode) - { - case CANCEL_DISABLE: - pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, 0 ); - break; - case CANCEL_DEFERRED: - pthread_setcanceltype( PTHREAD_CANCEL_DEFERRED, 0 ); - pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, 0 ); - break; - case CANCEL_IMMEDIATE: - pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, 0 ); - pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, 0 ); - break; - default: - break; - } + _privateData->join(); +} + +SGMutex::SGMutex() : + _privateData(new PrivateData) +{ +} + +SGMutex::~SGMutex() +{ + delete _privateData; + _privateData = 0; +} + +void +SGMutex::lock() +{ + _privateData->lock(); +} + +void +SGMutex::unlock() +{ + _privateData->unlock(); +} + +SGWaitCondition::SGWaitCondition() : + _privateData(new PrivateData) +{ +} + +SGWaitCondition::~SGWaitCondition() +{ + delete _privateData; + _privateData = 0; +} + +void +SGWaitCondition::wait(SGMutex& mutex) +{ + _privateData->wait(*mutex._privateData); } bool -SGMutex::trylock() +SGWaitCondition::wait(SGMutex& mutex, unsigned msec) { - int status = pthread_mutex_lock( &mutex ); - if (status == EBUSY) - { - return false; - } - assert( status == 0 ); - return true; + return _privateData->wait(*mutex._privateData, msec); } -#if defined(_MSC_VER) || defined(__MINGW32__) -int gettimeofday(struct timeval* tp, void* tzp) { - LARGE_INTEGER t; - - if(QueryPerformanceCounter(&t)) { - /* hardware supports a performance counter */ - LARGE_INTEGER f; - QueryPerformanceFrequency(&f); - tp->tv_sec = t.QuadPart/f.QuadPart; - tp->tv_usec = ((float)t.QuadPart/f.QuadPart*1000*1000) - - (tp->tv_sec*1000*1000); - } else { - /* hardware doesn't support a performance counter, so get the - time in a more traditional way. */ - DWORD t; - t = timeGetTime(); - tp->tv_sec = t / 1000; - tp->tv_usec = t % 1000; - } - - /* 0 indicates that the call succeeded. */ - return 0; -} -#endif - -bool -SGPthreadCond::wait( SGMutex& mutex, unsigned long ms ) +void +SGWaitCondition::signal() { - struct timeval now; - ::gettimeofday( &now, 0 ); - - // Wait time is now + ms milliseconds - unsigned int sec = ms / 1000; - unsigned int nsec = (ms % 1000) * 1000; - struct timespec abstime; - abstime.tv_sec = now.tv_sec + sec; - abstime.tv_nsec = now.tv_usec*1000 + nsec; - - int status = pthread_cond_timedwait( &cond, &mutex.mutex, &abstime ); - if (status == ETIMEDOUT) - { - return false; - } - - assert( status == 0 ); - return true; + _privateData->signal(); } +void +SGWaitCondition::broadcast() +{ + _privateData->broadcast(); +} diff --git a/simgear/threads/SGThread.hxx b/simgear/threads/SGThread.hxx index 477ca458..7f3a651a 100644 --- a/simgear/threads/SGThread.hxx +++ b/simgear/threads/SGThread.hxx @@ -3,6 +3,7 @@ // Written by Bernie Bright, started April 2001. // // Copyright (C) 2001 Bernard Bright - bbright@bigpond.net.au +// Copyright (C) 2011 Mathias Froehlich // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -18,41 +19,18 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // -// $Id$ #ifndef SGTHREAD_HXX_INCLUDED #define SGTHREAD_HXX_INCLUDED 1 #include -#include -#include -#include - -class SGThread; - -extern "C" { - void* start_handler( void* ); -}; - /** * Encapsulate generic threading methods. * Users derive a class from SGThread and implement the run() member function. */ -class SGThread -{ +class SGThread { public: - /** - * SGThread cancelation modes. - */ - enum cancel_t - { - CANCEL_DISABLE = 0, - CANCEL_DEFERRED, - CANCEL_IMMEDIATE - }; -public: - /** * Create a new thread object. * When a SGThread object is created it does not begin execution @@ -62,18 +40,9 @@ public: /** * Start the underlying thread of execution. - * @param cpu An optional parameter to specify on which CPU to run this - * thread (only supported on IRIX at this time). * @return Pthread error code if execution fails, otherwise returns 0. */ - int start( unsigned cpu = 0 ); - - /** - * Sends a cancellation request to the underlying thread. The target - * thread will either ignore the request, honor it immediately or defer - * it until it reaches a cancellation point. - */ - void cancel(); + bool start(); /** * Suspends the exection of the calling thread until this thread @@ -89,82 +58,31 @@ protected: */ virtual ~SGThread(); - /** - * Set the threads cancellation mode. - * @param mode The required cancellation mode. - */ - void set_cancel( cancel_t mode ); - /** * All threads execute by deriving the run() method of SGThread. * If this function terminates then the thread also terminates. */ virtual void run() = 0; -private: - - /** - * Pthread thread identifier. - */ - pthread_t tid; - - friend void* start_handler( void* ); - private: // Disable copying. - SGThread( const SGThread& ); - SGThread& operator=( const SGThread& ); + SGThread(const SGThread&); + SGThread& operator=(const SGThread&); + + struct PrivateData; + PrivateData* _privateData; + + friend struct PrivateData; }; -inline -SGThread::SGThread() -{ -} - -inline -SGThread::~SGThread() -{ -} - -inline int -SGThread::start( unsigned cpu ) -{ - int status = pthread_create( &tid, 0, start_handler, this ); - assert( status == 0 ); - (void)status; -#if defined( sgi ) - if ( !status && !cpu ) - pthread_setrunon_np( cpu ); -#endif - return status; -} - -inline void -SGThread::join() -{ - int status = pthread_join( tid, 0 ); - assert( status == 0 ); - (void)status; -} - -inline void -SGThread::cancel() -{ - int status = pthread_cancel( tid ); - assert( status == 0 ); - (void)status; -} +class SGWaitCondition; /** * A mutex is used to protect a section of code such that at any time * only a single thread can execute the code. */ -class SGMutex -{ - friend class SGPthreadCond; - +class SGMutex { public: - /** * Create a new mutex. * Under Linux this is a 'fast' mutex. @@ -186,86 +104,46 @@ public: * mutex is already locked and owned by the calling thread, the calling * thread is suspended until the mutex is unlocked, effectively causing * the calling thread to deadlock. - * - * @see SGMutex::trylock */ void lock(); - /** - * Try to lock the mutex for the current thread. Behaves like lock except - * that it doesn't block the calling thread. - * @return true if mutex was successfully locked, otherwise false. - * @see SGMutex::lock - */ - bool trylock(); - /** * Unlock this mutex. * It is assumed that the mutex is locked and owned by the calling thread. */ void unlock(); -protected: +private: + struct PrivateData; + PrivateData* _privateData; - /** - * Pthread mutex. - */ - pthread_mutex_t mutex; + friend class SGWaitCondition; }; -inline SGMutex::SGMutex() -{ - int status = pthread_mutex_init( &mutex, 0 ); - assert( status == 0 ); - (void)status; -} - -inline SGMutex::~SGMutex() -{ - int status = pthread_mutex_destroy( &mutex ); - assert( status == 0 ); - (void)status; -} - -inline void SGMutex::lock() -{ - int status = pthread_mutex_lock( &mutex ); - assert( status == 0 ); - (void)status; -} - -inline void SGMutex::unlock() -{ - int status = pthread_mutex_unlock( &mutex ); - assert( status == 0 ); - (void)status; -} - /** - * A condition variable is a synchronization device that allows threads to + * A condition variable is a synchronization device that allows threads to * suspend execution until some predicate on shared data is satisfied. * A condition variable is always associated with a mutex to avoid race - * conditions. + * conditions. */ -class SGPthreadCond -{ +class SGWaitCondition { public: /** * Create a new condition variable. */ - SGPthreadCond(); + SGWaitCondition(); /** * Destroy the condition object. */ - ~SGPthreadCond(); + ~SGWaitCondition(); /** * Wait for this condition variable to be signaled. * * @param SGMutex& reference to a locked mutex. */ - void wait( SGMutex& ); + void wait(SGMutex&); /** * Wait for this condition variable to be signaled for at most @@ -274,9 +152,9 @@ public: * @param mutex reference to a locked mutex. * @param ms milliseconds to wait for a signal. * - * @return + * @return */ - bool wait( SGMutex& mutex, unsigned long ms ); + bool wait(SGMutex& mutex, unsigned msec); /** * Wake one thread waiting on this condition variable. @@ -294,50 +172,11 @@ public: private: // Disable copying. - SGPthreadCond(const SGPthreadCond& ); - SGPthreadCond& operator=(const SGPthreadCond& ); + SGWaitCondition(const SGWaitCondition&); + SGWaitCondition& operator=(const SGWaitCondition&); -private: - - /** - * The Pthread conditon variable. - */ - pthread_cond_t cond; + struct PrivateData; + PrivateData* _privateData; }; -inline SGPthreadCond::SGPthreadCond() -{ - int status = pthread_cond_init( &cond, 0 ); - assert( status == 0 ); - (void)status; -} - -inline SGPthreadCond::~SGPthreadCond() -{ - int status = pthread_cond_destroy( &cond ); - assert( status == 0 ); - (void)status; -} - -inline void SGPthreadCond::signal() -{ - int status = pthread_cond_signal( &cond ); - assert( status == 0 ); - (void)status; -} - -inline void SGPthreadCond::broadcast() -{ - int status = pthread_cond_broadcast( &cond ); - assert( status == 0 ); - (void)status; -} - -inline void SGPthreadCond::wait( SGMutex& mutex ) -{ - int status = pthread_cond_wait( &cond, &mutex.mutex ); - assert( status == 0 ); - (void)status; -} - #endif /* SGTHREAD_HXX_INCLUDED */