Change tracking of JNI merged libraries in SoLoader.java
Change tracking of JNI merged libraries in SoLoader.java

Summary:
<Overview> this moves tracking of merged JNI libraries out of JNI up to Java. In isolation the JNI is said to be more memory-efficient, but the caveat is that, in order to reach the JNI merging code for any given library, the Java code may have to acquire two moderately-contested locks (and must hold one of them during the merging). By short-circuiting before lock acquisition, we hope to reduce ANRs.

<Why?> per T24279883, loads of "graphservice-jni" (of which there are 18) sometimes hit ANRs while waiting on `synchronized (loadingLibLock)` on the UI thread. Given that library is /always/ already loaded as part of libcoldstart.so, the only meaningful thing that should occur inside the `synchronized (loadingLibLock) {}` block for it is the `MergedSoMapping.invokeJniOnload(..)` call. Now, if we're ANR-ing while the lib is actually being merged for the first time, so be it and we have bigger problems. However, if we're ANR-ing long after the lib has been merged but while waiting for  MergedSoMapping.invokeJniOnload to verify it, well, we can do something about that, which is the case this diff attempts to address. (Also note that "compactdisk-current-jni" has 20 load sites, so "graphservice-jni" isn't the only one desperate to be loaded.)

<Methodology> I opted for a ConcurrentHashMap because (i) in cases like "graphservice-jni" (with 18 load calls), we're likely to have more reads than writes, (ii) the /get/ operation isn't synchronized (and thus may be stale), but (iii) the only operations on the map are /add/ and /get/, so worst case a stale /get/ tells you a library isn't merged when it already is, and thereby we fall back on the JNI merging logic. Also, to reiterate, (iv) if the /get/ finds a match, we bypass all synchronization penalties.

In terms of performance, (a) we use `Collections.newSetFromMap` on the ConcurrentHashMap, but it's pretty bare-bones. (b) ConcurrentHashMap might have some heft to it, but if it can stop ANRs in their tracks, that'd be pretty great. (c) it doesn't seem we can A/B test this logic, sadly?

In terms of logic, `sLoadedAndMergedLibraries` uses `shortName` for the key, whereas the other two `s...Libraries` use `soName` for the key. That's not my favorite thing, but so the logic plays out. Consequently, I also would have liked if `sLoadedLibraries` could have been incorporated into `sLoadedAndMergedLibraries`, but it seemed better to keep things separate for clarity now.

<Final Notes> maybe one option we can use to test this is to only run the `sLoadedAndMergedLibraries` logic on "graphservice-jni" in v156, to prove it out and for our team to accept some of the risk. I'm open to that.

Differential Revision: D6694901

fbshipit-source-id: c68980926b37aef3aa605dbb8769f9f19007e184

facebook/SoLoader,facebook/SoLoader,facebook/SoLoader