mirror of
https://github.com/libretro/parallel-gx2.git
synced 2026-11-07 04:25:58 -10:00
The core builds with a static Makefile (Makefile + Makefile.common) in the manner of the other ParaLLEl cores, for every target .gitlab-ci.yml has: Linux x86_64/aarch64, Windows (MinGW, native or cross), macOS, iOS, tvOS, Android arm64/x86_64 and webOS. CMake, vcpkg and their overlay ports are gone; the libraries the core needs are submodules built with it (fmt 12.1.0, glm 1.0.1, glslang 15.1.0, libusb 1.0.29, pugixml 1.15, zlib 1.3.1, zstd 1.5.7), with the generated files and the graphic packs produced by the Makefile itself (tools/pack_graphic_packs.c), and a precompiled header for gcc and clang. The core is renamed parallel_gx2 (parallel_gx2_libretro.info, library name "ParaLLEl GX2"). HTTP and TLS go through RetroArch's libretro-common (net_http with the per-request TLS settings, the built-in TLS client, x509, the hashes and the KDF) in place of libcurl, OpenSSL and mbedTLS: dependencies/curl-compat is the small libcurl API nlibcurl and napi use, on one transfer thread with lock-free submission and per-handle event queues, with its own tests against local HTTP and TLS servers, plain and under ASan/UBSan and TSan, in both CIs. The libretro-common files are plain copies from RetroArch. boost is replaced by the few headers the core uses (boost-compat), and the CI files build with the Makefile.
145 lines
4.6 KiB
C
145 lines
4.6 KiB
C
/* Copyright (C) 2010-2026 The RetroArch team
|
|
*
|
|
* ---------------------------------------------------------------------
|
|
* The following license statement only applies to this file (mpsc_stack.h).
|
|
* ---------------------------------------------------------------------
|
|
*
|
|
* Permission is hereby granted, free of charge,
|
|
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE 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. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifndef __LIBRETRO_SDK_MPSC_STACK_H__
|
|
#define __LIBRETRO_SDK_MPSC_STACK_H__
|
|
|
|
/* A many-producer, single-consumer intrusive stack: any thread pushes,
|
|
* one thread takes everything at once. The shape a worker-to-main
|
|
* handoff wants - deferred messages, retire lists - where the consumer
|
|
* drains on its own cadence and order within the batch is recovered by
|
|
* reversing, since a push stacks newest-first.
|
|
*
|
|
* Producers never wait and never touch a lock; the consumer's drain is
|
|
* one exchange. There is no ABA hazard in this shape: nodes are only
|
|
* pushed, and the consumer takes the whole chain, so a producer's
|
|
* compare-exchange retries against a moved head and nothing else.
|
|
*
|
|
* The node's link field belongs to the stack from push until the
|
|
* consumer has taken the chain; everything else in the node is
|
|
* published by the push and safe for the consumer to read after
|
|
* mpsc_stack_drain() returned it.
|
|
*/
|
|
|
|
#include <boolean.h>
|
|
#include <retro_atomic.h>
|
|
#include <retro_inline.h>
|
|
|
|
RETRO_BEGIN_DECLS
|
|
|
|
typedef struct mpsc_stack_node
|
|
{
|
|
struct mpsc_stack_node *next;
|
|
} mpsc_stack_node_t;
|
|
|
|
#if defined(RETRO_ATOMIC_HAS_PTR)
|
|
|
|
typedef struct mpsc_stack
|
|
{
|
|
retro_atomic_ptr_t head;
|
|
} mpsc_stack_t;
|
|
|
|
static INLINE void mpsc_stack_init(mpsc_stack_t *stack)
|
|
{
|
|
retro_atomic_ptr_init(&stack->head, NULL);
|
|
}
|
|
|
|
/* Any thread. The node's payload must be written before the push;
|
|
* the compare-exchange publishes it. */
|
|
static INLINE void mpsc_stack_push(mpsc_stack_t *stack,
|
|
mpsc_stack_node_t *node)
|
|
{
|
|
do
|
|
{
|
|
node->next = (mpsc_stack_node_t *)
|
|
retro_atomic_load_acquire_ptr(&stack->head);
|
|
} while (!retro_atomic_cas_ptr(&stack->head, node->next, node));
|
|
}
|
|
|
|
/* True when a drain would return nothing: the consumer's cheap
|
|
* per-iteration probe. */
|
|
static INLINE bool mpsc_stack_empty(mpsc_stack_t *stack)
|
|
{
|
|
return !retro_atomic_load_acquire_ptr(&stack->head);
|
|
}
|
|
|
|
/* The consumer. Takes the whole chain, newest-first; the caller walks
|
|
* or reverses it. */
|
|
static INLINE mpsc_stack_node_t *mpsc_stack_drain(mpsc_stack_t *stack)
|
|
{
|
|
return (mpsc_stack_node_t *)
|
|
retro_atomic_exchange_ptr(&stack->head, NULL);
|
|
}
|
|
|
|
#else
|
|
|
|
/* The volatile fallback backend offers no pointer atomics by design:
|
|
* it serves single-context targets where plain access is all the
|
|
* platform has. The stack keeps its shape with plain operations, and
|
|
* its guarantees follow the backend's - none beyond what the target
|
|
* itself provides, exactly as with every retro_atomic op there. */
|
|
typedef struct mpsc_stack
|
|
{
|
|
mpsc_stack_node_t * volatile head;
|
|
} mpsc_stack_t;
|
|
|
|
static INLINE void mpsc_stack_init(mpsc_stack_t *stack)
|
|
{
|
|
stack->head = NULL;
|
|
}
|
|
|
|
static INLINE void mpsc_stack_push(mpsc_stack_t *stack,
|
|
mpsc_stack_node_t *node)
|
|
{
|
|
node->next = stack->head;
|
|
stack->head = node;
|
|
}
|
|
|
|
static INLINE bool mpsc_stack_empty(mpsc_stack_t *stack)
|
|
{
|
|
return !stack->head;
|
|
}
|
|
|
|
static INLINE mpsc_stack_node_t *mpsc_stack_drain(mpsc_stack_t *stack)
|
|
{
|
|
mpsc_stack_node_t *node = stack->head;
|
|
stack->head = NULL;
|
|
return node;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* The drained chain, oldest-first. */
|
|
static INLINE mpsc_stack_node_t *mpsc_stack_reverse(mpsc_stack_node_t *node)
|
|
{
|
|
mpsc_stack_node_t *chain = NULL;
|
|
while (node)
|
|
{
|
|
mpsc_stack_node_t *next = node->next;
|
|
node->next = chain;
|
|
chain = node;
|
|
node = next;
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
RETRO_END_DECLS
|
|
|
|
#endif
|