This is an automated email from the ASF dual-hosted git repository. pengzheng pushed a commit to branch feature/556-osgi-uninstall in repository https://gitbox.apache.org/repos/asf/celix.git
commit 73dac42f05e86d8e5974d14c2a66d5d8b6865c7c Author: PengZheng <[email protected]> AuthorDate: Thu Jun 1 19:16:21 2023 +0800 Implement OSGi uninstall, and make install/uninstall thread-safe. --- libs/framework/gtest/src/BundleArchiveTestSuite.cc | 27 +++++++++++++-- .../src/CelixBundleContextBundlesTestSuite.cc | 40 +++++++++++++++++++--- libs/framework/src/framework.c | 21 +++++++++--- .../src/framework_bundle_lifecycle_handler.c | 4 +-- libs/framework/src/framework_private.h | 4 ++- 5 files changed, 81 insertions(+), 15 deletions(-) diff --git a/libs/framework/gtest/src/BundleArchiveTestSuite.cc b/libs/framework/gtest/src/BundleArchiveTestSuite.cc index 98b376a8..59cc1c1c 100644 --- a/libs/framework/gtest/src/BundleArchiveTestSuite.cc +++ b/libs/framework/gtest/src/BundleArchiveTestSuite.cc @@ -74,8 +74,18 @@ TEST_F(CxxBundleArchiveTestSuite, BundleArchiveReusedTest) { auto firstBundleRevisionTime = installTime; lock.unlock(); - //uninstall and reinstall - ctx->uninstallBundle(bndId1); + tracker.reset(); + fw = celix::createFramework({ + {"CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL", "trace"}, + {CELIX_FRAMEWORK_CLEAN_CACHE_DIR_ON_CREATE, "false"} + }); + ctx = fw->getFrameworkBundleContext(); + tracker = ctx->trackBundles() + .addOnInstallCallback([&](const celix::Bundle& b) { + std::lock_guard<std::mutex> lock{m}; + auto *archive = celix_bundle_getArchive(b.getCBundle()); + EXPECT_EQ(CELIX_SUCCESS, celix_bundleArchive_getLastModified(archive, &installTime)); + }).build(); std::this_thread::sleep_for(std::chrono::milliseconds{100}); //wait so that the zip <-> archive dir modification time is different long bndId2 = ctx->installBundle(SIMPLE_TEST_BUNDLE1_LOCATION); EXPECT_GT(bndId2, -1); @@ -89,7 +99,18 @@ TEST_F(CxxBundleArchiveTestSuite, BundleArchiveReusedTest) { auto secondBundleRevisionTime = installTime; - ctx->uninstallBundle(bndId1); + tracker.reset(); + fw = celix::createFramework({ + {"CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL", "trace"}, + {CELIX_FRAMEWORK_CLEAN_CACHE_DIR_ON_CREATE, "false"} + }); + ctx = fw->getFrameworkBundleContext(); + tracker = ctx->trackBundles() + .addOnInstallCallback([&](const celix::Bundle& b) { + std::lock_guard<std::mutex> lock{m}; + auto *archive = celix_bundle_getArchive(b.getCBundle()); + EXPECT_EQ(CELIX_SUCCESS, celix_bundleArchive_getLastModified(archive, &installTime)); + }).build(); std::this_thread::sleep_for(std::chrono::milliseconds{100}); //wait so that the zip <-> archive dir modification time is different celix_utils_touch(SIMPLE_TEST_BUNDLE1_LOCATION); //touch the bundle zip file to force an update long bndId3 = ctx->installBundle(SIMPLE_TEST_BUNDLE1_LOCATION); diff --git a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc index cf9c08bf..e6213766 100644 --- a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc @@ -28,6 +28,7 @@ #include <celix_log_utils.h> #include "celix_api.h" +#include "celix_file_utils.h" class CelixBundleContextBundlesTestSuite : public ::testing::Test { public: @@ -46,7 +47,7 @@ public: CelixBundleContextBundlesTestSuite() { properties = properties_create(); properties_set(properties, "LOGHELPER_ENABLE_STDOUT_FALLBACK", "true"); - properties_set(properties, "org.osgi.framework.storage.clean", "onFirstInit"); + properties_set(properties, "org.osgi.framework.storage.clean", "true"); properties_set(properties, "org.osgi.framework.storage", ".cacheBundleContextTestFramework"); fw = celix_frameworkFactory_createFramework(properties); @@ -152,6 +153,29 @@ TEST_F(CelixBundleContextBundlesTestSuite, InstallAndUninstallBundlesTest) { ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId2)); //not auto started ASSERT_TRUE(celix_bundleContext_isBundleActive(ctx, bndId3)); + char *bndRoot1 = nullptr; + ASSERT_TRUE(celix_bundleContext_useBundle(ctx, bndId1, &bndRoot1, [](void* handle, const celix_bundle_t* bnd) { + char **root = static_cast<char **>(handle); + *root = celix_bundle_getEntry(bnd, "/"); + })); + ASSERT_TRUE(bndRoot1 != nullptr); + char* bndRoot2 = nullptr; + ASSERT_TRUE(celix_bundleContext_useBundle(ctx, bndId2, &bndRoot2, [](void* handle, const celix_bundle_t* bnd) { + char **root = static_cast<char **>(handle); + *root = celix_bundle_getEntry(bnd, "/"); + })); + ASSERT_TRUE(bndRoot2 != nullptr); + char* bndRoot3 = nullptr; + ASSERT_TRUE(celix_bundleContext_useBundle(ctx, bndId3, &bndRoot3, [](void* handle, const celix_bundle_t* bnd) { + char **root = static_cast<char **>(handle); + *root = celix_bundle_getEntry(bnd, "/"); + })); + ASSERT_TRUE(bndRoot3 != nullptr); + + ASSERT_TRUE(celix_utils_directoryExists(bndRoot1)); + ASSERT_TRUE(celix_utils_directoryExists(bndRoot2)); + ASSERT_TRUE(celix_utils_directoryExists(bndRoot3)); + //uninstall bundles ASSERT_TRUE(celix_bundleContext_uninstallBundle(ctx, bndId1)); ASSERT_TRUE(celix_bundleContext_uninstallBundle(ctx, bndId2)); @@ -165,17 +189,25 @@ TEST_F(CelixBundleContextBundlesTestSuite, InstallAndUninstallBundlesTest) { ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId2)); ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId3)); + ASSERT_FALSE(celix_utils_directoryExists(bndRoot1)); + ASSERT_FALSE(celix_utils_directoryExists(bndRoot2)); + ASSERT_FALSE(celix_utils_directoryExists(bndRoot3)); + + free(bndRoot1); + free(bndRoot2); + free(bndRoot3); + //reinstall bundles long bndId4 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true); long bndId5 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, false); long bndId6 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true); ASSERT_TRUE(bndId4 >= 0L); - ASSERT_TRUE(bndId1 == bndId4); //bundle cache -> reuse of bundle id. + ASSERT_FALSE(bndId1 == bndId4); //bundle cache -> reuse of bundle id. ASSERT_TRUE(bndId5 >= 0L); - ASSERT_TRUE(bndId2 == bndId5); //bundle cache -> reuse of bundle id. + ASSERT_FALSE(bndId2 == bndId5); //bundle cache -> reuse of bundle id. ASSERT_TRUE(bndId6 >= 0L); - ASSERT_TRUE(bndId3 == bndId6); //bundle cache -> reuse of bundle id. + ASSERT_FALSE(bndId3 == bndId6); //bundle cache -> reuse of bundle id. } TEST_F(CelixBundleContextBundlesTestSuite, StartBundleWithException) { diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c index 93138d13..823d3d19 100644 --- a/libs/framework/src/framework.c +++ b/libs/framework/src/framework.c @@ -239,6 +239,7 @@ celix_status_t framework_create(framework_pt *out, celix_properties_t* config) { celixThreadMutex_create(&framework->dispatcher.mutex, NULL); celixThreadMutex_create(&framework->frameworkListenersLock, NULL); celixThreadMutex_create(&framework->bundleListenerLock, NULL); + celixThreadMutex_create(&framework->installLock, NULL); celixThreadMutex_create(&framework->installedBundles.mutex, NULL); celixThreadCondition_init(&framework->dispatcher.cond, NULL); framework->dispatcher.active = true; @@ -359,6 +360,7 @@ celix_status_t framework_destroy(framework_pt framework) { celixThreadMutex_unlock(&framework->installedBundles.mutex); celix_arrayList_destroy(framework->installedBundles.entries); celixThreadMutex_destroy(&framework->installedBundles.mutex); + celixThreadMutex_destroy(&framework->installLock); //teardown framework bundle lifecycle handling assert(celix_arrayList_size(framework->bundleLifecycleHandling.bundleLifecycleHandlers) == 0); @@ -646,11 +648,13 @@ celix_framework_installBundleInternal(celix_framework_t* framework, const char* } } + celixThreadMutex_lock(&framework->installLock); if (status == CELIX_SUCCESS) { id = framework_getBundle(framework, bndLoc); if (id != -1L) { celix_framework_bundleEntry_decreaseUseCount(fwBundleEntry); *bundleOut = id; + celixThreadMutex_unlock(&framework->installLock); return CELIX_SUCCESS; } @@ -677,7 +681,7 @@ celix_framework_installBundleInternal(celix_framework_t* framework, const char* } celix_framework_bundleEntry_decreaseUseCount(fwBundleEntry); - + celixThreadMutex_unlock(&framework->installLock); return status; } @@ -1177,7 +1181,7 @@ static void* framework_shutdown(void *framework) { } for (int i = size-1; i >= 0; --i) { //note loop in reverse order -> uninstall later installed bundle first celix_framework_bundle_entry_t *entry = celix_arrayList_get(stopEntries, i); - celix_framework_uninstallBundleEntry(fw, entry); + celix_framework_uninstallBundleEntry(fw, entry, false); } celix_arrayList_destroy(stopEntries); @@ -1888,10 +1892,11 @@ void celix_framework_uninstallBundleAsync(celix_framework_t *fw, long bndId) { celix_framework_uninstallBundleInternal(fw, bndId, true); } -celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* framework, celix_framework_bundle_entry_t* bndEntry) { +celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* framework, celix_framework_bundle_entry_t* bndEntry, bool permanent) { assert(!celix_framework_isCurrentThreadTheEventLoop(framework)); - celixThreadRwlock_writeLock(&bndEntry->fsmMutex); + celixThreadMutex_lock(&framework->installLock); + celixThreadRwlock_writeLock(&bndEntry->fsmMutex); celix_bundle_state_e bndState = celix_bundle_getState(bndEntry->bnd); if (bndState == CELIX_BUNDLE_STATE_ACTIVE) { celix_framework_stopBundleEntryInternal(framework, bndEntry); @@ -1900,6 +1905,7 @@ celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* framework if (!fw_bundleEntry_removeBundleEntry(framework, bndEntry)) { celixThreadRwlock_unlock(&bndEntry->fsmMutex); celix_framework_bundleEntry_decreaseUseCount(bndEntry); + celixThreadMutex_unlock(&framework->installLock); return CELIX_ILLEGAL_STATE; } @@ -1932,8 +1938,13 @@ celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* framework celix_framework_waitForEmptyEventQueue(framework); //to ensure that the uninstall event is triggered and handled (void)bundle_closeModules(bnd); (void)bundle_destroy(bnd); - (void)bundleArchive_destroy(archive); + if(permanent) { + (void)celix_bundleCache_destroyArchive(framework->cache, archive); + } else { + (void)bundleArchive_destroy(archive); + } } + celixThreadMutex_unlock(&framework->installLock); framework_logIfError(framework->logger, status, "", "Cannot uninstall bundle"); return status; } diff --git a/libs/framework/src/framework_bundle_lifecycle_handler.c b/libs/framework/src/framework_bundle_lifecycle_handler.c index c0851943..669778d8 100644 --- a/libs/framework/src/framework_bundle_lifecycle_handler.c +++ b/libs/framework/src/framework_bundle_lifecycle_handler.c @@ -47,7 +47,7 @@ static void* celix_framework_BundleLifecycleHandlingThread(void *data) { break; case CELIX_BUNDLE_LIFECYCLE_UNINSTALL: celix_framework_bundleEntry_decreaseUseCount(handler->bndEntry); - celix_framework_uninstallBundleEntry(handler->framework, handler->bndEntry); + celix_framework_uninstallBundleEntry(handler->framework, handler->bndEntry, true); break; default: //update celix_framework_updateBundleEntry(handler->framework, handler->bndEntry, handler->updatedBundleUrl); @@ -143,7 +143,7 @@ celix_status_t celix_framework_uninstallBundleOnANonCelixEventThread(celix_frame celix_framework_createAndStartBundleLifecycleHandler(fw, bndEntry, CELIX_BUNDLE_LIFECYCLE_UNINSTALL, NULL); return CELIX_SUCCESS; } else { - return celix_framework_uninstallBundleEntry(fw, bndEntry); + return celix_framework_uninstallBundleEntry(fw, bndEntry, true); } } diff --git a/libs/framework/src/framework_private.h b/libs/framework/src/framework_private.h index 3d98c3d8..f2944cac 100644 --- a/libs/framework/src/framework_private.h +++ b/libs/framework/src/framework_private.h @@ -40,6 +40,7 @@ #include "celix_threads.h" #include "service_registry.h" +#include <stdbool.h> #ifndef CELIX_FRAMEWORK_DEFAULT_STATIC_EVENT_QUEUE_SIZE #define CELIX_FRAMEWORK_DEFAULT_STATIC_EVENT_QUEUE_SIZE 1024 @@ -147,6 +148,7 @@ struct celix_framework { celix_thread_t thread; } shutdown; + celix_thread_mutex_t installLock; // serialize install/uninstall struct { celix_array_list_t *entries; //value = celix_framework_bundle_entry_t*. Note ordered by installed bundle time //i.e. later installed bundle are last @@ -426,7 +428,7 @@ celix_status_t celix_framework_stopBundleEntry(celix_framework_t* fw, celix_fram /** * Uninstall a bundle. Cannot be called on the Celix event thread. */ -celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* fw, celix_framework_bundle_entry_t* bndEntry); +celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* fw, celix_framework_bundle_entry_t* bndEntry, bool permanent); /** * Uninstall a bundle. Cannot be called on the Celix event thread.
