[NUI.Gadget] Implement Unload() method (#5898)
authorhjhun <36876573+hjhun@users.noreply.github.com>
Wed, 24 Jan 2024 04:25:23 +0000 (13:25 +0900)
committerGitHub <noreply@github.com>
Wed, 24 Jan 2024 04:25:23 +0000 (13:25 +0900)
To unload the loaded assembly, NUIGadgetManager.Unload() method is
added.

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetAssembly.cs [new file with mode: 0644]
src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetInfo.cs
src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetManager.cs

diff --git a/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetAssembly.cs b/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetAssembly.cs
new file mode 100644 (file)
index 0000000..9cfb199
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+* Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the License);
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an AS IS BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace Tizen.NUI
+{
+    internal class NUIGadgetAssemblyLoadContext : AssemblyLoadContext
+    {
+        public NUIGadgetAssemblyLoadContext() : base(isCollectible: true)
+        {
+        }
+
+        protected override Assembly Load(AssemblyName name)
+        {
+            return null;
+        }
+    }
+
+    internal class NUIGadgetAssembly
+    {
+        private static readonly object _assemblyLock = new object();
+        private readonly string _assemblyPath;
+        private WeakReference _assemblyRef;
+        private Assembly _assembly = null;
+
+        public NUIGadgetAssembly(string assemblyPath) { _assemblyPath = assemblyPath; }
+
+        public void Load()
+        {
+            lock (_assemblyLock)
+            {
+                if (_assembly != null)
+                {
+                    return;
+                }
+
+                Log.Warn("Load(): " + _assemblyPath + " ++");
+                NUIGadgetAssemblyLoadContext context = new NUIGadgetAssemblyLoadContext();
+                _assemblyRef = new WeakReference(context);
+                using (MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(_assemblyPath)))
+                {
+                    _assembly = context.LoadFromStream(memoryStream);
+                }
+                Log.Warn("Load(): " + _assemblyPath + " --");
+            }
+        }
+
+        public bool IsLoaded { get { return _assembly != null; } }
+
+        public NUIGadget CreateInstance(string className)
+        {
+            lock (_assemblyLock)
+            {
+                return (NUIGadget)_assembly?.CreateInstance(className);
+            }
+        }
+
+        public void Unload()
+        {
+            lock (_assemblyLock)
+            {
+                if (_assembly == null)
+                {
+                    return;
+                }
+
+                Log.Warn("Unload(): " + _assemblyPath + " ++");
+                if (_assemblyRef.IsAlive)
+                {
+                    (_assemblyRef.Target as NUIGadgetAssemblyLoadContext).Unload();
+                }
+
+                _assembly = null;
+                Log.Warn("Unload(): " + _assemblyPath + " --");
+            }
+        }
+    }
+}
\ No newline at end of file
index 6a959cb..6f177c7 100755 (executable)
@@ -108,7 +108,7 @@ namespace Tizen.NUI
 
         internal string ResourceClassName { get; set; }
 
-        internal Assembly Assembly { get; set; }
+        internal NUIGadgetAssembly Assembly { get; private set; }
 
         internal static NUIGadgetInfo CreateNUIGadgetInfo(string packageId)
         {
@@ -199,6 +199,7 @@ namespace Tizen.NUI
                 Log.Warn("Failed to destroy package info. error = " + errorCode);
             }
 
+            info.Assembly = new NUIGadgetAssembly(info.ResourcePath + info.ExecutableFile);
             return info;
         }
     }
index bd3025c..9773961 100755 (executable)
@@ -21,8 +21,10 @@ using System.IO;
 using Tizen.Applications;
 using System.ComponentModel;
 using System.Runtime.InteropServices;
+using System.Runtime.Loader;
 using System.Reflection;
 using System.Threading.Tasks;
+using System.Security.AccessControl;
 
 namespace Tizen.NUI
 {
@@ -144,17 +146,54 @@ namespace Tizen.NUI
             }
 
             NUIGadgetInfo info = Find(resourceType);
+            Load(info);
+        }
+
+        private static void Load(NUIGadgetInfo info)
+        {
+            if (info == null)
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
             try
             {
-                Load(info);
+                lock (info)
+                {
+                    if (!info.Assembly.IsLoaded)
+                    {
+                        info.Assembly.Load();
+                    }
+                }
             }
             catch (FileLoadException e)
             {
                 throw new InvalidOperationException(e.Message);
             }
+            catch (BadImageFormatException e)
+            {
+                throw new InvalidOperationException(e.Message);
+            }
         }
 
-        private static void Load(NUIGadgetInfo info)
+        /// <summary>
+        /// Unloads the loaded assembly of the NUIGadget.
+        /// </summary>
+        /// <param name="resourceType">The resource type of the NUIGadget package.</param>
+        /// <exception cref="ArgumentException">Thrown when failed because of a invalid argument.</exception>
+        /// <since_tizen> 11 </since_tizen>
+        public static void Unload(string resourceType)
+        {
+            if (string.IsNullOrEmpty(resourceType))
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
+            NUIGadgetInfo info = Find(resourceType);
+            Unload(info);
+        }
+
+        private static void Unload(NUIGadgetInfo info)
         {
             if (info == null)
             {
@@ -163,11 +202,9 @@ namespace Tizen.NUI
 
             lock (info)
             {
-                if (info.Assembly == null)
+                if (info.Assembly.IsLoaded)
                 {
-                    Log.Warn("NUIGadgetAssembly.Load(): " + info.ResourcePath + info.ExecutableFile + " ++");
-                    info.Assembly = Assembly.Load(File.ReadAllBytes(info.ResourcePath + info.ExecutableFile));
-                    Log.Warn("NUIGadgetAssembly.Load(): " + info.ResourcePath + info.ExecutableFile + " --");
+                    info.Assembly.Unload();
                 }
             }
         }
@@ -189,16 +226,9 @@ namespace Tizen.NUI
             }
 
             NUIGadgetInfo info = Find(resourceType);
-            try
-            {
-                Load(info);
-            }
-            catch (FileLoadException e)
-            {
-                throw new InvalidOperationException(e.Message);
-            }
+            Load(info);
 
-            NUIGadget gadget = info.Assembly.CreateInstance(className, true) as NUIGadget;
+            NUIGadget gadget = info.Assembly.CreateInstance(className);
             if (gadget == null)
             {
                 throw new InvalidOperationException("Failed to create instance. className: " + className);