1 /* 2 * Copyright (C) 2024 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 package libcore.util; 17 18 import com.android.ravenwood.RavenwoodRuntimeNative; 19 20 import java.lang.ref.Cleaner; 21 import java.lang.ref.Reference; 22 23 /** 24 * Re-implementation of ART's NativeAllocationRegistry for Ravenwood. 25 * - We don't track the native allocation size on Ravenwood. 26 * - sun.misc.Cleaner isn't available on the desktop JVM, so we use java.lang.ref.Cleaner. 27 * (Should ART switch to java.lang.ref.Cleaner?) 28 */ 29 public class NativeAllocationRegistry { 30 private final long mFreeFunction; 31 private static final Cleaner sCleaner = Cleaner.create(); 32 createNonmalloced( ClassLoader classLoader, long freeFunction, long size)33 public static NativeAllocationRegistry createNonmalloced( 34 ClassLoader classLoader, long freeFunction, long size) { 35 return new NativeAllocationRegistry(classLoader, freeFunction, size); 36 } 37 createNonmalloced( Class clazz, long freeFunction, long size)38 public static NativeAllocationRegistry createNonmalloced( 39 Class clazz, long freeFunction, long size) { 40 return new NativeAllocationRegistry(clazz.getClassLoader(), freeFunction, size); 41 } 42 createMalloced( ClassLoader classLoader, long freeFunction, long size)43 public static NativeAllocationRegistry createMalloced( 44 ClassLoader classLoader, long freeFunction, long size) { 45 return new NativeAllocationRegistry(classLoader, freeFunction, size); 46 } 47 createMalloced( ClassLoader classLoader, long freeFunction)48 public static NativeAllocationRegistry createMalloced( 49 ClassLoader classLoader, long freeFunction) { 50 return new NativeAllocationRegistry(classLoader, freeFunction, 0); 51 } 52 createMalloced( Class clazz, long freeFunction, long size)53 public static NativeAllocationRegistry createMalloced( 54 Class clazz, long freeFunction, long size) { 55 return new NativeAllocationRegistry(clazz.getClassLoader(), freeFunction, size); 56 } 57 NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size)58 public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size) { 59 if (size < 0) { 60 throw new IllegalArgumentException("Invalid native allocation size: " + size); 61 } 62 mFreeFunction = freeFunction; 63 } 64 65 private class CleanerThunk implements Runnable { 66 private long nativePtr; 67 CleanerThunk()68 public CleanerThunk() { 69 nativePtr = 0; 70 } 71 setNativePtr(long ptr)72 public void setNativePtr(long ptr) { 73 nativePtr = ptr; 74 } 75 76 @Override run()77 public void run() { 78 if (nativePtr != 0) { 79 applyFreeFunction(mFreeFunction, nativePtr); 80 } 81 } 82 } 83 84 private static class CleanableRunner implements Runnable { 85 private final Cleaner.Cleanable mCleanable; 86 CleanableRunner(Cleaner.Cleanable cleanable)87 public CleanableRunner(Cleaner.Cleanable cleanable) { 88 mCleanable = cleanable; 89 } 90 run()91 public void run() { 92 mCleanable.clean(); 93 } 94 } 95 registerNativeAllocation(Object referent, long nativePtr)96 public Runnable registerNativeAllocation(Object referent, long nativePtr) { 97 if (referent == null) { 98 throw new IllegalArgumentException("referent is null"); 99 } 100 if (mFreeFunction == 0) { 101 return () -> {}; // do nothing 102 } 103 if (nativePtr == 0) { 104 throw new IllegalArgumentException("nativePtr is null"); 105 } 106 107 final CleanerThunk thunk; 108 final CleanableRunner result; 109 try { 110 thunk = new CleanerThunk(); 111 final var cleanable = sCleaner.register(referent, thunk); 112 result = new CleanableRunner(cleanable); 113 } catch (VirtualMachineError vme /* probably OutOfMemoryError */) { 114 applyFreeFunction(mFreeFunction, nativePtr); 115 throw vme; 116 } 117 118 // Enable the cleaner only after we can no longer throw anything, including OOME. 119 thunk.setNativePtr(nativePtr); 120 // Ensure that cleaner doesn't get invoked before we enable it. 121 Reference.reachabilityFence(referent); 122 return result; 123 } 124 applyFreeFunction(long freeFunction, long nativePtr)125 public static void applyFreeFunction(long freeFunction, long nativePtr) { 126 RavenwoodRuntimeNative.applyFreeFunction(freeFunction, nativePtr); 127 } 128 } 129