LCOV - code coverage report
Current view: top level - cppthread - guard.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 18 38 47.4 %
Date: 2021-08-21 09:27:22 Functions: 5 7 71.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2013-2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/cppthread
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19             : 
      20             : /** \file
      21             :  * \brief Implementation of the Thread Runner and Managers.
      22             :  *
      23             :  * This file includes the implementation used by the cppthread environment.
      24             :  */
      25             : 
      26             : 
      27             : // self
      28             : //
      29             : #include    "cppthread/guard.h"
      30             : 
      31             : #include    "cppthread/exception.h"
      32             : #include    "cppthread/log.h"
      33             : #include    "cppthread/mutex.h"
      34             : 
      35             : 
      36             : // last include
      37             : //
      38             : #include    <snapdev/poison.h>
      39             : 
      40             : 
      41             : 
      42             : namespace cppthread
      43             : {
      44             : 
      45             : 
      46             : 
      47             : /** \class guard
      48             :  * \brief Lock a mutex in an RAII manner.
      49             :  *
      50             :  * This class is used to lock mutexes in a safe manner in regard to
      51             :  * exceptions. It is extremely important to unlock all mutexes before
      52             :  * a thread quits otherwise the application will lock up.
      53             :  *
      54             :  * \code
      55             :  *    {
      56             :  *        cppthread::guard lock(my_mutex);
      57             :  *        ... // atomic work
      58             :  *    }
      59             :  * \endcode
      60             :  *
      61             :  * \warning
      62             :  * This guard implementation assumes that the guard itself is used in one
      63             :  * single thread. In other words, even though you could add a guard inside
      64             :  * an object as a variable member, the lock() and is_locked() functions are
      65             :  * not safe in that situation. The constructor and destructor are, so you
      66             :  * could still do it that way. It is still preferred to push a guard on
      67             :  * the stack as shown in the example above, rather than as a variable member
      68             :  * of a class you then create on the stack.
      69             :  */
      70             : 
      71             : 
      72             : 
      73             : 
      74             : 
      75             : 
      76             : /** \brief Lock a mutex.
      77             :  *
      78             :  * This function locks the specified mutex and keep track of the lock
      79             :  * until the destructor is called.
      80             :  *
      81             :  * The mutex parameter cannot be a reference to a nullptr pointer.
      82             :  *
      83             :  * \param[in] m  The Snap! mutex to lock.
      84             :  */
      85         137 : guard::guard(mutex & m)
      86         137 :     : f_mutex(&m)
      87             : {
      88         137 :     if(f_mutex == nullptr)
      89             :     {
      90             :         // mutex is mandatory
      91             :         //
      92           0 :         throw cppthread_logic_error("mutex missing in guard() constructor");
      93             :     }
      94         137 :     f_mutex->lock();
      95         137 :     f_locked = true;
      96         137 : }
      97             : 
      98             : 
      99             : /** \brief Ensure that the mutex was unlocked.
     100             :  *
     101             :  * The destructor ensures that the mutex gets unlocked. Note that it is
     102             :  * written to avoid exceptions, however, if an exception occurs it ends
     103             :  * up calling exit(1).
     104             :  *
     105             :  * \note
     106             :  * If a function throws it logs information using the Snap! logger.
     107             :  */
     108         274 : guard::~guard()
     109             : {
     110             :     try
     111             :     {
     112         137 :         unlock();
     113             :     }
     114           0 :     catch(std::exception const & e)
     115             :     {
     116             :         // a log was already printed, we do not absolutely need another one
     117           0 :         log << log_level_t::fatal
     118           0 :             << "mutex::unlock() threw an exception while in the ~guard() function."
     119           0 :             << end;
     120           0 :         std::terminate();
     121             :     }
     122         137 : }
     123             : 
     124             : 
     125             : /** \brief Unlock this mutex.
     126             :  *
     127             :  * This function can be called any number of times. If the mutex is currently
     128             :  * locked, the function unlocks it, otherwise nothing happens.
     129             :  *
     130             :  * If necessary, you can relock the mutex using the lock() function.
     131             :  *
     132             :  * This function may throw an exception if the mutex::unlock() call fails.
     133             :  *
     134             :  * \param[in] done  Whether you are done with this guard, if so, the pointer
     135             :  * will be set to null and you can then destroy the mutex (this is the
     136             :  * default). If instead you want to be able to re-lock the mutex, then set
     137             :  * this parameter to false. The mutex cannot be destroyed if this parameter
     138             :  * is set to false.
     139             :  *
     140             :  * \sa lock()
     141             :  */
     142         137 : void guard::unlock(bool done)
     143             : {
     144         137 :     if(f_locked)
     145             :     {
     146         137 :         mutex * m(f_mutex);
     147         137 :         f_locked = false;
     148         137 :         if(done)
     149             :         {
     150         137 :             f_mutex = nullptr;
     151             :         }
     152         137 :         m->unlock();
     153             :     }
     154         137 : }
     155             : 
     156             : 
     157             : /** \brief Relock this mutex.
     158             :  *
     159             :  * This function can be called any number of times. If called while the
     160             :  * mutex is not locked, then it gets relocked, otherwise nothing happens.
     161             :  *
     162             :  * Note that when creating the guard, the mutex is automatically locked,
     163             :  * so you rarely need to call this function.
     164             :  *
     165             :  * This is most often used when a mutex needs to be unlocked within a
     166             :  * guarded block:
     167             :  *
     168             :  * \code
     169             :  *     {
     170             :  *         cppthread::guard lock(f_mutex);
     171             :  *
     172             :  *         ...do some things...
     173             :  *
     174             :  *         if(this_or_that)
     175             :  *         {
     176             :  *             lock.unlock();
     177             :  *             do_special_thing_while_unlocked();
     178             :  *             lock.lock();
     179             :  *         }
     180             :  *
     181             :  *         ...do more things...
     182             :  *     }
     183             :  * \endcode
     184             :  *
     185             :  * This example shows how one can run do_special_thing_while_unlocked()
     186             :  * while the lock is not being held. The way it is written is still
     187             :  * RAII safe. If the do_special_thing_while_unlocked() throws, the mutex
     188             :  * is in a known state (i.e. unlocked when exiting the guarded block).
     189             :  *
     190             :  * \note
     191             :  * This implementation always attempts a mutex::lock(), checks whether it
     192             :  * was necessary (i.e. is the f_locked flag false?) and if not, it unlocks
     193             :  * the mutex (since the guard already had the lock to itself).
     194             :  *
     195             :  * \warning
     196             :  * It is not 100% safe to call this function when the guard::unlock()
     197             :  * function is called with its done parameter set to true, which is the
     198             :  * default if you don't specify false in your call. It is safe if the
     199             :  * guard is only used on the stack.
     200             :  *
     201             :  * \sa unlock()
     202             :  */
     203           0 : void guard::lock()
     204             : {
     205           0 :     if(f_mutex == nullptr)
     206             :     {
     207           0 :         return;
     208             :     }
     209             : 
     210           0 :     f_mutex->lock();
     211             : 
     212           0 :     if(f_locked)
     213             :     {
     214           0 :         f_mutex->unlock();
     215             :     }
     216             :     else
     217             :     {
     218           0 :         f_locked = true;
     219             :     }
     220             : }
     221             : 
     222             : 
     223             : /** \brief This function returns whether the guard is current locked.
     224             :  *
     225             :  * This function returns the f_locked flag of the guard object. If true,
     226             :  * then the guard is expected to have its mutex locked. If false, then
     227             :  * the guard mutex is not currently locked.
     228             :  *
     229             :  * \warning
     230             :  * This function is not 100% safe if you call unlock() with its done
     231             :  * parameter set to true, which is the default. It is safe if the guard
     232             :  * is only used by on the stack.
     233             :  *
     234             :  * \return true if the guard currently holds the lock.
     235             :  */
     236           0 : bool guard::is_locked() const
     237             : {
     238           0 :     bool result(f_mutex != nullptr);
     239           0 :     if(result)
     240             :     {
     241           0 :         f_mutex->lock();
     242           0 :         result = f_locked;
     243           0 :         f_mutex->unlock();
     244             :     }
     245             : 
     246           0 :     return result;
     247             : }
     248             : 
     249             : 
     250             : 
     251             : 
     252             : /** \fn guard::guard(guard const & rhs)
     253             :  * \brief The copy operator is deleted.
     254             :  *
     255             :  * The guard class saves a bare pointer to the mutex it is guarding.
     256             :  * Because of that, a copy is not really possible and it's also not
     257             :  * useful. Plus Effective C++ wants it this way (which is great).
     258             :  *
     259             :  * \param[in] rhs  The right hand side.
     260             :  */
     261             : 
     262             : 
     263             : /** \fn guard & guard::operator = (guard const & rhs)
     264             :  * \brief The assignment operator is deleted.
     265             :  *
     266             :  * The guard class saves a bare pointer to the mutex it is guarding.
     267             :  * Because of that, an assignment is not really possible and it's also
     268             :  * not useful. Plus Effective C++ wants it this way (which is great).
     269             :  *
     270             :  * \param[in] rhs  The right hand side.
     271             :  *
     272             :  * \return A reference to this object.
     273             :  */
     274             : 
     275             : 
     276             : 
     277             : /** \var guard::f_mutex
     278             :  * \brief The mutex used by the guard class.
     279             :  *
     280             :  * Whenever you want to lock a part of your code so only one thread
     281             :  * runs it at any given time, you want to use a guard. This guard
     282             :  * makes use of a mutex that you pass to it on construction.
     283             :  *
     284             :  * The guard object keeps a reference to your mutex and uses
     285             :  * it to lock on construction and unlock on destruction. This
     286             :  * generates a perfect safe guard around your code. Safe guard
     287             :  * which is exception safe since it will still get unlocked when
     288             :  * an exception occurs.
     289             :  *
     290             :  * \warning
     291             :  * Note that it is not safe if you get a Unix signal. The lock
     292             :  * will very likely still be in place if such a signal happens
     293             :  * while within the lock.
     294             :  */
     295             : 
     296             : 
     297             : /** \var guard::f_locked
     298             :  * \brief Whether the guard is currently in effect.
     299             :  *
     300             :  * This flag is used to know whether the mutex is currently considered locked
     301             :  * by the guard object.
     302             :  */
     303             : 
     304             : 
     305             : 
     306           6 : } // namespace cppthread
     307             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13