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