From 700d4bbcbf59c4447becbab21a6aa7204a8da2f4 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Wed, 6 Nov 2024 16:01:36 -0800 Subject: I believe MyriadFs is now threadsafe. It could probably be more optimized, but it does work. --- src/experimental/debugmutex.cpp | 78 ++++++++++++++++++++++++++++++ src/experimental/debugmutex.h | 102 ++++++++++++++++++++++++++++++++++++++++ src/stable/mutex.h | 8 ++-- src/unstable/myriadfs.cpp | 11 +++++ src/unstable/myriadfs.h | 12 +++-- 5 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 src/experimental/debugmutex.cpp create mode 100644 src/experimental/debugmutex.h (limited to 'src') diff --git a/src/experimental/debugmutex.cpp b/src/experimental/debugmutex.cpp new file mode 100644 index 0000000..2b61ae2 --- /dev/null +++ b/src/experimental/debugmutex.cpp @@ -0,0 +1,78 @@ +#include "bu/debugmutex.h" + +#include "bu/exceptionbase.h" + +Bu::DebugMutex::DebugMutex() +{ +} + +Bu::DebugMutex::~DebugMutex() +{ +} + +int Bu::DebugMutex::lock() +{ + pthread_t self = pthread_self(); + mState.lock(); + bool bFound = false; + for( ThreadList::iterator i = lThreads.begin(); i; i++ ) + { + if( (*i) == self ) + { + bFound = true; + if( (*i).bLocked == true ) + { + throw Bu::ExceptionBase( Bu::String("Double lock in thread: %1").arg( (*i).sName ).end().getStr() ); + + } + else + { + (*i).bLocked = true; + } + break; + } + } + if( bFound == false ) + { + lThreads.append( ThreadInfo( true ) ); + } + mState.unlock(); + return Bu::Mutex::lock(); +} + +int Bu::DebugMutex::unlock() +{ + pthread_t self = pthread_self(); + mState.lock(); + bool bFound = false; + for( ThreadList::iterator i = lThreads.begin(); i; i++ ) + { + if( (*i) == self ) + { + bFound = true; + if( (*i).bLocked == false ) + { + throw Bu::ExceptionBase( Bu::String("Unlock in thread that did not lock: %1").arg( (*i).sName ).end().getStr() ); + + } + else + { + (*i).bLocked = false; + } + break; + } + } + if( bFound == false ) + { + ThreadInfo info( false ); + throw Bu::ExceptionBase( Bu::String("Unlock in thread that never locked mutex: %1").arg( info.sName ).end().getStr() ); + } + mState.unlock(); + return Bu::Mutex::unlock(); +} + +int Bu::DebugMutex::trylock() +{ + return Bu::Mutex::trylock(); +} + diff --git a/src/experimental/debugmutex.h b/src/experimental/debugmutex.h new file mode 100644 index 0000000..ca8ef9f --- /dev/null +++ b/src/experimental/debugmutex.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2007-2023 Xagasoft, All rights reserved. + * + * This file is part of the libbu++ library and is released under the + * terms of the license contained in the file LICENSE. + */ + +#ifndef BU_DEBUG_MUTEX_H +#define BU_DEBUG_MUTEX_H + +#include "bu/mutex.h" +#include "bu/list.h" +#include "bu/string.h" + +namespace Bu +{ + /** + * Simple mutex wrapper. Currently this doesn't do anything extra for you + * except keep all of the functionality together in an OO sorta' way and + * keep you from having to worry about cleaning up your mutexes properly, + * or initing them. + *@ingroup Threading + */ + class DebugMutex : public Mutex + { + public: + /** + * Create an unlocked mutex. + */ + DebugMutex(); + + /** + * Destroy a mutex. This can only be done when a mutex is unlocked. + * Failure to unlock before destroying a mutex object could cause it to + * wait for the mutex to unlock, the odds of which are usually farily + * low at deconstruction time. + */ + virtual ~DebugMutex(); + + /** + * Lock the mutex. This causes all future calls to lock on this + * instance of mutex to block until the first thread that called mutex + * unlocks it. At that point the next thread that called lock will get + * a chance to go to work. Because of the nature of a mutex lock it is + * a very bad idea to do any kind of serious or rather time consuming + * computation within a locked section. This can cause thread-deadlock + * and your program may hang. + */ + virtual int lock(); + + /** + * Unlock the mutex. This allows the next thread that asked for a lock + * to lock the mutex and continue with execution. + */ + virtual int unlock(); + + /** + * Try to lock the mutex. This is the option to go with if you cannot + * avoid putting lengthy operations within a locked section. trylock + * will attempt to lock the mutex, if the mutex is already locked this + * function returns immediately with an error code. + */ + virtual int trylock(); + + private: + Bu::Mutex mState; + + class ThreadInfo + { + public: + ThreadInfo( bool bLocked=false ) : + idThread( pthread_self() ), + bLocked( bLocked ) + { + char buf[64]; + if( pthread_getname_np( idThread, buf, 64 ) == 0 ) + sName = buf; + } + ~ThreadInfo() {} + + bool operator==( const ThreadInfo &rhs ) + { + return pthread_equal( idThread, rhs.idThread ); + } + + bool operator==( const pthread_t &rhs ) + { + return pthread_equal( idThread, rhs ); + } + + pthread_t idThread; + Bu::String sName; + bool bLocked; + }; + typedef Bu::List ThreadList; + ThreadList lThreads; + }; +} + +#endif + + diff --git a/src/stable/mutex.h b/src/stable/mutex.h index d9e8910..8034974 100644 --- a/src/stable/mutex.h +++ b/src/stable/mutex.h @@ -33,7 +33,7 @@ namespace Bu * wait for the mutex to unlock, the odds of which are usually farily * low at deconstruction time. */ - ~Mutex(); + virtual ~Mutex(); /** * Lock the mutex. This causes all future calls to lock on this @@ -44,13 +44,13 @@ namespace Bu * computation within a locked section. This can cause thread-deadlock * and your program may hang. */ - int lock(); + virtual int lock(); /** * Unlock the mutex. This allows the next thread that asked for a lock * to lock the mutex and continue with execution. */ - int unlock(); + virtual int unlock(); /** * Try to lock the mutex. This is the option to go with if you cannot @@ -58,7 +58,7 @@ namespace Bu * will attempt to lock the mutex, if the mutex is already locked this * function returns immediately with an error code. */ - int trylock(); + virtual int trylock(); protected: pthread_mutex_t mutex; /**< The internal mutex reference. */ diff --git a/src/unstable/myriadfs.cpp b/src/unstable/myriadfs.cpp index a2386c2..f748a53 100644 --- a/src/unstable/myriadfs.cpp +++ b/src/unstable/myriadfs.cpp @@ -108,6 +108,7 @@ Bu::MyriadFs::~MyriadFs() void Bu::MyriadFs::stat( const Bu::String &sPath, Bu::MyriadFs::Stat &rBuf ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent; int32_t iNode = lookupInode( sPath, iParent ); Bu::MyriadStream is = mStore.open( 2, Bu::Myriad::Read ); @@ -117,6 +118,7 @@ void Bu::MyriadFs::stat( const Bu::String &sPath, Bu::MyriadFs::Stat &rBuf ) Bu::MyriadStream Bu::MyriadFs::open( const Bu::String &sPath, int iMode, uint16_t uPerms ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; int32_t iNode; try @@ -165,6 +167,7 @@ void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms, void Bu::MyriadFs::create( const Bu::String &sPath, uint16_t iPerms, uint32_t uSpecial ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; // int32_t iNode; try @@ -201,6 +204,7 @@ void Bu::MyriadFs::mkDir( const Bu::String &sPath, uint16_t iPerms ) void Bu::MyriadFs::mkSymLink( const Bu::String &sTarget, const Bu::String &sPath ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; int32_t iNode; try @@ -233,6 +237,7 @@ void Bu::MyriadFs::mkSymLink( const Bu::String &sTarget, void Bu::MyriadFs::mkHardLink( const Bu::String &sTarget, const Bu::String &sPath ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; int32_t iNode; @@ -268,6 +273,7 @@ void Bu::MyriadFs::mkHardLink( const Bu::String &sTarget, Bu::String Bu::MyriadFs::readSymLink( const Bu::String &sPath ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; int32_t iNode; iNode = lookupInode( sPath, iParent ); @@ -280,6 +286,7 @@ Bu::String Bu::MyriadFs::readSymLink( const Bu::String &sPath ) Bu::MyriadFs::Dir Bu::MyriadFs::readDir( const Bu::String &sPath ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; int32_t iNode = lookupInode( sPath, iParent ); return readDir( iNode ); @@ -288,6 +295,7 @@ Bu::MyriadFs::Dir Bu::MyriadFs::readDir( const Bu::String &sPath ) void Bu::MyriadFs::setTimes( const Bu::String &sPath, int64_t iATime, int64_t iMTime ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; int32_t iNode; @@ -298,6 +306,7 @@ void Bu::MyriadFs::setTimes( const Bu::String &sPath, int64_t iATime, void Bu::MyriadFs::unlink( const Bu::String &sPath ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; // int32_t iNode; @@ -353,6 +362,7 @@ void Bu::MyriadFs::unlink( const Bu::String &sPath ) void Bu::MyriadFs::setFileSize( const Bu::String &sPath, int32_t iSize ) { + Bu::MutexLocker lLock( mAccess ); int32_t iParent = -1; int32_t iNode; iNode = lookupInode( sPath, iParent ); @@ -362,6 +372,7 @@ void Bu::MyriadFs::setFileSize( const Bu::String &sPath, int32_t iSize ) void Bu::MyriadFs::rename( const Bu::String &sFrom, const Bu::String &sTo ) { + Bu::MutexLocker lLock( mAccess ); mkHardLink( sFrom, sTo ); unlink( sFrom ); } diff --git a/src/unstable/myriadfs.h b/src/unstable/myriadfs.h index 4e1749e..e3008bc 100644 --- a/src/unstable/myriadfs.h +++ b/src/unstable/myriadfs.h @@ -11,7 +11,7 @@ #include #include "bu/myriad.h" -#include "bu/mutex.h" +#include "bu/debugmutex.h" namespace Bu { @@ -172,7 +172,13 @@ namespace Bu typedef Bu::Hash NodeIndex; private: + /** + * Lookup inode. + */ int32_t lookupInode( const Bu::String &sPath, int32_t &iParent ); + /** + * Lookup inode. + */ int32_t lookupInode( Bu::String::const_iterator iStart, int32_t iNode, int32_t &iParent ); void readInode( int32_t iNode, RawStat &rs, MyriadStream &rIs ); @@ -190,12 +196,12 @@ namespace Bu void setTimes( int32_t iNode, int64_t iATime, int64_t iMTime ); void destroyNode( int32_t iNode ); - Bu::String filePart( const Bu::String &sPath ); + static Bu::String filePart( const Bu::String &sPath ); private: Bu::Stream &rStore; Bu::Myriad mStore; - Bu::Mutex mAccess; + Bu::DebugMutex mAccess; NodeIndex hNodeIndex; int32_t iUser; int32_t iGroup; -- cgit v1.2.3