LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   [C++] Creating a Mutex locker class - RAII (https://www.linuxquestions.org/questions/programming-9/%5Bc-%5D-creating-a-mutex-locker-class-raii-4175422341/)

Aquarius_Girl 08-16-2012 01:09 AM

[C++] Creating a Mutex locker class - RAII
 
Code:

class mutexLocker
{
        private:
        /* Declaration of a Mutex variable `mutexA`. */
        pthread_mutex_t &mutexA;
       
        /* `mutexStatus` holds the return value of the function`pthread_mutex_lock `.
        This value has to be returned to the callee so we need to preserve it in a class
        variable. */
        int            mutexStatus;

        public:
        /* Constructor attempts to lock the desired mutex variable. */
        mutexLocker (pthread_mutex_t argMutexVariable)
        : mutexA (argMutexVariable)
        {
            /* Return value is needed in order to know whether the mutex has been
            successfully locked or not. */
            int mutexStatus = pthread_mutex_lock (&argMutexVariable);
        }
       
        /* Since the constructor can't return anything, we need to have a separate function
        which returns the status of the lock. */
        int getMutexLockStatus ()
        {
            return mutexStatus;
        }
       
        /* We may need this Mutex variable as an argument for `pthread_cond_wait()`*/
        pthread_mutex_t getLockedMutex ()
        {
            if (mutexStatus >= 0)
                return mutexA;
        }
       
        /* The destructor will get called automatically whereever the callee's scope ends, and
        will get the mutex unlocked. */
        ~mutexLocker ()
        {
            if (mutexStatus >= 0)
                pthread_mutex_unlock (&mutexA);
        }
};

What other functionalities should be provided in a DIY mutex locker class?

dwhitney67 08-16-2012 07:26 PM

IMHO, you don't need the method to return the status; consider throwing an exception should a runtime error occur. After all, the point of the wrapper class is to avoid exposing OS-dependent interfaces to the user, right?

As for exposing the pthread_mutex_t, I also would not recommend doing that. The pthread_mutex_t should be an attribute of your Mutex class, not a reference to an external object (which could be destroyed, manipulated, etc). As for your ConditionMutex class, it should contain a Mutex object.

Here's some code I threw together in the past when trying to mimic the Boost Thread Library (I jokingly called it 'liteboost'):

mutex.hpp:
Code:

/*
# Copyright (C) 2008 David M. Whitney (aka "dwhitney67")
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
*/

#ifndef LITE_BOOST_MUTEX_H
#define LITE_BOOST_MUTEX_H

#include <pthread.h>
#include <stdexcept>

namespace liteboost
{

class condition;

class mutex
{
  public:
    mutex(int kind = PTHREAD_MUTEX_DEFAULT)
    {
      pthread_mutexattr_t attr;

      if (pthread_mutexattr_init(&attr) != 0)
      {
        throw std::runtime_error("Mutex::initMutex() -- failed to init attr.");
      }

      if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE) != 0)
      {
        throw std::runtime_error("Mutex::initMutex() -- failed to set attr process type.");
      }

      if (pthread_mutexattr_settype(&attr, kind) != 0)
      {
        throw std::runtime_error("Mutex::initMutex() -- failed to set attr type.");
      }

      if (pthread_mutex_init(&m_mutex, &attr) != 0)
      {
        throw std::runtime_error("Mutex::initMutex() -- failed to init mutex attr.");
      }
    }

    ~mutex()
    {
      if (pthread_mutex_destroy(&m_mutex) != 0)
      {
        throw std::runtime_error("Mutex::~Mutex() -- unable to destroy the mutex.");
      }
    }


    class scoped_lock
    {
      public:
        scoped_lock(mutex& mutex) : m_mutex(mutex)
        {
          m_mutex.lock();
        }

        ~scoped_lock()
        {
          m_mutex.unlock();
        }

      private:
        mutex& m_mutex;
    };


  private:
    friend class condition;

    void lock()
    {
      if (pthread_mutex_lock(&m_mutex) != 0)
      {
        throw std::runtime_error("Mutex::lock() -- unable to lock the mutex.");
      }
    }

    void unlock()
    {
      if (pthread_mutex_unlock(&m_mutex) != 0)
      {
        throw std::runtime_error("Mutex::unlock() -- unable to unlock the mutex.");
      }
    }

    pthread_mutex_t* get_mutex()
    {
      return &m_mutex;
    }

    pthread_mutex_t m_mutex;
};

} // end namespace

#endif

condition.hpp:
Code:

/*
# Copyright (C) 2008 David M. Whitney (aka "dwhitney67")
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
*/

