You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pe...@apache.org on 2023/06/02 15:04:04 UTC

[celix] 03/08: Implement OSGi uninstall, and make install/uninstall thread-safe.

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 <ho...@gmail.com>
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.