CrossProcessSemaphore_posix.cpp (3847B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "CrossProcessSemaphore.h" 8 #include "nsDebug.h" 9 #include "nsISupportsImpl.h" 10 #include <errno.h> 11 12 static const uint64_t kNsPerMs = 1000000; 13 static const uint64_t kNsPerSec = 1000000000; 14 15 namespace { 16 17 struct SemaphoreData { 18 sem_t mSemaphore; 19 mozilla::Atomic<int32_t> mRefCount; 20 uint32_t mInitialValue; 21 }; 22 23 } // namespace 24 25 namespace mozilla { 26 27 /* static */ 28 CrossProcessSemaphore* CrossProcessSemaphore::Create(const char*, 29 uint32_t aInitialValue) { 30 auto handle = ipc::shared_memory::Create(sizeof(SemaphoreData)); 31 if (!handle) { 32 return nullptr; 33 } 34 35 auto mapping = handle.Map(); 36 if (!mapping) { 37 return nullptr; 38 } 39 40 SemaphoreData* data = mapping.DataAs<SemaphoreData>(); 41 42 if (!data) { 43 return nullptr; 44 } 45 46 if (sem_init(&data->mSemaphore, 1, aInitialValue)) { 47 return nullptr; 48 } 49 50 CrossProcessSemaphore* sem = new CrossProcessSemaphore; 51 sem->mHandle = std::move(handle); 52 sem->mSharedBuffer = std::move(mapping); 53 sem->mSemaphore = &data->mSemaphore; 54 sem->mRefCount = &data->mRefCount; 55 *sem->mRefCount = 1; 56 57 data->mInitialValue = aInitialValue; 58 59 return sem; 60 } 61 62 /* static */ 63 CrossProcessSemaphore* CrossProcessSemaphore::Create( 64 CrossProcessSemaphoreHandle aHandle) { 65 auto mapping = aHandle.Map(); 66 if (!mapping) { 67 return nullptr; 68 } 69 70 aHandle = nullptr; 71 72 SemaphoreData* data = mapping.DataAs<SemaphoreData>(); 73 74 if (!data) { 75 return nullptr; 76 } 77 78 int32_t oldCount = data->mRefCount++; 79 if (oldCount == 0) { 80 // The other side has already let go of their CrossProcessSemaphore, so now 81 // mSemaphore is garbage. We need to re-initialize it. 82 if (sem_init(&data->mSemaphore, 1, data->mInitialValue)) { 83 data->mRefCount--; 84 return nullptr; 85 } 86 } 87 88 CrossProcessSemaphore* sem = new CrossProcessSemaphore; 89 sem->mSharedBuffer = std::move(mapping); 90 sem->mSemaphore = &data->mSemaphore; 91 sem->mRefCount = &data->mRefCount; 92 return sem; 93 } 94 95 CrossProcessSemaphore::CrossProcessSemaphore() 96 : mSemaphore(nullptr), mRefCount(nullptr) { 97 MOZ_COUNT_CTOR(CrossProcessSemaphore); 98 } 99 100 CrossProcessSemaphore::~CrossProcessSemaphore() { 101 int32_t oldCount = --(*mRefCount); 102 103 if (oldCount == 0) { 104 // Nothing can be done if the destroy fails so ignore return code. 105 (void)sem_destroy(mSemaphore); 106 } 107 108 MOZ_COUNT_DTOR(CrossProcessSemaphore); 109 } 110 111 bool CrossProcessSemaphore::Wait(const Maybe<TimeDuration>& aWaitTime) { 112 MOZ_ASSERT(*mRefCount > 0, 113 "Attempting to wait on a semaphore with zero ref count"); 114 int ret; 115 if (aWaitTime.isSome()) { 116 struct timespec ts; 117 if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { 118 return false; 119 } 120 121 uint64_t ns = uint64_t(kNsPerMs * aWaitTime->ToMilliseconds()) + ts.tv_nsec; 122 ts.tv_sec += ns / kNsPerSec; 123 ts.tv_nsec = ns % kNsPerSec; 124 125 while ((ret = sem_timedwait(mSemaphore, &ts)) == -1 && errno == EINTR) { 126 } 127 } else { 128 while ((ret = sem_wait(mSemaphore)) == -1 && errno == EINTR) { 129 } 130 } 131 return ret == 0; 132 } 133 134 void CrossProcessSemaphore::Signal() { 135 MOZ_ASSERT(*mRefCount > 0, 136 "Attempting to signal a semaphore with zero ref count"); 137 sem_post(mSemaphore); 138 } 139 140 CrossProcessSemaphoreHandle CrossProcessSemaphore::CloneHandle() { 141 if (mSharedBuffer) { 142 auto handle = mHandle.Clone(); 143 if (!handle) { 144 MOZ_CRASH(); 145 } 146 return handle; 147 } 148 return nullptr; 149 } 150 151 void CrossProcessSemaphore::CloseHandle() { mHandle = nullptr; } 152 153 } // namespace mozilla