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
|