/*************************************************************************/ /* safe_refcount.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* 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 SAFE_REFCOUNT_H #define SAFE_REFCOUNT_H #include "os/mutex.h" /* x86/x86_64 GCC */ #include "platform_config.h" #ifdef NO_THREADS struct SafeRefCount { int count; public: // destroy() is called when weak_count_ drops to zero. bool ref() { //true on success if (count == 0) return false; count++; return true; } int refval() { //true on success if (count == 0) return 0; count++; return count; } bool unref() { // true if must be disposed of if (count > 0) count--; return count == 0; } long get() const { // nothrow return static_cast(count); } void init(int p_value = 1) { count = p_value; }; }; #else #if defined(PLATFORM_REFCOUNT) #include "platform_refcount.h" #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define REFCOUNT_T volatile int #define REFCOUNT_GET_T int const volatile & static inline int atomic_conditional_increment(volatile int *pw) { // int rv = *pw; // if( rv != 0 ) ++*pw; // return rv; int rv, tmp; __asm__( "movl %0, %%eax\n\t" "0:\n\t" "test %%eax, %%eax\n\t" "je 1f\n\t" "movl %%eax, %2\n\t" "incl %2\n\t" "lock\n\t" "cmpxchgl %2, %0\n\t" "jne 0b\n\t" "1:" : "=m"(*pw), "=&a"(rv), "=&r"(tmp) : // outputs (%0, %1, %2) "m"(*pw) : // input (%3) "cc" // clobbers ); return rv; } static inline int atomic_decrement(volatile int *pw) { // return --(*pw); unsigned char rv; __asm__( "lock\n\t" "decl %0\n\t" "setne %1" : "=m"(*pw), "=qm"(rv) : "m"(*pw) : "memory"); return static_cast(rv); } /* PowerPC32/64 GCC */ #elif (defined(__GNUC__)) && (defined(__powerpc__) || defined(__ppc__)) #define REFCOUNT_T int #define REFCOUNT_GET_T int const volatile & inline int atomic_conditional_increment(int *pw) { // if( *pw != 0 ) ++*pw; // return *pw; int rv; __asm__( "0:\n\t" "lwarx %1, 0, %2\n\t" "cmpwi %1, 0\n\t" "beq 1f\n\t" "addi %1, %1, 1\n\t" "1:\n\t" "stwcx. %1, 0, %2\n\t" "bne- 0b" : "=m"(*pw), "=&b"(rv) : "r"(pw), "m"(*pw) : "cc"); return rv; } inline int atomic_decrement(int *pw) { // return --*pw; int rv; __asm__ __volatile__( "sync\n\t" "0:\n\t" "lwarx %1, 0, %2\n\t" "addi %1, %1, -1\n\t" "stwcx. %1, 0, %2\n\t" "bne- 0b\n\t" "isync" : "=m"(*pw), "=&b"(rv) : "r"(pw), "m"(*pw) : "memory", "cc"); return rv; } /* CW ARM */ #elif defined(__GNUC__) && (defined(__arm__)) #define REFCOUNT_T int #define REFCOUNT_GET_T int const volatile & inline int atomic_conditional_increment(volatile int *v) { int t; int tmp; __asm__ __volatile__( "1: ldrex %0, [%2] \n" " cmp %0, #0 \n" " beq 2f \n" " add %0, %0, #1 \n" "2: \n" " strex %1, %0, [%2] \n" " cmp %1, #0 \n" " bne 1b \n" : "=&r"(t), "=&r"(tmp) : "r"(v) : "cc", "memory"); return t; } inline int atomic_decrement(volatile int *v) { int t; int tmp; __asm__ __volatile__( "1: ldrex %0, [%2] \n" " add %0, %0, #-1 \n" " strex %1, %0, [%2] \n" " cmp %1, #0 \n" " bne 1b \n" : "=&r"(t), "=&r"(tmp) : "r"(v) : "cc", "memory"); return t; } /* CW PPC */ #elif (defined(__MWERKS__)) && defined(__POWERPC__) inline long atomic_conditional_increment(register long *pw) { register int a; asm { loop: lwarx a, 0, pw cmpwi a, 0 beq store addi a, a, 1 store: stwcx. a, 0, pw bne- loop } return a; } inline long atomic_decrement(register long *pw) { register int a; asm { sync loop: lwarx a, 0, pw addi a, a, -1 stwcx. a, 0, pw bne- loop isync } return a; } /* Any Windows (MSVC) */ #elif defined(_MSC_VER) // made functions to not pollute namespace.. #define REFCOUNT_T long #define REFCOUNT_GET_T long const volatile & long atomic_conditional_increment(register long *pw); long atomic_decrement(register long *pw); #if 0 #elif defined(__GNUC__) && defined(ARMV6_ENABLED) #endif #else #error This platform cannot use safe refcount, compile with NO_THREADS or implement it. #endif struct SafeRefCount { REFCOUNT_T count; public: // destroy() is called when weak_count_ drops to zero. bool ref() { //true on success return atomic_conditional_increment(&count) != 0; } int refval() { //true on success return atomic_conditional_increment(&count); } bool unref() { // true if must be disposed of if (atomic_decrement(&count) == 0) { return true; } return false; } long get() const { // nothrow return static_cast(count); } void init(int p_value = 1) { count = p_value; }; }; #endif // no thread safe #endif