LEFT | RIGHT |
1 // Copyright 2012 Google Inc. All Rights Reserved. | 1 // Copyright 2012 Google Inc. All Rights Reserved. |
2 // Use of this source code is governed by an Apache-style license that can be | 2 // Use of this source code is governed by an Apache-style license that can be |
3 // found in the COPYING file. | 3 // found in the COPYING file. |
4 | 4 |
5 #include "rlz/mac/lib/rlz_value_store_mac.h" | 5 #include "rlz/mac/lib/rlz_value_store_mac.h" |
6 | 6 |
7 #include "base/mac/foundation_util.h" | 7 #include "base/mac/foundation_util.h" |
8 #include "base/file_path.h" | 8 #include "base/file_path.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/sys_string_conversions.h" | 10 #include "base/sys_string_conversions.h" |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 namespace { | 217 namespace { |
218 | 218 |
219 // Creating a recursive cross-process mutex on windows is one line. On mac, | 219 // Creating a recursive cross-process mutex on windows is one line. On mac, |
220 // there's no primitve for that, so this lock is emulated by an in-process | 220 // there's no primitve for that, so this lock is emulated by an in-process |
221 // mutex to get the recursive part, followed by a cross-process lock for the | 221 // mutex to get the recursive part, followed by a cross-process lock for the |
222 // cross-process part. | 222 // cross-process part. |
223 | 223 |
224 // This is a struct so that it doesn't need a static initializer. | 224 // This is a struct so that it doesn't need a static initializer. |
225 struct RecursiveCrossProcessLock { | 225 struct RecursiveCrossProcessLock { |
226 // Tries to acquire a recursive cross-process lock. Note that this _always_ | 226 // Tries to acquire a recursive cross-process lock. Note that this _always_ |
227 // acquires the in-process lock (if it wasn't already acquired). | 227 // acquires the in-process lock (if it wasn't already acquired). The parent |
228 bool TryGetCrossProcessLock(); | 228 // directory of |lock_file| must exist. |
| 229 bool TryGetCrossProcessLock(NSString* lock_filename); |
229 | 230 |
230 // Releases the lock. Should always be called, even if | 231 // Releases the lock. Should always be called, even if |
231 // TryGetCrossProcessLock() returns false. | 232 // TryGetCrossProcessLock() returns false. |
232 void ReleaseLock(); | 233 void ReleaseLock(); |
233 | 234 |
| 235 pthread_mutex_t recursive_lock_; |
| 236 pthread_t locking_thread_; |
| 237 |
| 238 NSDistributedLock* file_lock_; |
| 239 } g_recursive_lock = { |
234 // PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy | 240 // PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy |
235 // on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate | 241 // on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate |
236 // recursive locking with a normal non-recursive mutex. | 242 // recursive locking with a normal non-recursive mutex. |
237 pthread_mutex_t recursive_lock_; | |
238 pthread_t locking_thread_; | |
239 | |
240 NSDistributedLock* file_lock_; | |
241 } g_recursive_lock = { | |
242 PTHREAD_MUTEX_INITIALIZER | 243 PTHREAD_MUTEX_INITIALIZER |
243 }; | 244 }; |
244 | 245 |
245 // Tries to acquire a recursive cross-process lock. Note that this _always_ | 246 bool RecursiveCrossProcessLock::TryGetCrossProcessLock( |
246 // acquires the in-process lock (if it wasn't already acquired). | 247 NSString* lock_filename) { |
247 bool RecursiveCrossProcessLock::TryGetCrossProcessLock() { | |
248 bool just_got_lock = false; | 248 bool just_got_lock = false; |
249 | 249 |
250 // Emulate a recursive mutex with a non-recursive one. | 250 // Emulate a recursive mutex with a non-recursive one. |
251 if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) { | 251 if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) { |
252 if (pthread_equal(pthread_self(), locking_thread_) == 0) { | 252 if (pthread_equal(pthread_self(), locking_thread_) == 0) { |
253 // Some other thread has the lock, wait for it. | 253 // Some other thread has the lock, wait for it. |
254 pthread_mutex_lock(&recursive_lock_); | 254 pthread_mutex_lock(&recursive_lock_); |
255 CHECK(locking_thread_ == 0); | 255 CHECK(locking_thread_ == 0); |
256 just_got_lock = true; | 256 just_got_lock = true; |
257 } | 257 } |
258 } else { | 258 } else { |
259 just_got_lock = true; | 259 just_got_lock = true; |
260 } | 260 } |
261 | 261 |
262 locking_thread_ = pthread_self(); | 262 locking_thread_ = pthread_self(); |
263 | 263 |
264 // Try to acquire flie lock. | 264 // Try to acquire file lock. |
265 if (just_got_lock) { | 265 if (just_got_lock) { |
266 const int kMaxTimeoutMS = 5000; // Matches windows. | 266 const int kMaxTimeoutMS = 5000; // Matches windows. |
267 const int kSleepPerTryMS = 200; | 267 const int kSleepPerTryMS = 200; |
268 | 268 |
269 CHECK(!file_lock_); | 269 CHECK(!file_lock_); |
270 file_lock_ = [[NSDistributedLock alloc] initWithPath:RlzLockFilename()]; | 270 file_lock_ = [[NSDistributedLock alloc] initWithPath:lock_filename]; |
271 | 271 |
272 BOOL got_file_lock = NO; | 272 BOOL got_file_lock = NO; |
273 int elapsedMS = 0; | 273 int elapsedMS = 0; |
274 while (!(got_file_lock = [file_lock_ tryLock]) && | 274 while (!(got_file_lock = [file_lock_ tryLock]) && |
275 elapsedMS < kMaxTimeoutMS) { | 275 elapsedMS < kMaxTimeoutMS) { |
276 usleep(kSleepPerTryMS * 1000); | 276 usleep(kSleepPerTryMS * 1000); |
277 elapsedMS += kSleepPerTryMS; | 277 elapsedMS += kSleepPerTryMS; |
278 } | 278 } |
279 | 279 |
280 if (!got_file_lock) { | 280 if (!got_file_lock) { |
281 [file_lock_ release]; | 281 [file_lock_ release]; |
282 file_lock_ = nil; | 282 file_lock_ = nil; |
283 return false; | 283 return false; |
284 } | 284 } |
285 } | 285 return true; |
286 return true; | 286 } else { |
| 287 return file_lock_ != nil; |
| 288 } |
287 } | 289 } |
288 | 290 |
289 void RecursiveCrossProcessLock::ReleaseLock() { | 291 void RecursiveCrossProcessLock::ReleaseLock() { |
290 if (file_lock_) { | 292 if (file_lock_) { |
291 [file_lock_ unlock]; | 293 [file_lock_ unlock]; |
292 [file_lock_ release]; | 294 [file_lock_ release]; |
293 file_lock_ = nil; | 295 file_lock_ = nil; |
294 } | 296 } |
295 | 297 |
296 locking_thread_ = 0; | 298 locking_thread_ = 0; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 // Returns the path of the rlz lock file, also creates the parent directory | 347 // Returns the path of the rlz lock file, also creates the parent directory |
346 // path if it doesn't exist. | 348 // path if it doesn't exist. |
347 NSString* RlzLockFilename() { | 349 NSString* RlzLockFilename() { |
348 NSString* const kRlzFile = @"lockfile"; | 350 NSString* const kRlzFile = @"lockfile"; |
349 return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile]; | 351 return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile]; |
350 } | 352 } |
351 | 353 |
352 } // namespace | 354 } // namespace |
353 | 355 |
354 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { | 356 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { |
355 bool got_distributed_lock = g_recursive_lock.TryGetCrossProcessLock(); | 357 bool got_distributed_lock = |
| 358 g_recursive_lock.TryGetCrossProcessLock(RlzLockFilename()); |
356 // At this point, we hold the in-process lock, no matter the value of | 359 // At this point, we hold the in-process lock, no matter the value of |
357 // |got_distributed_lock|. | 360 // |got_distributed_lock|. |
358 | 361 |
359 ++g_lock_depth; | 362 ++g_lock_depth; |
| 363 if (g_lock_depth > 1) { |
| 364 // Reuse the already existing store object. |
| 365 // All user code early-exits when a lock fails, so a recursive lock will |
| 366 // never end up with |g_store_object| that is NULL. |
| 367 CHECK(got_distributed_lock); |
| 368 CHECK(g_store_object); |
| 369 store_.reset(g_store_object); |
| 370 return; |
| 371 } |
360 | 372 |
361 if (!got_distributed_lock) { | 373 if (!got_distributed_lock) { |
362 // Give up. |store_| isn't set, which signals to callers that acquiring | 374 // Give up. |store_| isn't set, which signals to callers that acquiring |
363 // the lock failed. |g_recursive_lock| will be released by the | 375 // the lock failed. |g_recursive_lock| will be released by the |
364 // destructor. | 376 // destructor. |
365 return; | |
366 } | |
367 | |
368 if (g_lock_depth > 1) { | |
369 // Reuse the already existing store object. | |
370 // All user code early-exits when a lock fails, so a recursive lock will | |
371 // never end up with |g_store_object| that is NULL. | |
372 CHECK(g_store_object); | |
373 store_.reset(g_store_object); | |
374 return; | 377 return; |
375 } | 378 } |
376 | 379 |
377 CHECK(!g_store_object); | 380 CHECK(!g_store_object); |
378 | 381 |
379 NSString* plist = RlzPlistFilename(); | 382 NSString* plist = RlzPlistFilename(); |
380 | 383 |
381 // Create an empty file if none exists yet. | 384 // Create an empty file if none exists yet. |
382 NSFileManager* manager = [NSFileManager defaultManager]; | 385 NSFileManager* manager = [NSFileManager defaultManager]; |
383 if (![manager fileExistsAtPath:plist isDirectory:NULL]) | 386 if (![manager fileExistsAtPath:plist isDirectory:NULL]) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
437 } else { | 440 } else { |
438 // Not Unsafe on OS X. | 441 // Not Unsafe on OS X. |
439 g_test_folder = | 442 g_test_folder = |
440 [[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()]; | 443 [[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()]; |
441 } | 444 } |
442 } | 445 } |
443 | 446 |
444 } // namespace testing | 447 } // namespace testing |
445 | 448 |
446 } // namespace rlz_lib | 449 } // namespace rlz_lib |
LEFT | RIGHT |