diff options
| -rw-r--r-- | runtime/base/time_utils.h | 4 | ||||
| -rw-r--r-- | runtime/gc/heap.cc | 42 | ||||
| -rw-r--r-- | runtime/gc/heap.h | 4 | ||||
| -rw-r--r-- | runtime/gc/space/large_object_space.cc | 34 | ||||
| -rw-r--r-- | runtime/java_vm_ext.cc | 7 | ||||
| -rw-r--r-- | runtime/mem_map.cc | 24 | ||||
| -rw-r--r-- | runtime/runtime.cc | 19 | ||||
| -rw-r--r-- | runtime/well_known_classes.cc | 5 | ||||
| -rw-r--r-- | runtime/well_known_classes.h | 2 | ||||
| -rw-r--r-- | sigchainlib/version-script.txt | 1 | ||||
| -rw-r--r-- | test/004-NativeAllocations/src/Main.java | 30 |
11 files changed, 132 insertions, 40 deletions
diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h index f58c22a7cc..55d2764576 100644 --- a/runtime/base/time_utils.h +++ b/runtime/base/time_utils.h @@ -68,8 +68,8 @@ static constexpr inline uint64_t NsToMs(uint64_t ns) { } // Converts the given number of milliseconds to nanoseconds -static constexpr inline uint64_t MsToNs(uint64_t ns) { - return ns * 1000 * 1000; +static constexpr inline uint64_t MsToNs(uint64_t ms) { + return ms * 1000 * 1000; } #if defined(__APPLE__) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f039f6beb3..aeab7d80b4 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -110,6 +110,9 @@ static constexpr size_t kVerifyObjectAllocationStackSize = 16 * KB / sizeof(mirror::HeapReference<mirror::Object>); static constexpr size_t kDefaultAllocationStackSize = 8 * MB / sizeof(mirror::HeapReference<mirror::Object>); +// System.runFinalization can deadlock with native allocations, to deal with this, we have a +// timeout on how long we wait for finalizers to run. b/21544853 +static constexpr uint64_t kNativeAllocationFinalizeTimeout = MsToNs(250u); Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, double foreground_heap_growth_multiplier, @@ -232,10 +235,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity; } if (!image_file_name.empty()) { + ATRACE_BEGIN("ImageSpace::Create"); std::string error_msg; - space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(), - image_instruction_set, - &error_msg); + auto* image_space = space::ImageSpace::Create(image_file_name.c_str(), image_instruction_set, + &error_msg); + ATRACE_END(); if (image_space != nullptr) { AddSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space @@ -287,6 +291,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } std::string error_str; std::unique_ptr<MemMap> non_moving_space_mem_map; + ATRACE_BEGIN("Create heap maps"); if (separate_non_moving_space) { // If we are the zygote, the non moving space becomes the zygote space when we run // PreZygoteFork the first time. In this case, call the map "zygote space" since we can't @@ -323,6 +328,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max capacity_, &error_str)); CHECK(main_mem_map_2.get() != nullptr) << error_str; } + ATRACE_END(); + ATRACE_BEGIN("Create spaces"); // Create the non moving space first so that bitmaps don't take up the address range. if (separate_non_moving_space) { // Non moving space is always dlmalloc since we currently don't have support for multiple @@ -340,7 +347,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (foreground_collector_type_ == kCollectorTypeCC) { region_space_ = space::RegionSpace::Create("Region space", capacity_ * 2, request_begin); AddSpace(region_space_); - } else if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) { + } else if (IsMovingGc(foreground_collector_type_) && + foreground_collector_type_ != kCollectorTypeGSS) { // Create bump pointer spaces. // We only to create the bump pointer if the foreground collector is a compacting GC. // TODO: Place bump-pointer spaces somewhere to minimize size of card table. @@ -411,10 +419,12 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (main_space_backup_.get() != nullptr) { RemoveSpace(main_space_backup_.get()); } + ATRACE_END(); // Allocate the card table. + ATRACE_BEGIN("Create card table"); card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity)); CHECK(card_table_.get() != nullptr) << "Failed to create card table"; - + ATRACE_END(); if (foreground_collector_type_ == kCollectorTypeCC && kUseTableLookupReadBarrier) { rb_table_.reset(new accounting::ReadBarrierTable()); DCHECK(rb_table_->IsAllCleared()); @@ -2252,8 +2262,8 @@ void Heap::PreZygoteFork() { // Set all the cards in the mod-union table since we don't know which objects contain references // to large objects. mod_union_table->SetCards(); - large_object_space_->SetAllLargeObjectsAsZygoteObjects(self); AddModUnionTable(mod_union_table); + large_object_space_->SetAllLargeObjectsAsZygoteObjects(self); if (collector::SemiSpace::kUseRememberedSet) { // Add a new remembered set for the post-zygote non-moving space. accounting::RememberedSet* post_zygote_non_moving_space_rem_set = @@ -3531,22 +3541,16 @@ bool Heap::IsGCRequestPending() const { return concurrent_gc_pending_.LoadRelaxed(); } -void Heap::RunFinalization(JNIEnv* env) { - // Can't do this in WellKnownClasses::Init since System is not properly set up at that point. - if (WellKnownClasses::java_lang_System_runFinalization == nullptr) { - CHECK(WellKnownClasses::java_lang_System != nullptr); - WellKnownClasses::java_lang_System_runFinalization = - CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V"); - CHECK(WellKnownClasses::java_lang_System_runFinalization != nullptr); - } - env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, - WellKnownClasses::java_lang_System_runFinalization); +void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) { + env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_runFinalization, + static_cast<jlong>(timeout)); } void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { Thread* self = ThreadForEnv(env); if (native_need_to_run_finalization_) { - RunFinalization(env); + RunFinalization(env, kNativeAllocationFinalizeTimeout); UpdateMaxNativeFootprint(); native_need_to_run_finalization_ = false; } @@ -3562,7 +3566,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { if (new_native_bytes_allocated > growth_limit_) { if (WaitForGcToComplete(kGcCauseForNativeAlloc, self) != collector::kGcTypeNone) { // Just finished a GC, attempt to run finalizers. - RunFinalization(env); + RunFinalization(env, kNativeAllocationFinalizeTimeout); CHECK(!env->ExceptionCheck()); // Native bytes allocated may be updated by finalization, refresh it. new_native_bytes_allocated = native_bytes_allocated_.LoadRelaxed(); @@ -3570,7 +3574,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { // If we still are over the watermark, attempt a GC for alloc and run finalizers. if (new_native_bytes_allocated > growth_limit_) { CollectGarbageInternal(gc_type, kGcCauseForNativeAlloc, false); - RunFinalization(env); + RunFinalization(env, kNativeAllocationFinalizeTimeout); native_need_to_run_finalization_ = false; CHECK(!env->ExceptionCheck()); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c72414a1ab..81a97414ba 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -776,8 +776,8 @@ class Heap { bool IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Run the finalizers. - void RunFinalization(JNIEnv* env); + // Run the finalizers. If timeout is non zero, then we use the VMRuntime version. + void RunFinalization(JNIEnv* env, uint64_t timeout); // Blocks the caller until the garbage collector becomes idle and returns the type of GC we // waited for. diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 9436c3f700..52192e212c 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -46,8 +46,8 @@ class ValgrindLargeObjectMapSpace FINAL : public LargeObjectMapSpace { } } - virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, size_t* bytes_tl_bulk_allocated) + mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, + size_t* usable_size, size_t* bytes_tl_bulk_allocated) OVERRIDE { mirror::Object* obj = LargeObjectMapSpace::Alloc(self, num_bytes + kValgrindRedZoneBytes * 2, bytes_allocated, @@ -63,26 +63,35 @@ class ValgrindLargeObjectMapSpace FINAL : public LargeObjectMapSpace { return object_without_rdz; } - virtual size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE { - mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>( - reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); - return LargeObjectMapSpace::AllocationSize(object_with_rdz, usable_size); + size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE { + return LargeObjectMapSpace::AllocationSize(ObjectWithRedzone(obj), usable_size); } - virtual size_t Free(Thread* self, mirror::Object* obj) OVERRIDE { - mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>( - reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); + bool IsZygoteLargeObject(Thread* self, mirror::Object* obj) const OVERRIDE { + return LargeObjectMapSpace::IsZygoteLargeObject(self, ObjectWithRedzone(obj)); + } + + size_t Free(Thread* self, mirror::Object* obj) OVERRIDE { + mirror::Object* object_with_rdz = ObjectWithRedzone(obj); VALGRIND_MAKE_MEM_UNDEFINED(object_with_rdz, AllocationSize(obj, nullptr)); return LargeObjectMapSpace::Free(self, object_with_rdz); } bool Contains(const mirror::Object* obj) const OVERRIDE { - mirror::Object* object_with_rdz = reinterpret_cast<mirror::Object*>( - reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); - return LargeObjectMapSpace::Contains(object_with_rdz); + return LargeObjectMapSpace::Contains(ObjectWithRedzone(obj)); } private: + static const mirror::Object* ObjectWithRedzone(const mirror::Object* obj) { + return reinterpret_cast<const mirror::Object*>( + reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); + } + + static mirror::Object* ObjectWithRedzone(mirror::Object* obj) { + return reinterpret_cast<mirror::Object*>( + reinterpret_cast<uintptr_t>(obj) - kValgrindRedZoneBytes); + } + static constexpr size_t kValgrindRedZoneBytes = kPageSize; }; @@ -253,6 +262,7 @@ class AllocationInfo { } // Updates the allocation size and whether or not it is free. void SetByteSize(size_t size, bool free) { + DCHECK_EQ(size & ~kFlagsMask, 0u); DCHECK_ALIGNED(size, FreeListSpace::kAlignment); alloc_size_ = (size / FreeListSpace::kAlignment) | (free ? kFlagFree : 0u); } diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index 2d3d19ce3e..eb9c32d7ad 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -16,6 +16,8 @@ #include "jni_internal.h" +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include <cutils/trace.h> #include <dlfcn.h> #include "art_method.h" @@ -788,9 +790,11 @@ void JavaVMExt::VisitRoots(RootVisitor* visitor) { // JNI Invocation interface. extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { + ATRACE_BEGIN(__FUNCTION__); const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args); if (IsBadJniVersion(args->version)) { LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version; + ATRACE_END(); return JNI_EVERSION; } RuntimeOptions options; @@ -800,6 +804,7 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { } bool ignore_unrecognized = args->ignoreUnrecognized; if (!Runtime::Create(options, ignore_unrecognized)) { + ATRACE_END(); return JNI_ERR; } Runtime* runtime = Runtime::Current(); @@ -808,10 +813,12 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { delete Thread::Current()->GetJniEnv(); delete runtime->GetJavaVM(); LOG(WARNING) << "CreateJavaVM failed"; + ATRACE_END(); return JNI_ERR; } *p_env = Thread::Current()->GetJniEnv(); *p_vm = runtime->GetJavaVM(); + ATRACE_END(); return JNI_OK; } diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index cf4233c3d1..7feac8ab0c 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -314,7 +314,31 @@ MemMap* MemMap::MapAnonymous(const char* name, uint8_t* expected_ptr, size_t byt if (low_4gb && expected_ptr == nullptr) { bool first_run = true; + MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_); for (uintptr_t ptr = next_mem_pos_; ptr < 4 * GB; ptr += kPageSize) { + // Use maps_ as an optimization to skip over large maps. + // Find the first map which is address > ptr. + auto it = maps_->upper_bound(reinterpret_cast<void*>(ptr)); + if (it != maps_->begin()) { + auto before_it = it; + --before_it; + // Start at the end of the map before the upper bound. + ptr = std::max(ptr, reinterpret_cast<uintptr_t>(before_it->second->BaseEnd())); + CHECK_ALIGNED(ptr, kPageSize); + } + while (it != maps_->end()) { + // How much space do we have until the next map? + size_t delta = reinterpret_cast<uintptr_t>(it->first) - ptr; + // If the space may be sufficient, break out of the loop. + if (delta >= page_aligned_byte_count) { + break; + } + // Otherwise, skip to the end of the map. + ptr = reinterpret_cast<uintptr_t>(it->second->BaseEnd()); + CHECK_ALIGNED(ptr, kPageSize); + ++it; + } + if (4U * GB - ptr < page_aligned_byte_count) { // Not enough memory until 4GB. if (first_run) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 65ea77ad29..9d651bf6a4 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -22,6 +22,8 @@ #include <linux/fs.h> #endif +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include <cutils/trace.h> #include <signal.h> #include <sys/syscall.h> #include <valgrind.h> @@ -492,8 +494,12 @@ bool Runtime::Start() { ScopedObjectAccess soa(self); gc::space::ImageSpace* image_space = heap_->GetImageSpace(); if (image_space != nullptr) { + ATRACE_BEGIN("AddImageStringsToTable"); GetInternTable()->AddImageStringsToTable(image_space); + ATRACE_END(); + ATRACE_BEGIN("MoveImageClassesToClassTable"); GetClassLinker()->MoveImageClassesToClassTable(); + ATRACE_END(); } } @@ -512,7 +518,9 @@ bool Runtime::Start() { // InitNativeMethods needs to be after started_ so that the classes // it touches will have methods linked to the oat file if necessary. + ATRACE_BEGIN("InitNativeMethods"); InitNativeMethods(); + ATRACE_END(); // Initialize well known thread group values that may be accessed threads while attaching. InitThreadGroups(self); @@ -533,7 +541,9 @@ bool Runtime::Start() { GetInstructionSetString(kRuntimeISA)); } + ATRACE_BEGIN("StartDaemonThreads"); StartDaemonThreads(); + ATRACE_END(); { ScopedObjectAccess soa(self); @@ -763,6 +773,7 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames, } bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) { + ATRACE_BEGIN("Runtime::Init"); CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize); MemMap::Init(); @@ -773,6 +784,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) ParsedOptions::Create(raw_options, ignore_unrecognized, &runtime_options)); if (parsed_options.get() == nullptr) { LOG(ERROR) << "Failed to parse options"; + ATRACE_END(); return false; } VLOG(startup) << "Runtime::Init -verbose:startup enabled"; @@ -826,6 +838,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) zygote_max_failed_boots_ = runtime_options.GetOrDefault(Opt::ZygoteMaxFailedBoots); XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption); + ATRACE_BEGIN("CreateHeap"); heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize), runtime_options.GetOrDefault(Opt::HeapGrowthLimit), runtime_options.GetOrDefault(Opt::HeapMinFree), @@ -855,9 +868,11 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) xgc_option.verify_post_gc_rosalloc_, runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM), runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs)); + ATRACE_END(); if (heap_->GetImageSpace() == nullptr && !allow_dex_file_fallback_) { LOG(ERROR) << "Dex file fallback disabled, cannot continue without image."; + ATRACE_END(); return false; } @@ -957,7 +972,9 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); class_linker_ = new ClassLinker(intern_table_); if (GetHeap()->HasImageSpace()) { + ATRACE_BEGIN("InitFromImage"); class_linker_->InitFromImage(); + ATRACE_END(); if (kIsDebugBuild) { GetHeap()->GetImageSpace()->VerifyImageAllocations(); } @@ -1090,6 +1107,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) VLOG(startup) << "Runtime::Init exiting"; + ATRACE_END(); + return true; } diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 3dbfe1b516..e7857a0602 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -34,6 +34,7 @@ jclass WellKnownClasses::dalvik_system_DexFile; jclass WellKnownClasses::dalvik_system_DexPathList; jclass WellKnownClasses::dalvik_system_DexPathList__Element; jclass WellKnownClasses::dalvik_system_PathClassLoader; +jclass WellKnownClasses::dalvik_system_VMRuntime; jclass WellKnownClasses::java_lang_BootClassLoader; jclass WellKnownClasses::java_lang_ClassLoader; jclass WellKnownClasses::java_lang_ClassNotFoundException; @@ -63,6 +64,7 @@ jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer; jmethodID WellKnownClasses::com_android_dex_Dex_create; +jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization; jmethodID WellKnownClasses::java_lang_Boolean_valueOf; jmethodID WellKnownClasses::java_lang_Byte_valueOf; jmethodID WellKnownClasses::java_lang_Character_valueOf; @@ -209,6 +211,8 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList"); dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element"); dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader"); + dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime"); + java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader"); java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader"); java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException"); @@ -238,6 +242,7 @@ void WellKnownClasses::Init(JNIEnv* env) { org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer"); + dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V"); com_android_dex_Dex_create = CacheMethod(env, com_android_dex_Dex, true, "create", "(Ljava/nio/ByteBuffer;)Lcom/android/dex/Dex;"); java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index d25d1c3f7d..66b9abece7 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -45,6 +45,7 @@ struct WellKnownClasses { static jclass dalvik_system_DexPathList; static jclass dalvik_system_DexPathList__Element; static jclass dalvik_system_PathClassLoader; + static jclass dalvik_system_VMRuntime; static jclass java_lang_BootClassLoader; static jclass java_lang_ClassLoader; static jclass java_lang_ClassNotFoundException; @@ -74,6 +75,7 @@ struct WellKnownClasses { static jclass org_apache_harmony_dalvik_ddmc_DdmServer; static jmethodID com_android_dex_Dex_create; + static jmethodID dalvik_system_VMRuntime_runFinalization; static jmethodID java_lang_Boolean_valueOf; static jmethodID java_lang_Byte_valueOf; static jmethodID java_lang_Character_valueOf; diff --git a/sigchainlib/version-script.txt b/sigchainlib/version-script.txt index ce1505490b..08c312e7e2 100644 --- a/sigchainlib/version-script.txt +++ b/sigchainlib/version-script.txt @@ -5,6 +5,7 @@ global: InvokeUserSignalHandler; InitializeSignalChain; EnsureFrontOfChain; + SetSpecialSignalHandlerFn; sigaction; signal; sigprocmask; diff --git a/test/004-NativeAllocations/src/Main.java b/test/004-NativeAllocations/src/Main.java index a99fe92081..92f4e21f40 100644 --- a/test/004-NativeAllocations/src/Main.java +++ b/test/004-NativeAllocations/src/Main.java @@ -19,6 +19,8 @@ import java.lang.Runtime; public class Main { static Object nativeLock = new Object(); + static Object deadlockLock = new Object(); + static boolean aboutToDeadlockLock = false; static int nativeBytes = 0; static Object runtime; static Method register_native_allocation; @@ -28,13 +30,15 @@ public class Main { static class NativeAllocation { private int bytes; - NativeAllocation(int bytes) throws Exception { + NativeAllocation(int bytes, boolean testingDeadlock) throws Exception { this.bytes = bytes; register_native_allocation.invoke(runtime, bytes); synchronized (nativeLock) { - nativeBytes += bytes; - if (nativeBytes > maxMem) { - throw new OutOfMemoryError(); + if (!testingDeadlock) { + nativeBytes += bytes; + if (nativeBytes > maxMem) { + throw new OutOfMemoryError(); + } } } } @@ -44,6 +48,9 @@ public class Main { nativeBytes -= bytes; } register_native_free.invoke(runtime, bytes); + aboutToDeadlockLock = true; + synchronized (deadlockLock) { + } } } @@ -59,7 +66,20 @@ public class Main { int allocation_count = 256; NativeAllocation[] allocations = new NativeAllocation[count]; for (int i = 0; i < allocation_count; ++i) { - allocations[i % count] = new NativeAllocation(size); + allocations[i % count] = new NativeAllocation(size, false); + } + // Test that we don't get a deadlock if we are holding nativeLock. If there is no timeout, + // then we will get a finalizer timeout exception. + aboutToDeadlockLock = false; + synchronized (deadlockLock) { + for (int i = 0; aboutToDeadlockLock != true; ++i) { + allocations[i % count] = new NativeAllocation(size, true); + } + // Do more allocations now that the finalizer thread is deadlocked so that we force + // finalization and timeout. + for (int i = 0; i < 10; ++i) { + allocations[i % count] = new NativeAllocation(size, true); + } } System.out.println("Test complete"); } |