• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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 
17 package com.facebook.jni;
18 
19 import com.facebook.jni.annotations.DoNotStripAny;
20 
21 /**
22  * This object holds a native C++ member for hybrid Java/C++ objects.
23  *
24  * <p>NB: THREAD SAFETY
25  *
26  * <p>{@link #resetNative} deletes the corresponding native object synchronously on whatever thread
27  * the method is called on. Otherwise, deletion will occur on the {@link DestructorThread} thread.
28  */
29 @DoNotStripAny
30 public class HybridData {
31 
32   static {
33     System.loadLibrary("fbjni");
34   }
35 
36   private final Destructor mDestructor = new Destructor(this);
37 
38   /**
39    * To explicitly delete the instance, call resetNative(). If the C++ instance is referenced after
40    * this is called, a NullPointerException will be thrown. resetNative() may be called multiple
41    * times safely. Because the {@link DestructorThread} also calls resetNative, the instance will
42    * not leak if this is not called, but timing of deletion and the thread the C++ dtor is called on
43    * will be at the whim of the Java GC. If you want to control the thread and timing of the
44    * destructor, you should call resetNative() explicitly.
45    */
resetNative()46   public synchronized void resetNative() {
47     mDestructor.destruct();
48   }
49 
50   /**
51    * N.B. Thread safety. If you call isValid from a different thread than {@link #resetNative()}
52    * then be sure to do so while synchronizing on the hybrid. For example:
53    *
54    * <pre><code>
55    * synchronized(hybrid) {
56    *   if (hybrid.isValid) {
57    *     // Do stuff.
58    *   }
59    * }
60    * </code></pre>
61    */
isValid()62   public boolean isValid() {
63     return mDestructor.mNativePointer != 0;
64   }
65 
66   @DoNotStripAny
67   public static class Destructor extends DestructorThread.Destructor {
68 
69     // Private C++ instance
70     private volatile long mNativePointer;
71 
Destructor(Object referent)72     Destructor(Object referent) {
73       super(referent);
74     }
75 
76     @Override
destruct()77     protected final void destruct() {
78       // When invoked from the DestructorThread instead of resetNative,
79       // the DestructorThread has exclusive ownership of the HybridData
80       // so synchronization is not necessary.
81       deleteNative(mNativePointer);
82       mNativePointer = 0;
83     }
84 
deleteNative(long pointer)85     static native void deleteNative(long pointer);
86   }
87 }
88