[ElmSharp] Fix memory leak on Image.LoadAsync method (#720)
authorSeungkeun Lee <sngn.lee@samsung.com>
Fri, 22 Feb 2019 00:00:50 +0000 (09:00 +0900)
committerGitHub <noreply@github.com>
Fri, 22 Feb 2019 00:00:50 +0000 (09:00 +0900)
src/ElmSharp/ElmSharp/EvasObject.cs
src/ElmSharp/ElmSharp/EvasObjectEvent.cs
src/ElmSharp/ElmSharp/Image.cs
src/ElmSharp/ElmSharp/SmartEvent.cs

index 049bc10..20b8b87 100644 (file)
@@ -1277,7 +1277,8 @@ namespace ElmSharp
 
         private void DisposeEvent()
         {
-            foreach (var evt in _eventStore)
+            var events = new List<IInvalidatable>(_eventStore);
+            foreach (var evt in events)
             {
                 evt.Dispose();
             }
@@ -1297,5 +1298,10 @@ namespace ElmSharp
         {
             _eventStore.Add(item);
         }
+
+        internal void RemoveFromEventLifeTracker(IInvalidatable item)
+        {
+            _eventStore.Remove(item);
+        }
     }
 }
index f551fa2..cfe8db1 100644 (file)
@@ -351,7 +351,7 @@ namespace ElmSharp
             {
                 if (disposing)
                 {
-                    // Place holder to dispose managed state (managed objects).
+                    _sender.RemoveFromEventLifeTracker(this);
                 }
                 if (_handle != IntPtr.Zero)
                 {
index c740e1d..f6403dd 100644 (file)
@@ -431,7 +431,7 @@ namespace ElmSharp
         public bool Load(string file)
         {
             if (file == null)
-                throw new ArgumentNullException("file");
+                throw new ArgumentNullException(nameof(file));
 
             Interop.Elementary.elm_image_async_open_set(RealHandle, false);
             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true);
@@ -447,7 +447,7 @@ namespace ElmSharp
         public bool Load(Uri uri)
         {
             if (uri == null)
-                throw new ArgumentNullException("uri");
+                throw new ArgumentNullException(nameof(uri));
 
             return Load(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri);
         }
@@ -468,7 +468,7 @@ namespace ElmSharp
         public unsafe bool Load(byte* img, long size)
         {
             if (img == null)
-                throw new ArgumentNullException("img");
+                throw new ArgumentNullException(nameof(img));
 
             Interop.Elementary.elm_image_async_open_set(RealHandle, false);
             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true);
@@ -484,7 +484,7 @@ namespace ElmSharp
         public bool Load(Stream stream)
         {
             if (stream == null)
-                throw new ArgumentNullException("stream");
+                throw new ArgumentNullException(nameof(stream));
 
             Interop.Elementary.elm_image_async_open_set(RealHandle, false);
             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, true);
@@ -507,10 +507,10 @@ namespace ElmSharp
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>(true = success, false = error)</returns>
         /// <since_tizen> preview </since_tizen>
-        public Task<bool> LoadAsync(string file, CancellationToken cancellationToken = default(CancellationToken))
+        public async Task<bool> LoadAsync(string file, CancellationToken cancellationToken = default(CancellationToken))
         {
             if (file == null)
-                throw new ArgumentNullException("file");
+                throw new ArgumentNullException(nameof(file));
 
             Interop.Elementary.elm_image_async_open_set(RealHandle, true);
             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, false);
@@ -528,7 +528,6 @@ namespace ElmSharp
             SmartEvent loadReady = new SmartEvent(this, RealHandle, "load,ready");
             loadReady.On += (s, e) =>
             {
-                loadReady.Dispose();
                 LoadingCompleted?.Invoke(this, EventArgs.Empty);
                 if (tcs != null && !tcs.Task.IsCompleted)
                 {
@@ -539,7 +538,6 @@ namespace ElmSharp
             SmartEvent loadError = new SmartEvent(this, RealHandle, "load,error");
             loadError.On += (s, e) =>
             {
-                loadError.Dispose();
                 LoadingFailed?.Invoke(this, EventArgs.Empty);
                 if (tcs != null && !tcs.Task.IsCompleted)
                 {
@@ -547,13 +545,17 @@ namespace ElmSharp
                 }
             };
 
-            bool ret = Interop.Elementary.elm_image_file_set(RealHandle, file, null);
-            if (!ret)
+            using (loadReady)
+            using (loadError)
             {
-                throw new InvalidOperationException("Failed to set file to Image");
+                bool ret = Interop.Elementary.elm_image_file_set(RealHandle, file, null);
+                if (!ret)
+                {
+                    throw new InvalidOperationException("Failed to set file to Image");
+                }
+                // it should be return on main thread, because SmartEvent should be disposed on main thread
+                return await tcs.Task.ConfigureAwait(true); 
             }
-
-            return tcs.Task;
         }
 
         /// <summary>
@@ -566,7 +568,7 @@ namespace ElmSharp
         public Task<bool> LoadAsync(Uri uri, CancellationToken cancellationToken = default(CancellationToken))
         {
             if (uri == null)
-                throw new ArgumentNullException("uri");
+                throw new ArgumentNullException(nameof(uri));
 
             return LoadAsync(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri, cancellationToken);
         }
@@ -581,7 +583,7 @@ namespace ElmSharp
         public async Task<bool> LoadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
         {
             if (stream == null)
-                throw new ArgumentNullException("stream");
+                throw new ArgumentNullException(nameof(stream));
 
             Interop.Elementary.elm_image_async_open_set(RealHandle, true);
             Interop.Elementary.elm_image_preload_disabled_set(RealHandle, false);
@@ -599,7 +601,6 @@ namespace ElmSharp
             SmartEvent loadReady = new SmartEvent(this, RealHandle, "load,ready");
             loadReady.On += (s, e) =>
             {
-                loadReady.Dispose();
                 LoadingCompleted?.Invoke(this, EventArgs.Empty);
                 if (tcs != null && !tcs.Task.IsCompleted)
                 {
@@ -610,7 +611,6 @@ namespace ElmSharp
             SmartEvent loadError = new SmartEvent(this, RealHandle, "load,error");
             loadError.On += (s, e) =>
             {
-                loadError.Dispose();
                 LoadingFailed?.Invoke(this, EventArgs.Empty);
                 if (tcs != null && !tcs.Task.IsCompleted)
                 {
@@ -619,8 +619,10 @@ namespace ElmSharp
             };
 
             using (MemoryStream memstream = new MemoryStream())
+            using (loadReady)
+            using (loadError)
             {
-                await stream.CopyToAsync(memstream);
+                await stream.CopyToAsync(memstream).ConfigureAwait(true);
 
                 unsafe
                 {
@@ -634,9 +636,9 @@ namespace ElmSharp
                         }
                     }
                 }
+                // it should be return on main thread, because SmartEvent should be disposed on main thread
+                return await tcs.Task.ConfigureAwait(true);
             }
-
-            return await tcs.Task;
         }
 
         /// <summary>
index e52aeec..1200e0d 100644 (file)
@@ -171,7 +171,7 @@ namespace ElmSharp
         {
             if (disposing)
             {
-                // Place holder to dispose managed state (managed objects).
+                _sender.RemoveFromEventLifeTracker(this);
             }
             if (_handle != IntPtr.Zero)
             {