Help on Atomics for ARM 32 bit Build

299 views
Skip to first unread message

Tuhin Sengupta

unread,
Mar 30, 2020, 9:23:17 PM3/30/20
to android-ndk
Hi,
  I am using following built-in atomics API for my project :

 __sync_val_compare_and_swap 
__sync_fetch_and_add
__sync_fetch_and_sub
__sync_fetch_and_or
__sync_lock_test_and_set 

All my values are _int64 type. 

all functions are working fine on x86_64 and arm64-v8a architecture.

But, for armeabi-v7a Architecture, I am getting SIGBUS for __sync_lock_test_and_set  call.

Does these API work on 64 bit values? If not, which API I should use for atomic operation on 64 bit values? I tried __atomic* APIs as well, but no luck.

Second question - I am not supporting x86 (32-bit) builts, I know Intel stopped producing x86 (32bit) Atom chip from 2016, and there is no current device with 32 bit x86. what is status of ARM 32 bit? Should I support ARM 32 bit devices? If yes, I need a solution for above problem.

Tuhin Sengupta

unread,
Mar 31, 2020, 4:57:25 AM3/31/20
to android-ndk
Here is the Sample JNI code which is failing :

===============================
#include <jni.h>
#include <string>
#include <atomic>
#include <android/log.h>

#define _LOG_TO_CONSOLE_(x) ((void)__android_log_print(ANDROID_LOG_INFO, "JNI", x))


typedef void* VPtr;
typedef __int64_t TInt64;

#define SYNC_EXCL_MASK ((TInt64) (0x8000000000000001))

VPtr
SetV (volatile VPtr * pDest, VPtr pValToSet)
{
__android_log_print(ANDROID_LOG_INFO,"JNI","Vptr Set");
return (VPtr) __sync_lock_test_and_set ((volatile TInt64 *) pDest, (TInt64) pValToSet);
}

TInt64
SetI (volatile TInt64 * pDest, TInt64 pValToSet)
{
__android_log_print(ANDROID_LOG_INFO,"JNI","Int64 set = %u", pValToSet);
return __sync_lock_test_and_set(pDest, pValToSet);
}

typedef int (*FuncPtr) ();

int testFunct()
{
return 0;
}

void TestAtomics()
{

TInt64 *dest;

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing Set with int = %d", sizeof(dest));

SetI(dest, SYNC_EXCL_MASK);

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing lock text and set - value set = %u ", *dest);

FuncPtr function;

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing Set with function pointer");

SetV((volatile VPtr *)&function, (VPtr) testFunct);



}


extern "C" JNIEXPORT jstring JNICALL
Java_com_tally_atomictest_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";

TestAtomics();

return env->NewStringUTF(hello.c_str());
}
=======================

Note:

If I change :

typedef __int64_t TInt64;

to:

typedef __int32_t TInt64;

It works fine! So definitely the issue with H/W support of atomic on 64 bit values ....

Please help, how to make it 64 bit atomic set for ARM devices ....

Ryan Prichard

unread,
Mar 31, 2020, 6:11:51 AM3/31/20
to android-ndk
Have you tried using the C++11 std::atomic APIs instead? https://en.cppreference.com/w/cpp/atomic/atomic

I think the current NDK has lock-free atomics for 64-bit integers on all of our supported architectures. (It also appears to have lock-free 128-bit atomics on arm64 and x86_64. Previously it was necessary to enable -mcx16 for x86_64, but that is unnecessary as of this LLVM commit, which is in NDK r21.)

I think your code has a mistake:

        TInt64  *dest;

        __android_log_print(ANDROID_LOG_INFO,"JNI","Testing Set with int = %d", sizeof(dest));

        SetI(dest, SYNC_EXCL_MASK);

This code never initializes dest, but then writes to *dest. Maybe you meant this?

TInt64 dest;
 
SetI(&dest, SYNC_EXCL_MASK);

-Ryan


--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/d1e8aac4-4336-4570-956a-217317106cf4%40googlegroups.com.

Tuhin Sengupta

