1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /*================================================================================================== 18 19 Source Name: SYNCML_DM_Lock.cc 20 21 General Description: Implementation of the DMFileLockingThread, Clock and DMLockManager classes 22 23 ==================================================================================================*/ 24 25 #include "dmLock.h" 26 #include "xpl_Logger.h" 27 #include "xpl_dm_Manager.h" 28 #include "dm_tree_class.H" 29 30 #ifndef DM_NO_LOCKING 31 // memory aging check interval in seconds 32 #define DEFAULT_AGING_CHECK_INTERVAL 60 33 34 // Static member 35 DMCriticalSection DMLock::m_csLock; 36 37 DMFileLockingThread::DMFileLockingThread( DMLockManager* pLockManager ) 38 { 39 m_nReady=SYNCML_DM_THREAD_STARTING; 40 m_pLockManager = pLockManager; 41 42 m_nAgingCheckInterval = DEFAULT_AGING_CHECK_INTERVAL; 43 CPCHAR dm_aging_env = XPL_DM_GetEnv(SYNCML_DM_MEMORY_AGING_INTERVAL); 44 if ( dm_aging_env != NULL) { 45 INT32 interval = DmAtoi(dm_aging_env); 46 if (interval >= 0) { 47 m_nAgingCheckInterval = interval; 48 } else { 49 m_nAgingCheckInterval = 0; 50 } 51 } 52 } 53 54 void* DMFileLockingThread ::Run() 55 { 56 BOOLEAN bDone = FALSE; 57 DMVector< DMFileLockParam > aMessages; 58 59 m_nReady = SYNCML_DM_THREAD_STARTED; 60 INT32 nAgingCheck = m_nAgingCheckInterval; 61 INT32 nElapsed = 0; 62 XPL_CLK_CLOCK_T startTime, endTime; 63 64 while(!bDone) 65 { 66 INT64 nTimeoutMS; 67 if (m_nAgingCheckInterval <= 0) 68 { 69 nTimeoutMS = (aMessages.size() == 0 ? DM_WAIT_FOREVER : 100 ); 70 } 71 else 72 { 73 nTimeoutMS = (aMessages.size()==0 ? (nAgingCheck * 1000) : 100 ); 74 } 75 startTime = XPL_CLK_GetClock(); 76 DMThreadEvent evt; 77 78 if ( m_pLockManager->GetQueue()->Wait(nTimeoutMS, evt)) 79 { 80 switch ( evt.GetEventType() ) 81 { 82 case SYNCML_DM_THREAD_EVENT_TYPE_TIMEOUT: 83 dmTreeObj.GetLockContextManager().ReleaseAll(); 84 break; 85 case SYNCML_DM_THREAD_EVENT_TYPE_FILELOCK: 86 { 87 DMFileLockParam* ptr = (DMFileLockParam*)evt.GetData(); 88 aMessages.push_back( *ptr ); 89 DmFreeMem( ptr ); 90 break; 91 } 92 case SYNCML_DM_THREAD_EVENT_TYPE_SHUTDOWN: 93 bDone = true; 94 break; 95 default: 96 case SYNCML_DM_THREAD_EVENT_TYPE_NONE: 97 { 98 // DM: just to supress warning, do nothing here 99 break; 100 } 101 } 102 103 endTime = XPL_CLK_GetClock(); 104 nAgingCheck -= (int)(endTime - startTime); 105 nElapsed += (int)(endTime - startTime); 106 } 107 else 108 { 109 110 endTime = XPL_CLK_GetClock(); 111 nElapsed += (int)(endTime - startTime); 112 113 if ( (nTimeoutMS == nAgingCheck * 1000) && 114 (!m_pLockManager->GetQueue()->IsTimerSet()) ) 115 { 116 XPL_LOG_DM_TMN_Debug(("Memory aging time out = %d sec.\n", nElapsed)); 117 dmTreeObj.CheckMemoryAging(); 118 nAgingCheck = m_nAgingCheckInterval; 119 nElapsed = 0; 120 } 121 else 122 { 123 nAgingCheck -= (int)(endTime - startTime); 124 } 125 } 126 127 if (nAgingCheck < 1) 128 { 129 endTime = XPL_CLK_GetClock(); 130 XPL_LOG_DM_TMN_Debug(("Memory aging time out = %d sec.\n", nElapsed )); 131 nAgingCheck = 1; 132 } 133 134 // try to lock/unlock all postponed requests: 135 for ( int i = 0; i < aMessages.size(); i++ ) { 136 DMFileLockParam& pFileLockMsg = aMessages[i]; 137 138 if ( m_pLockManager->ProcessLockRequest( &pFileLockMsg ) ) { 139 aMessages.remove(i); 140 i--; // don't skip next 141 } 142 } 143 } 144 145 m_nReady = SYNCML_DM_THREAD_STARTING; 146 147 return NULL; 148 } 149 150 DMLock::DMLock() 151 { 152 m_pFile = NULL; 153 m_nReadWriteCounter = 0; 154 } 155 156 DMLock::~DMLock() 157 { 158 if (m_pFile) 159 { 160 Destroy(); 161 } 162 } 163 164 SYNCML_DM_RET_STATUS_T 165 DMLock::Init( CPCHAR p_LockFileName) 166 { 167 m_nReadWriteCounter = 0; 168 m_pFile = new DMFileHandler(p_LockFileName); 169 170 SYNCML_DM_RET_STATUS_T nRes = m_pFile->open(XPL_FS_FILE_RDWR); 171 172 if ( nRes != SYNCML_DM_SUCCESS ) 173 Destroy(); 174 175 return nRes; 176 } 177 178 SYNCML_DM_RET_STATUS_T 179 DMLock::Destroy(void) 180 { 181 if (!m_pFile) 182 return SYNCML_DM_SUCCESS; 183 184 m_pFile->unlock(); 185 m_pFile->close(); 186 delete m_pFile; 187 m_pFile = NULL; 188 189 return SYNCML_DM_SUCCESS; 190 } 191 192 SYNCML_DM_RET_STATUS_T 193 DMLock::Lock( SYNCML_DM_TREE_LOCK_TYPE_T eLockType) 194 { 195 DMSingleLock oLock( DMLock::m_csLock ); 196 197 INT32 nPrevCounter = m_nReadWriteCounter; 198 SYNCML_DM_RET_STATUS_T nRes = SYNCML_DM_SUCCESS; 199 200 if ( eLockType == SYNCML_DM_LOCK_TYPE_EXCLUSIVE ) 201 { 202 if ( m_nReadWriteCounter != 0 ) 203 return SYNCML_DM_LOCK_TRY_AGAIN; // not available 204 205 m_nReadWriteCounter = -1; // writer 206 } 207 else 208 { // shared lock 209 if ( m_nReadWriteCounter < 0 ) 210 return SYNCML_DM_LOCK_TRY_AGAIN; // writer in progress 211 212 m_nReadWriteCounter++; // reader 213 } 214 215 216 if ( m_nReadWriteCounter <= 1 ) 217 { // only first reader/writer should lock the file 218 if (!m_pFile) 219 return SYNCML_DM_FAIL; 220 221 if ( eLockType == SYNCML_DM_LOCK_TYPE_EXCLUSIVE ) 222 nRes = m_pFile->lock( TRUE ); 223 else 224 nRes = m_pFile->lock( FALSE ); 225 226 if ( nRes == SYNCML_DM_LOCK_TRY_AGAIN ) 227 m_nReadWriteCounter = nPrevCounter; 228 } 229 230 return nRes; 231 } 232 233 234 SYNCML_DM_RET_STATUS_T DMLock::Unlock() 235 { 236 SYNCML_DM_RET_STATUS_T nRes = SYNCML_DM_SUCCESS; 237 DMSingleLock oLock( DMLock::m_csLock ); 238 239 if ( m_pFile && IsLastLock() ) 240 nRes = m_pFile->unlock(); 241 242 if ( m_nReadWriteCounter < 0 ) 243 m_nReadWriteCounter = 0; // writer 244 else 245 m_nReadWriteCounter--; 246 247 return nRes; 248 } 249 250 251 DMLockManager::DMLockManager() : 252 m_oThread( this ) 253 { 254 m_ptrQueue = NULL; 255 } 256 257 DMLockManager::~DMLockManager() 258 { 259 Destroy(); 260 } 261 262 SYNCML_DM_RET_STATUS_T DMLockManager::Init( CPCHAR szLockFileNamePrefix, INT32 nNumberOfLocks ) 263 { 264 265 nNumberOfLocks += 2; // count for ACL lock as well (the last one) and Evt lock 266 m_aLocks.set_size( nNumberOfLocks ); 267 if ( m_aLocks.size() != nNumberOfLocks ) 268 { 269 m_aLocks.clear(); 270 return SYNCML_DM_DEVICE_FULL; 271 } 272 273 SYNCML_DM_RET_STATUS_T dm_stat = SYNCML_DM_SUCCESS; 274 275 while( nNumberOfLocks-- ) 276 { 277 char szIndex[10]; 278 DMString strName = szLockFileNamePrefix; 279 DmSnprintf( szIndex, sizeof(szIndex), "%d", nNumberOfLocks ); 280 strName += szIndex; 281 282 dm_stat = m_aLocks[ nNumberOfLocks ].Init( strName.c_str() ); 283 284 if ( dm_stat != SYNCML_DM_SUCCESS ) 285 return dm_stat; 286 } 287 288 return dm_stat; 289 } 290 291 292 SYNCML_DM_RET_STATUS_T DMLockManager::StartThread() 293 { 294 295 if ( m_ptrQueue == NULL ) 296 { 297 m_ptrQueue = new DMThreadQueue; 298 if( m_ptrQueue == NULL) 299 return SYNCML_DM_DEVICE_FULL; 300 301 if ( !m_oThread.StartThread() ) 302 return SYNCML_DM_UNABLE_START_THREAD; 303 304 305 INT32 nThreadState; 306 307 while ( (nThreadState = m_oThread.GetThreadState()) == SYNCML_DM_THREAD_STARTING ) 308 DmThSleep( 1000 ); /// give a chance to start 309 310 if ( nThreadState != SYNCML_DM_THREAD_STARTED ) 311 { 312 Destroy(); 313 return SYNCML_DM_UNABLE_START_THREAD; 314 } 315 } 316 317 return SYNCML_DM_SUCCESS; 318 } 319 320 SYNCML_DM_RET_STATUS_T DMLockManager::Destroy() 321 { 322 if(m_ptrQueue != NULL) 323 { 324 if ( m_oThread.GetThreadState() == SYNCML_DM_THREAD_STARTED ) 325 { 326 m_ptrQueue->Post(SYNCML_DM_THREAD_EVENT_TYPE_SHUTDOWN); 327 m_oThread.StopThread(); 328 329 } 330 m_ptrQueue = NULL; 331 } 332 333 //release locks 334 335 for( int i = 0; i < m_aLocks.size(); i++ ) 336 m_aLocks[i].Destroy(); 337 338 m_aLocks.clear(); 339 340 341 return SYNCML_DM_SUCCESS; 342 } 343 344 345 SYNCML_DM_RET_STATUS_T DMLockManager::LockSet( FILESETTYPE nLockSet, 346 SYNCML_DM_TREE_LOCK_TYPE_T eLockType ) 347 { 348 349 SYNCML_DM_RET_STATUS_T dm_stat = SYNCML_DM_SUCCESS; 350 351 if ( eLockType != SYNCML_DM_LOCK_TYPE_SHARED ) 352 { 353 dm_stat = StartThread(); 354 if ( dm_stat != SYNCML_DM_SUCCESS ) 355 return dm_stat; 356 357 dm_stat = SendAndWait( TRUE, nLockSet, eLockType ); 358 } 359 else 360 { 361 INT32 nNumber = m_aLocks.size(); 362 363 for ( int i = 0; i < nNumber; i++ ) 364 { 365 if ( (1 << i) & nLockSet ) 366 { 367 while( m_aLocks[i].Lock( eLockType ) == SYNCML_DM_LOCK_TRY_AGAIN ) 368 DmThSleep( 1000 ); 369 } 370 } 371 } 372 return dm_stat; 373 } 374 375 SYNCML_DM_RET_STATUS_T 376 DMLockManager::UnlockSet( FILESETTYPE nLockSet, 377 SYNCML_DM_TREE_LOCK_TYPE_T eLockType ) 378 { 379 if ( eLockType != SYNCML_DM_LOCK_TYPE_SHARED ) 380 return SendAndWait( FALSE, nLockSet, 0 ); 381 else 382 { 383 INT32 nNumber = m_aLocks.size(); 384 385 for ( int i = 0; i < nNumber; i++ ) 386 { 387 if ( (1 << i) & nLockSet ) 388 { 389 m_aLocks[i].Unlock(); 390 } 391 } 392 return SYNCML_DM_SUCCESS; 393 } 394 } 395 396 SYNCML_DM_RET_STATUS_T DMLockManager::SendAndWait(BOOLEAN bIsLock, FILESETTYPE nLockSet, INT32 eLockType ) 397 { 398 BOOLEAN bOK = FALSE; 399 400 while ( !bOK ) { 401 DMFileLockParam* pFileLockMsg = (DMFileLockParam*)DmAllocMem(sizeof(DMFileLockParam) ); 402 403 if ( !pFileLockMsg ) 404 return SYNCML_DM_DEVICE_FULL; 405 406 int nStatus = SYNCML_DM_FILE_LOCK_STATUS_IN_PROGRESS; 407 408 pFileLockMsg->m_bIsLock = bIsLock; 409 pFileLockMsg->m_pDoneFlag = bIsLock ? &nStatus : NULL; 410 pFileLockMsg->m_eLockType = eLockType; 411 pFileLockMsg->m_nLockSet = nLockSet; 412 413 if ( !m_ptrQueue->Post( SYNCML_DM_THREAD_EVENT_TYPE_FILELOCK, pFileLockMsg) ) { 414 DmFreeMem(pFileLockMsg); 415 return SYNCML_DM_FAIL; 416 } 417 418 if ( !bIsLock ) 419 break; 420 421 // wait for "done" flag marked 422 while ( nStatus == SYNCML_DM_FILE_LOCK_STATUS_IN_PROGRESS ) 423 DmThSleep(1000); 424 425 if ( nStatus != SYNCML_DM_FILE_LOCK_STATUS_TRY_AGAIN ) 426 return nStatus == SYNCML_DM_FILE_LOCK_STATUS_OK ? SYNCML_DM_SUCCESS : SYNCML_DM_FAIL; 427 } 428 429 return SYNCML_DM_SUCCESS; 430 } 431 432 433 BOOLEAN DMLockManager::ProcessLockRequest( DMFileLockParam* pFileLockMsg ) 434 { 435 INT32 nNumber = m_aLocks.size(); 436 SYNCML_DM_RET_STATUS_T nRes = SYNCML_DM_SUCCESS; 437 438 for ( int i = 0; i < nNumber; i++ ){ 439 if ( (1 << i) & pFileLockMsg->m_nLockSet ) { 440 if ( !pFileLockMsg->m_bIsLock ) 441 { 442 nRes = m_aLocks[i].Unlock(); 443 } 444 else 445 { 446 if ( (nRes = m_aLocks[i].Lock( pFileLockMsg->m_eLockType)) == SYNCML_DM_LOCK_TRY_AGAIN ) 447 return FALSE; 448 } 449 // mark processed files 450 pFileLockMsg->m_nLockSet &= ~((FILESETTYPE)((FILESETTYPE)1 << i)); 451 } 452 } 453 454 if ( pFileLockMsg->m_pDoneFlag ) 455 *(pFileLockMsg->m_pDoneFlag) = (nRes == SYNCML_DM_SUCCESS ? SYNCML_DM_FILE_LOCK_STATUS_OK : SYNCML_DM_FILE_LOCK_STATUS_ERROR); 456 457 return TRUE; 458 } 459 460 461 SYNCML_DM_RET_STATUS_T 462 DMLockManager::ReleaseFile(SYNCML_DM_FILE_TYPE_T eFileType) 463 { 464 if ( eFileType == SYNCML_DM_FILE_ACL ) 465 return SendAndWait( FALSE, 1 << (m_aLocks.size()-1), 0 ); 466 else 467 return SendAndWait( FALSE, 1 << (m_aLocks.size()-2), 0 ); 468 } 469 470 SYNCML_DM_RET_STATUS_T 471 DMLockManager::AcquireFile(SYNCML_DM_FILE_TYPE_T eFileType ) 472 { 473 if ( eFileType == SYNCML_DM_FILE_ACL ) 474 return LockSet( 1 << (m_aLocks.size()-1), SYNCML_DM_LOCK_TYPE_EXCLUSIVE ); 475 else 476 return LockSet( 1 << (m_aLocks.size()-2), SYNCML_DM_LOCK_TYPE_EXCLUSIVE ); 477 } 478 479 #endif 480