xref: /AOO41X/main/sal/rtl/source/unload.cxx (revision 79e556ee827beb4823bf162e2fd4108e56b8fd4e)
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( &currentTime))
96     {
97         TimeValue addedTime;
98         addTimeValue( unusedSince, timespan, &addedTime);
99         if( isGreaterEqualTimeValue( &currentTime, &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