This commit is contained in:
Johan Lorensson 2025-07-30 15:56:35 +02:00 committed by GitHub
commit b1be43e0aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 2535 additions and 62 deletions

View File

@ -41,6 +41,7 @@
#include <cstdarg>
#include <signal.h>
#include <minipal/thread.h>
#include <minipal/env.h>
#ifdef TARGET_LINUX
#include <sys/syscall.h>
@ -988,21 +989,25 @@ UInt32_BOOL PalResetEvent(HANDLE event)
uint32_t PalGetEnvironmentVariable(const char * name, char * buffer, uint32_t size)
{
const char* value = getenv(name);
if (value == NULL)
size_t valueLen = 0;
if (!minipal_env_get_s(&valueLen, buffer, size, name))
{
return 0;
}
size_t valueLen = strlen(value);
// minipal_env_get_s returns the length of the value including the null terminator.
if (valueLen > 0)
{
valueLen--;
}
if (valueLen < size)
{
strcpy(buffer, value);
return valueLen;
return (uint32_t)valueLen;
}
// return required size including the null character or 0 if the size doesn't fit into uint32_t
return (valueLen < UINT32_MAX) ? (valueLen + 1) : 0;
return valueLen < UINT32_MAX ? (uint32_t)(valueLen + 1) : 0;
}
uint16_t PalCaptureStackBackTrace(uint32_t arg1, uint32_t arg2, void* arg3, uint32_t* arg4)

View File

@ -99,7 +99,6 @@
#define PAL_THREAD_PRIORITY_MIN 0
#define PAL_THREAD_PRIORITY_MAX 0
#cmakedefine01 HAVE__NSGETENVIRON
#cmakedefine01 DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
#cmakedefine PAL_PTRACE(cmd, pid, addr, data) @PAL_PTRACE@
#cmakedefine01 SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING

View File

@ -890,7 +890,6 @@ if(NOT CLR_CMAKE_HOST_ARCH_ARM AND NOT CLR_CMAKE_HOST_ARCH_ARM64)
endif()
if(CLR_CMAKE_TARGET_APPLE)
set(HAVE__NSGETENVIRON 1)
set(DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 1)
set(PAL_PTRACE "ptrace((cmd), (pid), (caddr_t)(addr), (data))")
set(HAVE_SCHED_OTHER_ASSIGNABLE 1)

View File

