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