// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. // Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_hoofs/platform/semaphore.hpp" #include "iceoryx_hoofs/platform/ipc_handle_manager.hpp" #include static std::string generateSemaphoreName(const UniqueSystemId& id) { return "iox_semaphore_" + static_cast(id); } static HANDLE acquireSemaphoreHandle(iox_sem_t* sem) { if (!sem->isInterprocessSemaphore) { return sem->handle; } HANDLE newHandle; if (IpcHandleManager::getInstance().getHandle(sem->uniqueId, newHandle)) { return newHandle; } newHandle = Win32Call(OpenSemaphoreA, SEMAPHORE_ALL_ACCESS, false, generateSemaphoreName(sem->uniqueId).c_str()).value; if (newHandle == nullptr) { fprintf(stderr, "interprocess semaphore %s is corrupted - segmentation fault immenent\n", generateSemaphoreName(sem->uniqueId).c_str()); return nullptr; } IpcHandleManager::getInstance().addHandle(sem->uniqueId, OwnerShip::LOAN, newHandle); return newHandle; } int iox_sem_getvalue(iox_sem_t* sem, int* sval) { LONG previousValue; auto waitResult = Win32Call(WaitForSingleObject, acquireSemaphoreHandle(sem), 0).value; switch (waitResult) { case WAIT_OBJECT_0: { auto releaseResult = Win32Call(ReleaseSemaphore, acquireSemaphoreHandle(sem), 1, &previousValue).value; if (releaseResult) { *sval = previousValue + 1; return 0; } return 0; } case WAIT_TIMEOUT: { *sval = 0; return 0; } default: { return -1; } } } int iox_sem_post(iox_sem_t* sem) { int retVal = Win32Call(ReleaseSemaphore, acquireSemaphoreHandle(sem), 1, nullptr).value; return (retVal != 0) ? 0 : -1; } int iox_sem_wait(iox_sem_t* sem) { int retVal = Win32Call(WaitForSingleObject, acquireSemaphoreHandle(sem), INFINITE).value; return (retVal == WAIT_OBJECT_0) ? 0 : -1; } int iox_sem_trywait(iox_sem_t* sem) { int retVal = Win32Call(WaitForSingleObject, acquireSemaphoreHandle(sem), 0).value; if (retVal != WAIT_OBJECT_0) { errno = EAGAIN; } return (retVal == WAIT_OBJECT_0) ? 0 : -1; } int iox_sem_timedwait(iox_sem_t* sem, const struct timespec* abs_timeout) { struct timeval tv; gettimeofday(&tv, nullptr); if (abs_timeout->tv_sec < tv.tv_sec || (abs_timeout->tv_sec == tv.tv_sec && abs_timeout->tv_nsec <= tv.tv_usec * 1000)) { return iox_sem_trywait(sem); } constexpr uint64_t NANO_SECOND = 1000000000; constexpr uint64_t HALF_MILLI_SECOND_ROUNDING_CORRECTION_IN_NS = 500000; constexpr uint64_t NANO_SECONDS_PER_MICRO_SECOND = 1000; constexpr uint64_t NANO_SECONDS_PER_MILLI_SECOND = 1000000; uint64_t epochCurrentTimeDiffInNanoSeconds = (abs_timeout->tv_sec - tv.tv_sec) * NANO_SECOND; long milliseconds = (epochCurrentTimeDiffInNanoSeconds + (abs_timeout->tv_nsec - tv.tv_usec * NANO_SECONDS_PER_MICRO_SECOND) + HALF_MILLI_SECOND_ROUNDING_CORRECTION_IN_NS) / NANO_SECONDS_PER_MILLI_SECOND; auto state = Win32Call(WaitForSingleObject, acquireSemaphoreHandle(sem), (milliseconds == 0) ? 1 : milliseconds).value; if (state == WAIT_TIMEOUT) { errno = ETIMEDOUT; } return (state == WAIT_OBJECT_0) ? 0 : -1; } int iox_sem_close(iox_sem_t* sem) { // we close a named semaphore, therefore we do not need to perform an ipc cleanup int retVal = Win32Call(CloseHandle, acquireSemaphoreHandle(sem)).value ? 0 : -1; delete sem; return (retVal) ? 0 : -1; } int iox_sem_destroy(iox_sem_t* sem) { CloseHandle(acquireSemaphoreHandle(sem)); if (sem->isInterprocessSemaphore) { IpcHandleManager::getInstance().removeHandle(sem->uniqueId); } return 0; } static HANDLE sem_create_win32_semaphore(LONG value, LPCSTR name) { SECURITY_ATTRIBUTES securityAttribute; SECURITY_DESCRIPTOR securityDescriptor; Win32Call(InitializeSecurityDescriptor, &securityDescriptor, SECURITY_DESCRIPTOR_REVISION).value; auto permissions = TEXT("D:") TEXT("(A;OICI;GA;;;BG)") // access to built-in guests TEXT("(A;OICI;GA;;;AN)") // access to anonymous logon TEXT("(A;OICI;GRGWGX;;;AU)") // access to authenticated users TEXT("(A;OICI;GA;;;BA)"); // access to administrators Win32Call(ConvertStringSecurityDescriptorToSecurityDescriptor, reinterpret_cast(permissions), static_cast(SDDL_REVISION_1), static_cast(&(securityAttribute.lpSecurityDescriptor)), static_cast(NULL)); securityAttribute.nLength = sizeof(SECURITY_ATTRIBUTES); securityAttribute.lpSecurityDescriptor = &securityDescriptor; securityAttribute.bInheritHandle = FALSE; HANDLE returnValue = Win32Call(CreateSemaphoreA, &securityAttribute, value, MAX_SEMAPHORE_VALUE, name).value; return returnValue; } int iox_sem_init(iox_sem_t* sem, int pshared, unsigned int value) { sem->isInterprocessSemaphore = (pshared == 1); if (sem->isInterprocessSemaphore) { sem->handle = sem_create_win32_semaphore(value, generateSemaphoreName(sem->uniqueId).c_str()); if (sem->handle != nullptr) { IpcHandleManager::getInstance().addHandle(sem->uniqueId, OwnerShip::OWN, sem->handle); } } else { sem->handle = sem_create_win32_semaphore(value, nullptr); } return (sem->handle != nullptr) ? 0 : -1; } int iox_sem_unlink(const char* name) { // semaphores are unlinked in windows when the last process which is // holding a semaphore calls CloseHandle return 0; } iox_sem_t* iox_sem_open_impl(const char* name, int oflag, ...) // mode_t mode, unsigned int value { if (strlen(name) == 0) { return SEM_FAILED; } iox_sem_t* sem = new iox_sem_t; if (oflag & (O_CREAT | O_EXCL)) { va_list va; va_start(va, oflag); mode_t mode = va_arg(va, mode_t); unsigned int value = va_arg(va, unsigned int); va_end(va); sem->handle = sem_create_win32_semaphore(value, name); if (oflag & O_EXCL && GetLastError() == ERROR_ALREADY_EXISTS) { iox_sem_close(sem); return SEM_FAILED; } else if (sem->handle == nullptr) { delete sem; return SEM_FAILED; } } else { sem->handle = Win32Call(OpenSemaphoreA, SEMAPHORE_ALL_ACCESS, false, name).value; if (sem->handle == nullptr) { delete sem; return SEM_FAILED; } } return sem; }