@ -23,9 +23,8 @@ Revision History:
#include "pal/dbgmsg.h"
#include "pal/environ.h"
#if HAVE_CRT_EXTERNS_H
#include <crt_externs.h>
#endif
#include "minipal/env.h"
#include "minipal/mutex.h"
#include <stdlib.h>
@ -895,35 +894,6 @@ char* EnvironGetenv(const char* name, BOOL copyValue)
return retValue;
}
/*++
Function:
EnvironGetSystemEnvironment
Get a pointer to the array of pointers representing the process's
environment.
See 'man environ' for details.
Return Value
A pointer to the environment.
--*/
char** EnvironGetSystemEnvironment()
{
char** sysEnviron;
#if HAVE__NSGETENVIRON
sysEnviron = *(_NSGetEnviron());
#else // HAVE__NSGETENVIRON
extern char **environ;
sysEnviron = environ;
#endif // HAVE__NSGETENVIRON
return sysEnviron;
}
/*++
Function:
EnvironInitialize
@ -943,7 +913,8 @@ EnvironInitialize(void)
CPalThread * pthrCurrent = InternalGetCurrentThread();
minipal_mutex_enter(&gcsEnvironment);
char** sourceEnviron = EnvironGetSystemEnvironment();
char** sourceEnviron = minipal_env_get_environ_copy();
_ASSERT(sourceEnviron != NULL);
int variableCount = 0;
while (sourceEnviron[variableCount] != nullptr)
@ -965,7 +936,9 @@ EnvironInitialize(void)
_ASSERTE(palEnvironment != nullptr);
for (int i = 0; i < variableCount; ++i)
{
palEnvironment[i] = strdup(sourceEnviron[i]);
// Transfer ownership of the string to palEnvironment.
palEnvironment[i] = sourceEnviron[i];
sourceEnviron[i] = nullptr;
palEnvironmentCount++;
}
@ -973,6 +946,8 @@ EnvironInitialize(void)
palEnvironment[variableCount] = nullptr;
}
minipal_env_free_environ(sourceEnviron);
minipal_mutex_leave(&gcsEnvironment);
return ret;
}

View File

@ -235,14 +235,25 @@ void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxByte
// in this case, interpret the number as GB
maxBytesPerThread *= (1024 * 1024 * 1024);
}
theLog.MaxSizePerThread = (unsigned)min(maxBytesPerThread,(size_t)0xffffffff);
size_t maxBytesTotal = maxBytesTotalArg;
#ifdef MEMORY_MAPPED_STRESSLOG
if (logFilename != nullptr && logFilename[0] != '\0')
{
// we need at least sizeof(StressLogHeader) bytes for memory mapped stress log
maxBytesTotal = sizeof(StressLogHeader) + maxBytesTotal;
}
#endif
if (maxBytesTotal < STRESSLOG_CHUNK_SIZE * 256)
{
// in this case, interpret the number as GB
maxBytesTotal *= (1024 * 1024 * 1024);
}
theLog.MaxSizeTotal = (unsigned)min(maxBytesTotal, (size_t)0xffffffff);
theLog.totalChunk = 0;
theLog.facilitiesToLog = facilities | LF_ALWAYS;

View File

@ -70,11 +70,16 @@ if (CLR_CMAKE_TARGET_APPLE)
else()
list (APPEND NATIVE_SOURCES
pal_autoreleasepool.c
pal_environment.c
pal_searchpath.c
pal_log.c
pal_iossupportversion.c)
if (CLR_CMAKE_TARGET_ANDROID)
list (APPEND NATIVE_SOURCES pal_environment_android.c)
else()
list (APPEND NATIVE_SOURCES pal_environment.c)
endif()
if (CLR_CMAKE_TARGET_WASI)
list (APPEND NATIVE_SOURCES pal_console_wasi.c)
else()

View File

@ -3,26 +3,16 @@
#include "pal_config.h"
#include "pal_environment.h"
#include <stdlib.h>
#include <string.h>
#if HAVE_NSGETENVIRON
#include <crt_externs.h>
#endif
#include <minipal/env.h>
char* SystemNative_GetEnv(const char* variable)
{
return getenv(variable);
return minipal_env_get(variable);
}
char** SystemNative_GetEnviron(void)
{
#if HAVE_NSGETENVIRON
return *(_NSGetEnviron());
#else
extern char **environ;
return environ;
#endif
return minipal_env_get_environ();
}
void SystemNative_FreeEnviron(char** environ)

View File

@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#include "pal_config.h"
#include "pal_environment.h"
#include <minipal/env.h>
char* SystemNative_GetEnv(const char* variable)
{
return minipal_env_get(variable);
}
char** SystemNative_GetEnviron(void)
{
return minipal_env_get_environ_copy();
}
void SystemNative_FreeEnviron(char** environ)
{
minipal_env_free_environ(environ);
}

View File

@ -12,6 +12,7 @@ set(SOURCES
utf8.c
xoshiro128pp.c
log.c
env.c
)
# Provide an object library for scenarios where we ship static libraries

View File

@ -0,0 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef HAVE_MINIPAL_ATOMIC_H
#define HAVE_MINIPAL_ATOMIC_H
#ifdef HOST_WINDOWS
#include <windows.h>
#else
#include <stdatomic.h>
#if ATOMIC_POINTER_LOCK_FREE != 2
#pragma message("C11 atomic pointer types are not lock free on targeted platform")
#endif
#endif
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
/**
* @brief Atomic compares and exchange a pointer value.
*
* @param dest Pointer to the destination pointer to compare and swap.
* @param exch The value to store if the comparison succeeds.
* @param comp The value to compare against.
*
* @return The previous value of the pointer.
*/
static inline void* minipal_atomic_compare_exchange_ptr(volatile void** dest, void* exch, void* comp)
{
#ifdef HOST_WINDOWS
return InterlockedCompareExchangePointer((PVOID volatile *)dest, (PVOID)exch, (PVOID)comp);
#else
atomic_compare_exchange_strong((volatile _Atomic(void *) *)dest, &comp, exch);
atomic_thread_fence(memory_order_seq_cst);
return comp;
#endif
}
#ifdef __cplusplus
}
#endif // __cplusplus
#endif /* HAVE_MINIPAL_ATOMIC_H */

