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