/*************************************************************************/ /* safe_refcount.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* 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<int const volatile &>( 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<int>(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<REFCOUNT_GET_T>( count ); } void init(int p_value=1) { count=p_value; }; }; #endif // no thread safe #endif