View File

@ -5,15 +5,43 @@ include(CheckSymbolExists)
check_include_files("windows.h;bcrypt.h" HAVE_BCRYPT_H)
check_include_files("sys/auxv.h;asm/hwcap.h" HAVE_AUXV_HWCAP_H)
check_include_files("asm/hwprobe.h" HAVE_HWPROBE_H)
check_include_files("crt_externs.h" HAVE_CRT_EXTERNS_H)
check_function_exists(sysctlbyname HAVE_SYSCTLBYNAME)
check_function_exists(fsync HAVE_FSYNC)
check_symbol_exists(sysctlbyname "sys/sysctl.h" HAVE_SYSCTLBYNAME)
check_symbol_exists(fsync "unistd.h" HAVE_FSYNC)
check_symbol_exists(arc4random_buf "stdlib.h" HAVE_ARC4RANDOM_BUF)
check_symbol_exists(O_CLOEXEC fcntl.h HAVE_O_CLOEXEC)
check_symbol_exists(CLOCK_MONOTONIC time.h HAVE_CLOCK_MONOTONIC)
check_symbol_exists(CLOCK_MONOTONIC_COARSE time.h HAVE_CLOCK_MONOTONIC_COARSE)
check_symbol_exists(clock_gettime_nsec_np time.h HAVE_CLOCK_GETTIME_NSEC_NP)
check_symbol_exists(O_CLOEXEC "fcntl.h" HAVE_O_CLOEXEC)
check_symbol_exists(CLOCK_MONOTONIC "time.h" HAVE_CLOCK_MONOTONIC)
check_symbol_exists(CLOCK_MONOTONIC_COARSE "time.h" HAVE_CLOCK_MONOTONIC_COARSE)
check_symbol_exists(clock_gettime_nsec_np "time.h" HAVE_CLOCK_GETTIME_NSEC_NP)
check_symbol_exists(getenv "stdlib.h" HAVE_GETENV)
check_symbol_exists(strcpy_s "string.h" HAVE_STRCPY_S)
check_symbol_exists(strncpy_s "string.h" HAVE_STRNCPY_S)
check_symbol_exists(strcat_s "string.h" HAVE_STRCAT_S)
if (HAVE_CRT_EXTERNS_H)
check_c_source_compiles(
"
#include <crt_externs.h>
int main(void) { char** e = *(_NSGetEnviron()); return 0; }
"
HAVE__NSGETENVIRON)
endif()
check_c_source_compiles(
"
#include <stdlib.h>
int main(void) { char** e = _environ; return 0; }
"
HAVE__ENVIRON)
check_c_source_compiles(
"
int main(void) { extern char **environ; char** e = environ; return 0; }
"
HAVE_ENVIRON)
if(CMAKE_C_BYTE_ORDER STREQUAL "BIG_ENDIAN")
set(BIGENDIAN 1)

1826
src/native/minipal/env.c Normal file

File diff suppressed because it is too large Load Diff

177
src/native/minipal/env.h Normal file
View File

