summaryrefslogtreecommitdiff
path: root/src/modules/json/JSONNode_Mutex.cpp
blob: 2c5236632062c96942e0868d84c4f0a841badf6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-12 Miranda IM, 2012-13 Miranda NG project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "..\..\core\commonheaders.h"
#include <m_json.h>

#include "libJSON.h"

#ifdef JSON_MUTEX_CALLBACKS

json_mutex_callback_t json_lock_callback = 0;
json_mutex_callback_t json_unlock_callback = 0;
void * global_mutex = 0;
void * manager_mutex = 0;

struct AutoLock {
	AutoLock(void){
		json_lock_callback(manager_mutex);
	}
	~AutoLock(void){
		json_unlock_callback(manager_mutex);
	}
};

#include <map>
#ifdef JSON_MUTEX_MANAGE
	json_mutex_callback_t json_destroy = 0;
	std::map<void *, unsigned int> mutex_manager;

	//make sure that the global mutex is taken care of too
	struct auto_global {
		auto_global(void){}
		~auto_global(void){
			if (global_mutex){
				JSON_ASSERT_SAFE(json_destroy, JSON_TEXT("No json_destroy in mutex managed mode"), return;);
				json_destroy(global_mutex);
			}
		}
	};
	auto_global cleanupGlobal;
#endif

void JSONNode::register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, void * manager_lock){
	json_lock_callback = lock;
	json_unlock_callback = unlock;
	manager_mutex = manager_lock;
}

void JSONNode::set_global_mutex(void * mutex){
	global_mutex = mutex;
}

void JSONNode::set_mutex(void * mutex){
	makeUniqueInternal();
	internal -> _set_mutex(mutex);
}

std::map<int, std::map<void *, unsigned int> > threadlocks;

void * JSONNode::getThisLock(JSONNode * pthis){
	if (pthis -> internal -> mylock){
		return pthis -> internal -> mylock;
	}
	JSON_ASSERT_SAFE(global_mutex, JSON_TEXT("No global_mutex"), return 0;);
	return global_mutex;
}

void JSONNode::lock(int thread){
	JSON_ASSERT_SAFE(json_lock_callback, JSON_TEXT("No locking callback"), return;);

	AutoLock lockControl;

	//first, figure out what needs to be locked
	void * thislock = getThisLock(this);
	#ifdef JSON_SAFE
		if (!thislock) return;
	#endif

	//make sure that the same thread isn't locking it more than once (possible due to complex ref counting)
	std::map<int, std::map<void *, unsigned int> >::iterator it = threadlocks.find(thread);
	if (it == threadlocks.end()) {
		std::map<void *, unsigned int> newthread;
		newthread[thislock] = 1;
		threadlocks.insert(std::pair<int, std::map<void *, unsigned int> >(thread, newthread));
	} else { //this thread already has some things locked, check if the current mutex is
		std::map<void *, unsigned int> & newthread = it -> second;
		std::map<void *, unsigned int>::iterator locker = newthread.find(thislock);
		if (locker == newthread.end()) { //current mutex is not locked, set it to locked
			newthread.insert(std::pair<void *, unsigned int>(thislock, 1));
		} else { //it's already locked, don't relock it
			++(locker -> second);
			return; //don't try to relock, it will deadlock the program
		}
	}

	//if I need to, lock it
	json_lock_callback(thislock);
}

void JSONNode::unlock(int thread){
	JSON_ASSERT_SAFE(json_unlock_callback, JSON_TEXT("No unlocking callback"), return;);

	AutoLock lockControl;

	//first, figure out what needs to be locked
	void * thislock = getThisLock(this);
	#ifdef JSON_SAFE
		if (!thislock) return;
	#endif

	//get it out of the map
	std::map<int, std::map<void *, unsigned int> >::iterator it = threadlocks.find(thread);
	JSON_ASSERT_SAFE(it != threadlocks.end(), JSON_TEXT("thread unlocking something it didn't lock"), return;);

	//get the mutex out of the thread
	std::map<void *, unsigned int> & newthread = it -> second;
	std::map<void *, unsigned int>::iterator locker = newthread.find(thislock);
	JSON_ASSERT_SAFE(locker != newthread.end(), JSON_TEXT("thread unlocking mutex it didn't lock"), return;);

	//unlock it
	if (--(locker -> second)) return; //other nodes is this same thread still have a lock on it

	//if I need to, unlock it
	newthread.erase(locker);
	json_unlock_callback(thislock);
}

#ifdef JSON_MUTEX_MANAGE
	void JSONNode::register_mutex_destructor(json_mutex_callback_t destroy){
		json_destroy = destroy;
	}
#endif

void internalJSONNode::_set_mutex(void * mutex, bool unset){
	if (unset) _unset_mutex(); //for reference counting
	mylock = mutex;
	if (mutex){
		#ifdef JSON_MUTEX_MANAGE
			std::map<void *, unsigned int>::iterator it = mutex_manager.find(mutex);
			if (it == mutex_manager.end()) {
				mutex_manager.insert(std::pair<void *, unsigned int>(mutex, 1));
			} else {
				++it -> second;
			}
		#endif
		json_foreach(Children, myrunner){
			(*myrunner) -> set_mutex(mutex);
		}
	}
}

void internalJSONNode::_unset_mutex(void){
	#ifdef JSON_MUTEX_MANAGE
		if (mylock){
			std::map<void *, unsigned int>::iterator it = mutex_manager.find(mylock);
			JSON_ASSERT_SAFE(it != mutex_manager.end(), JSON_TEXT("Mutex not managed"), return;);
			--it -> second;
			if (it -> second == 0){
				JSON_ASSERT_SAFE(json_destroy, JSON_TEXT("You didn't register a destructor for mutexes"), return;);
				mutex_manager.erase(it);
			}
		}
	#endif
}

#ifdef JSON_DEBUG
	#ifndef JSON_LIBRARY
		JSONNode internalJSONNode::DumpMutex(void) const {
			JSONNode mut(JSON_NODE);
			mut.set_name(JSON_TEXT("mylock"));
			#ifdef JSON_MUTEX_MANAGE
				if (mylock){
				mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("this"), (long)mylock)));
				std::map<void *, unsigned int>::iterator it = mutex_manager.find(mylock);
				if (it == mutex_manager.end()) {
					mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("references"), JSON_TEXT("error"))));
				} else {
					mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("references"), it -> second)));
				}
				} else {
				mut = (long)mylock;
				}
			#else
				mut = (long)mylock;
			#endif
			return mut;
		}
	#endif
#endif

#else
	#ifdef JSON_MUTEX_MANAGE
		#error You can not have JSON_MUTEX_MANAGE on without JSON_MUTEX_CALLBACKS
	#endif
#endif