Line data Source code
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 "mozilla/Unused.h"
9 : #include "nsDebug.h"
10 : #include "nsISupportsImpl.h"
11 : #include <errno.h>
12 :
13 : static const uint64_t kNsPerMs = 1000000;
14 : static const uint64_t kNsPerSec = 1000000000;
15 :
16 : namespace {
17 :
18 :
19 : struct SemaphoreData {
20 : sem_t mSemaphore;
21 : mozilla::Atomic<int32_t> mRefCount;
22 : uint32_t mInitialValue;
23 : };
24 :
25 : } // namespace
26 :
27 : namespace mozilla {
28 :
29 : /* static */ CrossProcessSemaphore*
30 9 : CrossProcessSemaphore::Create(const char*, uint32_t aInitialValue)
31 : {
32 18 : RefPtr<ipc::SharedMemoryBasic> sharedBuffer = new ipc::SharedMemoryBasic;
33 9 : if (!sharedBuffer->Create(sizeof(SemaphoreData))) {
34 0 : return nullptr;
35 : }
36 :
37 9 : if (!sharedBuffer->Map(sizeof(SemaphoreData))) {
38 0 : return nullptr;
39 : }
40 :
41 9 : SemaphoreData* data = static_cast<SemaphoreData*>(sharedBuffer->memory());
42 :
43 9 : if (!data) {
44 0 : return nullptr;
45 : }
46 :
47 9 : if (sem_init(&data->mSemaphore, 1, aInitialValue)) {
48 0 : return nullptr;
49 : }
50 :
51 9 : CrossProcessSemaphore* sem = new CrossProcessSemaphore;
52 9 : sem->mSharedBuffer = sharedBuffer;
53 9 : sem->mSemaphore = &data->mSemaphore;
54 9 : sem->mRefCount = &data->mRefCount;
55 9 : *sem->mRefCount = 1;
56 :
57 9 : data->mInitialValue = aInitialValue;
58 :
59 9 : return sem;
60 : }
61 :
62 : /* static */ CrossProcessSemaphore*
63 33 : CrossProcessSemaphore::Create(CrossProcessSemaphoreHandle aHandle)
64 : {
65 66 : RefPtr<ipc::SharedMemoryBasic> sharedBuffer = new ipc::SharedMemoryBasic;
66 :
67 33 : if (!sharedBuffer->IsHandleValid(aHandle)) {
68 0 : return nullptr;
69 : }
70 :
71 33 : if (!sharedBuffer->SetHandle(aHandle, ipc::SharedMemory::RightsReadWrite)) {
72 0 : return nullptr;
73 : }
74 :
75 33 : if (!sharedBuffer->Map(sizeof(SemaphoreData))) {
76 0 : return nullptr;
77 : }
78 :
79 33 : SemaphoreData* data = static_cast<SemaphoreData*>(sharedBuffer->memory());
80 :
81 33 : if (!data) {
82 0 : return nullptr;
83 : }
84 :
85 33 : int32_t oldCount = data->mRefCount++;
86 33 : if (oldCount == 0) {
87 : // The other side has already let go of their CrossProcessSemaphore, so now
88 : // mSemaphore is garbage. We need to re-initialize it.
89 0 : if (sem_init(&data->mSemaphore, 1, data->mInitialValue)) {
90 0 : data->mRefCount--;
91 0 : return nullptr;
92 : }
93 : }
94 :
95 33 : CrossProcessSemaphore* sem = new CrossProcessSemaphore;
96 33 : sem->mSharedBuffer = sharedBuffer;
97 33 : sem->mSemaphore = &data->mSemaphore;
98 33 : sem->mRefCount = &data->mRefCount;
99 33 : return sem;
100 : }
101 :
102 :
103 42 : CrossProcessSemaphore::CrossProcessSemaphore()
104 : : mSemaphore(nullptr)
105 42 : , mRefCount(nullptr)
106 : {
107 42 : MOZ_COUNT_CTOR(CrossProcessSemaphore);
108 42 : }
109 :
110 74 : CrossProcessSemaphore::~CrossProcessSemaphore()
111 : {
112 37 : int32_t oldCount = --(*mRefCount);
113 :
114 37 : if (oldCount == 0) {
115 : // Nothing can be done if the destroy fails so ignore return code.
116 6 : Unused << sem_destroy(mSemaphore);
117 : }
118 :
119 37 : MOZ_COUNT_DTOR(CrossProcessSemaphore);
120 37 : }
121 :
122 : bool
123 67 : CrossProcessSemaphore::Wait(const Maybe<TimeDuration>& aWaitTime)
124 : {
125 67 : MOZ_ASSERT(*mRefCount > 0, "Attempting to wait on a semaphore with zero ref count");
126 : int ret;
127 67 : if (aWaitTime.isSome()) {
128 : struct timespec ts;
129 34 : if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
130 0 : return false;
131 : }
132 :
133 34 : ts.tv_nsec += (kNsPerMs * aWaitTime->ToMilliseconds());
134 34 : ts.tv_sec += ts.tv_nsec / kNsPerSec;
135 34 : ts.tv_nsec %= kNsPerSec;
136 :
137 34 : while ((ret = sem_timedwait(mSemaphore, &ts)) == -1 && errno == EINTR) {
138 0 : continue;
139 : }
140 : } else {
141 33 : while ((ret = sem_wait(mSemaphore)) == -1 && errno == EINTR) {
142 0 : continue;
143 : }
144 : }
145 67 : return ret == 0;
146 : }
147 :
148 : void
149 65 : CrossProcessSemaphore::Signal()
150 : {
151 65 : MOZ_ASSERT(*mRefCount > 0, "Attempting to signal a semaphore with zero ref count");
152 65 : sem_post(mSemaphore);
153 65 : }
154 :
155 : CrossProcessSemaphoreHandle
156 33 : CrossProcessSemaphore::ShareToProcess(base::ProcessId aTargetPid)
157 : {
158 33 : CrossProcessSemaphoreHandle result = ipc::SharedMemoryBasic::NULLHandle();
159 :
160 33 : if (mSharedBuffer && !mSharedBuffer->ShareToProcess(aTargetPid, &result)) {
161 0 : MOZ_CRASH();
162 : }
163 :
164 33 : return result;
165 : }
166 :
167 : } // namespace mozilla
|