@ -0,0 +1,177 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef HAVE_MINIPAL_ENV_H
#define HAVE_MINIPAL_ENV_H
#include <minipal/types.h>
#include <stdbool.h>
#include <stdarg.h>
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
/**
* @brief Loads and cache the environment variable subsystem using process data.
*
* Loads and cache environment variables from the system and prepares internal data structures.
* If environment variables have already been used through minipal_env_get, minipal_env_get_copy, minipal_env_get_s,
* minipal_env_set or minipal_env_put existing cached values will be merged into loaded environment.
*
* Function exists to explicitly load and cache environment.
*
* @return true on success, false on failure.
*/
bool minipal_env_load_environ(void);
/**
* @brief Unload and free cached environment subsystem.
*
* Unloads and free cache environment variables. Any changes done to the environment will be lost.
* This function should be called when the environment is no longer needed or if the environment needs
* to be reloaded using minipal_env_load_environ.
*/
void minipal_env_unload_environ(void);
/**
* @brief Get a pointer to an internal environment variable array.
*
* @warning The returned pointer is not thread-safe and should not be modified.
* This function should only be called from code that guarantees environment won't
* change while using returned pointer.
*
* @return Pointer to the environment variable array.
*
* @remarks
* - Access internal environment variable array wihout doing allocs or taking locks.
*/
char** minipal_env_get_environ(void);
/**
* @brief Get a pointer to the environment variable array.
*
* @return Pointer to the environment variable array, or NULL on failure.
*
* @remarks
* - Returned environment is a copy and owned by caller, freed using minipal_env_free_environ.
* - If environment has NOT been explicitly loaded, a copy of the underlying system environment
* will created and returned on each call. To avoid recreating environment on each call,
* explicit load environment using minipal_env_load_environ before calling this function.
*/
char** minipal_env_get_environ_copy(void);
/**
* @brief Frees an environment variable array previously returned by minipal_env_get_environ_copy.
*
* This function releases all memory associated with the environment array returned by
* minipal_env_get_environ_copy. The pointer passed must not be used after this call.
*
* @param data Pointer to the environment variable array to free. May be NULL.
*
* @remarks
* - Only use this function to free arrays returned by minipal_env_get_environ_copy.
* - Passing NULL is safe and has no effect.
*/
void minipal_env_free_environ(char** data);
/**
* @brief Checks if specified environment variable exists.
*
* @param name Name of the environment variable to check.
*
* @return true if the variable was found, false otherwise.
*/
bool minipal_env_exists(const char* name);
/**
* @brief Get the value of an environment variable.
*
* @param name The name of the environment variable to get.
*
* @warning The returned pointer is not thread-safe and should not be modified.
* This function should only be called from code that guarantees environment won't
* change while using returned pointer.
*
* @return String containing the value, ownership is still held by the environment and should NOT
* be freed by caller. Returns NULL if not found or on error.
*/
char* minipal_env_get(const char* name);
/**
* @brief Get a copy of the value of an environment variable.
*
* @param name The name of the environment variable to get.
*
* @return Newly allocated string containing the value. Returns NULL if not found or on error.
*
* @remarks
* - Caller is responsible freeing the returned string.
*/
char* minipal_env_get_copy(const char* name);
/**
* @brief Get the value of an environment variable into a user allocated buffer.
*
* This functions gives callers ability to use a pre-allocated buffer when
* requesting an environment variable. Passing in a pointer for len, but NULL
* for value and 0 for valuesz returns needed size in bytes to hold full environment
* variable value in len.
*
* @param len If not NULL, receives the length of the value in bytes.
* @param value Buffer receiving the value.
* @param valuesz Size of the value buffer in bytes.
* @param name Name of the environment variable value to get.
*
* @return true if the variable was found, false otherwise.
*
* @remarks
* - If the buffer is too small, len is set to the required size in bytes
* (including null terminator) and function returns success.
* - If complete value fit into buffer, len includes the number copied bytes
* (including the null terminator).
*/
bool minipal_env_get_s(size_t* len, char* value, size_t valuesz, const char* name);
/**
* @brief Set or update an environment variable.
*
* @param name Name of the environment variable.
* @param value Value to set. If NULL, sets the variable to an empty string.
* @param overwrite If false and the variable exists, does not update it.
*
* @return true on success, false on failure.
*/
bool minipal_env_set(const char* name, const char* value, bool overwrite);
/**
* @brief Add or update an environment variable from a "NAME=VALUE" string.
*
* @param env_s Environment variable to add/update.
*
* @return true on success, false on failure.
*
* @remarks
* - Function makes a copy so caller continues to owns env_s.
*/
bool minipal_env_put(const char* env_s);
/**
* @brief Remove an environment variable.
*
* @param name Name of the environment variable to remove.
*
* @return true if environment variable is successfully removed or not found, false on failure.
*
* @remarks
* - If environment has NOT been explicitly loaded, function will automatically load
* enviroment maintaining a unified view of all changes.
*/
bool minipal_env_unset(const char* name);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif /* HAVE_MINIPAL_ENV_H */