#ifndef LITE_BOOST_CONDITION_H
#define LITE_BOOST_CONDITION_H

#include <liteboost/thread/mutex.hpp>
#include <pthread.h>
#include <sys/time.h>
#include <cerrno>
#include <limits>
#include <stdexcept>

namespace liteboost
{

class condition
{
  public:
    condition()
    {
      pthread_condattr_t attr;

      if (pthread_condattr_init(&attr) != 0)
      {
        throw std::runtime_error("condition(): pthread_condattr_init() failed.");
      }

      if (pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE) != 0)
      {
        throw std::runtime_error("condition(): pthread_condattr_setpshared() failed.");
      }

      if (pthread_cond_init(&m_cond, &attr) != 0)
      {
        throw std::runtime_error("condition(): pthread_condattr_init() failed.");
      }
    }

    ~condition()
    {
      if (pthread_cond_destroy(&m_cond) != 0)
      {
        throw std::runtime_error("~condition(): pthread_cond_destroy() failed.");
      }
    }

    void wait()
    {
      if (pthread_cond_wait(&m_cond, m_mutex.get_mutex()) != 0)
      {
        throw std::runtime_error("wait(): pthread_cond_wait() failed.");
      }
    }

    bool timed_wait(const unsigned int waitTime_ns)
    {
      static const unsigned int NANOSECS_PER_SEC  = 1000000000;
      static const unsigned int NANOSECS_PER_USEC = 1000;

      // Convert given nanosecond time into an abstime (absolute time)
      struct timeval td;
      gettimeofday(&td, 0);

      struct timespec t;
      t.tv_sec  = td.tv_sec +  (waitTime_ns / NANOSECS_PER_SEC);
      t.tv_nsec = (td.tv_usec * NANOSECS_PER_USEC) + (waitTime_ns % NANOSECS_PER_SEC);

      // Check for overflow in the computed nanoseconds
      if ((unsigned int)t.tv_nsec >= NANOSECS_PER_SEC)
      {
        t.tv_sec  += 1;
        t.tv_nsec -= NANOSECS_PER_SEC;
      }

      // Loop until the condition signal is received, or a timeout occurs
      while (1)
      {
        switch (pthread_cond_timedwait(&m_cond, m_mutex.get_mutex(), &t))
        {
          // Everything is ok; condition-lock was obtained.
          case 0:
              return true;

          // Timeout occurred before receiving a signal; condition-lock was not obtained.
          case ETIMEDOUT:
              return false;

          // Error occurred.
          default:
              throw std::runtime_error("timed_wait(): pthread_cond_timedwait() failed.");
        }
      }
    }

    void signal()
    {
      if (pthread_cond_signal(&m_cond) != 0)
      {
        throw std::runtime_error("signal(): phread_cond_signal() failed.");
      }
    }

    void broadcast()
    {
      if (pthread_cond_broadcast(&m_cond) != 0)
      {
        throw std::runtime_error("signal(): phread_cond_broadcast() failed.");
      }
    }

    class scoped_cond_lock
    {
      public:
        scoped_cond_lock(condition& cond) : m_cond(cond)
        {
          m_cond.lock();
        }

        ~scoped_cond_lock()
        {
          m_cond.unlock();
        }

      private:
        condition& m_cond;
    };

  private:
    void lock()
    {
      m_mutex.lock();
    }

    void unlock()
    {
      m_mutex.unlock();
    }

    pthread_cond_t m_cond;
    mutex          m_mutex;
};

} // end namespace

#endif

Sample usage:
Code:

#include "liteboost/thread/mutex.hpp"
#include <map>
#include <stdexcept>

class SharedObject
{
  private:
    typedef std::map<unsigned int, unsigned int> MyMap;
    typedef MyMap::const_iterator                MyMapIter;

  public:
    static SharedObject& instance();

    void insert(const unsigned int key, const unsigned int value);

    unsigned int getValue(const unsigned int key);

  private:
    SharedObject() {}

    MyMap            m_map;
    liteboost::mutex m_mutex;
};


SharedObject&
SharedObject::instance()
{
  static SharedObject obj;
  return obj;
}


void
SharedObject::insert(const unsigned int key, const unsigned int value)
{
  liteboost::mutex::scoped_lock lock(m_mutex);

  m_map[key] = value;
}


unsigned int
SharedObject::getValue(const unsigned int key)
{
  liteboost::mutex::scoped_lock lock(m_mutex);

  MyMapIter it = m_map.find(key);

  if (it == m_map.end())
  {
    throw std::runtime_error("SharedObject::getValue() -- key not found!");
  }

  return it->second;
}



All times are GMT -5. The time now is 02:59 PM.