LCOV - code coverage report
Current view: top level - snapwebsites - snap_lock.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 3 203 1.5 %
Date: 2019-12-15 17:13:15 Functions: 2 34 5.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Lock -- class to handle inter-process and inter-computer locks
       2             : // Copyright (c) 2016-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      14             : // You should have received a copy of the GNU General Public License
      15             : // along with this program; if not, write to the Free Software
      16             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17             : 
      18             : 
      19             : // self
      20             : //
      21             : #include "snapwebsites/snap_lock.h"
      22             : 
      23             : 
      24             : // snapwebsites lib
      25             : //
      26             : #include "snapwebsites/log.h"
      27             : #include "snapwebsites/qstring_stream.h"
      28             : #include "snapwebsites/snap_communicator_dispatcher.h"
      29             : 
      30             : 
      31             : // snapdev lib
      32             : //
      33             : #include <snapdev/not_reached.h>
      34             : #include <snapdev/not_used.h>
      35             : 
      36             : 
      37             : // C++ lib
      38             : //
      39             : #include <iostream>
      40             : 
      41             : 
      42             : // C lib
      43             : //
      44             : #include <unistd.h>
      45             : #include <sys/syscall.h>
      46             : 
      47             : 
      48             : // last include
      49             : //
      50             : #include <snapdev/poison.h>
      51             : 
      52             : 
      53             : 
      54             : /** \file
      55             :  * \brief Implementation of the Snap Lock class.
      56             :  *
      57             :  * This class is used to create an inter-process lock within a entire
      58             :  * Snap cluster.
      59             :  *
      60             :  * The class uses a blocking socket to communicate to a snaplock
      61             :  * instance and wait for the LOCKED event. Once received, it
      62             :  * let you run your code.
      63             :  *
      64             :  * If instead we receive a LOCKFAILED or UNLOCKED (on a timeout we may
      65             :  * get an UNLOCKED event) message as a response, then the class throws
      66             :  * since the lock was not obtained.
      67             :  */
      68             : 
      69             : namespace snap
      70             : {
      71             : 
      72             : namespace
      73             : {
      74             : 
      75             : 
      76             : /** \brief The default time to live of a lock.
      77             :  *
      78             :  * By default the inter-process locks are kept for only five seconds.
      79             :  * You may change the default using the
      80             :  * snaplock::initialize_lock_duration_timeout() function.
      81             :  *
      82             :  * You can specify how long a lock should be kept around by setting its
      83             :  * duration at the time you create it (see the snap_lock constructor.)
      84             :  */
      85             : snap_lock::timeout_t g_lock_duration_timeout = snap_lock::SNAP_LOCK_DEFAULT_TIMEOUT;
      86             : 
      87             : 
      88             : /** \brief The default time to wait in order to obtain a lock.
      89             :  *
      90             :  * By default the snaplock waits five seconds to realize an
      91             :  * inter-process lock. If the locak cannot be obtained within
      92             :  * that short period of time, the locking fails.
      93             :  *
      94             :  * You may change the default using the
      95             :  * snaplock::initialize_lock_obtention_timeout() function.
      96             :  *
      97             :  * You can specify how long to wait for a lock to take by setting
      98             :  * its obtention duration at the time you create it (see the snap_lock
      99             :  * constructor.)
     100             :  *
     101             :  * \note
     102             :  * By default we use the same amount of the g_lock_duration_timeout
     103             :  * variable, which at this point is fortuitious.
     104             :  */
     105             : snap_lock::timeout_t g_lock_obtention_timeout = snap_lock::SNAP_LOCK_DEFAULT_TIMEOUT;
     106             : 
     107             : 
     108             : /** \brief The default time to wait for a timed out lock acknowledgement.
     109             :  *
     110             :  * Whenever a lock is obtained, it can time out if the owner does not send
     111             :  * an UNLOCK message on time.
     112             :  *
     113             :  * When a lock times out, the snaplock daemon forcibly sends an UNLOCKED
     114             :  * message with an error message set to "timedout". When the service that
     115             :  * owns that lock receives that message, it is expected to acknowledge it.
     116             :  * If no acknowledgement happens before this duration elapses, then the
     117             :  * lock is released no matter what. This, indeed, means that another
     118             :  * process may obtain the same lock and access the same resources in
     119             :  * parallel...
     120             :  *
     121             :  * To acknowledge an UNLOCKED message, reply with UNLOCK. No other UNLOCKED
     122             :  * message gets sent after that.
     123             :  *
     124             :  * By default the unlock duraction is set to
     125             :  * snap_lock::SNAP_UNLOCK_MINIMUM_TIMEOUT which is one minute.
     126             :  */
     127             : snap_lock::timeout_t g_unlock_duration_timeout = snap_lock::SNAP_UNLOCK_MINIMUM_TIMEOUT;
     128             : 
     129             : 
     130             : #ifdef _DEBUG
     131             : /** \brief Whether the snapcommunicator parameters were initialized.
     132             :  *
     133             :  * This variable is only used in debug mode. This allows us to know whether
     134             :  * the initialize_snapcommunicator() was called before we make use of the
     135             :  * snap_lock() interface. Without that initialization, we may run in various
     136             :  * problems if the administrator changed his snapcommunicator parameters
     137             :  * such as the port to which we need to connect. This would be a bug in
     138             :  * the code.
     139             :  *
     140             :  * In release mode we ignore that flag.
     141             :  */
     142             : bool g_snapcommunicator_initialized = false;
     143             : #endif
     144             : 
     145             : 
     146             : /** \brief The default snapcommunicator address.
     147             :  *
     148             :  * This variable holds the snapcommunicator IP address used to create
     149             :  * locks by connecting (Sending messages) to snaplock processes.
     150             :  *
     151             :  * It can be changed using the snaplock::initialize_snapcommunicator()
     152             :  * function.
     153             :  */
     154           2 : std::string g_snapcommunicator_address = "127.0.0.1";
     155             : 
     156             : 
     157             : /** \brief The default snapcommunicator port.
     158             :  *
     159             :  * This variable holds the snapcommunicator port used to create
     160             :  * locks by connecting (Sending messages) to snaplock processes.
     161             :  *
     162             :  * It can be changed using the snaplock::initialize_snapcommunicator()
     163             :  * function.
     164             :  */
     165             : int g_snapcommunicator_port = 4040;
     166             : 
     167             : 
     168             : /** \brief The default snapcommunicator mode.
     169             :  *
     170             :  * This variable holds the snapcommunicator mode used to create
     171             :  * locks by connecting (Sending messages) to snaplock processes.
     172             :  *
     173             :  * It can be changed using the snaplock::initialize_snapcommunicator()
     174             :  * function.
     175             :  */
     176             : tcp_client_server::bio_client::mode_t g_snapcommunicator_mode = tcp_client_server::bio_client::mode_t::MODE_PLAIN;
     177             : 
     178             : 
     179             : /** \brief A unique number used to name the lock service.
     180             :  *
     181             :  * Each time we create a new lock service we need to have a new unique name
     182             :  * because otherwise we could receive replies from a previous lock and
     183             :  * there is no other way for us to distinguish them. This is important
     184             :  * only if the user is trying to lock the same object several times in
     185             :  * a row.
     186             :  */
     187             : int g_unique_number = 0;
     188             : 
     189             : 
     190             : /** \brief Retrieve the current thread identifier.
     191             :  *
     192             :  * This function retrieves the current thread identifier.
     193             :  *
     194             :  * \return The thread identifier, which is a pid_t specific to each thread
     195             :  *         of a process.
     196             :  */
     197           0 : pid_t gettid()
     198             : {
     199           0 :     return syscall(SYS_gettid);
     200             : }
     201             : 
     202             : }
     203             : 
     204             : 
     205             : 
     206             : 
     207             : 
     208             : namespace details
     209             : {
     210             : 
     211             : /** \brief The actual implementation of snap_lock.
     212             :  *
     213             :  * This class is the actual implementation of snap_lock which is completely
     214             :  * hidden from the users of snap_lock. (i.e. implementation details.)
     215             :  *
     216             :  * The class connects to the snapcommunicator daemon, sends a LOCK event
     217             :  * and waits for the LOCKED message or a failure at which point the
     218             :  * daemon returns.
     219             :  *
     220             :  * The connection remains _awake_ even once the lock was obtained in
     221             :  * case the snaplock daemon wants to send us another message and so
     222             :  * we can send it an UNLOCK message. It is also important to check
     223             :  * whether the lock timed out after a while. The snaplock daemon
     224             :  * sends us an UNLOCKED message which we need to acknowledge with
     225             :  * an UNLOCK. This is important to allow the next user in line to
     226             :  * quickly obtain his own lock.
     227             :  */
     228             : class lock_connection
     229             :     : public snap_communicator::snap_tcp_blocking_client_message_connection
     230             :     , public snap::dispatcher<lock_connection>
     231             : {
     232             : public:
     233             :     typedef std::shared_ptr<lock_connection>        pointer_t;
     234             : 
     235             :                             lock_connection(QString const & object_name, snap_lock::timeout_t lock_duration, snap_lock::timeout_t lock_obtention_timeout, snap_lock::timeout_t unlock_duration);
     236             :     virtual                 ~lock_connection() override;
     237             : 
     238             :     void                    run();
     239             :     void                    unlock();
     240             : 
     241             :     bool                    is_locked() const;
     242             :     bool                    lock_timedout() const;
     243             :     time_t                  get_lock_timeout_date() const;
     244             : 
     245             :     // implementation of snap_communicator::snap_tcp_blocking_client_message_connection
     246             :     virtual void            process_timeout() override;
     247             :     //virtual void            process_message(snap_communicator_message const & message) override;
     248             : 
     249             :     // implementation of snap_communicator::connection_with_send_message
     250             :     virtual void            ready(snap_communicator_message & message) override;
     251             :     virtual void            stop(bool quitting) override;
     252             : 
     253             : private:
     254             :     void                    msg_locked(snap::snap_communicator_message & message);
     255             :     void                    msg_lockfailed(snap::snap_communicator_message & message);
     256             :     void                    msg_unlocked(snap::snap_communicator_message & message);
     257             : 
     258             :     static snap::dispatcher<lock_connection>::dispatcher_match::vector_t const    g_lock_connection_messages;
     259             : 
     260             :     pid_t const                 f_owner;
     261             :     QString const               f_service_name;
     262             :     QString const               f_object_name;
     263             :     snap_lock::timeout_t const  f_lock_duration;
     264             :     time_t                      f_lock_timeout_date = 0;
     265             :     time_t const                f_obtention_timeout_date;
     266             :     snap_lock::timeout_t const  f_unlock_duration = snap_lock::SNAP_UNLOCK_USES_LOCK_TIMEOUT;
     267             :     bool                        f_lock_timedout = false;
     268             : };
     269             : 
     270             : 
     271             : 
     272           2 : snap::dispatcher<lock_connection>::dispatcher_match::vector_t const lock_connection::g_lock_connection_messages =
     273             : {
     274             :     {
     275             :         "LOCKED"
     276             :       , &lock_connection::msg_locked
     277             :     },
     278             :     {
     279             :         "LOCKFAILED"
     280             :       , &lock_connection::msg_lockfailed
     281             :     },
     282             :     {
     283             :         "UNLOCKED"
     284             :       , &lock_connection::msg_unlocked
     285             :     }
     286             : };
     287             : 
     288             : 
     289             : 
     290             : /** \brief Initiate an inter-process lock.
     291             :  *
     292             :  * This constructor is used to obtain an inter-process lock.
     293             :  *
     294             :  * The lock will be effective on all the computers that have access to
     295             :  * the running snaplock instances you can connect to via snapcommunicator.
     296             :  *
     297             :  * The LOCK and other messages are sent to the snaplock daemon using
     298             :  * snapcommunicator.
     299             :  *
     300             :  * Note that the constructor creates a "lock service" which is given a
     301             :  * name which is "lock" and the current thread identifier and a unique
     302             :  * number. We use an additional unique number to make sure messages
     303             :  * sent to our old instance(s) do not make it to a newer instance, which
     304             :  * could be confusing and break the lock mechanism in case the user
     305             :  * was trying to get a lock multiple times on the same object.
     306             :  *
     307             :  * In a perfect world, the following shows what happens, message wise,
     308             :  * as far as the client is concerned. The snaplock sends many more
     309             :  * messages in order to obtain the lock (See src/snaplock/snaplock_ticket.cpp
     310             :  * for details about those events.)
     311             :  *
     312             :  * \note
     313             :  * The REGISTER message is sent from the constructor to initiate the
     314             :  * process. This function starts by receiving the READY message.
     315             :  *
     316             :  * \msc
     317             :  *    client,snapcommunicator,snaplock;
     318             :  *
     319             :  *    client->snapcommunicator [label="REGISTER"];
     320             :  *    snapcommunicator->client [label="READY"];
     321             :  *    snapcommunicator->client [label="HELP"];
     322             :  *    client->snapcommunicator [label="COMMANDS"];
     323             :  *    client->snapcommunicator [label="LOCK"];
     324             :  *    snapcommunicator->snaplock [label="LOCK"];
     325             :  *    snaplock->snapcommunicator [label="LOCKED"];
     326             :  *    snapcommunicator->client [label="LOCKED"];
     327             :  *    ...;
     328             :  *    client->snapcommunicator [label="UNLOCK"];
     329             :  *    snapcommunicator->snaplock [label="UNLOCK"];
     330             :  *    client->snapcommunicator [label="UNREGISTER"];
     331             :  * \endmsc
     332             :  *
     333             :  * If somehow the lock fails, we may receive LOCKFAILED or UNLOCKED instead
     334             :  * of the LOCK message.
     335             :  *
     336             :  * \warning
     337             :  * The g_unique_number and the other global variables are not handle
     338             :  * safely if you attempt locks in a multithread application. The
     339             :  * unicity of the lock name will still be correct because the name
     340             :  * includes the thread identifier (see gettid() function.) In a
     341             :  * multithread application, the initialize_...() function should be
     342             :  * called once by the main thread before the other threads get created.
     343             :  *
     344             :  * \param[in] object_name  The name of the object being locked.
     345             :  * \param[in] lock_duration  The amount of time the lock will last if obtained.
     346             :  * \param[in] lock_obtention_timeout  The amount of time to wait for the lock.
     347             :  * \param[in] unlock_duration  The amount of time we have to acknowledge a
     348             :  *                             timed out lock.
     349             :  */
     350           0 : lock_connection::lock_connection(QString const & object_name, snap_lock::timeout_t lock_duration, snap_lock::timeout_t lock_obtention_timeout, snap_lock::timeout_t unlock_duration)
     351             :     : snap_tcp_blocking_client_message_connection(g_snapcommunicator_address, g_snapcommunicator_port, g_snapcommunicator_mode)
     352             :     , dispatcher(this, g_lock_connection_messages)
     353           0 :     , f_owner(gettid())
     354           0 :     , f_service_name(QString("lock_%1_%2").arg(gettid()).arg(++g_unique_number))
     355             :     , f_object_name(object_name)
     356           0 :     , f_lock_duration(lock_duration == -1 ? g_lock_duration_timeout : lock_duration)
     357             :     //, f_lock_timeout_date(0) -- only determined if we obtain the lock
     358           0 :     , f_obtention_timeout_date((lock_obtention_timeout == -1 ? g_lock_obtention_timeout : lock_obtention_timeout) + time(nullptr))
     359           0 :     , f_unlock_duration(unlock_duration)
     360             : {
     361             : #ifdef _DEBUG
     362           0 :     if(!g_snapcommunicator_initialized)
     363             :     {
     364           0 :         throw snap_lock_not_initialized("your process must call snap::snap_lock::initialize_snapcommunicator() at least once before you can create locks.");
     365             :     }
     366             : #endif
     367           0 :     add_snap_communicator_commands();
     368             : 
     369             :     // tell the lower level when the lock obtention times out;
     370             :     // that one is in microseconds; if we do obtain the lock,
     371             :     // the timeout is then changed to the lock duration
     372             :     // (which is computed from the time at which we get the lock)
     373             :     //
     374           0 :     set_timeout_date(f_obtention_timeout_date * 1000000L);
     375           0 : }
     376             : 
     377             : 
     378             : /** \brief Send the UNLOCK message to snaplock to terminate the lock.
     379             :  *
     380             :  * The destructor makes sure that the lock is released.
     381             :  */
     382           0 : lock_connection::~lock_connection()
     383             : {
     384             :     try
     385             :     {
     386           0 :         unlock();
     387             :     }
     388           0 :     catch(snap_communicator_invalid_message const &)
     389             :     {
     390             :     }
     391           0 : }
     392             : 
     393             : 
     394             : /** \brief Listen for messages.
     395             :  *
     396             :  * This function overloads the blocking connection run() function so we
     397             :  * can properly initialize the lock_connection object (some things just
     398             :  * cannot be done in the construtor.)
     399             :  *
     400             :  * The function makes sure we have the dispatcher setup and sends the
     401             :  * REGISTER message so we get registered with snapcommunicator.
     402             :  */
     403           0 : void lock_connection::run()
     404             : {
     405           0 :     set_dispatcher(std::dynamic_pointer_cast<lock_connection>(shared_from_this()));
     406             : 
     407             :     // need to register with snap communicator
     408             :     //
     409           0 :     snap::snap_communicator_message register_message;
     410           0 :     register_message.set_command("REGISTER");
     411           0 :     register_message.add_parameter("service", f_service_name);
     412           0 :     register_message.add_parameter("version", snap::snap_communicator::VERSION);
     413           0 :     send_message(register_message);
     414             : 
     415             :     // now wait for the READY and HELP replies, send LOCK, and
     416             :     // either timeout or get the LOCKED message
     417             :     //
     418           0 :     snap_tcp_blocking_client_message_connection::run();
     419           0 : }
     420             : 
     421             : 
     422             : /** \brief Send the UNLOCK early (before the destructor is called).
     423             :  *
     424             :  * This function releases the lock obtained by the constructor.
     425             :  *
     426             :  * It is safe to call the function multiple times. It will send the
     427             :  * UNLOCK only the first time.
     428             :  *
     429             :  * Note that it is not possible to re-obtain the lock once unlocked.
     430             :  * You will have to create a new lock_conenction object to do so.
     431             :  *
     432             :  * \note
     433             :  * If you fork or attempt to unlock from another thread, the unlock()
     434             :  * function will do nothing. Only the exact thread that created the
     435             :  * lock can actually unlock. This does happen in snap_backend children
     436             :  * which attempt to remove the lock their parent setup because the
     437             :  * f_lock variable is part of the connection which is defined in a
     438             :  * global variable.
     439             :  */
     440           0 : void lock_connection::unlock()
     441             : {
     442           0 :     if(f_lock_timeout_date != 0
     443           0 :     && f_owner == gettid())
     444             :     {
     445           0 :         f_lock_timeout_date = 0;
     446             : 
     447             :         // done
     448             :         //
     449             :         // explicitly send the UNLOCK message and then make sure to unregister
     450             :         // from snapcommunicator; note that we do not wait for a reply to the
     451             :         // UNLOCK message, since to us it does not matter much as long as the
     452             :         // message was sent...
     453             :         //
     454           0 :         snap_communicator_message unlock_message;
     455           0 :         unlock_message.set_command("UNLOCK");
     456           0 :         unlock_message.set_service("snaplock");
     457           0 :         unlock_message.add_parameter("object_name", f_object_name);
     458           0 :         unlock_message.add_parameter("pid", gettid());
     459           0 :         send_message(unlock_message);
     460             : 
     461           0 :         snap_communicator_message unregister_message;
     462           0 :         unregister_message.set_command("UNREGISTER");
     463           0 :         unregister_message.add_parameter("service", f_service_name);
     464           0 :         send_message(unregister_message);
     465             :     }
     466           0 : }
     467             : 
     468             : 
     469             : /** \brief Check whether the lock worked (true) or not (false).
     470             :  *
     471             :  * This function returns true if the lock succeeded and is still
     472             :  * active. This function detects whether the lock timed out and
     473             :  * returns false if so.
     474             :  *
     475             :  * The following is a simple usage example. Note that the UNLOCK
     476             :  * message will be sent to the snaplock daemon whenever the
     477             :  * snap_lock get destroyed and a lock was obtained. There is
     478             :  * no need for you to call the unlock() function. However, it can
     479             :  * be useful to perform the very important tasks on the resource
     480             :  * being locked first and if it times out, forfeit further less
     481             :  * important work.
     482             :  *
     483             :  * \code
     484             :  *      {
     485             :  *          snap_lock l(...);
     486             :  *
     487             :  *          ...do work with resource...
     488             :  *          if(l.is_locked())
     489             :  *          {
     490             :  *              ...do more work with resource...
     491             :  *          }
     492             :  *          // l.unlock() is implicit
     493             :  *      }
     494             :  * \endcode
     495             :  *
     496             :  * You may check how long the lock has left using the
     497             :  * get_lock_timeout_date() which is the date when the lock
     498             :  * times out.
     499             :  *
     500             :  * Note that the get_lock_timeout_date() function returns zero
     501             :  * if the lock was not obtained and a threshold date in case the
     502             :  * lock was obtained, then zero again after a call to the unlock()
     503             :  * function or when the UNLOCKED message was received and processed
     504             :  * (i.e. to get the UNLOCKED message processed you need to call the
     505             :  * lock_timedout() function).
     506             :  *
     507             :  * \return true if the lock is currently active.
     508             :  *
     509             :  * \sa get_lock_timeout_date()
     510             :  * \sa unlock()
     511             :  * \sa lock_timedout()
     512             :  */
     513           0 : bool lock_connection::is_locked() const
     514             : {
     515             :     // if the lock timeout date was not yet defined, it is not yet
     516             :     // locked (we may still be trying to obtain the lock, though);
     517             :     // one not zero, the lock is valid as long as that date is
     518             :     // after 'now'
     519             :     //
     520           0 :     return f_lock_timeout_date != 0 && f_lock_timeout_date > time(nullptr);
     521             : }
     522             : 
     523             : 
     524             : /** \brief Check whether the lock timed out.
     525             :  *
     526             :  * This function checks whether an UNLOCKED was received while you hold
     527             :  * the lock. You cannot call the function if you did not yet obtain the
     528             :  * lock.
     529             :  *
     530             :  * If you are going to destroy the lock right away after determining that
     531             :  * it timed out, call the is_locked() function instead. The lock object
     532             :  * will automatically send an UNLOCK message when it gets destroyed so
     533             :  * just that is enough.
     534             :  *
     535             :  * Now, you want to use this lock_timedout() function in case you want
     536             :  * to test whether a lock is about to be released by the snaplock daemon
     537             :  * which took ownship of the lock. It will not send the UNLOCK event
     538             :  * back (acknowlegement). You are responsible to call the unlock()
     539             :  * function once this function returns true.
     540             :  *
     541             :  * This function is used on a snap_lock object that is kept around
     542             :  * for a while. If you are going to destroy the lock anyway, you
     543             :  * can just do that as soon as possible and you will be just fine.
     544             :  *
     545             :  * The following more or less shows a case where the lock_timedout()
     546             :  * and unlock() can be used. The lock_timedout() function can be
     547             :  * called any number of time, so if you do work in a loop, you can
     548             :  * safely call it once or twice per iteration:
     549             :  *
     550             :  * \code
     551             :  *  snap_lock l;
     552             :  *
     553             :  *  for(;;)
     554             :  *  {
     555             :  *      l.lock(...);
     556             :  *
     557             :  *      do
     558             :  *      {
     559             :  *          ...start synchronized work here...
     560             :  *          if(l.lock_timedout())
     561             :  *          {
     562             :  *              // make sure we unlock
     563             :  *              l.unlock();
     564             :  *              break;
     565             :  *          }
     566             :  *          ...more synchronized work
     567             :  *      }
     568             :  *      while(false);
     569             :  *  }
     570             :  * \endcode
     571             :  *
     572             :  * This example shows an explicit unlock() call. If you immediately
     573             :  * try a new lock() call, then the unlock() is call implicitely.
     574             :  *
     575             :  * \note
     576             :  * As mentioned above, you still got a little bit of time after this
     577             :  * function returns true. However, the sooner you call the unlock()
     578             :  * function after this function returns true, the safer.
     579             :  *
     580             :  * \return true if the lock timed out, false otherwise.
     581             :  *
     582             :  * \sa is_locked()
     583             :  * \sa unlock()
     584             :  */
     585           0 : bool lock_connection::lock_timedout() const
     586             : {
     587           0 :     if(f_lock_timeout_date != 0)
     588             :     {
     589             :         // if the timeout date has not yet elapsed then the lock cannot
     590             :         // have dropped yet (unless a big problem happened and checking
     591             :         // with snaplock would probably fail anyway.)
     592             :         //
     593           0 :         if(f_lock_timeout_date > time(nullptr))
     594             :         {
     595           0 :             return false;
     596             :         }
     597             : 
     598             :         // it looks like we timed out, check for an UNLOCKED event
     599             :         //
     600             :         // just do a peek(), that is enough since the msg_unlocked()
     601             :         // will set the f_lock_timedout flag as required; if we did
     602             :         // not yet receive a full event, we return false (i.e. not
     603             :         // yet timed out); it will also set f_lock_timeout_date back
     604             :         // to zero
     605             :         //
     606           0 :         const_cast<lock_connection *>(this)->peek();
     607             :     }
     608             : 
     609           0 :     return f_lock_timedout;
     610             : }
     611             : 
     612             : 
     613             : /** \brief Retrieve the cutoff time for this lock.
     614             :  *
     615             :  * This lock will time out when this date is reached.
     616             :  *
     617             :  * If the value is zero, then the lock was not yet obtained.
     618             :  *
     619             :  * \return The lock timeout date
     620             :  */
     621           0 : time_t lock_connection::get_lock_timeout_date() const
     622             : {
     623           0 :     return f_lock_timeout_date;
     624             : }
     625             : 
     626             : 
     627             : /** \brief The lock was not obtained in time.
     628             :  *
     629             :  * This function gets called whenever the lock does not take with
     630             :  * the 'obtention_timeout' amount.
     631             :  *
     632             :  * Here we tell the system we are done with the lock so that way the
     633             :  * run() function returns silently (instead of throwing an error.)
     634             :  *
     635             :  * The lock will not be active so a call to is_locked() will say
     636             :  * so (i.e. return false.)
     637             :  */
     638           0 : void lock_connection::process_timeout()
     639             : {
     640           0 :     mark_done();
     641           0 : }
     642             : 
     643             : 
     644             : /** \brief The snapcommunicator is ready to talk to us.
     645             :  *
     646             :  * This function gets called once the connection between the snapcommunicator
     647             :  * and us is up and running.
     648             :  *
     649             :  * We immediately send the LOCK message to the snaplock running on this
     650             :  * system.
     651             :  */
     652           0 : void lock_connection::ready(snap_communicator_message & message)
     653             : {
     654           0 :     NOTUSED(message);
     655             : 
     656             :     // no reply expected from the COMMANDS message,
     657             :     // so send the LOCK now which initiates the locking
     658             :     //
     659           0 :     snap_communicator_message lock_message;
     660           0 :     lock_message.set_command("LOCK");
     661           0 :     lock_message.set_service("snaplock");
     662           0 :     lock_message.add_parameter("object_name", f_object_name);
     663           0 :     lock_message.add_parameter("pid", gettid());
     664           0 :     lock_message.add_parameter("timeout", f_obtention_timeout_date);
     665           0 :     lock_message.add_parameter("duration", f_lock_duration);
     666           0 :     if(f_unlock_duration != snap_lock::SNAP_UNLOCK_USES_LOCK_TIMEOUT)
     667             :     {
     668           0 :         lock_message.add_parameter("unlock_duration", f_unlock_duration);
     669             :     }
     670           0 :     send_message(lock_message);
     671           0 : }
     672             : 
     673             : 
     674             : /** \brief Stop the lock connections.
     675             :  *
     676             :  * Whenever a STOP or QUITTING is received this function gets called.
     677             :  *
     678             :  * We should never receive these messages in a lock_connection, although
     679             :  * it is definitely possible and part of the protocol. What is more likely
     680             :  * to happen is that the function
     681             :  *
     682             :  * \param[in] quitting  Whether the sending is telling us that we are quitting
     683             :  *            (i.e. rebooting) or just stopping.
     684             :  */
     685           0 : void lock_connection::stop(bool quitting)
     686             : {
     687           0 :     SNAP_LOG_WARNING("we received the ")
     688           0 :                     (quitting ? "QUITTING" : "STOP")
     689           0 :                     (" command while waiting for a lock.");
     690             : 
     691           0 :     mark_done();
     692           0 : }
     693             : 
     694             : 
     695             : /** \brief Process the LOCKED message.
     696             :  *
     697             :  * This function processes the LOCKED message meaning that it saves the
     698             :  * timeout_date as determined by the snaplock daemon and mark the message
     699             :  * loop as done which means it will return to the caller.
     700             :  *
     701             :  * Note however that the TCP/IP connection to snapcommunicator is kept
     702             :  * alive since we want to keep the lock until we are done with it.
     703             :  *
     704             :  * \param[in] message  The message we just received.
     705             :  */
     706           0 : void lock_connection::msg_locked(snap::snap_communicator_message & message)
     707             : {
     708             :     // the lock worked?
     709             :     //
     710           0 :     if(message.get_parameter("object_name") != f_object_name)
     711             :     {
     712             :         // somehow we received the LOCKED message with the wrong object name
     713             :         //
     714           0 :         throw snap_lock_failed_exception(QString("received lock confirmation for object \"%1\" instead of \"%2\" (LOCKED).")
     715           0 :                         .arg(message.get_parameter("object_name"))
     716           0 :                         .arg(f_object_name));
     717             :     }
     718           0 :     f_lock_timeout_date = message.get_integer_parameter("timeout_date");
     719             : 
     720             :     // change the timeout to the new date, since we are about to
     721             :     // quit the run() loop anyway, it should not be necessary
     722             :     //
     723           0 :     set_timeout_date(f_lock_timeout_date * 1000000L);
     724             : 
     725             :     // release hand back to the user while lock is still active
     726             :     //
     727           0 :     mark_done();
     728           0 : }
     729             : 
     730             : 
     731             : /** \brief Process the LOCKFAILED message.
     732             :  *
     733             :  * This function processes the LOCKFAILED message meaning that it saves
     734             :  * the lock was never obtained. In most cases this is due to a timeout.
     735             :  * The timeout can be due to the fact that the snaplock too is not yet
     736             :  * handling locks (LOCKREADY was not yet sent.)
     737             :  *
     738             :  * Since the lock failed, you must not make use of the resource(s) you
     739             :  * were trying to lock.
     740             :  *
     741             :  * \param[in] message  The message we just received.
     742             :  */
     743           0 : void lock_connection::msg_lockfailed(snap::snap_communicator_message & message)
     744             : {
     745           0 :     if(message.get_parameter("object_name") == f_object_name)
     746             :     {
     747           0 :         SNAP_LOG_WARNING("lock for object \"")
     748           0 :                         (f_object_name)
     749           0 :                         ("\" failed (LOCKEDFAILED) with reason: ")
     750           0 :                         (message.get_parameter("error"))
     751           0 :                         (".");
     752             :     }
     753             :     else
     754             :     {
     755             :         // this should not happen
     756             :         //
     757           0 :         SNAP_LOG_ERROR("object \"")
     758           0 :                       (message.get_parameter("object_name"))
     759           0 :                       ("\" just reported a lock failure and we received its message while trying to lock \"")
     760           0 :                       (f_object_name)
     761           0 :                       ("\" (LOCKEDFAILED) with reason: ")
     762           0 :                       (message.get_parameter("error"))
     763           0 :                       (".");
     764             :     }
     765             : 
     766           0 :     mark_done();
     767           0 : }
     768             : 
     769             : 
     770             : /** \brief Process the UNLOCKLED message.
     771             :  *
     772             :  * This function processes the UNLOCKED message.
     773             :  *
     774             :  * There are three potential cases when we can receive an UNLOCKED:
     775             :  *
     776             :  * \li a spurious message -- i.e. the message was not meant for this lock;
     777             :  *     this is not ever expected, we throw when this happens
     778             :  * \li lock timed out -- if your process takes too long before releasing
     779             :  *     the lock, you get this type of UNLOCKED with an error message
     780             :  *     saying "timedout"
     781             :  * \li acknowledgement -- when we send the UNLOCK message to a snaplock, it
     782             :  *     acknowledges with UNLOCKED (unless we already received the UNLOCKED
     783             :  *     because of a timeout, then it does not get sent twice)
     784             :  *
     785             :  * The first case (object name mismatched) is just not likely to ever
     786             :  * occur. It is more of a sanity test.
     787             :  *
     788             :  * The second case (lock timed out) can happen if your task takes longer
     789             :  * then expected. In that case, you should end your task, at least the
     790             :  * part that required the locked resources. You task must then acknowledge
     791             :  * that it is done by sending an UNLOCK message to snaplock.
     792             :  *
     793             :  * The third case (acknowledgement) is sent when you initiate the
     794             :  * release of your lock by sending the UNLOCK message (see unlock()
     795             :  * for more details.)
     796             :  *
     797             :  * \attention
     798             :  * In the second case (lock timed out) the lock will still be in place
     799             :  * until your send the UNLOCK message or the amount of time specified
     800             :  * in the unlock duration parameter. By default (which is also the minimum,)
     801             :  * this is 60 seconds. You can still be accessing your resources, but should
     802             :  * consider stopping as soon as possible and re-obtain a new lock to
     803             :  * continue your work.
     804             :  *
     805             :  * \exception snap_lock_failed_exception
     806             :  * This exception is raised when the UNLOCKED is received unexpectendly
     807             :  * (i.e. the object name does not match or the UNLOCKED is received before
     808             :  * the LOCKED was ever received.)
     809             :  *
     810             :  * \param[in] message  The message we just received.
     811             :  */
     812           0 : void lock_connection::msg_unlocked(snap::snap_communicator_message & message)
     813             : {
     814             :     // we should never receive an UNLOCKED from the wrong object
     815             :     // because each lock attempt is given a new name
     816             :     //
     817           0 :     if(message.get_parameter("object_name") != f_object_name)
     818             :     {
     819           0 :         f_lock_timeout_date = 0;
     820           0 :         SNAP_LOG_FATAL("object \"")
     821           0 :                       (message.get_parameter("object_name"))
     822           0 :                       ("\" just got unlocked and we received its message while trying to lock \"")
     823           0 :                       (f_object_name)
     824           0 :                       ("\" (UNLOCKED).");
     825           0 :         throw snap_lock_failed_exception(QString("object \"%1\" just got unlocked and we received its message while trying to lock \"%2\" (UNLOCKED).")
     826           0 :                             .arg(message.get_parameter("object_name"))
     827           0 :                             .arg(f_object_name));
     828             :     }
     829             : 
     830             :     // we should not receive the UNLOCKED before the LOCKED
     831             :     // also snaplock prevents the sending of more than one
     832             :     // UNLOCKED event so the following should never be true
     833             :     //
     834           0 :     if(f_lock_timeout_date == 0)
     835             :     {
     836           0 :         SNAP_LOG_FATAL("lock for object \"")
     837           0 :                       (f_object_name)
     838           0 :                       ("\" failed (UNLOCKED received before LOCKED).");
     839           0 :         throw snap_lock_failed_exception(QString("lock for object \"%1\" failed (UNLOCKED received before LOCKED).")
     840           0 :                         .arg(f_object_name));
     841             :     }
     842             : 
     843           0 :     f_lock_timeout_date = 0;
     844             : 
     845           0 :     if(message.has_parameter("error"))
     846             :     {
     847           0 :         QString const error(message.get_parameter("error"));
     848           0 :         if(error == "timedout")
     849             :         {
     850           0 :             SNAP_LOG_INFO("lock for object \"")
     851           0 :                             (f_object_name)
     852           0 :                             ("\" timed out.");
     853             : 
     854             :             // we are expected to send an acknowledgement in this case
     855             :             // but we do not do so here, the caller is expected to take
     856             :             // care of that by calling the unlock() function explicitly
     857             :             //
     858           0 :             f_lock_timedout = true;
     859             :         }
     860             :         else
     861             :         {
     862           0 :             SNAP_LOG_WARNING("lock for object \"")
     863           0 :                             (f_object_name)
     864           0 :                             ("\" failed with error: ")
     865           0 :                             (error)
     866           0 :                             (".");
     867             :         }
     868             :     }
     869           0 : }
     870             : 
     871             : 
     872             : 
     873             : 
     874             : 
     875             : 
     876             : 
     877             : }
     878             : // details namespace
     879             : 
     880             : 
     881             : 
     882             : // once in a while these definitions may be required (especially in debug mode)
     883             : //
     884             : // TODO: remove once we use C++17
     885             : //
     886             : constexpr snap_lock::timeout_t    snap_lock::SNAP_LOCK_DEFAULT_TIMEOUT;
     887             : constexpr snap_lock::timeout_t    snap_lock::SNAP_LOCK_MINIMUM_TIMEOUT;
     888             : constexpr snap_lock::timeout_t    snap_lock::SNAP_UNLOCK_MINIMUM_TIMEOUT;
     889             : constexpr snap_lock::timeout_t    snap_lock::SNAP_UNLOCK_USES_LOCK_TIMEOUT;
     890             : constexpr snap_lock::timeout_t    snap_lock::SNAP_MAXIMUM_OBTENTION_TIMEOUT;
     891             : constexpr snap_lock::timeout_t    snap_lock::SNAP_MAXIMUM_TIMEOUT;
     892             : 
     893             : 
     894             : 
     895             : 
     896             : 
     897             : 
     898             : 
     899             : 
     900             : 
     901             : 
     902             : 
     903             : 
     904             : /** \brief Create an inter-process lock.
     905             :  *
     906             :  * This constructor blocks until you obtained an inter-process lock
     907             :  * named \p object_name when you specify such a name. If you pass
     908             :  * an empty string, then the lock object is in the "unlocked" state.
     909             :  * You may call the lock() function once you are ready to lock the
     910             :  * system.
     911             :  *
     912             :  * By default you do not have to specify the lock_duration and
     913             :  * lock_obtention_timeout. When set to -1 (the default in the
     914             :  * constructor declaration,) these values are replaced by their
     915             :  * defaults that are set using the initialize_lock_duration_timeout()
     916             :  * and initialize_lock_obtention_timeout() functions.
     917             :  *
     918             :  * \warning
     919             :  * If the object name is specified in the constructor, then the system
     920             :  * attempts to lock the object immediately meaning that the only way
     921             :  * we can let you know that the lock failed is by throwing an exception.
     922             :  * If you want to avoid the exception, instead of doing a try/catch,
     923             :  * use the contructor without any parameters and then call the lock()
     924             :  * function and check whether you get true or false.
     925             :  *
     926             :  * \param[in] object_name  The name of the lock.
     927             :  * \param[in] lock_duration  The amount of time the lock will last if obtained.
     928             :  * \param[in] lock_obtention_timeout  The amount of time to wait for the lock.
     929             :  * \param[in] unlock_duration  The amount of time we have to acknowledge a
     930             :  *                             timed out lock.
     931             :  *
     932             :  * \sa snap_lock::initialize_lock_duration_timeout()
     933             :  */
     934           0 : snap_lock::snap_lock(QString const & object_name, timeout_t lock_duration, timeout_t lock_obtention_timeout, timeout_t unlock_duration)
     935             : {
     936           0 :     if(!object_name.isEmpty())
     937             :     {
     938           0 :         if(!lock(object_name, lock_duration, lock_obtention_timeout, unlock_duration))
     939             :         {
     940           0 :             throw snap_lock_failed_exception(QString("locking \"%1\" failed.").arg(object_name));
     941             :         }
     942             :     }
     943           0 : }
     944             : 
     945             : 
     946             : /** \brief Set how long inter-process locks last.
     947             :  *
     948             :  * This function lets you change the default amount of time the
     949             :  * inter-process locks last (i.e. their "Time To Live") in seconds.
     950             :  *
     951             :  * For example, to keep locks for 1 hour, use 3600.
     952             :  *
     953             :  * This value is used whenever a lock is created with its lock
     954             :  * duration set to -1.
     955             :  *
     956             :  * \warning
     957             :  * This function is not thread safe.
     958             :  *
     959             :  * \param[in] timeout  The total number of seconds locks will last for by default.
     960             :  */
     961           0 : void snap_lock::initialize_lock_duration_timeout(timeout_t timeout)
     962             : {
     963             :     // ensure a minimum of 3 seconds
     964           0 :     if(timeout < SNAP_LOCK_MINIMUM_TIMEOUT)
     965             :     {
     966           0 :         timeout = SNAP_LOCK_MINIMUM_TIMEOUT;
     967             :     }
     968           0 :     g_lock_duration_timeout = timeout;
     969           0 : }
     970             : 
     971             : 
     972             : /** \brief Retrieve the current lock duration.
     973             :  *
     974             :  * This function returns the current lock timeout. It can be useful if
     975             :  * you want to use a lock with a different timeout and then restore
     976             :  * the previous value afterward.
     977             :  *
     978             :  * Although if you have access/control of the lock itself, you may instead
     979             :  * want to specify the timeout in the snap_lock constructor directly.
     980             :  *
     981             :  * \return Current lock TTL in seconds.
     982             :  */
     983           0 : snap_lock::timeout_t snap_lock::current_lock_duration_timeout()
     984             : {
     985           0 :     return g_lock_duration_timeout;
     986             : }
     987             : 
     988             : 
     989             : /** \brief Set how long to wait for an inter-process lock to take.
     990             :  *
     991             :  * This function lets you change the default amount of time the
     992             :  * inter-process locks can wait before forfeiting the obtention
     993             :  * of a new lock.
     994             :  *
     995             :  * This amount can generally remain pretty small. For example,
     996             :  * you could say that you want to wait just 1 minute even though
     997             :  * the lock you want to get will last 24 hours. This means, within
     998             :  * one minute your process will be told that the lock cannot be
     999             :  * realized. In other words, you cannot do the work you intended
    1000             :  * to do. If the lock is released within the 1 minute and you
    1001             :  * are next on the list, you will get the lock and can proceed
    1002             :  * with the work you intended to do.
    1003             :  *
    1004             :  * The default is five seconds which for a front end is already
    1005             :  * quite enormous.
    1006             :  *
    1007             :  * This value is used whenever a lock is created with its lock
    1008             :  * obtention timeout value set to -1.
    1009             :  *
    1010             :  * \warning
    1011             :  * This function is not thread safe.
    1012             :  *
    1013             :  * \param[in] timeout  The total number of seconds to wait to obtain future locks.
    1014             :  */
    1015           0 : void snap_lock::initialize_lock_obtention_timeout(timeout_t timeout)
    1016             : {
    1017             :     // ensure a minimum of 3 seconds
    1018           0 :     if(timeout < SNAP_LOCK_MINIMUM_TIMEOUT)
    1019             :     {
    1020           0 :         timeout = SNAP_LOCK_MINIMUM_TIMEOUT;
    1021             :     }
    1022           0 :     g_lock_obtention_timeout = timeout;
    1023           0 : }
    1024             : 
    1025             : 
    1026             : /** \brief Retrieve the current lock obtention duration.
    1027             :  *
    1028             :  * This function returns the current timeout for the obtention of a lock.
    1029             :  * It can be useful if you want to use a lock with a different obtention
    1030             :  * timeout and then restore the previous value afterward.
    1031             :  *
    1032             :  * Although if you have access/control of the lock itself, you may instead
    1033             :  * want to specify the timeout in the snap_lock constructor directly.
    1034             :  *
    1035             :  * \return Current lock obtention maximum wait period in seconds.
    1036             :  */
    1037           0 : snap_lock::timeout_t snap_lock::current_lock_obtention_timeout()
    1038             : {
    1039           0 :     return g_lock_obtention_timeout;
    1040             : }
    1041             : 
    1042             : 
    1043             : /** \brief Set how long we wait on an inter-process unlock acknowledgement.
    1044             :  *
    1045             :  * This function lets you change the default amount of time the
    1046             :  * inter-process unlock last (i.e. their "Time To Survive" after
    1047             :  * a lock time out) in seconds.
    1048             :  *
    1049             :  * For example, to allow your application to take up to 5 minuts to
    1050             :  * acknowldege a timed out lock, set this value to 300.
    1051             :  *
    1052             :  * This value is used whenever a lock is created with its unlock
    1053             :  * duration set to -1. Note that by default this value is itself
    1054             :  * -1 meaning that the unlock duration will use the lock duration.
    1055             :  *
    1056             :  * \warning
    1057             :  * This function is not thread safe.
    1058             :  *
    1059             :  * \param[in] timeout  The total number of seconds timed out locks wait for
    1060             :  *                     an acknowledgement.
    1061             :  */
    1062           0 : void snap_lock::initialize_unlock_duration_timeout(timeout_t timeout)
    1063             : {
    1064             :     // ensure a minimum of 60 seconds
    1065             :     // but allow -1 as a valid value
    1066             :     //
    1067           0 :     if(timeout != SNAP_UNLOCK_USES_LOCK_TIMEOUT
    1068           0 :     && timeout < SNAP_UNLOCK_MINIMUM_TIMEOUT)
    1069             :     {
    1070           0 :         timeout = snap_lock::SNAP_UNLOCK_MINIMUM_TIMEOUT;
    1071             :     }
    1072           0 :     g_unlock_duration_timeout = timeout;
    1073           0 : }
    1074             : 
    1075             : 
    1076             : /** \brief Retrieve the current unlock duration.
    1077             :  *
    1078             :  * This function returns the current unlock duration. It can be useful
    1079             :  * if you want to use a lock with a different timeout and then restore
    1080             :  * the previous value afterward.
    1081             :  *
    1082             :  * Although if you have access/control of the lock itself, you may instead
    1083             :  * want to specify the unlock duration in the snap_lock constructor directly.
    1084             :  *
    1085             :  * \return Current unlock TTL in seconds.
    1086             :  */
    1087           0 : snap_lock::timeout_t snap_lock::current_unlock_duration_timeout()
    1088             : {
    1089           0 :     return g_unlock_duration_timeout;
    1090             : }
    1091             : 
    1092             : 
    1093             : /** \brief Initialize the snapcommunicator details.
    1094             :  *
    1095             :  * This function must be called before any lock can be obtained in order
    1096             :  * to define the address and port to use to connect to the snapcommunicator
    1097             :  * process.
    1098             :  *
    1099             :  * \warning
    1100             :  * This function is not thread safe.
    1101             :  *
    1102             :  * \param[in] addr  The address of snapcommunicator.
    1103             :  * \param[in] port  The port used to connect to snapcommunicator.
    1104             :  * \param[in] mode  The mode used to open the connection (i.e. plain or secure.)
    1105             :  */
    1106           0 : void snap_lock::initialize_snapcommunicator(std::string const & addr, int port, tcp_client_server::bio_client::mode_t mode)
    1107             : {
    1108           0 :     g_snapcommunicator_address = addr;
    1109           0 :     g_snapcommunicator_port = port;
    1110           0 :     g_snapcommunicator_mode = mode;
    1111             : 
    1112             : #ifdef _DEBUG
    1113           0 :     g_snapcommunicator_initialized = true;
    1114             : #endif
    1115           0 : }
    1116             : 
    1117             : 
    1118             : /** \brief Attempt to lock the specified object.
    1119             :  *
    1120             :  * This function attempts to lock the specified object. If a lock
    1121             :  * was already in place, the function always sends an UNLOCK first.
    1122             :  *
    1123             :  * On a time scale, the lock_duration and lock_obtention_timeout
    1124             :  * parameters are used as follow:
    1125             :  *
    1126             :  * \li Get current time ('now')
    1127             :  * \li Calculate when it will be too late for this process to get the
    1128             :  *     lock, this is 'now + lock_obtention_timeout', if we get the
    1129             :  *     lock before then, all good, if not, we return with false
    1130             :  * \li Assuming we obtained the lock, get the new current time ('locked_time')
    1131             :  *     and add the lock_duration to know when the lock ends
    1132             :  *     ('lock_time + lock_duration'). Your process can run up until that
    1133             :  *     date is reached.
    1134             :  *
    1135             :  * \note
    1136             :  * The existing lock, if there is one, gets unlock()'ed before the systems
    1137             :  * attempts to gain a new lock. This is particularly important especially
    1138             :  * since we may be trying to obtain the exact same lock again.
    1139             :  *
    1140             :  * \param[in] object_name  The name of the object to lock.
    1141             :  * \param[in] lock_duration  The amount of time the lock will last if obtained.
    1142             :  * \param[in] lock_obtention_timeout  The amount of time to wait for the lock.
    1143             :  *
    1144             :  * \return Whether the lock was obtained (true) or not (false).
    1145             :  */
    1146           0 : bool snap_lock::lock(QString const & object_name, timeout_t lock_duration, timeout_t lock_obtention_timeout, timeout_t unlock_duration)
    1147             : {
    1148             :     // although we have a shared pointer, the order in which the lock
    1149             :     // and unlock work would be inverted if we were to just call
    1150             :     // the reset() function (i.e. it would try to obtain the new lock
    1151             :     // first, then release the old lock second; which could cause a
    1152             :     // dead-lock,) therefore it is better if we explicitly call the
    1153             :     // unlock() function first
    1154             :     //
    1155           0 :     unlock();
    1156             : 
    1157           0 :     f_lock_connection = std::make_shared<details::lock_connection>(object_name, lock_duration, lock_obtention_timeout, unlock_duration);
    1158           0 :     f_lock_connection->run();
    1159             : 
    1160           0 :     return f_lock_connection->is_locked();
    1161             : }
    1162             : 
    1163             : 
    1164             : /** \brief Release the inter-process lock early.
    1165             :  *
    1166             :  * This function releases this inter-process lock early.
    1167             :  *
    1168             :  * If the lock was not active (i.e. lock() was never called or returned
    1169             :  * false last time you called it), then nothing happens.
    1170             :  *
    1171             :  * This is useful if you keep the same lock object around and want to
    1172             :  * lock/unlock it at various time. It is actually very important to
    1173             :  * unlock your locks so other processes can then gain access to the
    1174             :  * resources that they protect.
    1175             :  */
    1176           0 : void snap_lock::unlock()
    1177             : {
    1178           0 :     if(f_lock_connection != nullptr)
    1179             :     {
    1180             :         // be explicit although the destructor of the lock connection
    1181             :         // calls unlock() on its own
    1182             :         //
    1183           0 :         f_lock_connection->unlock();
    1184           0 :         f_lock_connection.reset();
    1185             :     }
    1186           0 : }
    1187             : 
    1188             : 
    1189             : /** \brief Get the exact time when the lock times out.
    1190             :  *
    1191             :  * This function can be used to check when the lock will be considerd
    1192             :  * out of date and thus when you should stop doing whatever requires
    1193             :  * said lock.
    1194             :  *
    1195             :  * The time is in second and you can compare it against time(nullptr) to
    1196             :  * know whether it timed out already or how long you still have:
    1197             :  *
    1198             :  * \code
    1199             :  *      snap::snap_lock::timeout_t const diff(lock.get_timeout_date() - time(nullptr));
    1200             :  *      if(diff <= 0)
    1201             :  *      {
    1202             :  *          // locked already timed out
    1203             :  *          ...
    1204             :  *      }
    1205             :  *      else
    1206             :  *      {
    1207             :  *          // you have another 'diff' seconds to work on your stuff
    1208             :  *          ...
    1209             :  *      }
    1210             :  * \endcode
    1211             :  *
    1212             :  * Remember that this exact date was sent to the snaplock system but you may
    1213             :  * have a clock with a one second or so difference between various computers
    1214             :  * so if the amount is really small (1 or 2) you should probably already
    1215             :  * considered that the locked has timed out.
    1216             :  *
    1217             :  * \return The date when this lock will be over, or zero if the lock is
    1218             :  *         not currently active.
    1219             :  *
    1220             :  * \sa lock_timedout()
    1221             :  * \sa is_locked()
    1222             :  */
    1223           0 : time_t snap_lock::get_timeout_date() const
    1224             : {
    1225           0 :     if(f_lock_connection != nullptr)
    1226             :     {
    1227           0 :         return f_lock_connection->get_lock_timeout_date();
    1228             :     }
    1229             : 
    1230           0 :     return 0;
    1231             : }
    1232             : 
    1233             : 
    1234             : /** \brief This function checks whether the lock is considered locked.
    1235             :  *
    1236             :  * This function checks whether the lock worked and is still considered
    1237             :  * locked, as in, it did not yet time out.
    1238             :  *
    1239             :  * This function does not access the network at all. It checks whether
    1240             :  * the lock is still valid using the current time and the time at which
    1241             :  * the LOCKED message said the lock would time out.
    1242             :  *
    1243             :  * If you want to know whether snaplock decided that the lock timed out
    1244             :  * then you need to consider calling the lock_timedout() function instead.
    1245             :  *
    1246             :  * If you want to know how my time you have left on this lock, use the
    1247             :  * get_timeout_date() instead and subtract time(nullptr). If positive,
    1248             :  * that's the number of seconds you have left.
    1249             :  *
    1250             :  * \note
    1251             :  * The function returns false if there is no lock connection, which
    1252             :  * means that there is no a lock in effect at this time.
    1253             :  *
    1254             :  * \return true if the lock is still in effect.
    1255             :  *
    1256             :  * \sa lock_timedout()
    1257             :  * \sa get_timeout_date()
    1258             :  */
    1259           0 : bool snap_lock::is_locked() const
    1260             : {
    1261           0 :     if(f_lock_connection != nullptr)
    1262             :     {
    1263           0 :         return f_lock_connection->is_locked();
    1264             :     }
    1265             : 
    1266           0 :     return false;
    1267             : }
    1268             : 
    1269             : 
    1270             : /** \brief This function checks whether the lock timed out.
    1271             :  *
    1272             :  * This function checks whether the lock went passed the deadline and
    1273             :  * we were sent an UNLOCKED message with the error set to "timedout".
    1274             :  * If so, this function returns true and that means we have to stop
    1275             :  * the work we are doing as soon as possible or the resource may be
    1276             :  * released to another process and thus a mess may happen.
    1277             :  *
    1278             :  * Note that the snaplock daemon holds a lock after it send an UNLOCKED
    1279             :  * with a "timedout" error for an amount of time equal to the duration
    1280             :  * of the lock or one minute, whichever is longer (most often, it will
    1281             :  * be one minute, although some locks for backends are often held for
    1282             :  * a much longer period of time.)
    1283             :  *
    1284             :  * \note
    1285             :  * The function checks whether a message was received, so it does not
    1286             :  * really spend much time at all with the network. It just looks at
    1287             :  * the socket buffer and if data is present, it reads only. However,
    1288             :  * it reads those bytes one by one, so that can be a bit slow. Only
    1289             :  * the UNLOCKED message should be sent and it will be rather short
    1290             :  * so I would not worry about it since it will work with a buffer
    1291             :  * which is going to be really fast anyway.
    1292             :  *
    1293             :  * \note
    1294             :  * The function returns true if there is no lock connection, which
    1295             :  * means that there is no a lock in effect at this time.
    1296             :  *
    1297             :  * \return true if the lock timed out and you need to stop work on
    1298             :  *         the locked resource as soon as possible.
    1299             :  *
    1300             :  * \sa is_locked()
    1301             :  * \sa get_timeout_date()
    1302             :  */
    1303           0 : bool snap_lock::lock_timedout() const
    1304             : {
    1305           0 :     if(f_lock_connection != nullptr)
    1306             :     {
    1307           0 :         return f_lock_connection->lock_timedout();
    1308             :     }
    1309             : 
    1310           0 :     return true;
    1311             : }
    1312             : 
    1313             : 
    1314             : /** \brief Initialize an RAII lock duration timeout.
    1315             :  *
    1316             :  * You may change the lock duration timeout for a part of your code by
    1317             :  * using this RAII lock object. Create it on the stack and it will
    1318             :  * automatically restore the old timeout once you exit your block.
    1319             :  *
    1320             :  * \code
    1321             :  *    {
    1322             :  *        // change duration to at least 5 min.
    1323             :  *        //
    1324             :  *        raii_lock_duration_timeout raii_lock_duration_timeout_instance(300);
    1325             :  *
    1326             :  *        // create the lock
    1327             :  *        //
    1328             :  *        snap_lock lock(object_we_are_to_work_on);
    1329             :  *
    1330             :  *        [...do work...]
    1331             :  *    }
    1332             :  *    // here the pervious timeout was restored
    1333             :  * \endcode
    1334             :  *
    1335             :  * \attention
    1336             :  * The new timeout is ignored if smaller than the current timeout.
    1337             :  *
    1338             :  * \todo
    1339             :  * We probably want to offer a way to define the comparison function so
    1340             :  * that way we can force the change, or make it only if smaller instead
    1341             :  * of larger.
    1342             :  *
    1343             :  * \param[in] temporary_lock_timeout  The new timeout.
    1344             :  */
    1345           0 : raii_lock_duration_timeout::raii_lock_duration_timeout(snap_lock::timeout_t temporary_lock_timeout)
    1346           0 :     : f_save_timeout(snap::snap_lock::current_lock_obtention_timeout())
    1347             : {
    1348           0 :     if(temporary_lock_timeout > f_save_timeout)
    1349             :     {
    1350           0 :         snap::snap_lock::initialize_lock_duration_timeout(temporary_lock_timeout);
    1351             :     }
    1352           0 : }
    1353             : 
    1354             : 
    1355             : /** \brief The destructor restores the previous timeout value.
    1356             :  *
    1357             :  * This destructor restores the lock duration timeout to the value it was
    1358             :  * set to before you instantiated this object.
    1359             :  */
    1360           0 : raii_lock_duration_timeout::~raii_lock_duration_timeout()
    1361             : {
    1362           0 :     snap::snap_lock::initialize_lock_duration_timeout(f_save_timeout);
    1363           0 : }
    1364             : 
    1365             : 
    1366             : 
    1367             : /** \brief Initialize an RAII lock obtention timeout.
    1368             :  *
    1369             :  * You may change the lock obtention timeout for a part of your code by
    1370             :  * using this RAII lock object. Create it on the stack and it will
    1371             :  * automatically restore the old timeout once you exit your block.
    1372             :  *
    1373             :  * \code
    1374             :  *    {
    1375             :  *        // change obtention to at least 5 min.
    1376             :  *        //
    1377             :  *        raii_lock_obtention_timeout raii_lock_obtention_timeout_instance(300);
    1378             :  *
    1379             :  *        // create the lock
    1380             :  *        //
    1381             :  *        snap_lock lock(object_we_are_to_work_on);
    1382             :  *
    1383             :  *        [...do work...]
    1384             :  *    }
    1385             :  *    // here the pervious timeout was restored
    1386             :  * \endcode
    1387             :  *
    1388             :  * \attention
    1389             :  * The new timeout is ignored if smaller than the current timeout.
    1390             :  *
    1391             :  * \todo
    1392             :  * We probably want to offer a way to define the comparison function so
    1393             :  * that way we can force the change, or make it only if smaller instead
    1394             :  * of larger.
    1395             :  *
    1396             :  * \param[in] temporary_lock_timeout  The new timeout.
    1397             :  */
    1398           0 : raii_lock_obtention_timeout::raii_lock_obtention_timeout(snap_lock::timeout_t temporary_lock_timeout)
    1399           0 :     : f_save_timeout(snap::snap_lock::current_lock_obtention_timeout())
    1400             : {
    1401           0 :     if(temporary_lock_timeout > f_save_timeout)
    1402             :     {
    1403           0 :         snap::snap_lock::initialize_lock_obtention_timeout(temporary_lock_timeout);
    1404             :     }
    1405           0 : }
    1406             : 
    1407             : 
    1408             : /** \brief The destructor restores the previous timeout value.
    1409             :  *
    1410             :  * This destructor restores the lock obtention timeout to the value it was
    1411             :  * set to before you instantiated this object.
    1412             :  */
    1413           0 : raii_lock_obtention_timeout::~raii_lock_obtention_timeout()
    1414             : {
    1415           0 :     snap::snap_lock::initialize_lock_obtention_timeout(f_save_timeout);
    1416           0 : }
    1417             : 
    1418             : 
    1419           6 : } // namespace snap
    1420             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13