unread,
Mar 31, 2020, 11:32:03 AM3/31/20
to andro...@googlegroups.com
Ok, with __atomic* API, it worked fine. (did not use std::atomic, but used the underlying __atomic* API). So, looks like __sync* is not suitable to use for 64 bit atomic on 32 bit H/W.

VPtr
SetV (volatile VPtr * pDest, VPtr pValToSet)
{
__android_log_print(ANDROID_LOG_INFO,"JNI","Vptr Set");
    //return (VPtr) __sync_lock_test_and_set ((volatile TInt64 *) pDest, (TInt64) pValToSet);
return (VPtr)__atomic_exchange_n(pDest, pValToSet, __ATOMIC_SEQ_CST);


}

TInt64
SetI (volatile TInt64 * pDest, TInt64 pValToSet)
{
__android_log_print(ANDROID_LOG_INFO,"JNI","Int64 set = %u", pValToSet);
    //return __sync_lock_test_and_set(pDest, pValToSet);
return __atomic_exchange_n(pDest, pValToSet, __ATOMIC_SEQ_CST);

}

On Tue, Mar 31, 2020 at 3:52 PM Tuhin Sengupta <tuhin.s...@gmail.com> wrote:
This code worked perfectly on x86_64. Anyway, I changed the pointer to stack variable.

void TestAtomics()
{

TInt64 dest;


__android_log_print(ANDROID_LOG_INFO,"JNI","Testing Set with int = %d", sizeof(dest));

        SetI(&dest, SYNC_EXCL_MASK);

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing lock text and set - value set = %u ", dest);


FuncPtr function;

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing Set with function pointer");

SetV((volatile VPtr *)&function, (VPtr) testFunct);



}
Worked perfectly again on x86_64.
Crashed on ARM architecture:

2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Testing Set with int = 8
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Int64 set = 2519096968
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Testing lock text and set - value set = 4
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Testing Set with function pointer
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Vptr Set
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest A/libc: Fatal signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xbefa2cc4 in tid 5762 (ally.atomictest), pid 5762 (ally.atomictest)
Let me try the std::atomic -> I am using C++17 

Tuhin Sengupta

unread,
Mar 31, 2020, 11:32:03 AM3/31/20
to andro...@googlegroups.com
This code worked perfectly on x86_64. Anyway, I changed the pointer to stack variable.

void TestAtomics()
{

TInt64 dest;

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing Set with int = %d", sizeof(dest));

        SetI(&dest, SYNC_EXCL_MASK);

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing lock text and set - value set = %u ", dest);


FuncPtr function;

__android_log_print(ANDROID_LOG_INFO,"JNI","Testing Set with function pointer");

SetV((volatile VPtr *)&function, (VPtr) testFunct);



}
Worked perfectly again on x86_64.
Crashed on ARM architecture:

2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Testing Set with int = 8
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Int64 set = 2519096968
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Testing lock text and set - value set = 4
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Testing Set with function pointer
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest I/JNI: Vptr Set
2020-03-31 15:49:11.096 5762-5762/com.tally.atomictest A/libc: Fatal signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xbefa2cc4 in tid 5762 (ally.atomictest), pid 5762 (ally.atomictest)
Let me try the std::atomic -> I am using C++17 
On Tue, Mar 31, 2020 at 3:41 PM 'Ryan Prichard' via android-ndk <andro...@googlegroups.com> wrote:

Ryan Prichard

unread,
Mar 31, 2020, 6:50:35 PM3/31/20
to android-ndk
On a 32-bit target, SetV is accessing a 32-bit object (of type VPtr) using a pointer-to-64-bit ((volatile TInt64 *) pDest). Removing the TInt64 casts fixes the problem, as does casting it to a 32-bit integer type instead.
typedef void* VPtr;
typedef __int64_t TInt64;

...

VPtr
SetV (volatile VPtr * pDest, VPtr pValToSet)
{
__android_log_print(ANDROID_LOG_INFO,"JNI","Vptr Set");
return (VPtr) __sync_lock_test_and_set ((volatile TInt64 *) pDest, (TInt64) pValToSet);
}
-Ryan


Reply all
Reply to author
Forward
0 new messages