203 lines
6.0 KiB
C
203 lines
6.0 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
*/
|
||
|
||
/*
|
||
* platform/threading.c - Minimal mutex abstraction bridging POSIX and
|
||
* embedded (spinlock-based) targets
|
||
*/
|
||
|
||
#include "../../include/platform_lock.h"
|
||
|
||
/* HISTORICAL: these two conditions also referenced L4RE_TARGET
|
||
* (`&& !defined(L4RE_TARGET)` / `|| defined(L4RE_TARGET)`) -- L4Re had no
|
||
* pthreads either, so it shared the spinlock path with STARFORTH_MINIMAL.
|
||
* L4Re support has been removed as an active target. */
|
||
#if !defined(STARFORTH_MINIMAL)
|
||
#include <pthread.h>
|
||
#endif
|
||
|
||
#if defined(STARFORTH_MINIMAL)
|
||
/* Spinlock-based mutex for minimal/embedded builds */
|
||
|
||
/**
|
||
* @brief Initialise a spinlock mutex (minimal build).
|
||
*
|
||
* Sets @c mutex->state to 0 (unlocked). Safe to call on a mutex that has
|
||
* already been destroyed. On embedded targets where @c pthread is unavailable,
|
||
* this replaces @c pthread_mutex_init().
|
||
*
|
||
* @param mutex Mutex to initialise; returns -1 if NULL
|
||
* @return 0 on success, -1 if @c mutex is NULL
|
||
*/
|
||
int sf_mutex_init(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return -1;
|
||
mutex->state = 0;
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief Destroy a spinlock mutex (minimal build).
|
||
*
|
||
* Resets @c mutex->state to 0. No resources are released because the
|
||
* spinlock uses only the single @c state field. Safe to call on an
|
||
* already-destroyed mutex.
|
||
*
|
||
* @param mutex Mutex to destroy; no-op if NULL
|
||
*/
|
||
void sf_mutex_destroy(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return;
|
||
mutex->state = 0;
|
||
}
|
||
|
||
/**
|
||
* @brief Acquire a spinlock mutex (minimal build).
|
||
*
|
||
* Busy-waits using @c __sync_lock_test_and_set() until the lock is obtained.
|
||
* Issues an architecture-appropriate yield hint (@c PAUSE on x86,
|
||
* @c YIELD on ARM/AArch64, @c NOP on RISC-V) inside the spin loop to
|
||
* reduce power consumption and SMT contention.
|
||
*
|
||
* @param mutex Mutex to lock; no-op if NULL
|
||
*/
|
||
void sf_mutex_lock(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return;
|
||
while (__sync_lock_test_and_set(&mutex->state, 1))
|
||
{
|
||
/* CPU yield to reduce power consumption and improve performance */
|
||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X64) || defined(_M_IX86)
|
||
/* x86/x86_64: PAUSE instruction reduces pipeline flush penalty */
|
||
__asm__ __volatile__("pause" ::: "memory");
|
||
#elif defined(__aarch64__) || defined(__arm__)
|
||
/* ARM/AArch64: YIELD hint for SMT threads */
|
||
__asm__ __volatile__("yield" ::: "memory");
|
||
#elif defined(__riscv)
|
||
/* RISC-V: NOP as yield hint */
|
||
__asm__ __volatile__("nop" ::: "memory");
|
||
#else
|
||
/* Fallback: compiler barrier prevents optimization */
|
||
__asm__ __volatile__("" ::: "memory");
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Release a spinlock mutex (minimal build).
|
||
*
|
||
* Clears the lock state atomically using @c __sync_lock_release(), which
|
||
* implies a full memory barrier on all supported architectures. Threads
|
||
* blocked in @c sf_mutex_lock() will observe the release on their next
|
||
* test-and-set iteration.
|
||
*
|
||
* @param mutex Mutex to unlock; no-op if NULL
|
||
*/
|
||
void sf_mutex_unlock(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return;
|
||
__sync_lock_release(&mutex->state);
|
||
}
|
||
|
||
#else /* POSIX */
|
||
|
||
/**
|
||
* @brief Initialise a POSIX mutex (hosted build).
|
||
*
|
||
* Delegates to @c pthread_mutex_init() with default attributes.
|
||
*
|
||
* @param mutex Mutex to initialise; returns -1 if NULL
|
||
* @return 0 on success, -1 if @c mutex is NULL, or a positive @c errno value
|
||
* propagated from @c pthread_mutex_init()
|
||
*/
|
||
int sf_mutex_init(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return -1;
|
||
return pthread_mutex_init(&mutex->handle, NULL);
|
||
}
|
||
|
||
/**
|
||
* @brief Destroy a POSIX mutex (hosted build).
|
||
*
|
||
* Delegates to @c pthread_mutex_destroy(). The mutex must not be locked
|
||
* when destroyed. Return value is discarded; callers that need the error
|
||
* code should call @c pthread_mutex_destroy() directly.
|
||
*
|
||
* @param mutex Mutex to destroy; no-op if NULL
|
||
*/
|
||
void sf_mutex_destroy(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return;
|
||
pthread_mutex_destroy(&mutex->handle);
|
||
}
|
||
|
||
/**
|
||
* @brief Acquire a POSIX mutex (hosted build).
|
||
*
|
||
* Delegates to @c pthread_mutex_lock(). Blocks until the mutex is available.
|
||
* Return value is discarded; a locking error will manifest as a deadlock or
|
||
* data race, not a silent failure.
|
||
*
|
||
* @param mutex Mutex to lock; no-op if NULL
|
||
*/
|
||
void sf_mutex_lock(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return;
|
||
pthread_mutex_lock(&mutex->handle);
|
||
}
|
||
|
||
/**
|
||
* @brief Release a POSIX mutex (hosted build).
|
||
*
|
||
* Delegates to @c pthread_mutex_unlock(). Must be called by the same thread
|
||
* that acquired the lock. Return value is discarded.
|
||
*
|
||
* @param mutex Mutex to unlock; no-op if NULL
|
||
*/
|
||
void sf_mutex_unlock(sf_mutex_t *mutex)
|
||
{
|
||
if (!mutex) return;
|
||
pthread_mutex_unlock(&mutex->handle);
|
||
}
|
||
|
||
#endif
|