fune/ipc/glue/CrossProcessSemaphore_posix.cpp
Lee Salzman f793972d4c Bug 1553691 - Implement CrossProcessSemaphore on macOS using Mach semaphores. r=nika
Unnamed POSIX semaphores are not supported on macOS, nor is timed wait supported on
named POSIX semaphores. SysV semaphores have similar limitations.

Mach semaphores are one of the only clean ways to create unnamed, shareable semaphores
on macOS. As of bug 1734735, we now can transparently send the mach ports across to
other processes without much added code, overall making this implementation of Mach
semaphores fairly easy.

Differential Revision: https://phabricator.services.mozilla.com/D188429
2023-09-19 21:16:46 +00:00

164 lines
4.2 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CrossProcessSemaphore.h"
#include "mozilla/Unused.h"
#include "nsDebug.h"
#include "nsISupportsImpl.h"
#include <errno.h>
static const uint64_t kNsPerMs = 1000000;
static const uint64_t kNsPerSec = 1000000000;
namespace {
struct SemaphoreData {
sem_t mSemaphore;
mozilla::Atomic<int32_t> mRefCount;
uint32_t mInitialValue;
};
} // namespace
namespace mozilla {
/* static */
CrossProcessSemaphore* CrossProcessSemaphore::Create(const char*,
uint32_t aInitialValue) {
RefPtr<ipc::SharedMemoryBasic> sharedBuffer = new ipc::SharedMemoryBasic;
if (!sharedBuffer->Create(sizeof(SemaphoreData))) {
return nullptr;
}
if (!sharedBuffer->Map(sizeof(SemaphoreData))) {
return nullptr;
}
SemaphoreData* data = static_cast<SemaphoreData*>(sharedBuffer->memory());
if (!data) {
return nullptr;
}
if (sem_init(&data->mSemaphore, 1, aInitialValue)) {
return nullptr;
}
CrossProcessSemaphore* sem = new CrossProcessSemaphore;
sem->mSharedBuffer = sharedBuffer;
sem->mSemaphore = &data->mSemaphore;
sem->mRefCount = &data->mRefCount;
*sem->mRefCount = 1;
data->mInitialValue = aInitialValue;
return sem;
}
/* static */
CrossProcessSemaphore* CrossProcessSemaphore::Create(
CrossProcessSemaphoreHandle aHandle) {
RefPtr<ipc::SharedMemoryBasic> sharedBuffer = new ipc::SharedMemoryBasic;
if (!sharedBuffer->IsHandleValid(aHandle)) {
return nullptr;
}
if (!sharedBuffer->SetHandle(std::move(aHandle),
ipc::SharedMemory::RightsReadWrite)) {
return nullptr;
}
if (!sharedBuffer->Map(sizeof(SemaphoreData))) {
return nullptr;
}
sharedBuffer->CloseHandle();
SemaphoreData* data = static_cast<SemaphoreData*>(sharedBuffer->memory());
if (!data) {
return nullptr;
}
int32_t oldCount = data->mRefCount++;
if (oldCount == 0) {
// The other side has already let go of their CrossProcessSemaphore, so now
// mSemaphore is garbage. We need to re-initialize it.
if (sem_init(&data->mSemaphore, 1, data->mInitialValue)) {
data->mRefCount--;
return nullptr;
}
}
CrossProcessSemaphore* sem = new CrossProcessSemaphore;
sem->mSharedBuffer = sharedBuffer;
sem->mSemaphore = &data->mSemaphore;
sem->mRefCount = &data->mRefCount;
return sem;
}
CrossProcessSemaphore::CrossProcessSemaphore()
: mSemaphore(nullptr), mRefCount(nullptr) {
MOZ_COUNT_CTOR(CrossProcessSemaphore);
}
CrossProcessSemaphore::~CrossProcessSemaphore() {
int32_t oldCount = --(*mRefCount);
if (oldCount == 0) {
// Nothing can be done if the destroy fails so ignore return code.
Unused << sem_destroy(mSemaphore);
}
MOZ_COUNT_DTOR(CrossProcessSemaphore);
}
bool CrossProcessSemaphore::Wait(const Maybe<TimeDuration>& aWaitTime) {
MOZ_ASSERT(*mRefCount > 0,
"Attempting to wait on a semaphore with zero ref count");
int ret;
if (aWaitTime.isSome()) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
return false;
}
uint64_t ns = uint64_t(kNsPerMs * aWaitTime->ToMilliseconds()) + ts.tv_nsec;
ts.tv_sec += ns / kNsPerSec;
ts.tv_nsec = ns % kNsPerSec;
while ((ret = sem_timedwait(mSemaphore, &ts)) == -1 && errno == EINTR) {
}
} else {
while ((ret = sem_wait(mSemaphore)) == -1 && errno == EINTR) {
}
}
return ret == 0;
}
void CrossProcessSemaphore::Signal() {
MOZ_ASSERT(*mRefCount > 0,
"Attempting to signal a semaphore with zero ref count");
sem_post(mSemaphore);
}
CrossProcessSemaphoreHandle CrossProcessSemaphore::CloneHandle() {
CrossProcessSemaphoreHandle result = ipc::SharedMemoryBasic::NULLHandle();
if (mSharedBuffer) {
result = mSharedBuffer->CloneHandle();
if (!result) {
MOZ_CRASH();
}
}
return result;
}
void CrossProcessSemaphore::CloseHandle() { mSharedBuffer->CloseHandle(); }
} // namespace mozilla