/* lifted from a VM test */ static int64_t testAtomicSpeedSub(int repeatCount) { static int value = 7; int *valuePtr = &value; int64_t start, end; int i; start = getRelativeTimeNsec(); for (i = repeatCount / 10; i != 0; i--) { if (USE_ATOMIC) { // succeed 10x android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); android_atomic_release_cas(7, 7, valuePtr); } else { // succeed 10x compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); compareAndSwap(7, 7, valuePtr); } } end = getRelativeTimeNsec(); dvmFprintf(stdout, "."); fflush(stdout); return end - start; }
/* * Exercise several of the atomic ops. */ static void doAtomicTest(int num) { int addVal = (num & 0x01) + 1; int i; for (i = 0; i < ITERATION_COUNT; i++) { if (USE_ATOMIC) { android_atomic_inc(&incTest); android_atomic_dec(&decTest); android_atomic_add(addVal, &addTest); int val; do { val = casTest; } while (android_atomic_release_cas(val, val + 3, &casTest) != 0); do { val = casTest; } while (android_atomic_acquire_cas(val, val - 1, &casTest) != 0); int64_t wval; do { wval = dvmQuasiAtomicRead64(&wideCasTest); } while (dvmQuasiAtomicCas64(wval, wval + 0x0000002000000001LL, &wideCasTest) != 0); do { wval = dvmQuasiAtomicRead64(&wideCasTest); } while (dvmQuasiAtomicCas64(wval, wval - 0x0000002000000001LL, &wideCasTest) != 0); } else { incr(); decr(); add(addVal); int val; do { val = casTest; } while (compareAndSwap(val, val + 3, &casTest) != 0); do { val = casTest; } while (compareAndSwap(val, val - 1, &casTest) != 0); int64_t wval; do { wval = wideCasTest; } while (compareAndSwapWide(wval, wval + 0x0000002000000001LL, &wideCasTest) != 0); do { wval = wideCasTest; } while (compareAndSwapWide(wval, wval - 0x0000002000000001LL, &wideCasTest) != 0); } } }
/* * public native boolean compareAndSwapInt(Object obj, long offset, * int expectedValue, int newValue); */ static void Dalvik_sun_misc_Unsafe_compareAndSwapInt(const u4 *args, JValue *pResult) { // We ignore the this pointer in args[0]. Object *obj = (Object *) args[1]; s8 offset = GET_ARG_LONG(args, 2); s4 expectedValue = args[4]; s4 newValue = args[5]; volatile int32_t *address = (volatile int32_t *) (((u1 *) obj) + offset); // Note: android_atomic_release_cas() returns 0 on success, not failure. int result = android_atomic_release_cas(expectedValue, newValue, address); RETURN_BOOLEAN(result == 0); }
/* * public native boolean compareAndSwapObject(Object obj, long offset, * Object expectedValue, Object newValue); */ static void Dalvik_sun_misc_Unsafe_compareAndSwapObject(const u4 *args, JValue *pResult) { // We ignore the this pointer in args[0]. Object *obj = (Object *) args[1]; s8 offset = GET_ARG_LONG(args, 2); Object *expectedValue = (Object *) args[4]; Object *newValue = (Object *) args[5]; int32_t *address = (int32_t *) (((u1 *) obj) + offset); // Note: android_atomic_cmpxchg() returns 0 on success, not failure. int result = android_atomic_release_cas((int32_t) expectedValue, (int32_t) newValue, address); dvmWriteBarrierField(obj, address); RETURN_BOOLEAN(result == 0); }
/* * Create and initialize a monitor. */ Monitor* dvmCreateMonitor(Object* obj) { Monitor* mon; mon = (Monitor*) calloc(1, sizeof(Monitor)); if (mon == NULL) { ALOGE("Unable to allocate monitor"); dvmAbort(); } mon->obj = obj; dvmInitMutex(&mon->lock); /* replace the head of the list with the new monitor */ do { mon->next = gDvm.monitorList; } while (android_atomic_release_cas((int32_t)mon->next, (int32_t)mon, (int32_t*)(void*)&gDvm.monitorList) != 0); return mon; }
int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { return android_atomic_release_cas(oldvalue,newvalue,addr); }
/* * Start tests, show results. */ bool dvmTestAtomicSpeed() { pthread_t threads[THREAD_COUNT]; void *(*startRoutine)(void *) = atomicTest; int64_t startWhen, endWhen; #if defined(__ARM_ARCH__) dvmFprintf(stdout, "__ARM_ARCH__ is %d\n", __ARM_ARCH__); #endif #if defined(ANDROID_SMP) dvmFprintf(stdout, "ANDROID_SMP is %d\n", ANDROID_SMP); #endif dvmFprintf(stdout, "Creating threads\n"); int i; for (i = 0; i < THREAD_COUNT; i++) { void *arg = (void *) i; if (pthread_create(&threads[i], NULL, startRoutine, arg) != 0) { dvmFprintf(stderr, "thread create failed\n"); } } /* wait for all the threads to reach the starting line */ while (1) { pthread_mutex_lock(&waitLock); if (threadsStarted == THREAD_COUNT) { dvmFprintf(stdout, "Starting test\n"); startWhen = getRelativeTimeNsec(); pthread_cond_broadcast(&waitCond); pthread_mutex_unlock(&waitLock); break; } pthread_mutex_unlock(&waitLock); usleep(100000); } for (i = 0; i < THREAD_COUNT; i++) { void *retval; if (pthread_join(threads[i], &retval) != 0) { dvmFprintf(stderr, "thread join (%d) failed\n", i); } } endWhen = getRelativeTimeNsec(); dvmFprintf(stdout, "All threads stopped, time is %.6fms\n", (endWhen - startWhen) / 1000000.0); /* * Show results; expecting: * * incTest = 5000000 * decTest = -5000000 * addTest = 7500000 * casTest = 10000000 * wideCasTest = 0x6600000077000000 */ dvmFprintf(stdout, "incTest = %d\n", incTest); dvmFprintf(stdout, "decTest = %d\n", decTest); dvmFprintf(stdout, "addTest = %d\n", addTest); dvmFprintf(stdout, "casTest = %d\n", casTest); dvmFprintf(stdout, "wideCasTest = 0x%llx\n", wideCasTest); /* do again, serially (SMP check) */ startWhen = getRelativeTimeNsec(); for (i = 0; i < THREAD_COUNT; i++) { doAtomicTest(i); } endWhen = getRelativeTimeNsec(); dvmFprintf(stdout, "Same iterations done serially: time is %.6fms\n", (endWhen - startWhen) / 1000000.0); /* * Hard to do a meaningful thrash test on these, so just do a simple * function test. */ andTest = 0xffd7fa96; orTest = 0x122221ff; android_atomic_and(0xfffdaf96, &andTest); android_atomic_or(0xdeaaeb00, &orTest); if (android_atomic_release_cas(failingCasTest + 1, failingCasTest - 1, &failingCasTest) == 0) dvmFprintf(stdout, "failing test did not fail!\n"); dvmFprintf(stdout, "andTest = %#x\n", andTest); dvmFprintf(stdout, "orTest = %#x\n", orTest); dvmFprintf(stdout, "failingCasTest = %d\n", failingCasTest); #ifdef TEST_BIONIC /* * Quick function test on the bionic ops. */ int prev; int tester = 7; prev = __atomic_inc(&tester); __atomic_inc(&tester); __atomic_inc(&tester); dvmFprintf(stdout, "bionic 3 inc: %d -> %d\n", prev, tester); prev = __atomic_dec(&tester); __atomic_dec(&tester); __atomic_dec(&tester); dvmFprintf(stdout, "bionic 3 dec: %d -> %d\n", prev, tester); prev = __atomic_swap(27, &tester); dvmFprintf(stdout, "bionic swap: %d -> %d\n", prev, tester); int swapok = __atomic_cmpxchg(27, 72, &tester); dvmFprintf(stdout, "bionic cmpxchg: %d (%d)\n", tester, swapok); #endif testAtomicSpeed(); return 0; }