diff options
author | Igor Murashkin <iam@google.com> | 2016-09-19 16:12:18 +0000 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2016-09-19 16:45:59 +0000 |
commit | bdcc75a703111ac148a4126075d0c656b155a92f (patch) | |
tree | 584878b1236cc4eb5617824ae7f4c36fa0ba3c45 | |
parent | eee4dc28a1fa627ae6aedb18b3b6af5aa081c17b (diff) |
Revert "Revert "libjavacore: Implement JNI_OnUnload for icu""
This reverts commit 7288f8dd9f2b727a6de774c9a3d0b9f983cf8233.
Change-Id: If8e16d683bc759ed94eb90383c0fcc0d93326794
-rw-r--r-- | luni/src/main/native/Register.cpp | 17 | ||||
-rw-r--r-- | luni/src/main/native/libcore_icu_ICU.cpp | 152 |
2 files changed, 139 insertions, 30 deletions
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp index 5fd4a7d7dd..52df40e598 100644 --- a/luni/src/main/native/Register.cpp +++ b/luni/src/main/native/Register.cpp @@ -53,3 +53,20 @@ jint JNI_OnLoad(JavaVM* vm, void*) { return JNI_VERSION_1_6; } + +// DalvikVM calls this on shutdown, do any global cleanup here. +// -- Very important if we restart multiple DalvikVMs in the same process to reset the state. +void JNI_OnUnload(JavaVM* vm, void*) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + ALOGE("JavaVM::GetEnv() failed"); + abort(); + } + ALOGV("libjavacore JNI_OnUnload"); + + ScopedLocalFrame localFrame(env); + +#define UNREGISTER(FN) extern void FN(JNIEnv*); FN(env) + UNREGISTER(unregister_libcore_icu_ICU); +#undef UNREGISTER +} diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp index 3eda9233c8..63be8c3774 100644 --- a/luni/src/main/native/libcore_icu_ICU.cpp +++ b/luni/src/main/native/libcore_icu_ICU.cpp @@ -839,55 +839,136 @@ static JNINativeMethod gMethods[] = { }; #define FAIL_WITH_STRERROR(s) \ - ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \ + ALOGE("Couldn't " s " '%s': %s", path_.c_str(), strerror(errno)); \ return FALSE; #define MAYBE_FAIL_WITH_ICU_ERROR(s) \ if (status != U_ZERO_ERROR) {\ - ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \ + ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path_.c_str()); \ return FALSE; \ } -static bool mapIcuData(const std::string& path) { +// Contain the memory map for ICU data files. +// Automatically adds the data file to ICU's list of data files upon constructing. +// +// - Automatically unmaps in the destructor. +struct IcuDataMap { + // Map in ICU data at the path, returning null if it failed (prints error to ALOGE). + static std::unique_ptr<IcuDataMap> Create(const std::string& path) { + std::unique_ptr<IcuDataMap> map(new IcuDataMap(path)); + + if (!map->TryMap()) { + // madvise or ICU could fail but mmap still succeeds. + // Destructor will take care of cleaning up a partial init. + return nullptr; + } + + return map; + } + + // Unmap the ICU data. + ~IcuDataMap() { + TryUnmap(); + } + + private: + IcuDataMap(const std::string& path) + : path_(path), + data_(MAP_FAILED), + data_length_(0) + {} + + bool TryMap() { // Open the file and get its length. - android::base::unique_fd fd(open(path.c_str(), O_RDONLY)); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path_.c_str(), O_RDONLY))); + if (fd.get() == -1) { FAIL_WITH_STRERROR("open"); } + struct stat sb; if (fstat(fd.get(), &sb) == -1) { FAIL_WITH_STRERROR("stat"); } + data_length_ = sb.st_size; + // Map it. - void* data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd.get(), 0); - if (data == MAP_FAILED) { + data_ = mmap(NULL, data_length_, PROT_READ, MAP_SHARED, fd.get(), 0 /* offset */); + if (data_ == MAP_FAILED) { FAIL_WITH_STRERROR("mmap"); } // Tell the kernel that accesses are likely to be random rather than sequential. - if (madvise(data, sb.st_size, MADV_RANDOM) == -1) { + if (madvise(data_, data_length_, MADV_RANDOM) == -1) { FAIL_WITH_STRERROR("madvise(MADV_RANDOM)"); } UErrorCode status = U_ZERO_ERROR; // Tell ICU to use our memory-mapped data. - udata_setCommonData(data, &status); + udata_setCommonData(data_, &status); MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData"); - return TRUE; -} + return true; + } -void register_libcore_icu_ICU(JNIEnv* env) { - // Check the timezone override file exists. If it does, map it first so we use it in preference - // to the one that shipped with the device. + bool TryUnmap() { + // Don't need to do opposite of udata_setCommonData, + // u_cleanup (performed in unregister_libcore_icu_ICU) takes care of it. + + // Don't need to opposite of madvise, munmap will take care of it. + + if (data_ != MAP_FAILED) { + if (munmap(data_, data_length_) == -1) { + FAIL_WITH_STRERROR("munmap"); + } + } + + // Don't need to close the file, it was closed automatically during TryMap. + return true; + } + + std::string path_; // Save for error messages. + void* data_; // Save for munmap. + size_t data_length_; // Save for munmap. +}; + +static std::unique_ptr<IcuDataMap> sIcuDataMapFromData; +static std::unique_ptr<IcuDataMap> sIcuDataMapFromSystem; + +// Check the timezone override file exists. If it does, map it first so we use it in preference +// to the one that shipped with the device. +static std::string getTzDataOverridePath() { const char* dataPathPrefix = getenv("ANDROID_DATA"); if (dataPathPrefix == NULL) { ALOGE("ANDROID_DATA environment variable not set"); \ abort(); } + std::string dataPath; + dataPath = dataPathPrefix; + dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat"; + + return dataPath; +} +static std::string getSystemPath() { + const char* systemPathPrefix = getenv("ANDROID_ROOT"); + if (systemPathPrefix == NULL) { + ALOGE("ANDROID_ROOT environment variable not set"); \ + abort(); + } + + std::string systemPath; + systemPath = systemPathPrefix; + systemPath += "/usr/icu/"; + systemPath += U_ICUDATA_NAME; + systemPath += ".dat"; + return systemPath; +} + +// Init ICU, configuring it and loading the data files. +void register_libcore_icu_ICU(JNIEnv* env) { UErrorCode status = U_ZERO_ERROR; // Tell ICU it can *only* use our memory-mapped data. udata_setFileAccess(UDATA_NO_FILES, &status); @@ -896,15 +977,13 @@ void register_libcore_icu_ICU(JNIEnv* env) { abort(); } - // Map in optional TZ data files. - std::string dataPath; - dataPath = dataPathPrefix; - dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat"; + std::string dataPath = getTzDataOverridePath(); + // Map in optional TZ data files. struct stat sb; if (stat(dataPath.c_str(), &sb) == 0) { ALOGD("Timezone override file found: %s", dataPath.c_str()); - if (!mapIcuData(dataPath)) { + if ((sIcuDataMapFromData = IcuDataMap::Create(dataPath)) == nullptr) { ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str()); } } else { @@ -912,18 +991,7 @@ void register_libcore_icu_ICU(JNIEnv* env) { } // Use the ICU data files that shipped with the device for everything else. - const char* systemPathPrefix = getenv("ANDROID_ROOT"); - if (systemPathPrefix == NULL) { - ALOGE("ANDROID_ROOT environment variable not set"); \ - abort(); - } - std::string systemPath; - systemPath = systemPathPrefix; - systemPath += "/usr/icu/"; - systemPath += U_ICUDATA_NAME; - systemPath += ".dat"; - - if (!mapIcuData(systemPath)) { + if ((sIcuDataMapFromSystem = IcuDataMap::Create(getSystemPath())) == nullptr) { abort(); } @@ -938,3 +1006,27 @@ void register_libcore_icu_ICU(JNIEnv* env) { jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods)); } + +// De-init ICU, unloading the data files. Do the opposite of the above function. +void unregister_libcore_icu_ICU(JNIEnv* env) { + // Unregister JNI native methods explicitly. + { + ScopedLocalRef<jclass> c(env, env->FindClass("libcore/icu/ICU")); + if (c.get() != nullptr) { + env->UnregisterNatives(c.get()); + } else { + ALOGW("Couldn't find class libcore/icu/ICU to unregister natives"); + } + } + + // Reset libicu state to before it was loaded. + u_cleanup(); + + // Unmap ICU data files that shipped with the device for everything else. + sIcuDataMapFromSystem.reset(); + + // Unmap optional TZ data files. + sIcuDataMapFromData.reset(); + + // We don't need to call udata_setFileAccess because u_cleanup takes care of it. +} |