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 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 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 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 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 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 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 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 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 { 148 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 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 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 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 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 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 { 296 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 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 // available. Otherwise a new cookie will be provided. 331 // not a new value is returned. 332 333 typedef std::deque< sal_Int32 > queue_type; 334 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 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 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" 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" 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 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