private static boolean loadLibraryBySoNameImpl()

in java/com/facebook/soloader/SoLoader.java [866:999]


  private static boolean loadLibraryBySoNameImpl(
      String soName,
      @Nullable String shortName,
      @Nullable String mergedLibName,
      int loadFlags,
      @Nullable StrictMode.ThreadPolicy oldPolicy) {
    // As an optimization, avoid taking locks if the library has already loaded. Without locks this
    // does not provide 100% coverage (e.g. another thread may currently be loading or initializing
    // the library), so we'll need to check again with locks held, below.
    if (!TextUtils.isEmpty(shortName) && sLoadedAndMergedLibraries.contains(shortName)) {
      return false;
    }

    // LoadingLibLock is used to ensure that doLoadLibraryBySoName and its corresponding JniOnload
    // are only executed once per library. It also guarantees that concurrent calls to loadLibrary
    // for the same library do not return until both its load and JniOnLoad have completed.
    Object loadingLibLock;
    boolean loaded = false;
    synchronized (SoLoader.class) {
      if (sLoadedLibraries.contains(soName)) {
        if (mergedLibName == null) {
          // Not a merged lib, no need to init
          return false;
        }
        loaded = true;
      }
      if (sLoadingLibraries.containsKey(soName)) {
        loadingLibLock = sLoadingLibraries.get(soName);
      } else {
        loadingLibLock = new Object();
        sLoadingLibraries.put(soName, loadingLibLock);
      }
    }

    // Note that both doLoadLibraryBySoName and invokeJniOnload (below) may re-enter loadLibrary
    // (or loadLibraryBySoNameImpl), recursively acquiring additional library and soSource locks.
    //
    // To avoid bi-directional lock usage (threadA takes loadingLibLock then sSoSourcesLock, threadB
    // takes sSoSourcesLock then loadingLibLock) and potential deadlock as this method recursively
    // calls loadLibrary, we must acquire sSoSourcesLock first.
    sSoSourcesLock.readLock().lock();
    try {
      synchronized (loadingLibLock) {
        if (!loaded) {
          synchronized (SoLoader.class) {
            if (sLoadedLibraries.contains(soName)) {
              // Library was successfully loaded by other thread while we waited
              if (mergedLibName == null) {
                // Not a merged lib, no need to init
                return false;
              }
              loaded = true;
            }
            // Else, load was not successful on other thread. We will try in this one.
          }

          if (!loaded) {
            try {
              if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "About to load: " + soName);
              }
              doLoadLibraryBySoName(soName, loadFlags, oldPolicy);
            } catch (UnsatisfiedLinkError ex) {
              String message = ex.getMessage();
              if (message != null && message.contains("unexpected e_machine:")) {
                String machine_msg =
                    message.substring(message.lastIndexOf("unexpected e_machine:"));
                throw new WrongAbiError(ex, machine_msg);
              }
              throw ex;
            }
            if (Log.isLoggable(TAG, Log.DEBUG)) {
              Log.d(TAG, "Loaded: " + soName);
            }
            synchronized (SoLoader.class) {
              sLoadedLibraries.add(soName);
            }
          }
        }

        if ((loadFlags & SOLOADER_SKIP_MERGED_JNI_ONLOAD) == 0) {
          // MergedSoMapping#invokeJniOnload does not necessarily handle concurrent nor redundant
          // invocation. sLoadedAndMergedLibraries is used in conjunction with loadingLibLock to
          // ensure one invocation per library.
          boolean isAlreadyMerged =
              !TextUtils.isEmpty(shortName) && sLoadedAndMergedLibraries.contains(shortName);
          if (mergedLibName != null && !isAlreadyMerged) {
            if (SYSTRACE_LIBRARY_LOADING) {
              Api18TraceUtils.beginTraceSection("MergedSoMapping.invokeJniOnload[", shortName, "]");
            }
            try {
              if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "About to merge: " + shortName + " / " + soName);
              }
              MergedSoMapping.invokeJniOnload(shortName);
              sLoadedAndMergedLibraries.add(shortName);
            } catch (UnsatisfiedLinkError e) {
              // If you are seeing this exception, first make sure your library sets
              // allow_jni_merging=True.  Trying to merge a library without that
              // will trigger this error.  If that's already in place, you're probably
              // not defining JNI_OnLoad.  Calling SoLoader.loadLibrary on a library
              // that doesn't define JNI_OnLoad is a no-op when that library is not merged.
              // Once you enable merging, it throws an UnsatisfiedLinkError.
              // There are three main reasons a library might not define JNI_OnLoad,
              // and the solution depends on which case you have.
              // - You might be using implicit registration (native methods defined like
              //   `Java_com_facebook_Foo_bar(JNIEnv* env)`).  This is not safe on Android
              //   https://fb.workplace.com/groups/442333009148653/permalink/651212928260659/
              //   and is not compatible with FBJNI.  Stop doing it.  Use FBJNI registerNatives.
              // - You might have a C++-only library with no JNI bindings and no static
              //   initializers with side-effects.  You can just delete the loadLibrary call.
              // - You might have a C++-only library that needs to be loaded explicitly because
              //   it has static initializers whose side-effects are needed.  In that case,
              //   pass the SOLOADER_SKIP_MERGED_JNI_ONLOAD flag to loadLibrary.
              throw new RuntimeException(
                  "Failed to call JNI_OnLoad from '"
                      + shortName
                      + "', which has been merged into '"
                      + soName
                      + "'.  See comment for details.",
                  e);
            } finally {
              if (SYSTRACE_LIBRARY_LOADING) {
                Api18TraceUtils.endSection();
              }
            }
          }
        }
      }
    } finally {
      sSoSourcesLock.readLock().unlock();
    }
    return !loaded;
  }