Boris -- Thanks for the history and input.
Robert -- Per your instructions, posted here so that others can try it out.
Attached is my much simpler rewrite of OpenThreads::Mutex, using a
CRITICAL_SECTION, which has worked well on my day since before I hired on.
However, it depends on the underlying OS for scheduling, while it looks like
the current version of Mutex is a little more pro-active about which thread
gets scheduled.
My changes are wrapped with the CPP macro USE_CRITICAL_SECTION, which I've
defined in Win32MutexPrivateData.h. If you comment out the definition, you
get the current version, so you can do a heads to heads comparison.
It'll be interesting to see if this makes a difference.
-Paul
//
// OpenThread library, Copyright (C) 2002 - 2003 The Open Thread Group
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser 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
//
//
// Win32Mutex.c++ - C++ Mutex class .
// The idea for it is borrowed from SGI STL
// It looks like it's hard to use win32 CRITICALL_SECTIONS withour introducing
race
// conditions on InitializeCriticalSection() . So we use spin mutex here.
// ~~~~~~~~~~~~~~~~
//
#include <OpenThreads/Mutex>
#include "Win32MutexPrivateData.h"
using namespace OpenThreads;
Win32MutexPrivateData::Win32MutexPrivateData()
{
#ifdef USE_CRITICAL_SECTION
InitializeCriticalSection( &_cs );
#else
mutex = 0;
#endif
}
Win32MutexPrivateData::~Win32MutexPrivateData()
{
#ifdef USE_CRITICAL_SECTION
DeleteCriticalSection( &_cs );
#endif
}
#ifndef USE_CRITICAL_SECTION
template <int instance>
struct WIN32MutexSpin {
enum { __low_max = 30, __high_max = 1000 };
// Low if we suspect uniprocessor, high for multiprocessor.
static unsigned __max;
static unsigned __last;
};
template <int instance>
unsigned WIN32MutexSpin <instance>::__max = WIN32MutexSpin
<instance>::__low_max;
template <int instance>
unsigned WIN32MutexSpin <instance>::__last = 0;
static void _S_nsec_sleep(int __log_nsec) {
if (__log_nsec <= 20) {
SwitchToThread(); //Sleep(0); // adegli replaced it Sleep by
SwitchToThread
} else {
Sleep(1 << (__log_nsec - 20));
}
}
#if defined(_MSC_VER) && _MSC_VER <= 1300
template WIN32MutexSpin <0>;
#endif
#endif // USE_CRITICAL_SECTION
//----------------------------------------------------------------------------
//
// Decription: Constructor
//
// Use: public.
//
Mutex::Mutex() {
Win32MutexPrivateData *pd = new Win32MutexPrivateData();
_prvData = static_cast<void *>(pd);
}
//----------------------------------------------------------------------------
//
// Decription: Destructor
//
// Use: public.
//
Mutex::~Mutex() {
unlock();
delete static_cast<Win32MutexPrivateData*>(_prvData);
}
//----------------------------------------------------------------------------
//
// Decription: lock the mutex
//
// Use: public.
//
int Mutex::lock() {
Win32MutexPrivateData *pd =
static_cast<Win32MutexPrivateData*>(_prvData);
#ifdef USE_CRITICAL_SECTION
// Block until we can take this lock.
EnterCriticalSection( &(pd->_cs) );
return 0;
#else
volatile unsigned long* lock = &pd->mutex;
// InterlockedExchange returns old value
// if old_value == 0 mutex wasn't locked , now it is
if( !InterlockedExchange((long*)lock, 1L)) {
return 0;
}
unsigned my_spin_max = WIN32MutexSpin<0>::__max;
unsigned my_last_spins = WIN32MutexSpin<0>::__last;
volatile unsigned junk = 17;
unsigned i;
for (i = 0; i < my_spin_max; i++) {
if (i < my_last_spins/2 || *lock) {
junk *= junk; junk *= junk;
junk *= junk; junk *= junk;
continue;
}
if (!InterlockedExchange((long*)lock, 1L)) {
// got it!
// Spinning worked. Thus we're probably not being scheduled
// against the other process with which we were contending.
// Thus it makes sense to spin longer the next time.
WIN32MutexSpin<0>::__last = i;
WIN32MutexSpin<0>::__max = WIN32MutexSpin<0>::__high_max;
return 0;
}
}
// We are probably being scheduled against the other process. Sleep.
WIN32MutexSpin<0>::__max = WIN32MutexSpin<0>::__low_max;
for (i = 0 ;; ++i) {
int __log_nsec = i + 6;
if (__log_nsec > 27) __log_nsec = 27;
if (!InterlockedExchange((long*)lock, 1L)) {
return 0;
}
_S_nsec_sleep(__log_nsec);
}
return -1;
#endif // USE_CRITICAL_SECTION
}
//----------------------------------------------------------------------------
//
// Decription: unlock the mutex
//
// Use: public.
//
int Mutex::unlock() {
Win32MutexPrivateData *pd =
static_cast<Win32MutexPrivateData*>(_prvData);
#ifdef USE_CRITICAL_SECTION
// Release this lock. CRITICAL_SECTION is nested, thus
// unlock() calls must be paired with lock() calls.
LeaveCriticalSection( &(pd->_cs) );
return 0;
#else
volatile unsigned long* lock = &pd->mutex;
*lock = 0;
// This is not sufficient on many multiprocessors, since
// writes to protected variables and the lock may be reordered.
return 0;
#endif // USE_CRITICAL_SECTION
}
//----------------------------------------------------------------------------
//
// Decription: test if the mutex may be locked
//
// Use: public.
//
int Mutex::trylock() {
Win32MutexPrivateData *pd =
static_cast<Win32MutexPrivateData*>(_prvData);
#ifdef USE_CRITICAL_SECTION
// Take the lock if we can; regardless don't block.
// 'result' is FALSE if we took the lock or already held
// it amd TRUE if another thread already owns the lock.
BOOL result = TryEnterCriticalSection( &(pd->_cs) );
return( (result==TRUE) ? 0 : 1 );
#else
volatile unsigned long* lock = &pd->mutex;
if( !InterlockedExchange((long*)lock, 1L)) {
return 1; // TRUE
}
return 0; // FALSE
#endif // USE_CRITICAL_SECTION
}
//
// OpenThread library, Copyright (C) 2002 - 2003 The Open Thread Group
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser 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
//
//
// Win32MutexPrivateData.h - Private data structure for Mutex
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
#ifndef _Win32MUTEXPRIVATEDATA_H_
#define _Win32MUTEXPRIVATEDATA_H_
#ifndef _WINDOWS_
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0400 // was missing : adegli
#include <windows.h>
#endif
namespace OpenThreads {
class Win32MutexPrivateData {
friend class Mutex;
friend class Condition;
private:
Win32MutexPrivateData();
~Win32MutexPrivateData();
#define USE_CRITICAL_SECTION
#ifdef USE_CRITICAL_SECTION
CRITICAL_SECTION _cs;
#else
volatile unsigned long mutex;
#endif
};
}
#endif // !_Win32MUTEXPRIVATEDATA_H_
_______________________________________________
osg-users mailing list
[email protected]
http://openscenegraph.net/mailman/listinfo/osg-users
http://www.openscenegraph.org/