From fc970ab65ad8c860dd0654d59b70bbe271f95784 Mon Sep 17 00:00:00 2001 From: Diego Perez Date: Fri, 26 Feb 2016 11:03:39 +0000 Subject: [PATCH] Implement VirtualRefBasePtr native methods The VirtualRefBasePtr is used to track the usage of native objects from the java side by reference counting. Implementing the class delegate to make sure we dispose the objects correctly. Change-Id: I3dd4717944b0dbe79f30e49b3083bf65c6e5276d --- .../util/VirtualRefBasePtr_Delegate.java | 53 +++++++++++++++++++ .../bridge/impl/DelegateManager.java | 52 +++++++++++------- .../layoutlib/bridge/intensive/Main.java | 17 ++++++ .../tools/layoutlib/create/CreateInfo.java | 1 + 4 files changed, 105 insertions(+), 18 deletions(-) create mode 100644 tools/layoutlib/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java diff --git a/tools/layoutlib/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java new file mode 100644 index 0000000000000..01fe45d2644c2 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.util.LongSparseLongArray; + +/** + * Delegate used to provide new implementation the native methods of {@link VirtualRefBasePtr} + * + * Through the layoutlib_create tool, the original native methods of VirtualRefBasePtr have been + * replaced by calls to methods of the same name in this delegate class. + * + */ +@SuppressWarnings("unused") +public class VirtualRefBasePtr_Delegate { + private static final DelegateManager sManager = new DelegateManager<>(Object.class); + private static final LongSparseLongArray sRefCount = new LongSparseLongArray(); + + @LayoutlibDelegate + /*package*/ static synchronized void nIncStrong(long ptr) { + long counter = sRefCount.get(ptr); + sRefCount.put(ptr, ++counter); + } + + @LayoutlibDelegate + /*package*/ static synchronized void nDecStrong(long ptr) { + long counter = sRefCount.get(ptr); + + if (counter > 1) { + sRefCount.put(ptr, --counter); + } else { + sRefCount.delete(ptr); + sManager.removeJavaReferenceFor(ptr); + } + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java index baf2e2e11564c..c59b1a66bb027 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java @@ -22,9 +22,11 @@ import com.android.layoutlib.bridge.util.SparseWeakArray; import android.annotation.Nullable; import android.util.SparseArray; +import java.io.PrintStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; /** * Manages native delegates. @@ -73,14 +75,14 @@ import java.util.List; public final class DelegateManager { @SuppressWarnings("FieldCanBeLocal") private final Class mClass; - private final SparseWeakArray mDelegates = new SparseWeakArray(); + private static final SparseWeakArray sDelegates = new SparseWeakArray<>(); /** list used to store delegates when their main object holds a reference to them. * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed * @see #addNewDelegate(Object) * @see #removeJavaReferenceFor(long) */ - private final List mJavaReferences = new ArrayList(); - private int mDelegateCounter = 0; + private static final List sJavaReferences = new ArrayList<>(); + private static final AtomicLong sDelegateCounter = new AtomicLong(1); public DelegateManager(Class theClass) { mClass = theClass; @@ -97,9 +99,12 @@ public final class DelegateManager { * @return the delegate or null if not found. */ @Nullable - public synchronized T getDelegate(long native_object) { + public T getDelegate(long native_object) { if (native_object > 0) { - T delegate = mDelegates.get(native_object); + Object delegate; + synchronized (DelegateManager.class) { + delegate = sDelegates.get(native_object); + } if (Debug.DEBUG) { if (delegate == null) { @@ -109,7 +114,8 @@ public final class DelegateManager { } assert delegate != null; - return delegate; + //noinspection unchecked + return (T)delegate; } return null; } @@ -119,12 +125,13 @@ public final class DelegateManager { * @param newDelegate the delegate to add * @return a unique native int to identify the delegate */ - public synchronized long addNewDelegate(T newDelegate) { - long native_object = ++mDelegateCounter; - - mDelegates.put(native_object, newDelegate); - assert !mJavaReferences.contains(newDelegate); - mJavaReferences.add(newDelegate); + public long addNewDelegate(T newDelegate) { + long native_object = sDelegateCounter.getAndIncrement(); + synchronized (DelegateManager.class) { + sDelegates.put(native_object, newDelegate); + assert !sJavaReferences.contains(newDelegate); + sJavaReferences.add(newDelegate); + } if (Debug.DEBUG) { System.out.println( @@ -140,14 +147,23 @@ public final class DelegateManager { * Removes the main reference on the given delegate. * @param native_object the native integer representing the delegate. */ - public synchronized void removeJavaReferenceFor(long native_object) { - T delegate = getDelegate(native_object); + public void removeJavaReferenceFor(long native_object) { + synchronized (DelegateManager.class) { + T delegate = getDelegate(native_object); - if (Debug.DEBUG) { - System.out.println("Removing main Java ref on " + mClass.getSimpleName() + - " with int " + native_object); + if (Debug.DEBUG) { + System.out.println("Removing main Java ref on " + mClass.getSimpleName() + + " with int " + native_object); + } + + sJavaReferences.remove(delegate); } + } - mJavaReferences.remove(delegate); + public synchronized static void dump(PrintStream out) { + for (Object reference : sJavaReferences) { + int idx = sDelegates.indexOfValue(reference); + out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName()); + } } } diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index a5561fab953c6..09dd5f078b9a2 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -31,6 +31,7 @@ import com.android.io.FolderWrapper; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.RenderAction; +import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator; import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback; import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser; @@ -52,6 +53,7 @@ import android.util.DisplayMetrics; import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.net.URL; import java.util.Arrays; import java.util.concurrent.TimeUnit; @@ -298,6 +300,16 @@ public class Main { renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012); } + private static void gc() { + // See RuntimeUtil#gc in jlibs (http://jlibs.in/) + Object obj = new Object(); + WeakReference ref = new WeakReference(obj); + obj = null; + while(ref.get() != null) { + System.gc(); + } + } + @AfterClass public static void tearDown() { sLayoutLibLog = null; @@ -305,6 +317,11 @@ public class Main { sProjectResources = null; sLogger = null; sBridge = null; + + gc(); + + System.out.println("Objects still linked from the DelegateManager:"); + DelegateManager.dump(System.out); } /** Test expand_layout.xml */ diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 1a00cc942c42c..483bddc218596 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -302,6 +302,7 @@ public final class CreateInfo implements ICreateInfo { "android.text.StaticLayout", "android.util.PathParser", "android.view.Display", + "com.android.internal.util.VirtualRefBasePtr", "com.android.internal.view.animation.NativeInterpolatorFactoryHelper", "libcore.icu.ICU", };