Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0be8feb3 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Properly manage JniStringCache lifecycle and clear on unload." into main

parents e005af39 b74e22cb
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include <dlfcn.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/JniInvocation.h>
#include "com_android_internal_os_JniStringCache.h"
#include <server_configurable_flags/get_flags.h>
#include <signal.h>
#include <stdio.h>
@@ -109,6 +110,10 @@ extern int register_android_media_ToneGenerator(JNIEnv *env);
extern int register_android_media_audio_common_AidlConversion(JNIEnv* env);
extern int register_android_media_midi(JNIEnv *env);

extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
    JniStringCache::Unload(vm);
}

namespace android {

/*
+25 −4
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@
#include <android_runtime/AndroidRuntime.h>

#include <atomic>
#include <mutex>

namespace android {

namespace {

template <typename TChar>
uint32_t computeHash(const TChar* s, size_t len) {
    uint32_t h = 0;
@@ -65,11 +67,14 @@ bool StringsAreEqual(JNIEnv* env, jstring jstr, const char* chars, size_t len) {
    return result;
}

JniStringCache* gInstance = nullptr;
std::once_flag gInstanceFlag;

} // namespace

JniStringCache& JniStringCache::getInstance() {
    static JniStringCache instance;
    return instance;
    std::call_once(gInstanceFlag, []() { gInstance = new JniStringCache(); });
    return *gInstance;
}

JniStringCache::JniStringCache() : mHits(0), mMisses(0), mEvictions(0), mSkips(0) {}
@@ -261,12 +266,14 @@ size_t JniStringCache::skips() const {
}

void JniStringCache::clear() {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    clear(AndroidRuntime::getJNIEnv());
}

void JniStringCache::clear(JNIEnv* env) {
    if (env == nullptr) {
        DCHECK(false) << "JNIEnv is null, can't clear cache";
        return;
    }

    auto clear_cache = [&](std::atomic<CacheEntry>* cache) {
        for (size_t i = 0; i < kCacheSize; ++i) {
            std::atomic<CacheEntry>& slot = cache[i];
@@ -306,6 +313,20 @@ void JniStringCache::clear() {
    clear_cache(mUtf8Cache);
}

void JniStringCache::Unload(JavaVM* vm) {
    if (gInstance == nullptr) {
        return;
    }

    JNIEnv* env = nullptr;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_OK) {
        gInstance->clear(env);
    }

    delete gInstance;
    gInstance = nullptr;
}

static jlong com_android_internal_os_JniStringCache_nativeHits() {
    return static_cast<jlong>(JniStringCache::getInstance().hits());
}
+4 −0
Original line number Diff line number Diff line
@@ -41,6 +41,9 @@ public:
    // Global instance to use in order to maximize cache hits.
    static JniStringCache& getInstance();

    // Clears all global references held by the cache.
    static void Unload(JavaVM* vm);

    JniStringCache();
    ~JniStringCache();

@@ -67,6 +70,7 @@ public:
    // Under concurrent usage, some entries may not be cleared.
    // Use this for instance to trim memory usage if needed.
    void clear();
    void clear(JNIEnv* env);

private:
    struct CacheEntry {