View File

@ -4,6 +4,7 @@
#cmakedefine01 HAVE_ARC4RANDOM_BUF
#cmakedefine01 HAVE_AUXV_HWCAP_H
#cmakedefine01 HAVE_HWPROBE_H
#cmakedefine01 HAVE_CRT_EXTERNS_H
#cmakedefine01 HAVE_O_CLOEXEC
#cmakedefine01 HAVE_SYSCTLBYNAME
#cmakedefine01 HAVE_CLOCK_MONOTONIC
@ -12,5 +13,12 @@
#cmakedefine01 BIGENDIAN
#cmakedefine01 HAVE_BCRYPT_H
#cmakedefine01 HAVE_FSYNC
#cmakedefine01 HAVE__NSGETENVIRON
#cmakedefine01 HAVE__ENVIRON
#cmakedefine01 HAVE_ENVIRON
#cmakedefine01 HAVE_GETENV
#cmakedefine01 HAVE_STRCPY_S
#cmakedefine01 HAVE_STRNCPY_S
#cmakedefine01 HAVE_STRCAT_S
#endif

View File

@ -2,11 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
#include "strings.h"
#include <errno.h>
#include <string.h>
#ifdef HOST_WINDOWS
#include <wchar.h>
#endif
/**
* @see strings.h
*/
size_t minipal_u16_strlen(const CHAR16_T* str)
{
#ifdef HOST_WINDOWS
@ -20,3 +25,125 @@ size_t minipal_u16_strlen(const CHAR16_T* str)
return len;
#endif
}
/**
* @see strings.h
*/
char * minipal_strdup(const char* str)
{
#ifdef HOST_WINDOWS
return _strdup(str);
#else
return strdup(str);
#endif
}
/**
* @see strings.h
*/
int minipal_strcpy_s(char* dest, size_t destsz, const char* src)
{
#if HAVE_STRCPY_S
return strcpy_s(dest, destsz, src);
#else
if (dest == NULL || src == NULL || destsz == 0)
{
if (dest && destsz > 0)
{
dest[0] = '\0';
}
return EINVAL;
}
size_t src_len = strlen(src);
if (src_len + 1 > destsz)
{
dest[0] = '\0';
return ERANGE;
}
memcpy(dest, src, src_len + 1);
return 0;
#endif
}
/**
* @see strings.h
*/
int minipal_strncpy_s(char* dest, size_t destsz, const char* src, size_t count)
{
#if HAVE_STRNCPY_S
return strncpy_s(dest, destsz, src, count);
#else
if (dest == NULL || src == NULL || destsz == 0)
{
if (dest && destsz > 0)
{
dest[0] = '\0';
}
return EINVAL;
}
if (count >= destsz)
{
dest[0] = '\0';
return ERANGE;
}
size_t i = 0;
for (; i < count && i < destsz - 1 && src[i] != '\0'; ++i)
{
dest[i] = src[i];
}
dest[i] = '\0';
return 0;
#endif
}
/**
* @see strings.h
*/
int minipal_strcat_s(char* dest, size_t destsz, const char* src)
{
#if HAVE_STRCAT_S
return strcat_s(dest, destsz, src);
#else
if (dest == NULL || src == NULL || destsz == 0)
{
if (dest && destsz > 0)
{
dest[0] = '\0';
}
return EINVAL;
}
size_t dest_len = 0;
for (; dest_len < destsz; ++dest_len)
{
if (dest[dest_len] == '\0')
{
break;
}
}
if (dest_len == destsz)
{
dest[0] = '\0';
return ERANGE;
}
size_t src_len = strlen(src);
if (dest_len + src_len + 1 > destsz)
{
dest[0] = '\0';
return ERANGE;
}
memcpy(dest + dest_len, src, src_len + 1);
return 0;
#endif
}

View File

