xref: /trunk/main/sal/rtl/source/unload.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sal.hxx"
30 #include <rtl/unload.h>
31 #include <rtl/alloc.h>
32 #include <rtl/ustring.hxx>
33 #include <osl/mutex.hxx>
34 #include <hash_map>
35 #include "rtl/allocator.hxx"
36 
37 #include <functional>
38 #include <list>
39 #include <deque>
40 
41 using osl::MutexGuard;
42 
43 //----------------------------------------------------------------------------
44 
45 static void rtl_notifyUnloadingListeners();
46 
47 static sal_Bool isEqualTimeValue ( const TimeValue* time1,  const TimeValue* time2)
48 {
49 	if( time1->Seconds == time2->Seconds &&
50 		time1->Nanosec == time2->Nanosec)
51 		return sal_True;
52 	else
53 		return sal_False;
54 }
55 
56 static sal_Bool isGreaterTimeValue(  const TimeValue* time1,  const TimeValue* time2)
57 {
58 	sal_Bool retval= sal_False;
59 	if ( time1->Seconds > time2->Seconds)
60 		retval= sal_True;
61 	else if ( time1->Seconds == time2->Seconds)
62 	{
63 		if( time1->Nanosec > time2->Nanosec)
64 			retval= sal_True;
65 	}
66 	return retval;
67 }
68 
69 static sal_Bool isGreaterEqualTimeValue( const TimeValue* time1, const TimeValue* time2)
70 {
71 	if( isEqualTimeValue( time1, time2) )
72 		return sal_True;
73 	else if( isGreaterTimeValue( time1, time2))
74 		return sal_True;
75 	else
76 		return sal_False;
77 }
78 
79 static void addTimeValue( const TimeValue* value1, const TimeValue* value2, TimeValue* result)
80 {
81 	sal_uInt64 sum;
82 	result->Nanosec=0;
83 	result->Seconds=0;
84 
85 	sum= value1->Nanosec + value2->Nanosec;
86 	if( sum >= 1000000000 )
87 	{
88 		result->Seconds=1;
89 		sum -= 1000000000;
90 	}
91 	result->Nanosec= (sal_uInt32)sum;
92 	result->Seconds += value1->Seconds + value2->Seconds;
93 }
94 
95 
96 static sal_Bool hasEnoughTimePassed( const TimeValue* unusedSince, const TimeValue* timespan)
97 {
98 	sal_Bool retval= sal_False;
99 	TimeValue currentTime;
100 	if( osl_getSystemTime( &currentTime))
101 	{
102 		TimeValue addedTime;
103 		addTimeValue( unusedSince, timespan, &addedTime);
104 		if( isGreaterEqualTimeValue( &currentTime, &addedTime))
105 			retval= sal_True;
106 	}
107 
108 	return retval;
109 }
110 
111 static osl::Mutex* getUnloadingMutex()
112 {
113 	static osl::Mutex * g_pMutex= NULL;
114 	if (!g_pMutex)
115 	{
116 		MutexGuard guard( osl::Mutex::getGlobalMutex() );
117 		if (!g_pMutex)
118 		{
119 			static osl::Mutex g_aMutex;
120 			g_pMutex= &g_aMutex;
121 		}
122 	}
123 	return g_pMutex;
124 }
125 
126 extern "C" void rtl_moduleCount_acquire(rtl_ModuleCount * that )
127 {
128 	rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
129 	osl_incrementInterlockedCount( &pMod->counter);
130 }
131 
132 extern "C" void rtl_moduleCount_release( rtl_ModuleCount * that )
133 {
134 	rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
135 	OSL_ENSURE( pMod->counter >0 , "library counter incorrect" );
136 	osl_decrementInterlockedCount( &pMod->counter);
137 	if( pMod->counter == 0)
138 	{
139 		MutexGuard guard( getUnloadingMutex());
140 
141 		if( sal_False == osl_getSystemTime( &pMod->unusedSince) )
142 		{
143 			// set the time to 0 if we could not get the time
144 			pMod->unusedSince.Seconds= 0;
145 			pMod->unusedSince.Nanosec= 0;
146 		}
147 	}
148 }
149 
150 
151 struct hashModule
152 {
153 	size_t operator()( const oslModule& rkey) const
154 	{
155 		return (size_t)rkey;
156 	}
157 };
158 
159 typedef std::hash_map<
160     oslModule,
161 	std::pair<sal_uInt32, component_canUnloadFunc>,
162 	hashModule,
163 	std::equal_to<oslModule>,
164 	rtl::Allocator<oslModule>
165 > ModuleMap;
166 
167 typedef ModuleMap::iterator Mod_IT;
168 
169 static ModuleMap& getModuleMap()
170 {
171 	static ModuleMap * g_pMap= NULL;
172 	if (!g_pMap)
173 	{
174 		MutexGuard guard( getUnloadingMutex() );
175 		if (!g_pMap)
176 		{
177 			static ModuleMap g_aModuleMap;
178 			g_pMap= &g_aModuleMap;
179 		}
180 	}
181 	return *g_pMap;
182 }
183 
184 extern "C" sal_Bool rtl_moduleCount_canUnload( rtl_StandardModuleCount * that, TimeValue * libUnused)
185 {
186 	if (that->counter == 0)
187 	{
188 		MutexGuard guard( getUnloadingMutex());
189 		if (libUnused && (that->counter == 0))
190 		{
191 			rtl_copyMemory(libUnused, &that->unusedSince, sizeof(TimeValue));
192 		}
193 	}
194 	return (that->counter == 0);
195 }
196 
197 
198 extern "C" sal_Bool SAL_CALL rtl_registerModuleForUnloading( oslModule module)
199 {
200 	MutexGuard guard( getUnloadingMutex());
201 	ModuleMap& moduleMap= getModuleMap();
202 	sal_Bool ret= sal_True;
203 
204 	// If the module has been registered before, then find it and increment
205 	// its reference cout
206 	Mod_IT it= moduleMap.find( module);
207 	if( it != moduleMap.end())
208 	{
209 		//module already registered, increment ref count
210 		it->second.first++;
211 	}
212 	else
213 	{
214 		// Test if the module supports unloading (exports component_canUnload)
215 		rtl::OUString name(RTL_CONSTASCII_USTRINGPARAM( COMPONENT_CANUNLOAD));
216 		component_canUnloadFunc pFunc=
217 			(component_canUnloadFunc)osl_getFunctionSymbol( module, name.pData);
218 		if (pFunc)
219 		{
220 			//register module for the first time, set ref count to 1
221 			moduleMap[module]= std::make_pair((sal_uInt32)1, pFunc);
222 		}
223 		else
224 			ret= sal_False;
225 	}
226 	return ret;
227 }
228 
229 extern "C" void SAL_CALL rtl_unregisterModuleForUnloading( oslModule module)
230 {
231 	MutexGuard guard( getUnloadingMutex());
232 
233 	ModuleMap& moduleMap= getModuleMap();
234 	Mod_IT it= moduleMap.find( module);
235 	if( it != moduleMap.end() )
236 	{
237 		// The module is registered, decrement ref count.
238 		it->second.first --;
239 
240 		// If the refcount == 0 then remove the module from the map
241 		if( it->second.first == 0)
242 			moduleMap.erase( it);
243 	}
244 }
245 
246 extern "C" void SAL_CALL rtl_unloadUnusedModules( TimeValue* libUnused)
247 {
248 	MutexGuard guard( getUnloadingMutex());
249 
250 	typedef std::list< oslModule, rtl::Allocator<oslModule> > list_type;
251 	list_type unloadedModulesList;
252 
253 	ModuleMap& moduleMap= getModuleMap();
254 	Mod_IT it_e= moduleMap.end();
255 
256 	// notify all listeners
257 	rtl_notifyUnloadingListeners();
258 
259 	// prepare default TimeValue if argumetn is NULL
260 	TimeValue nullTime={0,0};
261 	TimeValue* pLibUnused= libUnused? libUnused : &nullTime;
262 
263 	Mod_IT it= moduleMap.begin();
264 	for (; it != it_e; ++it)
265 	{
266 		//can the module be unloaded?
267 		component_canUnloadFunc func= it->second.second;
268 		TimeValue unusedSince= {0, 0};
269 
270 		if( func( &unusedSince) )
271 		{
272 			// module can be unloaded if it has not been used at least for the time
273 			// specified by the argument libUnused
274 			if( hasEnoughTimePassed( &unusedSince, pLibUnused))
275 			{
276 				// get the reference count and unload the module as many times
277 				sal_uInt32 refCount= it->second.first;
278 
279 				for ( sal_uInt32 i=0; i < refCount; i++)
280 					osl_unloadModule( it->first);
281 
282 				// mark the module for later removal
283 				unloadedModulesList.push_front( it->first);
284 			}
285 		}
286 	}
287 
288 	// remove all entries containing invalid (unloaded) modules
289 	list_type::const_iterator un_it= unloadedModulesList.begin();
290 	for (; un_it != unloadedModulesList.end(); ++un_it)
291 	{
292 		moduleMap.erase( *un_it);
293 	}
294 }
295 
296 
297 // ==============================================================================
298 // Unloading Listener Administration
299 //===============================================================================
300 struct hashListener
301 {
302 	size_t operator()( const sal_Int32& rkey) const
303 	{
304 		return (size_t)rkey;
305 	}
306 };
307 
308 typedef std::hash_map<
309     sal_Int32,
310 	std::pair<rtl_unloadingListenerFunc, void*>,
311 	hashListener,
312 	std::equal_to<sal_Int32>,
313 	rtl::Allocator<sal_Int32>
314 > ListenerMap;
315 
316 typedef ListenerMap::iterator Lis_IT;
317 
318 static ListenerMap& getListenerMap()
319 {
320 	static ListenerMap * g_pListeners= NULL;
321 	if (!g_pListeners)
322 	{
323 		MutexGuard guard( getUnloadingMutex() );
324 		if (!g_pListeners)
325 		{
326 			static ListenerMap g_aListenerMap;
327 			g_pListeners= &g_aListenerMap;
328 		}
329 	}
330 	return *g_pListeners;
331 }
332 
333 
334 // This queue contains cookies which have been passed out by rtl_addUnloadingListener and
335 // which have been regainded by rtl_removeUnloadingListener. When rtl_addUnloadingListener
336 // is called then a cookie has to be returned. First we look into the set if there is one
337 // availabe. Otherwise a new cookie will be provided.
338 // not a new value is returned.
339 
340 typedef std::deque<
341     sal_Int32,
342 	rtl::Allocator<sal_Int32>
343 > queue_type;
344 
345 static queue_type& getCookieQueue()
346 {
347 	static queue_type * g_pCookies= NULL;
348 	if (!g_pCookies)
349 	{
350 		MutexGuard guard( getUnloadingMutex() );
351 		if (!g_pCookies)
352 		{
353 			static queue_type g_aCookieQueue;
354 			g_pCookies= &g_aCookieQueue;
355 		}
356 	}
357 	return *g_pCookies;
358 }
359 
360 static sal_Int32 getCookie()
361 {
362 	static sal_Int32 cookieValue= 1;
363 
364 	sal_Int32 retval;
365 	queue_type& regainedCookies= getCookieQueue();
366 	if( regainedCookies.empty() )
367 		retval= cookieValue++;
368 	else
369 	{
370 		retval= regainedCookies.front();
371 		regainedCookies.pop_front();
372 	}
373 	return retval;
374 }
375 
376 static inline void recycleCookie( sal_Int32 i)
377 {
378 	getCookieQueue().push_back(i);
379 }
380 
381 
382 // calling the function twice with the same arguments will return tow different cookies.
383 // The listener will then notified twice.
384 
385 extern "C"
386 sal_Int32 SAL_CALL rtl_addUnloadingListener( rtl_unloadingListenerFunc callback, void* _this)
387 {
388 	MutexGuard guard( getUnloadingMutex());
389 
390 	sal_Int32 cookie= getCookie();
391 	ListenerMap& listenerMap= getListenerMap();
392 	listenerMap[ cookie]= std::make_pair( callback, _this);
393 	return cookie;
394 }
395 
396 
397 extern "C"
398 void SAL_CALL rtl_removeUnloadingListener( sal_Int32 cookie )
399 {
400 	MutexGuard guard( getUnloadingMutex());
401 
402 	ListenerMap& listenerMap= getListenerMap();
403 	size_t removedElements= listenerMap.erase( cookie);
404 	if( removedElements )
405 		recycleCookie( cookie);
406 }
407 
408 
409 static void rtl_notifyUnloadingListeners()
410 {
411 	ListenerMap& listenerMap= getListenerMap();
412 	for( Lis_IT it= listenerMap.begin(); it != listenerMap.end(); ++it)
413 	{
414 		rtl_unloadingListenerFunc callbackFunc= it->second.first;
415 		callbackFunc( it->second.second);
416 	}
417 }
418