/* * * This software is released under the provisions of the GPL version 2. * see file "COPYING". If that file is not available, the full statement * of the license can be found at * * http://www.fsf.org/licensing/licenses/gpl.txt * * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. * */ /*********************************************************************** * THIS CODE IS HIGHLY CRITICAL TO THE SERVER'S STABILITY!!! * DO NOT MAKE CHANGES TO THE ATOMIC OPERATORS OR TO THE * MUTEX CODE WITHOUT A SERVER TEAM CODE-REVIEW! (dev@helix-server) */ /**************************************************************************** * $Id: atomicbase.h 587223 2006-09-21 23:14:40Z aoliveira $ * * atomicbase.h - Defines several atomic operations * * See server/common/util/pub/servatomic.h for broader platform support. * Also conditionally overrides InterlockedIncrement/Decrement * via USE_HX_ATOMIC_INTERLOCKED_INC_DEC. * * *********************************************************************** * * Defines: * * void HXAtomicIncINT32(INT32* p) -- Increment *p * void HXAtomicDecINT32(INT32* p) -- Decrement *p * void HXAtomicAddINT32(INT32* p, INT32 n) -- Increment *p by n * void HXAtomicSubINT32(INT32* p, INT32 n) -- Decrement *p by n * INT32 HXAtomicIncRetINT32(INT32* p) -- Increment *p and return it * INT32 HXAtomicDecRetINT32(INT32* p) -- Decrement *p and return it * INT32 HXAtomicAddRetINT32(INT32* p, INT32 n)-- Increment *p by n, return it * INT32 HXAtomicSubRetINT32(INT32* p, INT32 n)-- Increment *p by n, return it * * * There are also UINT32 versions: * * void HXAtomicIncUINT32(UINT32* p) * void HXAtomicDecUINT32(UINT32* p) * void HXAtomicAddUINT32(UINT32* p, UINT32 n) * void HXAtomicSubUINT32(UINT32* p, UINT32 n) * UINT32 HXAtomicIncRetUINT32(UINT32* p) * UINT32 HXAtomicDecRetUINT32(UINT32* p) * UINT32 HXAtomicAddRetUINT32(UINT32* p, UINT32 n) * UINT32 HXAtomicSubRetUINT32(UINT32* p, UINT32 n) * *********************************************************************** * * TODO: * Add INT64 versions * Obsolete the 0x80000000-based Solaris implementation entirely. * ***********************************************************************/ #ifndef _ATOMICBASE_H_ #define _ATOMICBASE_H_ /*********************************************************************** * Sun Solaris / SPARC (Native compiler) * * Implementation Notes: * This uses inline assembly from server/common/util/platform/solaris/atomicops.il * Note: Sparc/gcc is in include/atomicbase.h */ #if defined (_SOLARIS) && !defined (__GNUC__) #if defined(__cplusplus) extern "C" { #endif //UINT32 _HXAtomicIncRetUINT32 (UINT32* pNum); //UINT32 _HXAtomicDecRetUINT32 (UINT32* pNum); UINT32 _HXAtomicAddRetUINT32 (UINT32* pNum, UINT32 ulNum); UINT32 _HXAtomicSubRetUINT32 (UINT32* pNum, UINT32 ulNum); #if defined(__cplusplus) } #endif #define HXAtomicIncUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) #define HXAtomicDecUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) #define HXAtomicIncRetUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) #define HXAtomicDecRetUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) #define HXAtomicAddUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) #define HXAtomicAddRetUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubRetUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } /*********************************************************************** * Sun Solaris / SPARC (gcc) * * Implementation Notes: * The sparc method of pipelining and use of "delay slots" requires * the nop's. Be extra careful modifying these routines! * * This implementation sacrifices being able to store the value * 0x800000000 in the INT32 value, which is a special "busy" marker value. * Since these are intended for use primarily with AddRef/Release and * resource usage counters, this should be acceptable for now. If a counter * is incremented to the point it would conflict with the flag, it is * incremented one more to hop over it. The same in reverse for decrement. * This is far from ideal, however... See the inline-assembly file * server/common/util/platform/solaris/mutex_setbit.il for *much* * better implementations using newer sparc assembly operators. * * Basic design of the flag-based implementation: * 1. Load a register with 0x80000000 * 2. _atomically_ swap it with the INT32 (critical!) * 3. Compare what we got with 0x80000000 * 4. Branch if equal to #2 * 5. Increment (or decrement) the result * 6. Compare to 0x80000000 * 7. Branch if equal to #5 * 8. Save the new value to the INT32's location in memory * 9. Return new INT32 result if required * * This implementation primarily exists due to limitations in the ancient * version of gcc we used to use on Solaris (2.7.2.3), and more modern * gcc's can probably handle assembly more like what's used in Sun's * Native compiler version. * */ #elif defined (__sparc__) && defined (__GNUC__) /* Increment by 1 */ inline void HXAtomicIncUINT32(UINT32* pNum) { __asm__ __volatile__(\ "1: swap [%0], %2; ! Swap *pNum and %2\n" " nop; ! delay slot...\n" " cmp %2, %1; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" "2: inc %2; ! Increment %2\n" " cmp %2, %1; ! check for overflow\n" " be 2b; ! if so, inc again\n" " nop; ! but this means a delay, sigh\n" " st %2, [%0]; ! Save new value into *pNum\n" : /* no output */ : "r" (pNum), "r" (0x80000000), "r" (0x80000000) : "cc", "memory" ); } /* Decrement by 1 */ inline void HXAtomicDecUINT32(UINT32* pNum) { __asm__ __volatile__( "1: swap [%0], %2; ! Swap *pNum and %2\n" " nop; ! delay slot...\n" " cmp %2, %1; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" "2: dec %2; ! Increment %2\n" " cmp %2, %1; ! check for overflow\n" " be 2b; ! if so, dec again\n" " nop; ! but this means a delay, sigh\n" " st %2, [%0]; ! Save new value into *pNum\n" : /* no output */ : "r" (pNum), "r" (0x80000000), "r" (0x80000000) : "cc", "memory" ); } /* Increment by 1 and return new value */ inline UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %2, %0; ! Copy %2 to %0 \n" "1: swap [%1], %0; ! Swap *pNum and %0\n" " nop; ! delay slot...\n" " cmp %0, %2; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" "2: inc %0; ! Increment %0\n" " cmp %0, %2; ! check for overflow\n" " be 2b; ! if so, inc again\n" " nop; ! but this means a delay, sigh\n" " st %0, [%1]; ! Save new value into *pNum\n" : "=r" (ulRet) : "r" (pNum), "r" (0x80000000), "0" (ulRet) : "cc", "memory" ); return ulRet; } /* Decrement by 1 and return new value */ inline UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %2, %0; ! Copy %2 to %0 \n" "1: swap [%1], %0; ! Swap *pNum and %0\n" " nop; ! delay slot...\n" " cmp %0, %2; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" "2: dec %0; ! Decrement %0\n" " cmp %0, %2; ! check for overflow\n" " be 2b; ! if so, dec again\n" " nop; ! but this means a delay, sigh\n" " st %0, [%1]; ! Save new value into *pNum\n" : "=r" (ulRet) : "r" (pNum), "r" (0x80000000), "0" (ulRet) : "cc", "memory" ); return ulRet; } /* Add n */ inline void HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) { __asm__ __volatile__( "1: swap [%0], %2; ! Swap *pNum and %2\n" " nop; ! delay slot...\n" " cmp %2, %1; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" " add %2, %3, %2; ! Add ulNum to %2\n" " cmp %2, %1; ! check for overflow\n" " bne 2f; ! if not, skip to the end\n" " nop; ! but this means a delay, sigh\n" " inc %2; ! skip marker value\n" "2: st %2, [%0]; ! Save new value into *pNum\n" : /* no output */ : "r" (pNum), "r" (0x80000000), "r" (0x80000000), "r" (ulNum) : "cc", "memory" ); } /* Subtract n */ inline void HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) { __asm__ __volatile__( "1: swap [%0], %2; ! Swap *pNum and %2\n" " nop; ! delay slot...\n" " cmp %2, %1; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" " sub %2, %3, %2; ! Subtract ulNum to %2\n" " cmp %2, %1; ! check for overflow\n" " bne 2f; ! if not, skip to the end\n" " nop; ! but this means a delay, sigh\n" " inc %2; ! skip marker value\n" "2: st %2, [%0]; ! Save new value into *pNum\n" : /* no output */ : "r" (pNum), "r" (0x80000000), "r" (0x80000000), "r" (ulNum) : "cc", "memory" ); } /* Add n and return new value */ inline UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; \ __asm__ __volatile__( " mov %2, %0 ! Copy %2 to %0 \n" "1: swap [%1], %0; ! Swap *pNum and %0\n" " nop; ! delay slot...\n" " cmp %0, %2; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" " add %0, %3, %0; ! Add ulNum to %0\n" " cmp %0, %2; ! check for overflow\n" " bne 2f; ! if not, skip to the end\n" " nop; ! but this means a delay, sigh\n" " inc %0; ! skip marker value\n" "2: st %0, [%1]; ! Save new value into *pNum\n" : "=r" (ulRet) : "r" (pNum), "r" (0x80000000), "r" (ulNum), "0" (ulRet) : "cc", "memory" ); return ulRet; } /* Subtract n and return new value */ inline UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %2, %0 ! Copy %2 to %0 \n" "1: swap [%1], %0; ! Swap *pNum and %0\n" " nop; ! delay slot...\n" " cmp %0, %2; ! Is someone else using pNum?\n" " be 1b; ! If so, retry...\n" " nop; ! delay slot...yawn\n" " sub %0, %3, %0; ! Sub ulNum from %0\n" " cmp %0, %2; ! check for overflow\n" " bne 2f; ! if not, skip to the end\n" " nop; ! but this means a delay, sigh\n" " dec %0; ! skip marker value\n" "2: st %0, [%1]; ! Save new value into *pNum\n" : "=r" (ulRet) : "r" (pNum), "r" (0x80000000), "r" (ulNum), "0" (ulRet) : "cc", "memory" ); return ulRet; } inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } /*********************************************************************** * Windows / x86 (Visual C/C++) * * Implementation Notes: * 'xadd' is only available in the 486 series and later, not the 386. * There is no 'xsub' counterpart, you have to negate the operand * and use 'xadd'. Note the use of the 'lock' prefix to ensure * certain operations occur atomically. */ #elif defined (_M_IX86) /* && _M_IX86 > 300 XXX wschildbach: disabled until the build system delivers the correct value */ /* Increment by 1 */ static __inline void HXAtomicIncUINT32(UINT32* pNum) { // register usage summary: // eax - pointer to the value we're modifying _asm { mov eax, pNum ; Load the pointer into a register lock inc dword ptr [eax] ; Atomically increment *pNum } } /* Decrement by 1 */ static __inline void HXAtomicDecUINT32(UINT32* pNum) { // register usage summary: // eax - pointer to the value we're modifying _asm { mov eax, pNum ; Load the pointer into a register lock dec dword ptr [eax] ; Atomically decrement *pNum } } /* Increment by 1 and return new value */ static __inline UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; // register usage summary: // eax - pointer to the value we're modifying // ebx - work register _asm { mov eax, pNum ; Load the pointer into a register mov ebx, 0x1 ; Load increment amount into a register lock xadd dword ptr [eax], ebx ; Increment *pNum; ebx gets old value inc ebx ; Increment old value mov ulRet, ebx ; Set the return value } return ulRet; } /* Decrement by 1 and return new value */ static __inline UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; // register usage summary: // eax - pointer to the value we're modifying // ebx - work register // note: we increment by 0xffffffff to decrement by 1 _asm { mov eax, pNum ; Load the pointer into a register mov ebx, 0xffffffff ; Load decrement amount into a register lock xadd dword ptr [eax], ebx ; Decrement *pNum; ebx gets old value dec ebx ; decrement old value mov ulRet, ebx ; Set the return value } return ulRet; } /* Add n */ static __inline void HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) { // register usage summary: // eax - pointer to the value we're modifying // ebx - work register _asm { mov eax, pNum ; Load the pointer into a register mov ebx, ulNum ; Load increment amount into a register lock add dword ptr [eax], ebx ; Increment *pNum by ulNum } } /* Subtract n */ static __inline void HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) { // register usage summary: // eax - pointer to the value we're modifying // ebx - work register _asm { mov eax, pNum ; Load the pointer into a register mov ebx, ulNum ; Load increment amount into a register lock sub dword ptr [eax], ebx ; Atomically decrement *pNum by ulNum } } /* Add n and return new value */ static __inline UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; // register usage summary: // eax - pointer to the value we're modifying // ebx - work register // ecx - work register #2 _asm { mov eax, pNum ; Load the pointer into a register mov ebx, ulNum ; Load increment amount into a register mov ecx, ebx ; copy ebx into ecx lock xadd dword ptr [eax], ecx ; Increment *pNum; ecx gets old value add ecx, ebx ; Add ulNum to it mov ulRet, ecx ; save result in ulRet } return ulRet; } /* Subtract n and return new value */ static __inline UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; // register usage summary: // eax - pointer to the value we're modifying // ebx - work register // ecx - work register #2 _asm { mov eax, pNum ; Load the pointer into a register mov ebx, ulNum ; Load increment amount into a register mov ecx, 0x0 ; zero out ecx sub ecx, ebx ; compute -(ulNum), saving in ecx lock xadd dword ptr [eax], ecx ; Decrement *pNum; ecx gets old value sub ecx, ebx ; subtract ulNum from it mov ulRet, ecx ; save result in ulRet } return ulRet; } static __inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } static __inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } static __inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } static __inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } static __inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } static __inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } static __inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } static __inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } /*********************************************************************** * Intel x86 (gcc) / Unix -- i486 and higher - 32-bit * * Implementation Notes: * 'xadd' is only available in the 486 series and later, not the 386. * There is no 'xsub' counterpart, you have to negate the operand * and use 'xadd'. Note the use of the 'lock' prefix to ensure * certain operations occur atomically. * * OpenBSD is excluded since the standard assembler on x86 systems * can't handle the xadd instruction. * */ #elif defined(__GNUC__) && !defined(_OPENBSD) && \ (__GNUC__>2 || (__GNUC__==2 && __GNUC_MINOR__>=95)) && \ ( defined (__i486__) || defined (__i586__) || defined (__i686__) || \ defined (__pentium__) || defined (__pentiumpro__)) /* Increment by 1 */ static __inline__ void HXAtomicIncUINT32(UINT32* pNum) { __asm__ __volatile__( "lock incl (%0);" // atomically add 1 to *pNum : /* no output */ : "r" (pNum) : "cc", "memory" ); } /* Decrement by 1 */ static __inline__ void HXAtomicDecUINT32(UINT32* pNum) { __asm__ __volatile__( "lock decl (%0);" // atomically add -1 to *pNum : /* no output */ : "r" (pNum) : "cc", "memory" ); } /* Increment by 1 and return new value */ static __inline__ UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( "lock xaddl %0, (%1);" // atomically add 1 to *pNum " inc %0;" // old value in %0, increment it : "=r" (ulRet) : "r" (pNum), "0" (0x1) : "cc", "memory" ); return ulRet; } /* Decrement by 1 and return new value */ static __inline__ UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( "lock xaddl %0, (%1);" // atomically add -1 to *pNum " dec %0;" // old value in %0, decrement it : "=r" (ulRet) : "r" (pNum), "0" (-1) : "cc", "memory" ); return ulRet; } /* Add n */ static __inline__ void HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) { __asm__ __volatile__( "lock addl %1, (%0);" // atomically add ulNum to *pNum : /* no output */ : "r" (pNum), "r" (ulNum) : "cc", "memory" ); } /* Subtract n */ static __inline__ void HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) { __asm__ __volatile__( "lock subl %1, (%0);" // atomically add ulNum to *pNum : /* no output */ : "r" (pNum), "r" (ulNum) : "cc", "memory" ); } /* Add n and return new value */ static __inline__ UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %2, %0;" // copy ulNum into %0 "lock xaddl %0, (%1);" // atomically add ulNum to *pNum " add %2, %0;" // old value in %0, add ulNum : "=r" (ulRet) : "r" (pNum), "r" (ulNum), "0" (0) : "cc", "memory" ); return ulRet; } /* Subtract n and return new value */ static __inline__ UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; __asm__ __volatile__( " sub %2, %0;" // negate ulNum, saving in %0 "lock xaddl %0, (%1);" // atomically add -(ulNum) to *pNum " sub %2, %0;" // old value in %0, subtract ulNum : "=r" (ulRet) : "r" (pNum), "r" (ulNum), "0" (0) : "cc", "memory" ); return ulRet; } static __inline__ void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } static __inline__ void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } static __inline__ void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } static __inline__ void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } static __inline__ INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } static __inline__ INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } static __inline__ INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } static __inline__ INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } /*********************************************************************** * Intel x86/amd64/x86_64 (gcc) / Unix -- 64-bit * * Implementation Notes: * */ #elif defined(__GNUC__) && (defined (__amd64__) || defined (__x86_64__)) /* Increment by 1 */ static __inline__ void HXAtomicIncUINT32(UINT32* pNum) { __asm__ __volatile__( "lock incl (%%rax);" // atomically add 1 to *pNum : /* no output */ : "a" (pNum) : "cc", "memory" ); } /* Decrement by 1 */ static __inline__ void HXAtomicDecUINT32(UINT32* pNum) { __asm__ __volatile__( "lock decl (%%rax);" // atomically add -1 to *pNum : /* no output */ : "a" (pNum) : "cc", "memory" ); } /* Increment by 1 and return new value */ static __inline__ UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( "lock xaddl %%ebx, (%%rax);" // atomically add 1 to *pNum " incl %%ebx;" // old value in %%ebx, increment it : "=b" (ulRet) : "a" (pNum), "b" (0x1) : "cc", "memory" ); return ulRet; } /* Decrement by 1 and return new value */ static __inline__ UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( "lock xaddl %%ebx, (%%rax);" // atomically add -1 to *pNum " decl %%ebx;" // old value in %%ebx, decrement it : "=b" (ulRet) : "a" (pNum), "b" (-1) : "cc", "memory" ); return ulRet; } /* Add n */ static __inline__ void HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) { __asm__ __volatile__( "lock addl %%ebx, (%%rax);" // atomically add ulNum to *pNum : /* no output */ : "a" (pNum), "b" (ulNum) : "cc", "memory" ); } /* Subtract n */ static __inline__ void HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) { __asm__ __volatile__( "lock subl %%ebx, (%%rax);" // atomically add ulNum to *pNum : /* no output */ : "a" (pNum), "b" (ulNum) : "cc", "memory" ); } /* Add n and return new value */ static __inline__ UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; __asm__ __volatile__( " movl %%ebx, %%ecx;" // copy ulNum into %0 "lock xaddl %%ecx, (%%rax);" // atomically add ulNum to *pNum " addl %%ebx, %%ecx;" // old value in %%ecx, add ulNum : "=c" (ulRet) : "a" (pNum), "b" (ulNum), "c" (0) : "cc", "memory" ); return ulRet; } /* Subtract n and return new value */ static __inline__ UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; __asm__ __volatile__( " subl %%ebx, %%ecx;" // negate ulNum, saving in %0 "lock xaddl %%ecx, (%%rax);" // atomically add -(ulNum) to *pNum " subl %%ebx, %%ecx;" // old value in %%ecx, subtract ulNum : "=c" (ulRet) : "a" (pNum), "b" (ulNum), "c" (0) : "cc", "memory" ); return ulRet; } static __inline__ void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } static __inline__ void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } static __inline__ void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } static __inline__ void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } static __inline__ INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } static __inline__ INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } static __inline__ INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } static __inline__ INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } /*********************************************************************** * HP-UX / IA64 (Native compiler) * * Implementation Notes: * A work-in-progress... */ #elif defined(_HPUX) && defined(_IA64) #if defined(__cplusplus) extern "C" { #endif UINT32 _HXAtomicIncRetUINT32 (UINT32* pNum); UINT32 _HXAtomicDecRetUINT32 (UINT32* pNum); UINT32 _HXAtomicAddRetUINT32 (UINT32* pNum, UINT32 ulNum); UINT32 _HXAtomicSubRetUINT32 (UINT32* pNum, UINT32 ulNum); #if defined(__cplusplus) } #endif #define HXAtomicIncINT32(p) _HXAtomicIncRetUINT32((UINT32*)(p)) #define HXAtomicDecINT32(p) _HXAtomicDecRetUINT32((UINT32*)(p)) #define HXAtomicIncRetINT32(p) _HXAtomicIncRetUINT32((UINT32*)(p)) #define HXAtomicDecRetINT32(p) _HXAtomicDecRetUINT32((UINT32*)(p)) #define HXAtomicAddINT32(p,n) _HXAtomicAddRetUINT32((UINT32*)(p),(INT32)(n)) #define HXAtomicSubINT32(p,n) _HXAtomicSubRetUINT32((UINT32*)(p),(INT32)(n)) #define HXAtomicAddRetINT32(p,n) _HXAtomicAddRetUINT32((UINT32*)(p),(INT32)(n)) #define HXAtomicSubRetINT32(p,n) _HXAtomicSubRetUINT32((UINT32*)(p),(INT32)(n)) #define HXAtomicIncUINT32(p) _HXAtomicIncRetUINT32((p)) #define HXAtomicDecUINT32(p) _HXAtomicDecRetUINT32((p)) #define HXAtomicIncRetUINT32(p) _HXAtomicIncRetUINT32((p)) #define HXAtomicDecRetUINT32(p) _HXAtomicDecRetUINT32((p)) #define HXAtomicAddUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) #define HXAtomicAddRetUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubRetUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) /*********************************************************************** * Tru64 (OSF1) / Alpha (Native compiler) * * Implementation Notes: * * The Alpha CPU provides instructions to load-lock a value, * modify it, and attempt to write it back. If the value has * been modified by someone else since the load-lock occurred, * the write will fail and you can check the status code to * know whether you need to retry or not. * */ #elif defined (__alpha) #include /* Increment by 1 and return new value */ inline INT32 HXAtomicIncRetINT32(INT32* pNum) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " addl %t0, 1, %t0;" // Increment value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum); } /* Decrement by 1 and return new value */ inline INT32 HXAtomicDecRetINT32(INT32* pNum) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " subl %t0, 1, %t0;" // Decrement value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum); } /* Add n and return new value */ inline INT32 HXAtomicAddRetINT32(INT32* pNum, INT32 n) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " addl %t0, %a1, %t0;" // Add n to value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum, n); } /* Subtract n and return new value */ inline INT32 HXAtomicSubRetINT32(INT32* pNum, INT32 n) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " subl %t0, %a1, %t0;" // Subtract n from value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum, n); } /* Increment by 1 and return new value */ inline UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " addl %t0, 1, %t0;" // Increment value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum); } /* Decrement by 1 and return new value */ inline UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " subl %t0, 1, %t0;" // Decrement value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum); } /* Add n and return new value */ inline UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 n) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " addl %t0, %a1, %t0;" // Add n to value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum, n); } /* Subtract n and return new value */ inline UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 n) { return asm ( "10: ldl_l %t0, (%a0);" // Load-lock value into a register " subl %t0, %a1, %t0;" // Subtract n from value " or %t0, %zero, %v0;" // set new value for return. " stl_c %t0, (%a0);" // Save new value into *pNum " beq %t0, 10b;" // Retry if sequence failed , pNum, n); } #define HXAtomicIncINT32(p) HXAtomicIncRetINT32((p)) #define HXAtomicDecINT32(p) HXAtomicDecRetINT32((p)) #define HXAtomicAddINT32(p,n) HXAtomicAddRetINT32((p),(n)) #define HXAtomicSubINT32(p,n) HXAtomicSubRetINT32((p),(n)) #define HXAtomicIncUINT32(p) HXAtomicIncRetUINT32((p)) #define HXAtomicDecUINT32(p) HXAtomicDecRetUINT32((p)) #define HXAtomicAddUINT32(p,n) HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubUINT32(p,n) HXAtomicSubRetUINT32((p),(n)) /*********************************************************************** * AIX / PowerPC (Native compiler) * * Implementation Notes: * * XXXDC: The xlc compiler is able to do inline asm for C but when I do * it for C++ it crashes, so for now I have resorted to putting * the asm in a separate assembler routine. The way you inline with * xlc/xlC is difficult to use, requiring the use of "#pragma mc_func". */ #elif defined (_AIX) //defined in common/util/platform/aix/atomicops.s #if defined(__cplusplus) extern "C" { #endif INT32 _HXAtomicAddRetINT32 (INT32* pNum, INT32 lNum); INT32 _HXAtomicSubRetINT32 (INT32* pNum, INT32 lNum); UINT32 _HXAtomicAddRetUINT32 (UINT32* pNum, UINT32 ulNum); UINT32 _HXAtomicSubRetUINT32 (UINT32* pNum, UINT32 ulNum); #if defined(__cplusplus) } #endif #define HXAtomicIncINT32(p) _HXAtomicAddRetINT32((p),(INT32)1) #define HXAtomicDecINT32(p) _HXAtomicSubRetINT32((p),(INT32)1) #define HXAtomicIncRetINT32(p) _HXAtomicAddRetINT32((p),(INT32)1) #define HXAtomicDecRetINT32(p) _HXAtomicSubRetINT32((p),(INT32)1) #define HXAtomicAddINT32(p,n) _HXAtomicAddRetINT32((p),(n)) #define HXAtomicSubINT32(p,n) _HXAtomicSubRetINT32((p),(n)) #define HXAtomicAddRetINT32(p,n) _HXAtomicAddRetINT32((p),(n)) #define HXAtomicSubRetINT32(p,n) _HXAtomicSubRetINT32((p),(n)) #define HXAtomicIncUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) #define HXAtomicDecUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) #define HXAtomicIncRetUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) #define HXAtomicDecRetUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) #define HXAtomicAddUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) #define HXAtomicAddRetUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubRetUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) /*********************************************************************** * MAC / PowerPC (CW) * * Implementation Notes: * * This will need to be rewritten, probably, once we move away from CW to PB. * * Note: This is an imcompletely-defined platform, be aware that * not all standard HXAtomic operators are defined! * */ #elif defined(_MACINTOSH) && defined(__MWERKS__) inline UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { UINT32 zeroOffset = 0; UINT32 temp; asm { again: lwarx temp, zeroOffset, pNum addi temp, temp, 1 stwcx. temp, zeroOffset, pNum bne- again } return temp; } inline UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { UINT32 zeroOffset = 0; UINT32 temp; asm { again: lwarx temp, zeroOffset, pNum subi temp, temp, 1 stwcx. temp, zeroOffset, pNum bne- again } return temp; } /*********************************************************************** * MAC - PowerPC (PB or XCode) / Linux - PowerPC * * Implementation Notes: * * Use PowerPC load exclusive and store exclusive instructions * */ #elif defined(_MAC_UNIX) || (defined(_LINUX) && defined(__powerpc__)) // could also probably be defined(__GNUC__) && defined(__powerpc) static inline UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { volatile UINT32 result; __asm__ __volatile__ ( "1: lwarx %0, %3, %2;\n" " addi %0, %0, 1;\n" " stwcx. %0, %3, %2;\n" " bne- 1b;" : "=b" (result) : "0" (result), "b" (pNum), "b" (0x0) : "cc", "memory" ); return result; } static inline UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { volatile UINT32 result; __asm__ __volatile__ ( "1: lwarx %0, %3, %2;\n" " subi %0, %0, 1;\n" " stwcx. %0, %3, %2;\n" " bne- 1b;" : "=b" (result) : "0" (result), "b" (pNum), "b" (0x0) : "cc", "memory" ); return result; } static inline UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 result; __asm__ __volatile__ ( "1: lwarx %0, %3, %2;\n" " add %0, %0, %4;\n" " stwcx. %0, %3, %2;\n" " bne- 1b;" : "=b" (result) : "0" (result), "b" (pNum), "b" (0x0), "b" (ulNum) : "cc", "memory" ); return result; } static inline UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 result; __asm__ __volatile__ ( "1: lwarx %0, %3, %2;\n" " sub %0, %0, %4;\n" " stwcx. %0, %3, %2;\n" " bne- 1b;" : "=b" (result) : "0" (result), "b" (pNum), "b" (0x0), "b" (ulNum) : "cc", "memory" ); return result; } // the rest of these atomic operations can be implemented in terms of the four above. static inline void HXAtomicIncINT32(INT32* p) { (void)HXAtomicIncRetUINT32((UINT32*)p); } static inline void HXAtomicDecINT32(INT32* p) { (void)HXAtomicDecRetUINT32((UINT32*)p); } static inline void HXAtomicAddINT32(INT32* p, INT32 n) { (void)HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } static inline void HXAtomicSubINT32(INT32* p, INT32 n) { (void)HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } static inline INT32 HXAtomicIncRetINT32(INT32* p) { return (INT32)HXAtomicIncRetUINT32((UINT32*)p); } static inline INT32 HXAtomicDecRetINT32(INT32* p) { return (INT32)HXAtomicDecRetUINT32((UINT32*)p); } static inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return (INT32)HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } static inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return (INT32)HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } static inline void HXAtomicIncUINT32(UINT32* p) { (void)HXAtomicIncRetUINT32(p); } static inline void HXAtomicDecUINT32(UINT32* p) { (void)HXAtomicDecRetUINT32(p); } static inline void HXAtomicAddUINT32(UINT32* p, UINT32 n) { (void)HXAtomicAddRetUINT32(p, n); } static inline void HXAtomicSubUINT32(UINT32* p, UINT32 n) { (void)HXAtomicSubRetUINT32(p, n); } /*********************************************************************** * Generic * * Implementation Notes: * * This should work on any platform with a HXMutex-style mutex. * It allocates a pool of mutexes and hashes the int pointers * to one of the mutexes. Since the mutexes are held for * such a short time, only long enough to increment an int, * collisions should be extremely rare and this should work fine, * although it is probably less fast than the extra-high-performance * atomic operators provided above. You need to link in atomic.cpp * to get HXAtomic::m_pLocks defined. * * Basic design of the mutex-based lock-pool implementation: * At startup, allocate an array of N mutexes (where N is a power of 2). * When a method is called, hash the int pointer to one of the locks. * Lock this mutex. * Modify the value. * Unlock this mutex. * * * Platform-specific notes: * Any platforms that use this should be documented here! * Why are you using the generic operators for this platform? * * HP-UX / HP-PA: * This is used on the HP-PA processor since it doesn't provide the * necessary assembler operators to implement proper atomic updates * of ints. HP's mutex primitive seems pretty fast however, resulting * in a workable solution. * * OpenBSD: * The standard assembler on x86 can't handle the gcc/asm operators * defined above, so we're using the lock-pool approach for now. * This approach also makes it possible to support non-x86 OpenBSD * builds more easily (someday). * */ #elif defined(_HPUX) || defined(_OPENBSD) #if defined(__cplusplus) #include "microsleep.h" #include "hxcom.h" #include "hxmutexlock.h" class HXAtomic { public: HXAtomic(); ~HXAtomic(); void InitLockPool(); /* Users of the HXAtomic routines should *NEVER* call these directly. * They should *ALWAYS* use the HXAtomicAddRetINT32-style macros instead. */ INT32 _AddRetINT32 (INT32* pNum, INT32 nNum); UINT32 _AddRetUINT32 (UINT32* pNum, UINT32 ulNum); INT32 _SubRetINT32 (INT32* pNum, INT32 nNum); UINT32 _SubRetUINT32 (UINT32* pNum, UINT32 ulNum); private: void Lock (HX_MUTEX pLock); void Unlock (HX_MUTEX pLock); HX_MUTEX* m_pLocks; }; extern HXAtomic g_AtomicOps; //in common/util/atomicops.cpp #define HXAtomicIncINT32(p) g_AtomicOps._AddRetINT32((p),(INT32)1) #define HXAtomicDecINT32(p) g_AtomicOps._SubRetINT32((p),(INT32)1) #define HXAtomicIncRetINT32(p) g_AtomicOps._AddRetINT32((p),(INT32)1) #define HXAtomicDecRetINT32(p) g_AtomicOps._SubRetINT32((p),(INT32)1) #define HXAtomicAddRetINT32(p,n) g_AtomicOps._AddRetINT32((p),(n)) #define HXAtomicSubRetINT32(p,n) g_AtomicOps._SubRetINT32((p),(n)) #define HXAtomicAddINT32(p,n) g_AtomicOps._AddRetINT32((p),(n)) #define HXAtomicSubINT32(p,n) g_AtomicOps._SubRetINT32((p),(n)) #define HXAtomicIncUINT32(p) g_AtomicOps._AddRetUINT32((p),(UINT32)1) #define HXAtomicDecUINT32(p) g_AtomicOps._SubRetUINT32((p),(UINT32)1) #define HXAtomicIncRetUINT32(p) g_AtomicOps._AddRetUINT32((p),(UINT32)1) #define HXAtomicDecRetUINT32(p) g_AtomicOps._SubRetUINT32((p),(UINT32)1) #define HXAtomicAddRetUINT32(p,n) g_AtomicOps._AddRetUINT32((p),(n)) #define HXAtomicSubRetUINT32(p,n) g_AtomicOps._SubRetUINT32((p),(n)) #define HXAtomicAddUINT32(p,n) g_AtomicOps._AddRetUINT32((p),(n)) #define HXAtomicSubUINT32(p,n) g_AtomicOps._SubRetUINT32((p),(n)) #endif /*********************************************************************** * SYMBIAN * * Implementation Notes: * * Note: This is an imcompletely-defined platform, be aware that * not all standard HXAtomic operators are defined! * */ #elif defined(_SYMBIAN) /* Increment by 1 and return new value */ inline INT32 HXAtomicIncRetINT32(INT32* pNum) { return User::LockedInc(*((TInt*)pNum)) + 1; } /* Decrement by 1 and return new value */ inline INT32 HXAtomicDecRetINT32(INT32* pNum) { return User::LockedDec(*((TInt*)pNum)) - 1; } /* Increment by 1 and return new value */ inline UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { return ((UINT32)User::LockedInc(*((TInt*)pNum))) + 1; } /* Decrement by 1 and return new value */ inline UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { return ((UINT32)User::LockedDec(*((TInt*)pNum))) - 1; } #define HXAtomicIncINT32(p) HXAtomicIncRetINT32((p)) #define HXAtomicDecINT32(p) HXAtomicDecRetINT32((p)) #define HXAtomicIncUINT32(p) HXAtomicIncRetUINT32((p)) #define HXAtomicDecUINT32(p) HXAtomicDecRetUINT32((p)) #if 0 /* * Add and subtract operations are not implemented * at this time because there isn't an easy way to * do it using the facilities provided by Symbian. * Assembly will likely be needed. */ /* Add n and return new value */ inline INT32 HXAtomicAddRetINT32(INT32* pNum, INT32 n) { } /* Subtract n and return new value */ inline INT32 HXAtomicSubRetINT32(INT32* pNum, INT32 n) { } /* Add n and return new value */ inline UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 n) { } /* Subtract n and return new value */ inline UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 n) { } #define HXAtomicAddINT32(p,n) HXAtomicAddRetINT32((p),(n)) #define HXAtomicSubINT32(p,n) HXAtomicSubRetINT32((p),(n)) #define HXAtomicAddUINT32(p,n) HXAtomicAddRetUINT32((p),(n)) #define HXAtomicSubUINT32(p,n) HXAtomicSubRetUINT32((p),(n)) #endif /*********************************************************************** * Linux / ARM (gcc) * * Implementation Notes: * * This implementation sacrifices being able to store the value * 0x800000000 in the INT32 value, which is a special "busy" marker value. * Since these are intended for use primarily with AddRef/Release and * resource usage counters, this should be acceptable for now. If a counter * is incremented to the point it would conflict with the flag, it is * incremented one more to hop over it. The same in reverse for decrement. * * Basic design of the flag-based implementation: * 1. Load a register with 0x80000000 * 2. _atomically_ swap it with the INT32 (critical!) * 3. Compare what we got with 0x80000000 * 4. Branch if equal to #2 * 5. Increment (or decrement) the result * 6. Compare to 0x80000000 * 7. Increment (or decrement) again if equal * 8. Save the new value to the INT32's location in memory * 9. Return new INT32 result if required * */ #elif defined (_ARM) && defined (__GNUC__) /* Increment by 1 */ inline void HXAtomicIncUINT32(UINT32* pNum) { UINT32 ulTmp; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " add %0, %0, #1;\n" /* Increment ulTmp */ " cmp %0, #0x80000000;\n" /* check for overflow */ " addeq %0, %0, #1;\n" /* if so, increment again */ " str %0, [%1];\n" /* Save new value into *pNum */ : /* no output */ : "r" (ulTmp), "r" (pNum) : "cc", "memory" ); } /* Decrement by 1 */ inline void HXAtomicDecUINT32(UINT32* pNum) { UINT32 ulTmp; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " sub %0, %0, #1;\n" /* Decrement ulTmp */ " cmp %0, #0x80000000;\n" /* check for overflow */ " subeq %0, %0, #1;\n" /* if so, decrement again */ " str %0, [%1];\n" /* Save new value into *pNum */ : /* no output */ : "r" (ulTmp), "r" (pNum) : "cc", "memory" ); } /* Increment by 1 and return new value */ inline UINT32 HXAtomicIncRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " add %0, %0, #1;\n" /* Increment ulRet */ " cmp %0, #0x80000000;\n" /* check for overflow */ " addeq %0, %0, #1;\n" /* if so, increment again */ " str %0, [%1];\n" /* Save new value into *pNum */ : "=&r" (ulRet) : "r" (pNum) : "cc", "memory" ); return ulRet; } /* Decrement by 1 and return new value */ inline UINT32 HXAtomicDecRetUINT32(UINT32* pNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " sub %0, %0, #1;\n" /* Decrement ulRet */ " cmp %0, #0x80000000;\n" /* check for overflow */ " subeq %0, %0, #1;\n" /* if so, decrement again */ " str %0, [%1];\n" /* Save new value into *pNum */ : "=&r" (ulRet) : "r" (pNum) : "cc", "memory" ); return ulRet; } /* Add n */ inline void HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) { UINT32 ulTmp; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " add %0, %0, %2;\n" /* Add ulNum to ulTmp */ " cmp %0, #0x80000000;\n" /* check for overflow */ " addeq %0, %0, #1;\n" /* if so, increment again */ " str %0, [%1];\n" /* Save new value into *pNum */ : /* no output */ : "r" (ulTmp), "r" (pNum), "r" (ulNum) : "cc", "memory" ); } /* Subtract n */ inline void HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) { UINT32 ulTmp; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " sub %0, %0, %2;\n" /* Subtract ulNum from ulTmp */ " cmp %0, #0x80000000;\n" /* check for overflow */ " subeq %0, %0, #1;\n" /* if so, decrement again */ " str %0, [%1];\n" /* Save new value into *pNum */ : /* no output */ : "r" (ulTmp), "r" (pNum), "r" (ulNum) : "cc", "memory" ); } /* Add n and return new value */ inline UINT32 HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " add %0, %0, %2;\n" /* Add ulNum to ulRet */ " cmp %0, #0x80000000;\n" /* check for overflow */ " addeq %0, %0, #1;\n" /* if so, increment again */ " str %0, [%1];\n" /* Save new value into *pNum */ : "=&r" (ulRet) : "r" (pNum) , "r" (ulNum) : "cc", "memory" ); return ulRet; } /* Subtract n and return new value */ inline UINT32 HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) { volatile UINT32 ulRet; __asm__ __volatile__( " mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ "1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ " cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ " beq 1;\n" /* If so, retry... */ " sub %0, %0, %2;\n" /* Subtract ulNum from ulRet */ " cmp %0, #0x80000000;\n" /* check for overflow */ " subeq %0, %0, #1;\n" /* if so, decrement again */ " str %0, [%1];\n" /* Save new value into *pNum */ : "=&r" (ulRet) : "r" (pNum), "r" (ulNum) : "cc", "memory" ); return ulRet; } inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } /*********************************************************************** * Add new platforms above here */ #else // // Unsupported platform // #ifndef HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS // Defining HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS will use the ++ and -- // operators in place of atomic operators in some places in the code. These // operators are not thread-safe, and should only be used in the intermediary // stages of porting. # error "You need to create atomic dec/inc opers for your platform or #define HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS" #endif #endif /*************************************************************************/ /* * Conditional override of InterlockedIncrement/Decrement * * Place this in your Umakefil/.pcf file to turn off atomic * InterlockedIncrement/Decrement on a per-module basis, * or place it in your umake profile for system-wide scope. * If this is defined you'll still have access to the underlying * HXAtomicxxx operators (if they exist for your platform), * just that the specific InterlockedIncrement/InterlockedDecrement * macros won't be defined to use them. */ #if !defined (HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS) #undef InterlockedIncrement #undef InterlockedDecrement // Since many classes (incorrectly) implement their refcount using LONG32 // rather than the proper ULONG32, we have to use the typecast for things // to build on many platforms. #define InterlockedIncrement(p) HXAtomicIncRetUINT32((UINT32*)(p)) #define InterlockedDecrement(p) HXAtomicDecRetUINT32((UINT32*)(p)) #if !defined(HAVE_INTERLOCKED_INCREMENT) #define HAVE_INTERLOCKED_INCREMENT //so hxcom.h doesn't redefine these to ++/-- #endif // /HAVE_INTERLOCKED_INCREMENT. #endif /* !defined(HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS) */ #endif /* _ATOMICBASE_H_ */