@ -35,6 +35,26 @@ CHAR16_T minipal_tolower_invariant(CHAR16_T code);
*/
size_t minipal_u16_strlen(const CHAR16_T* str);
/**
* @brief xplat implementation of strdup.
*/
char* minipal_strdup(const char *str);
/**
* @brief xplat implementation of strcpy_s.
*/
int minipal_strcpy_s(char* dest, size_t destsz, const char* src);
/**
* @brief xplat implementation of strncpy_s.
*/
int minipal_strncpy_s(char* dest, size_t destsz, const char* src, size_t count);
/**
* @brief xplat implementation of strcat_s.
*/
int minipal_strcat_s(char* dest, size_t destsz, const char* src);
#ifdef __cplusplus
}
#endif // __cplusplus

View File

@ -0,0 +1,235 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef HAVE_MINIPAL_VOLATILE_H
#define HAVE_MINIPAL_VOLATILE_H
#include <stdint.h>
#ifdef _MSC_VER
#include <intrin.h>
#ifdef HOST_ARM64
#include <arm64intr.h>
#endif
#endif
//
// This code is extremely compiler- and CPU-specific, and will need to be altered to
// support new compilers and/or CPUs. Here we enforce that we can only compile using
// VC++, or GCC on x86 or AMD64.
//
#if !defined(_MSC_VER) && !defined(__GNUC__)
#error The Volatile type is currently only defined for Visual C++ and GNU C++
#endif
#if defined(__GNUC__) && !defined(HOST_X86) && !defined(HOST_AMD64) && !defined(HOST_ARM) && !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) && !defined(HOST_WASM) && !defined(HOST_RISCV64)
#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM, ARM64, LOONGARCH64, Wasm, RISCV64
#endif
#if defined(__GNUC__)
#if defined(HOST_ARM) || defined(HOST_ARM64)
// This is functionally equivalent to the MemoryBarrier() macro used on ARM on Windows.
#define MINIPAL_VOLATILE_MEMORY_BARRIER() asm volatile ("dmb ish" : : : "memory")
#elif defined(HOST_LOONGARCH64)
#define MINIPAL_VOLATILE_MEMORY_BARRIER() asm volatile ("dbar 0 " : : : "memory")
#elif defined(HOST_RISCV64)
#define MINIPAL_VOLATILE_MEMORY_BARRIER() asm volatile ("fence rw,rw" : : : "memory")
#else
//
// For GCC, we prevent reordering by the compiler by inserting the following after a volatile
// load (to prevent subsequent operations from moving before the read), and before a volatile
// write (to prevent prior operations from moving past the write). We don't need to do anything
// special to prevent CPU reorderings, because the x86 and AMD64 architectures are already
// sufficiently constrained for our purposes. If we ever need to run on weaker CPU architectures
// (such as PowerPC), then we will need to do more work.
//
// Please do not use this macro outside of this file. It is subject to change or removal without
// notice.
//
#define MINIPAL_VOLATILE_MEMORY_BARRIER() asm volatile ("" : : : "memory")
#endif // HOST_ARM || HOST_ARM64
#elif (defined(HOST_ARM) || defined(HOST_ARM64)) && _ISO_VOLATILE
// ARM & ARM64 have a very weak memory model and very few tools to control that model. We're forced to perform a full
// memory barrier to preserve the volatile semantics. Technically this is only necessary on MP systems but we
// currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it
// turns out to be a performance issue for the uni-proc case.
#define MINIPAL_VOLATILE_MEMORY_BARRIER() MemoryBarrier()
#else
//
// On VC++, reorderings at the compiler and machine level are prevented by the use of the
// "volatile" keyword in volatile_load and volatile_store. This should work on any CPU architecture
// targeted by VC++ with /iso_volatile-.
//
#define MINIPAL_VOLATILE_MEMORY_BARRIER()
#endif // __GNUC__
#if defined(HOST_ARM64) && defined(__GNUC__)
#ifdef __cplusplus
// Starting at version 3.8, clang errors out on initializing of type int * to volatile int *. To fix this, we add two templates to cast away volatility
// Helper structures for casting away volatileness
template<typename T>
struct RemoveVolatile
{
typedef T type;
};
template<typename T>
struct RemoveVolatile<volatile T>
{
typedef T type;
};
#define REMOVE_VOLATILE_T(T, val) const_cast<typename RemoveVolatile<T>::type *>(&val)
#else
#define REMOVE_VOLATILE_T(T, val) (T*)(&val)
#endif // __cplusplus
#define MINIPAL_VOLATILE_GCC_ATOMIC_LOAD(T, ptr, val) \
do { __atomic_load((T const*)(ptr), REMOVE_VOLATILE_T(T, val), __ATOMIC_ACQUIRE); } while (0)
#define MINIPAL_VOLATILE_GCC_ATOMIC_STORE(T, ptr, val) \
do { __atomic_store((T volatile*)(ptr), &val, __ATOMIC_RELEASE); } while (0)
#define MINIPAL_VOLATILE_LOAD_T(T, ptr, val) do { val = *(T volatile const*)(ptr); asm volatile ("dmb ishld" : : : "memory"); } while (0)
#define MINIPAL_VOLATILE_LOAD_8(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_LOAD(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_LOAD_16(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_LOAD(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_LOAD_32(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_LOAD(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_LOAD_64(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_LOAD(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_T(T, ptr, val) do { MINIPAL_VOLATILE_MEMORY_BARRIER(); *(T volatile*)(ptr) = val; } while (0)
#define MINIPAL_VOLATILE_STORE_8(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_STORE(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_16(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_STORE(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_32(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_STORE(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_64(T, ptr, val) do { MINIPAL_VOLATILE_GCC_ATOMIC_STORE(T, ptr, val); } while (0)
#elif defined(HOST_ARM64) && defined(_MSC_VER)
#define MINIPAL_VOLATILE_LOAD_T(T, ptr, val) do { val = *(T volatile const*)(ptr); __dmb(_ARM64_BARRIER_ISHLD); } while (0)
#define MINIPAL_VOLATILE_LOAD_8(T, ptr, val) do { *(uint8_t*)&val = __ldar8((uint8_t volatile*)(ptr)); } while (0)
#define MINIPAL_VOLATILE_LOAD_16(T, ptr, val) do { *(uint16_t*)&val = __ldar16((uint16_t volatile*)(ptr)); } while (0)
#define MINIPAL_VOLATILE_LOAD_32(T, ptr, val) do { *(uint32_t*)&val = __ldar32((uint32_t volatile*)(ptr)); } while (0)
#define MINIPAL_VOLATILE_LOAD_64(T, ptr, val) do { *(uint64_t*)&val = __ldar64((uint64_t volatile*)(ptr)); } while (0)
#define MINIPAL_VOLATILE_STORE_T(T, ptr, val) do { __dmb(_ARM64_BARRIER_ISHLD); *(T volatile*)ptr = val; } while (0)
#define MINIPAL_VOLATILE_STORE_8(T, ptr, val) do { __stlr8((uint8_t volatile*)(ptr), *(uint8_t*)&val); } while (0)
#define MINIPAL_VOLATILE_STORE_16(T, ptr, val) do { __stlr16((uint16_t volatile*)(ptr), *(uint16_t*)&val); } while (0)
#define MINIPAL_VOLATILE_STORE_32(T, ptr, val) do { __stlr32((uint32_t volatile*)(ptr), *(uint32_t*)&val); } while (0)
#define MINIPAL_VOLATILE_STORE_64(T, ptr, val) do { __stlr64((uint64_t volatile*)(ptr), *(uint64_t*)&val); } while (0)
#else
#define MINIPAL_VOLATILE_LOAD_T(T, ptr, val) do { val = *(T volatile const*)(ptr); MINIPAL_VOLATILE_MEMORY_BARRIER(); } while (0)
#define MINIPAL_VOLATILE_LOAD_8(T, ptr, val) do { MINIPAL_VOLATILE_LOAD_T(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_LOAD_16(T, ptr, val) do { MINIPAL_VOLATILE_LOAD_T(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_LOAD_32(T, ptr, val) do { MINIPAL_VOLATILE_LOAD_T(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_LOAD_64(T, ptr, val) do { MINIPAL_VOLATILE_LOAD_T(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_T(T, ptr, val) do { MINIPAL_VOLATILE_MEMORY_BARRIER(); *(T volatile*)(ptr) = val; } while (0)
#define MINIPAL_VOLATILE_STORE_8(T, ptr, val) do { MINIPAL_VOLATILE_STORE_T(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_16(T, ptr, val) do { MINIPAL_VOLATILE_STORE_T(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_32(T, ptr, val) do { MINIPAL_VOLATILE_STORE_T(T, ptr, val); } while (0)
#define MINIPAL_VOLATILE_STORE_64(T, ptr, val) do { MINIPAL_VOLATILE_STORE_T(T, ptr, val); } while (0)
#endif // defined(HOST_ARM64) && defined(__GNUC__)
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
// minipal_volatile_load_* loads a T from a pointer to T. It is guaranteed that this load will not be optimized
// away by the compiler, and that any operation that occurs after this load, in program order, will
// not be moved before this load. In general it is not guaranteed that the load will be atomic, though
// this is the case for most aligned scalar data types. If you need atomic loads or stores, you need
// to consult the compiler and CPU manuals to find which circumstances allow atomicity.
static inline uint8_t minipal_volatile_load_uint8_t(uint8_t const* ptr)
{
uint8_t value;
MINIPAL_VOLATILE_LOAD_8(uint8_t, ptr, value);
return value;
}
static inline uint16_t minipal_volatile_load_uint16_t(uint16_t const* ptr)
{
uint16_t value;
MINIPAL_VOLATILE_LOAD_16(uint16_t, ptr, value);
return value;
}
static inline uint32_t minipal_volatile_load_uint32_t(uint32_t const* ptr)
{
uint32_t value;
MINIPAL_VOLATILE_LOAD_32(uint32_t, ptr, value);
return value;
}
static inline uint64_t minipal_volatile_load_uint64_t(uint64_t const* ptr)
{
uint64_t value;
MINIPAL_VOLATILE_LOAD_64(uint64_t, ptr, value);
return value;
}
static inline void* minipal_volatile_load_ptr(void* const* ptr)
{
if (sizeof(uintptr_t) == 4)
{
uint32_t value;
MINIPAL_VOLATILE_LOAD_32(uint32_t, ptr, value);
return (void*)(uintptr_t)value;
}
else
{
uint64_t value;
MINIPAL_VOLATILE_LOAD_64(uint64_t, ptr, value);
return (void*)(uintptr_t)value;
}
}
// minipal_volatile_store_* stores a T into the target of a pointer to T. It is guaranteed that this store will
// not be optimized away by the compiler, and that any operation that occurs before this store, in program
// order, will not be moved after this store. In general, it is not guaranteed that the store will be
// atomic, though this is the case for most aligned scalar data types. If you need atomic loads or stores,
// you need to consult the compiler and CPU manuals to find which circumstances allow atomicity.
static inline void minipal_volatile_store_uint8_t(uint8_t* ptr, uint8_t value)
{
MINIPAL_VOLATILE_STORE_8(uint8_t, ptr, value);
}
static inline void minipal_volatile_store_uint16_t(uint16_t* ptr, uint16_t value)
{
MINIPAL_VOLATILE_STORE_16(uint16_t, ptr, value);
}
static inline void minipal_volatile_store_uint32_t(uint32_t* ptr, uint32_t value)
{
MINIPAL_VOLATILE_STORE_32(uint32_t, ptr, value);
}
static inline void minipal_volatile_store_uint64_t(uint64_t* ptr, uint64_t value)
{
MINIPAL_VOLATILE_STORE_64(uint64_t, ptr, value);
}
static inline void minipal_volatile_store_ptr(void** ptr, void* value)
{
if (sizeof(uintptr_t) == 4)
{
uint32_t value32 = (uint32_t)(uintptr_t)value;
MINIPAL_VOLATILE_STORE_32(uint32_t, ptr, value32);
}
else
{
uint64_t value64 = (uint64_t)(uintptr_t)value;
MINIPAL_VOLATILE_STORE_64(uint64_t, ptr, value64);
}
}
#ifdef __cplusplus
}
#endif // __cplusplus
#endif /* HAVE_MINIPAL_VOLATILE_H */