log4cpp source code reading: threading tool class

Posted by spambadger on Fri, 25 Feb 2022 17:44:14 +0100

threading namespace

  • Location: in the include/log4cpp/threading directory
  • The corresponding classes are under the threading namespace. This namespace contains some thread synchronization classes and methods to get the current thread. There is also an implementation of thread scoped local variables
  • Because thread is a system kernel object, and different compiler platforms also encapsulate the excuse of thread differently, it has many implementations, that is, the names of specific implementation classes on different platforms may be different. Different are masked here, and the same name is provided: Mutex, ScopedLock, ThreadLocalDataHolder, getThreadID

win platform

  • Location: Include / log4cpp / threading / msthreads xx

Mutex class

  • Function: this class implements the function of mutex, which can be used to protect some access and is thread safe. Let's take a look at his code implementation on win32 platform.
class  MSMutex {
public:
    //Create a critical area object in the constructor
    MSMutex() {
        InitializeCriticalSection(&_criticalSection);
    }
    // Destroy critical area objects in destructor
    ~MSMutex() {
        DeleteCriticalSection(&_criticalSection);
    }
    // Returns the critical area object of the wrapper
    inline LPCRITICAL_SECTION getCriticalSection() {
        return &_criticalSection;
    }
private:
    //Copy is prohibited, otherwise deadlock is likely to occur (because the operation is inconsistent)
    MSMutex(const MSMutex&);
    CRITICAL_SECTION _criticalSection;  //Critical zone object
};
typedef MSMutex Mutex; // A simple non recursive lock

It can be seen that Mutex is an alias of MSMutex. The implementation of MSMutex actually wraps a critical area object. The object is created when the object is constructed and destroyed when the object is destructed. It also provides an interface for users to access, getCriticalSection method, and finally defines two private methods to prohibit the object from being copied

ScopedLock class

//A scope local lock
class MSScopedLock {
public:
    //Enter lock in constructor
    MSScopedLock(MSMutex &mutex) {
        _criticalSection = mutex.getCriticalSection();
        EnterCriticalSection(_criticalSection);
    }
    // Release lock in destructor
    ~MSScopedLock() {
        LeaveCriticalSection(_criticalSection);
    }
private:
    MSScopedLock(const MSScopedLock& other); // Binary object is copied
    LPCRITICAL_SECTION _criticalSection;     // Internally saved critical area object pointer
};

typedef MSScopedLock ScopedLock;            // Here is the unified name type of ScopedLock defined

You can see that the real type of ScopedLock is MSCopedLock. The logic is very simple. You must use the Mutex object to construct and then enter the critical area object. Then, when the ScopedLock object is destructed again, leave the critical area, which can ensure that the methods in a scope are safe. such as

class Category {
public:
    void addAppender(Appender *appender) {
        ScopedLock scopedLock(_mutex);  
        _appenders.insert(appender);
    }
    void removeAppender(Appender *appender) {
        ScopedLock scopedLock(_mutex);  
        auto pos = _appenders.find(appender);
        if (pos != _appenders.end()) {
            delete appender;
            _appenders.erase(pos);
        }
    }
private:
    Mutex _mutex;
    std::set<Appender*> _appenders;
};

This implementation can ensure that_ appenders access is thread safe

ThreadLocalDataHolder

This class is the implementation of the thread local class in java. It is different from the implementation method in java. This class is completely the tls API function of the packaging system. In addition, it also provides the interface of intelligent pointer

template <typename T>
class ThreadLocalDataHolder {
public:
    //When constructing, call the library function to allocate the key of a thread local variable
    ThreadLocalDataHolder() {
        _key = TlsAlloc();
    }

    // When destructing, destroy the local key of the thread
    ~ThreadLocalDataHolder()
    {
        TlsFree(_key); //Here's a question. Don't you recycle the value corresponding to the corresponding key?????
    }

    // Gets the value of the T type owned by the current thread
    //Returns the pointer to T that is private to the thread, or false if the thread does not have a thread local set
    inline T* get() const{
        return (T*)TlsGetValue(_key);
    }

    // Gets the thread local variable of the current thread
    //The advantage of this design is that ThreadLocalDataHolder can be regarded as a smart pointer
    inline T *operator->() const{
        return get();
    }

    // Returns the thread local variable of the current thread, where the reference is returned
    inline T &operator*() const{
        return *get();
    }

    // Thread's current local variable release
    // Returns all local variables of the current thread, or nullptr (if the thread has no value set)
    inline T *release() {
        T *result = TlsGetValue(_key);
        TlsSetValue(_key, nullptr);
        return result;
    }

    // Reset the value of the thread local variable
    //First, the original value will be deleted, and then the new value will be set
    inline void reset(T *p = nullptr) {
        T *thing = (T*)TlsGetValue(_key);
        delete thing;
        TlsSetValue(_key, p);
    }

    private:
        DWORD _key;
};

getThreadID function

std::string getThreadID()
{
    char buf[16];
    sprintf_s(buf, "%lu", ::GetCurrentThreadId());
    return std::string(buf);
}

That is to call the api function GetCurrentThreadId() of the system to get the current thread identifier, because the identifier is DWORD type, and then call the formatting function to convert the DWORD type into a string and return it.

linux platform

  • Location: Include / log4cpp / threading / pthreads xx

Mutex

class Mutex {
      private:
            pthread_mutex_t mutex;

      public:
            inline Mutex() {
                ::pthread_mutex_init(&mutex, NULL);
            }

            inline void lock() {
                ::pthread_mutex_lock(&mutex);
            }

            inline void unlock() {
                ::pthread_mutex_unlock(&mutex);
            }

            inline ~Mutex() {
                ::pthread_mutex_destroy(&mutex);
            }

        private:
            Mutex(const Mutex& m);
            Mutex& operator=(const Mutex &m);
        };

ScopedLock

class ScopedLock {
            private:
            Mutex& _mutex;

            public:
            inline ScopedLock(Mutex& mutex) :
                _mutex(mutex) {
                _mutex.lock();
            }

            inline ~ScopedLock() {
                _mutex.unlock();
            }
};

ThreadLocalDataHolder

 template<typename T> 
 class ThreadLocalDataHolder {
     private:            
            pthread_key_t _key;              

     public:
            typedef T data_type;

            inline ThreadLocalDataHolder() {
                ::pthread_key_create(&_key, freeHolder);         
            }

            inline static void freeHolder(void *p) {
                assert(p != NULL);
                delete reinterpret_cast<T *>(p);
             }

            inline ~ThreadLocalDataHolder() {
                T *data = get();
                if (data != NULL) { 
                    delete data;
                }
                ::pthread_key_delete(_key);
            }
            
            inline T* get() const {
                return reinterpret_cast<T *>(::pthread_getspecific(_key)); 
            }

            inline T* operator->() const { return get(); }
            inline T& operator*() const { return *get(); }

            inline T* release() {
                T* result = get();
                ::pthread_setspecific(_key, NULL); 

                return result;
            }

            inline void reset(T* p = NULL) {
                T *data = get();
                if (data != NULL) {
                    delete data;
                }
                ::pthread_setspecific(_key, p); 
            }
        };

summary

Some tool classes and functions in threading namespace, including:

  • Mutex implements a platform independent mutex, which is said to be platform independent because each platform will implement its own mutex
  • ScopedLock implements the automatic locking and unlocking of the specified scope
  • ThreadLocalDataHolder is a template class that implements the thread local class. The main thing to note is that when this object is not used, it must be released and the data stored in this object.
  • getThreadID gets the string format of the thread identifier.

Topics: Java C++ Back-end