1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package libcore.libcore.util; 18 19import junit.framework.TestCase; 20 21import libcore.util.NativeAllocationRegistry; 22 23public class NativeAllocationRegistryTest extends TestCase { 24 25 static { 26 System.loadLibrary("javacoretests"); 27 } 28 29 private ClassLoader classLoader = NativeAllocationRegistryTest.class.getClassLoader(); 30 31 private static class TestConfig { 32 public boolean useAllocator; 33 public boolean shareRegistry; 34 35 public TestConfig(boolean useAllocator, boolean shareRegistry) { 36 this.useAllocator = useAllocator; 37 this.shareRegistry = shareRegistry; 38 } 39 } 40 41 private static class Allocation { 42 public byte[] javaAllocation; 43 public long nativeAllocation; 44 } 45 46 // Verify that NativeAllocations and their referents are freed before we run 47 // out of space for new allocations. 48 private void testNativeAllocation(TestConfig config) { 49 Runtime.getRuntime().gc(); 50 long max = Runtime.getRuntime().maxMemory(); 51 long total = Runtime.getRuntime().totalMemory(); 52 int size = 1024*1024; 53 int expectedMaxNumAllocations = (int)(max-total)/size; 54 int numSavedAllocations = expectedMaxNumAllocations/2; 55 Allocation[] saved = new Allocation[numSavedAllocations]; 56 57 final int nativeSize = size/2; 58 int javaSize = size/2; 59 NativeAllocationRegistry registry = new NativeAllocationRegistry( 60 classLoader, getNativeFinalizer(), nativeSize); 61 62 // Allocate more native allocations than will fit in memory. This should 63 // not throw OutOfMemoryError because the few allocations we save 64 // references to should easily fit. 65 for (int i = 0; i < expectedMaxNumAllocations * 10; i++) { 66 if (!config.shareRegistry) { 67 registry = new NativeAllocationRegistry( 68 classLoader, getNativeFinalizer(), nativeSize); 69 } 70 71 final Allocation alloc = new Allocation(); 72 alloc.javaAllocation = new byte[javaSize]; 73 if (config.useAllocator) { 74 NativeAllocationRegistry.Allocator allocator 75 = new NativeAllocationRegistry.Allocator() { 76 public long allocate() { 77 alloc.nativeAllocation = doNativeAllocation(nativeSize); 78 return alloc.nativeAllocation; 79 } 80 }; 81 registry.registerNativeAllocation(alloc, allocator); 82 } else { 83 alloc.nativeAllocation = doNativeAllocation(nativeSize); 84 registry.registerNativeAllocation(alloc, alloc.nativeAllocation); 85 } 86 87 saved[i%numSavedAllocations] = alloc; 88 } 89 90 // Verify most of the allocations have been freed. 91 long nativeBytes = getNumNativeBytesAllocated(); 92 assertTrue("Excessive native bytes still allocated (" + nativeBytes + ")" 93 + " given max memory of (" + max + ")", nativeBytes < 2 * max); 94 } 95 96 public void testNativeAllocationAllocatorAndSharedRegistry() { 97 testNativeAllocation(new TestConfig(true, true)); 98 } 99 100 public void testNativeAllocationNoAllocatorAndSharedRegistry() { 101 testNativeAllocation(new TestConfig(false, true)); 102 } 103 104 public void testNativeAllocationAllocatorAndNoSharedRegistry() { 105 testNativeAllocation(new TestConfig(true, false)); 106 } 107 108 public void testNativeAllocationNoAllocatorAndNoSharedRegistry() { 109 testNativeAllocation(new TestConfig(false, false)); 110 } 111 112 public void testBadSize() { 113 assertThrowsIllegalArgumentException(new Runnable() { 114 public void run() { 115 NativeAllocationRegistry registry = new NativeAllocationRegistry( 116 classLoader, getNativeFinalizer(), -8); 117 } 118 }); 119 } 120 121 public void testEarlyFree() { 122 long size = 1234; 123 NativeAllocationRegistry registry 124 = new NativeAllocationRegistry(classLoader, getNativeFinalizer(), size); 125 long nativePtr = doNativeAllocation(size); 126 Object referent = new Object(); 127 Runnable cleaner = registry.registerNativeAllocation(referent, nativePtr); 128 long numBytesAllocatedBeforeClean = getNumNativeBytesAllocated(); 129 130 // Running the cleaner should cause the native finalizer to run. 131 cleaner.run(); 132 long numBytesAllocatedAfterClean = getNumNativeBytesAllocated(); 133 assertEquals(numBytesAllocatedBeforeClean - size, numBytesAllocatedAfterClean); 134 135 // Running the cleaner again should have no effect. 136 cleaner.run(); 137 assertEquals(numBytesAllocatedAfterClean, getNumNativeBytesAllocated()); 138 139 // There shouldn't be any problems when the referent object is GC'd. 140 referent = null; 141 Runtime.getRuntime().gc(); 142 } 143 144 public void testNullArguments() { 145 final NativeAllocationRegistry registry 146 = new NativeAllocationRegistry(classLoader, getNativeFinalizer(), 1024); 147 final long dummyNativePtr = 0x1; 148 final Object referent = new Object(); 149 150 // referent should not be null 151 assertThrowsIllegalArgumentException(new Runnable() { 152 public void run() { 153 registry.registerNativeAllocation(null, dummyNativePtr); 154 } 155 }); 156 157 // nativePtr should not be null 158 assertThrowsIllegalArgumentException(new Runnable() { 159 public void run() { 160 registry.registerNativeAllocation(referent, 0); 161 } 162 }); 163 164 // referent should not be null 165 assertThrowsIllegalArgumentException(new Runnable() { 166 public void run() { 167 registry.registerNativeAllocation(null, 168 new NativeAllocationRegistry.Allocator() { 169 public long allocate() { 170 // The allocate function ought not to be called. 171 fail("allocate function called"); 172 return dummyNativePtr; 173 } 174 }); 175 } 176 }); 177 178 // Allocation that returns null should have no effect. 179 assertNull(registry.registerNativeAllocation(referent, 180 new NativeAllocationRegistry.Allocator() { 181 public long allocate() { 182 return 0; 183 } 184 })); 185 } 186 187 private static void assertThrowsIllegalArgumentException(Runnable runnable) { 188 try { 189 runnable.run(); 190 } catch (IllegalArgumentException ex) { 191 return; 192 } 193 fail("Expected IllegalArgumentException, but no exception was thrown."); 194 } 195 196 private static native long getNativeFinalizer(); 197 private static native long doNativeAllocation(long size); 198 private static native long getNumNativeBytesAllocated(); 199} 200