diff options
author | David 'Digit' Turner <digit@google.com> | 2011-09-30 03:45:16 +0200 |
---|---|---|
committer | David 'Digit' Turner <digit@google.com> | 2011-09-30 16:12:11 +0200 |
commit | 26cc9e3109d9df4dd7726edcb02982c26c207d20 (patch) | |
tree | 9e12425e9740d07639ba35f32244d2b04292f567 | |
parent | 1322b9f426ef6aca8b6ce569411cdd3443c1caf4 (diff) | |
download | ndk-26cc9e3109d9df4dd7726edcb02982c26c207d20.tar.gz |
GAbi++: Add one time construction support
This patch adds sources/cxx-stl/gabi++/src/one_time_construction.cc
and removes the dependency on libstdc++ from GAbi++
The implementation is based on pthread which is always provided by
the C library on Android (the latter is always linked to generated
binaries, so there is no need to list it in LOCAL_EXPORT_LDLIBS).
+ Add unit test to check that one-time-construction works properly.
Change-Id: Ic4af9f19814cf9efd7caa60bc84e9c03fc4bf656
-rwxr-xr-x | build/tools/build-gabi++.sh | 2 | ||||
-rwxr-xr-x | build/tools/build-stlport.sh | 2 | ||||
-rw-r--r-- | sources/cxx-stl/gabi++/Android.mk | 4 | ||||
-rw-r--r-- | sources/cxx-stl/gabi++/sources.mk | 1 | ||||
-rw-r--r-- | sources/cxx-stl/gabi++/src/one_time_construction.cc | 115 | ||||
-rw-r--r-- | tests/device/test-gabi++-guard/jni/Android.mk | 6 | ||||
-rw-r--r-- | tests/device/test-gabi++-guard/jni/Application.mk | 3 | ||||
-rw-r--r-- | tests/device/test-gabi++-guard/jni/test_guard.cpp | 76 |
8 files changed, 203 insertions, 6 deletions
diff --git a/build/tools/build-gabi++.sh b/build/tools/build-gabi++.sh index 9d1d68040..f5c4a702b 100755 --- a/build/tools/build-gabi++.sh +++ b/build/tools/build-gabi++.sh @@ -96,7 +96,7 @@ GABIXX_SRCDIR=$ANDROID_NDK_ROOT/$GABIXX_SUBDIR GABIXX_CFLAGS="-fPIC -O2 -DANDROID -D__ANDROID__" GABIXX_CFLAGS=$GABIXX_CFLAGS" -I$GABIXX_SRCDIR/include" GABIXX_CXXFLAGS="-fuse-cxa-atexit -fno-exceptions -frtti" -GABIXX_LDFLAGS="-lstdc++" +GABIXX_LDFLAGS= # List of sources to compile GABIXX_SOURCES=$(cd $GABIXX_SUBDIR && ls src/*.cc) diff --git a/build/tools/build-stlport.sh b/build/tools/build-stlport.sh index 37544fe29..d5a04ef2a 100755 --- a/build/tools/build-stlport.sh +++ b/build/tools/build-stlport.sh @@ -93,7 +93,7 @@ GABIXX_SRCDIR=$ANDROID_NDK_ROOT/$GABIXX_SUBDIR GABIXX_CFLAGS="-O2 -DANDROID -D__ANDROID__ -I$GABIXX_SRCDIR/include" GABIXX_CXXFLAGS="-fno-exceptions -frtti" GABIXX_SOURCES=$(cd $ANDROID_NDK_ROOT/$GABIXX_SUBDIR && ls src/*.cc) -GABIXX_LDFLAGS="-lstdc++" +GABIXX_LDFLAGS= # Location of the STLPort source tree STLPORT_SRCDIR=$ANDROID_NDK_ROOT/$STLPORT_SUBDIR diff --git a/sources/cxx-stl/gabi++/Android.mk b/sources/cxx-stl/gabi++/Android.mk index a4a1fed5e..09333dc9c 100644 --- a/sources/cxx-stl/gabi++/Android.mk +++ b/sources/cxx-stl/gabi++/Android.mk @@ -8,7 +8,6 @@ ifeq (,$(GABIXX_FORCE_REBUILD)) LOCAL_MODULE:= gabi++_shared LOCAL_SRC_FILES:= libs/$(TARGET_ARCH_ABI)/lib$(LOCAL_MODULE).so LOCAL_EXPORT_C_INCLUDES := $(libgabi++_c_includes) - LOCAL_EXPORT_LDLIBS := -lstdc++ LOCAL_CPP_FEATURES := rtti include $(PREBUILT_SHARED_LIBRARY) @@ -16,7 +15,6 @@ ifeq (,$(GABIXX_FORCE_REBUILD)) LOCAL_MODULE:= gabi++_static LOCAL_SRC_FILES:= libs/$(TARGET_ARCH_ABI)/lib$(LOCAL_MODULE).a LOCAL_EXPORT_C_INCLUDES := $(libgabi++_c_includes) - LOCAL_EXPORT_LDLIBS := -lstdc++ LOCAL_CPP_FEATURES := rtti include $(PREBUILT_STATIC_LIBRARY) @@ -31,7 +29,6 @@ else # ! GABIXX_FORCE_REBUILD LOCAL_CPP_EXTENSION := .cc LOCAL_SRC_FILES:= $(libgabi++_src_files) LOCAL_EXPORT_C_INCLUDES := $(libgabi++_c_includes) - LOCAL_EXPORT_LDLIBS := -lstdc++ LOCAL_C_INCLUDES := $(libgabi++_c_includes) LOCAL_CPP_FEATURES := rtti include $(BUILD_SHARED_LIBRARY) @@ -43,7 +40,6 @@ else # ! GABIXX_FORCE_REBUILD LOCAL_SRC_FILES:= $(libgabi++_src_files) LOCAL_CPP_EXTENSION := .cc LOCAL_EXPORT_C_INCLUDES := $(libgabi++_c_includes) - LOCAL_EXPORT_LDLIBS := -lstdc++ LOCAL_C_INCLUDES := $(libgabi++_c_includes) LOCAL_CPP_FEATURES := rtti include $(BUILD_STATIC_LIBRARY) diff --git a/sources/cxx-stl/gabi++/sources.mk b/sources/cxx-stl/gabi++/sources.mk index bc419f2d8..8210ee7e6 100644 --- a/sources/cxx-stl/gabi++/sources.mk +++ b/sources/cxx-stl/gabi++/sources.mk @@ -8,6 +8,7 @@ libgabi++_src_files := \ src/enum_type_info.cc \ src/function_type_info.cc \ src/new.cc \ + src/one_time_construction.cc \ src/pbase_type_info.cc \ src/pointer_type_info.cc \ src/pointer_to_member_type_info.cc \ diff --git a/sources/cxx-stl/gabi++/src/one_time_construction.cc b/sources/cxx-stl/gabi++/src/one_time_construction.cc new file mode 100644 index 000000000..c0b54be7c --- /dev/null +++ b/sources/cxx-stl/gabi++/src/one_time_construction.cc @@ -0,0 +1,115 @@ +// Copyright (C) 2011 The Android Open Source Project +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// +// One-time construction C++ runtime support +// See "3.3.2 One-time Construction API" of the Itanium C++ ABI reference +// And "3.2.3 Guard variables and the one-time construction API" in the ARM C++ ABI reference. + +/* Note that the ARM C++ ABI defines the size of each guard variable + * as 32-bit, while the generic/Itanium one defines it as 64-bit. + * + * Also the ARM C++ ABI uses the least-significant bit to indicate + * completion, while the generic/Itanium one uses the least-significant + * byte. In all cases the corresponding item is set to value '1' + * + * We will treat guard variables here as 32-bit values, even on x86, + * given that this representation is compatible with compiler-generated + * variables that are 64-bits on little-endian systems. This makes the + * code simpler and slightly more efficient + */ + +#include <stddef.h> +#include <pthread.h> + +/* In this implementation, we use a single global mutex+condvar pair. + * + * Pros: portable and doesn't require playing with futexes, atomics + * and memory barriers. + * + * Cons: Slower than necessary. + */ +static pthread_mutex_t sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; +static pthread_cond_t sCond = PTHREAD_COND_INITIALIZER; + + +extern "C" int __cxa_guard_acquire(int volatile * gv) +{ + pthread_mutex_lock(&sMutex); + for (;;) { + // while gv points to a volatile value, we use the + // previous pthread_mutex_lock or pthread_cond_wait + // as a trivial memory barrier + int guard = *gv; + if ((guard & 1) != 0) { + /* already initialized - return 0 */ + pthread_mutex_unlock(&sMutex); + return 0; + } + + // we use bit 8 to indicate that the guard value is being + // initialized, and bit 9 to indicate that there is another + // thread waiting for its completion. + if ((guard & 0x100) == 0) { + // nobody is initializing this yet, so mark the guard value + // first. and allow initialization to proceed. + *gv = 0x100; + pthread_mutex_unlock(&sMutex); + return 1; + } + + // already being initialized by amother thread, + // we must indicate that there is a waiter, then + // wait to be woken up before trying again. + *gv = guard | 0x200; + pthread_cond_wait(&sCond, &sMutex); + } +} + +extern "C" void __cxa_guard_release(int volatile * gv) +{ + pthread_mutex_lock(&sMutex); + + int guard = *gv; + // this indicates initialization for our two ABIs. + *gv = 0x1; + if ((guard & 0x200) != 0) + pthread_cond_broadcast(&sCond); + + pthread_mutex_unlock(&sMutex); +} + +extern "C" void __cxa_guard_abort(int volatile * gv) +{ + pthread_mutex_lock(&sMutex); + + int guard = *gv; + *gv = 0; + if ((guard & 0x200) != 0) + pthread_cond_broadcast(&sCond); + + pthread_mutex_unlock(&sMutex); +} diff --git a/tests/device/test-gabi++-guard/jni/Android.mk b/tests/device/test-gabi++-guard/jni/Android.mk new file mode 100644 index 000000000..5287f5e3a --- /dev/null +++ b/tests/device/test-gabi++-guard/jni/Android.mk @@ -0,0 +1,6 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := test_guard_variables +LOCAL_SRC_FILES := test_guard.cpp +include $(BUILD_EXECUTABLE) diff --git a/tests/device/test-gabi++-guard/jni/Application.mk b/tests/device/test-gabi++-guard/jni/Application.mk new file mode 100644 index 000000000..4ae2f6520 --- /dev/null +++ b/tests/device/test-gabi++-guard/jni/Application.mk @@ -0,0 +1,3 @@ +APP_STL := gabi++_shared +APP_ABI := all + diff --git a/tests/device/test-gabi++-guard/jni/test_guard.cpp b/tests/device/test-gabi++-guard/jni/test_guard.cpp new file mode 100644 index 000000000..047d6ac33 --- /dev/null +++ b/tests/device/test-gabi++-guard/jni/test_guard.cpp @@ -0,0 +1,76 @@ +/* This program is used to test that one-time-construction + * works correctly, even in the presence of several threads. + */ + +#include <new> +#include <pthread.h> +#include <stdio.h> + +#define MAX_THREADS 100 + +class Foo { +public: + Foo() { mValue++; } + int getValue() { return mValue; } +private: + static int mValue; +}; + +int Foo::mValue; + +static Foo* getInstance(void) +{ + // This construct forces the static creation of _instance + // the first time that getInstance() is called, in a thread-safe + // way. + static Foo _instance; + return &_instance; +} + +static Foo* sInstances[MAX_THREADS]; +static pthread_t sThreads[MAX_THREADS]; + +static void* thread_run(void* arg) +{ + int index = reinterpret_cast<int>(arg); + sInstances[index] = getInstance(); + return NULL; +} + +int main(void) +{ + /* Create all the threads */ + for (int nn = 0; nn < MAX_THREADS; nn++) { + pthread_create( &sThreads[nn], NULL, thread_run, reinterpret_cast<void*>(nn) ); + } + /* Wait for their completion */ + for (int nn = 0; nn < MAX_THREADS; nn++) { + void* dummy; + pthread_join( sThreads[nn], &dummy ); + } + /* Get the instance */ + Foo* foo = getInstance(); + + if (foo == NULL) { + fprintf(stderr, "ERROR: Foo instance is NULL!\n"); + return 1; + } + + if (foo->getValue() != 1) { + fprintf(stderr, "ERROR: Foo constructor called %d times (1 expected)\n", + foo->getValue()); + return 2; + } + + int count = 0; + for (int nn = 0; nn < MAX_THREADS; nn++) { + if (sInstances[nn] != foo) + count++; + } + + if (count != 0) { + fprintf(stderr, "ERROR: There are %d invalid instance pointers!\n", count); + return 3; + } + return 0; +} |