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( ¤tTime)) 101 { 102 TimeValue addedTime; 103 addTimeValue( unusedSince, timespan, &addedTime); 104 if( isGreaterEqualTimeValue( ¤tTime, &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