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