]> granicus.if.org Git - icinga2/blob - lib/base/objectlock.cpp
lib->compat->statusdatawriter: fix notifications_enabled
[icinga2] / lib / base / objectlock.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/objectlock.hpp"
21 #include <boost/thread/recursive_mutex.hpp>
22
23 using namespace icinga;
24
25 #define I2MUTEX_UNLOCKED 0
26 #define I2MUTEX_LOCKED 1
27
28 ObjectLock::~ObjectLock()
29 {
30         Unlock();
31 }
32
33 ObjectLock::ObjectLock(const Object::Ptr& object)
34         : m_Object(object.get()), m_Locked(false)
35 {
36         if (m_Object)
37                 Lock();
38 }
39
40 ObjectLock::ObjectLock(const Object *object)
41         : m_Object(object), m_Locked(false)
42 {
43         if (m_Object)
44                 Lock();
45 }
46
47 void ObjectLock::LockMutex(const Object *object)
48 {
49         unsigned int it = 0;
50
51 #ifdef _WIN32
52 #       ifdef _WIN64
53         while (likely(InterlockedCompareExchange64((LONGLONG *)&object->m_Mutex, I2MUTEX_LOCKED, I2MUTEX_UNLOCKED) != I2MUTEX_UNLOCKED)) {
54 #       else /* _WIN64 */
55         while (likely(InterlockedCompareExchange(&object->m_Mutex, I2MUTEX_LOCKED, I2MUTEX_UNLOCKED) != I2MUTEX_UNLOCKED)) {
56 #       endif /* _WIN64 */
57 #else /* _WIN32 */
58         while (likely(!__sync_bool_compare_and_swap(&object->m_Mutex, I2MUTEX_UNLOCKED, I2MUTEX_LOCKED))) {
59 #endif /* _WIN32 */
60                 if (likely(object->m_Mutex > I2MUTEX_LOCKED)) {
61                         auto *mtx = reinterpret_cast<boost::recursive_mutex *>(object->m_Mutex);
62                         mtx->lock();
63
64                         return;
65                 }
66
67                 Spin(it);
68                 it++;
69         }
70
71         auto *mtx = new boost::recursive_mutex();
72         mtx->lock();
73 #ifdef _WIN32
74 #       ifdef _WIN64
75         InterlockedCompareExchange64((LONGLONG *)&object->m_Mutex, reinterpret_cast<LONGLONG>(mtx), I2MUTEX_LOCKED);
76 #       else /* _WIN64 */
77         InterlockedCompareExchange(&object->m_Mutex, reinterpret_cast<LONG>(mtx), I2MUTEX_LOCKED);
78 #       endif /* _WIN64 */
79 #else /* _WIN32 */
80         __sync_bool_compare_and_swap(&object->m_Mutex, I2MUTEX_LOCKED, reinterpret_cast<uintptr_t>(mtx));
81 #endif /* _WIN32 */
82 }
83
84 void ObjectLock::Lock()
85 {
86         ASSERT(!m_Locked && m_Object);
87
88         LockMutex(m_Object);
89
90         m_Locked = true;
91
92 #ifdef I2_DEBUG
93 #       ifdef _WIN32
94         InterlockedExchange(&m_Object->m_LockOwner, GetCurrentThreadId());
95 #       else /* _WIN32 */
96         __sync_lock_test_and_set(&m_Object->m_LockOwner, pthread_self());
97 #       endif /* _WIN32 */
98 #endif /* I2_DEBUG */
99 }
100
101 void ObjectLock::Spin(unsigned int it)
102 {
103         if (it < 8) {
104                 /* Do nothing. */
105         }
106 #ifdef SPIN_PAUSE
107         else if (it < 16) {
108                 SPIN_PAUSE();
109         }
110 #endif /* SPIN_PAUSE */
111         else {
112 #ifdef _WIN32
113                 Sleep(0);
114 #else /* _WIN32 */
115                 sched_yield();
116 #endif /* _WIN32 */
117         }
118 }
119
120 void ObjectLock::Unlock()
121 {
122 #ifdef I2_DEBUG
123         if (m_Locked) {
124 #       ifdef _WIN32
125                 InterlockedExchange(&m_Object->m_LockOwner, 0);
126 #       else /* _WIN32 */
127                 __sync_lock_release(&m_Object->m_LockOwner);
128 #       endif /* _WIN32 */
129         }
130 #endif /* I2_DEBUG */
131
132         if (m_Locked) {
133                 reinterpret_cast<boost::recursive_mutex *>(m_Object->m_Mutex)->unlock();
134                 m_Locked = false;
135         }
136 }