xref: /trunk/main/sal/rtl/source/unload.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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