From: hjhun <36876573+hjhun@users.noreply.github.com> Date: Wed, 17 Apr 2024 23:21:51 +0000 (+0900) Subject: [NUI.Gadget] Support Unload() method to release assembly (#6073) X-Git-Tag: accepted/tizen/8.0/unified/20240613.065534~35 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0e7a805a780f68162f5fbdf2441aa1e5c895e8cc;p=platform%2Fcore%2Fcsapi%2Ftizenfx.git [NUI.Gadget] Support Unload() method to release assembly (#6073) * [NUI.Gadget] Support Unload() method to release assembly To release loaded assembly, the Unload() method is added. Signed-off-by: Hwankyu Jhun * Set nullptr to NUIGadgetInfo.NUIGadgetAssembly Signed-off-by: Hwankyu Jhun --------- Signed-off-by: Hwankyu Jhun --- diff --git a/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetAssembly.cs b/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetAssembly.cs new file mode 100644 index 0000000..9946fe8 --- /dev/null +++ b/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetAssembly.cs @@ -0,0 +1,98 @@ +/* +* 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; + +using SystemIO = System.IO; + +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); + string directoryPath = SystemIO.Path.GetDirectoryName(_assemblyPath); + string fileName = SystemIO.Path.GetFileNameWithoutExtension(_assemblyPath); + string nativeImagePath = directoryPath + "/.native_image/" + fileName + ".ni.dll"; + Log.Debug("NativeImagePath=" + nativeImagePath + ", AssemblyPath=" + _assemblyPath); + _assembly = context.LoadFromNativeImagePath(nativeImagePath, _assemblyPath); + 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 diff --git a/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetInfo.cs b/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetInfo.cs index fda1346..d25f520 100755 --- a/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetInfo.cs +++ b/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetInfo.cs @@ -87,6 +87,8 @@ namespace Tizen.NUI internal Assembly Assembly { get; set; } + internal NUIGadgetAssembly NUIGadgetAssembly { get; set; } + internal static NUIGadgetInfo CreateNUIGadgetInfo(string packageId) { Interop.PackageManagerInfo.ErrorCode errorCode = Interop.PackageManagerInfo.PackageInfoGet(packageId, out IntPtr handle); diff --git a/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetManager.cs b/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetManager.cs index 84b98e0..05aa174 100755 --- a/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetManager.cs +++ b/src/Tizen.NUI.Gadget/Tizen.NUI/NUIGadgetManager.cs @@ -142,16 +142,63 @@ namespace Tizen.NUI /// 10 public static void Load(string resourceType) { + Load(resourceType, true); + } + + /// + /// Loads an assembly of the NUIGadget. + /// + /// The resource type of the NUIGadget package. + /// The flag if ture, use a default load context. Otherwise, use a new load context. + /// Thrown when failed because of a invalid argument. + /// Thrown when failed because of an invalid operation. + /// 10 + public static void Load(string resourceType, bool useDefaultContext) + { if (string.IsNullOrEmpty(resourceType)) { throw new ArgumentException("Invalid argument"); } NUIGadgetInfo info = Find(resourceType); - Load(info); + Load(info, useDefaultContext); + } + + /// + /// Unloads the loaded assembly of the NUIGadget. + /// + /// The resource type of the NUIGadget package. + /// Thrown when failed because of a invalid argument. + /// 10 + 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) + { + throw new ArgumentException("Invalid argument"); + } + + lock (info) + { + if (info.NUIGadgetAssembly != null && info.NUIGadgetAssembly.IsLoaded) + { + info.NUIGadgetAssembly.Unload(); + info.NUIGadgetAssembly = null; + } + } } - private static void Load(NUIGadgetInfo info) + private static void Load(NUIGadgetInfo info, bool useDefaultContext) { if (info == null) { @@ -162,12 +209,25 @@ namespace Tizen.NUI { lock (info) { - if (info.Assembly == null) + if (useDefaultContext) { + if (info.Assembly == null) + { - Log.Warn("NUIGadgetAssembly.Load(): " + info.ResourcePath + info.ExecutableFile + " ++"); - info.Assembly = Assembly.Load(SystemIO.Path.GetFileNameWithoutExtension(info.ExecutableFile)); - Log.Warn("NUIGadgetAssembly.Load(): " + info.ResourcePath + info.ExecutableFile + " --"); + Log.Warn("NUIGadget.Load(): " + info.ResourcePath + info.ExecutableFile + " ++"); + info.Assembly = Assembly.Load(SystemIO.Path.GetFileNameWithoutExtension(info.ExecutableFile)); + Log.Warn("NUIGadget.Load(): " + info.ResourcePath + info.ExecutableFile + " --"); + } + } + else + { + if (info.NUIGadgetAssembly == null) + { + Log.Warn("NUIGadgetAssembly.Load(): " + info.ResourcePath + info.ExecutableFile + " ++"); + info.NUIGadgetAssembly = new NUIGadgetAssembly(info.ResourcePath + info.ExecutableFile); + info.NUIGadgetAssembly.Load(); + Log.Warn("NUIGadgetAssembly.Load(): " + info.ResourcePath + info.ExecutableFile + " --"); + } } } } @@ -192,15 +252,30 @@ namespace Tizen.NUI /// 10 public static NUIGadget Add(string resourceType, string className) { + return Add(resourceType, className, true); + } + + /// + /// Adds a NUIGadget to the NUIGadgetManager. + /// + /// The resource type of the NUIGadget package. + /// The class name of the NUIGadget. + /// The flag it true, use a default context. Otherwise, use a new load context. + /// The NUIGadget object. + /// Thrown when failed because of a invalid argument. + /// Thrown when failed because of an invalid operation. + /// 10 + public static NUIGadget Add(string resourceType, string className, bool useDefaultContext) + { if (string.IsNullOrEmpty(resourceType) || string.IsNullOrEmpty(className)) { throw new ArgumentException("Invalid argument"); } NUIGadgetInfo info = Find(resourceType); - Load(info); + Load(info, useDefaultContext); - NUIGadget gadget = info.Assembly.CreateInstance(className, true) as NUIGadget; + NUIGadget gadget = useDefaultContext ? info.Assembly.CreateInstance(className, true) as NUIGadget : info.NUIGadgetAssembly.CreateInstance(className); if (gadget == null) { throw new InvalidOperationException("Failed to create instance. className: " + className);