[f-spot: 12/40] Correctly Close and Dispose loaders and stream



commit d7ed9a96d4eea82a9f6836bea83d07675d569c69
Author: Stephane Delcroix <stephane delcroix org>
Date:   Sun Jun 21 13:50:58 2009 +0200

    Correctly Close and Dispose loaders and stream
    
    * ImageLoader.cs: Drop all the old code from AsyncPixbufLoader, override Dispose() so we know when it's disposed, Close the loader on Dispose ().
    
    * PhotoImageView.cs: keep a ref to the current loader, move all the loading stuffs together in a #region, don't process the delegates if they're coming from an event from a previous loader.

 src/ImageLoader.cs    |  457 +++++--------------------------------------------
 src/PhotoImageView.cs |   55 ++++--
 2 files changed, 77 insertions(+), 435 deletions(-)
---
diff --git a/src/ImageLoader.cs b/src/ImageLoader.cs
index 92cd72f..81caf82 100644
--- a/src/ImageLoader.cs
+++ b/src/ImageLoader.cs
@@ -1,11 +1,12 @@
 //
 // Fspot.ImageLoader.cs
 //
+// Copyright (c) 2009 Novell, Inc.
+//
 // Author(s)
-//	Larry Ewing  <lewing novell com>
 //	Stephane Delcroix  <sdelcroix novell com>
 //
-// This is free softwae. See COPYING fro details
+// This is free software. See COPYING for details
 //
 
 using Gtk;
@@ -52,6 +53,9 @@ namespace FSpot {
 
 		public void Load (Uri uri)
 		{
+			if (is_disposed)
+				return;
+
 			using (ImageFile image_file = ImageFile.Create (uri)) {
 				image_stream = image_file.PixbufStream ();
 				pixbuf_orientation = image_file.Orientation;
@@ -78,11 +82,28 @@ namespace FSpot {
 		public PixbufOrientation PixbufOrientation {
 			get { return pixbuf_orientation; }
 		}
+
+		bool is_disposed = false;
+		public override void Dispose ()
+		{
+			is_disposed = true;
+			image_stream.Close ();
+			try {
+				Close ();
+			} catch (GLib.GException) {
+				//it's normal to get an exception here if we're closing in the early loading stages, and it's safe to ignore
+				// that exception as we don't want the loading to finish but want to cancel it.
+			}
+			base.Dispose ();
+		}
 #endregion
 
 #region event handlers
 		protected override void OnAreaPrepared ()
 		{
+			if (is_disposed)
+				return;
+
 			base.OnAreaPrepared ();
 			prepared = true;
 			EventHandler<AreaPreparedEventArgs> eh = AreaPrepared;
@@ -92,6 +113,9 @@ namespace FSpot {
 
 		protected override void OnAreaUpdated (int x, int y, int width, int height)
 		{
+			if (is_disposed)
+				return;
+
 			base.OnAreaUpdated (x, y, width, height);
 			EventHandler<AreaUpdatedEventArgs> eh = AreaUpdated;
 			if (eh != null)
@@ -100,6 +124,9 @@ namespace FSpot {
 
 		protected virtual void OnCompleted ()
 		{
+			if (is_disposed)
+				return;
+
 			loading = false;
 			EventHandler eh = Completed;
 			if (eh != null)
@@ -110,12 +137,15 @@ namespace FSpot {
 
 #region private stuffs
 		System.IO.Stream image_stream;
-		const int count = 1 << 16;
+		const int count = 1 << 16; //64k
 
 		byte [] buffer = new byte [count];
 
 		void HandleReadDone (IAsyncResult ar)
 		{
+			if (is_disposed)
+				return;
+
 			int byte_read = image_stream.EndRead (ar);
 			if (byte_read == 0) {
 				image_stream.Close ();
@@ -124,421 +154,14 @@ namespace FSpot {
 			}
 
 			Gtk.Application.Invoke (this, null, delegate (object sender, EventArgs e) {
-				if (Write (buffer, (ulong)byte_read)) {
-					image_stream.BeginRead (buffer, 0, count, HandleReadDone, null);
-				} else
-					OnWriteError ();
+				try {
+					if (!is_disposed && Write (buffer, (ulong)byte_read))
+						image_stream.BeginRead (buffer, 0, count, HandleReadDone, null);
+				} catch (System.ObjectDisposedException od) {
+				} catch (GLib.GException ge) {
+				}
 			});
 		}
-
-		void OnWriteError ()
-		{
-			Console.WriteLine ("error while feeding the pixbufloader!");
-		}
 #endregion
-//
-//		StreamWrapper stream;
-//		Gdk.PixbufLoader loader;		
-//		Uri uri;
-//		bool area_prepared = false;
-//		bool done_reading = false;
-//		Gdk.Pixbuf pixbuf;
-//		Gdk.Pixbuf thumb;
-//		PixbufOrientation orientation;
-//
-//		private Gdk.AreaUpdatedHandler au;
-//		private System.EventHandler ap;
-//		private System.EventHandler ev;
-//
-//		byte [] buffer = new byte [1 << 16];
-//
-//		public event EventHandler<AreaUpdatedEventArgs> AreaUpdated;
-//		public event EventHandler<AreaPreparedEventArgs> AreaPrepared;
-//		public event System.EventHandler Done;
-//
-//		// If the photo we just loaded has an out of date
-//		// thumbnail save a new one
-//		bool validate_thumbnail = true;
-//
-//		// Limit pass control back to the main loop after
-//		// chunk_timeout miliseconds.
-//		int  chunk_timeout = 75;
-//
-//		Delay delay;
-//		IAsyncResult result;
-//
-//		Gdk.Rectangle damage;
-//
-//		public AsyncPixbufLoader ()
-//		{
-//			delay = new Delay (0, new GLib.IdleHandler (AsyncRead));
-//			ap = new System.EventHandler (HandleAreaPrepared);
-//			au = new Gdk.AreaUpdatedHandler (HandleAreaUpdated);
-//			ev = new System.EventHandler (HandleClosed);
-//		}
-//		
-//		public bool Loading
-//		{
-//			get { return ! done_reading; }
-//		}
-//
-//		public bool Prepared
-//		{
-//			get { return area_prepared; }
-//		}
-//
-//		public Gdk.Pixbuf Pixbuf {
-//			get { return pixbuf; }
-//		}
-//
-//		public Gdk.PixbufLoader Loader {
-//			get { return loader; }
-//		}
-//		
-//		private void FileLoad (ImageFile img)
-//		{
-//			pixbuf = img.Load ();
-//			done_reading = true;
-//			if (Done != null)
-//				Done (this, System.EventArgs.Empty);
-//		}
-//
-//		public void Load (Uri uri)
-//		{
-//			this.uri = uri;
-//
-//			delay.Stop ();
-//
-//			if (!done_reading)
-//				Close ();
-//
-//			done_reading = false;
-//			area_prepared = false;
-//			damage = Gdk.Rectangle.Zero;
-//
-//			using (ImageFile img = ImageFile.Create (uri)) {
-//				orientation = Accelerometer.GetViewOrientation (img.Orientation);
-//			
-//				try {
-//					PixbufOrientation thumb_orientation = Accelerometer.GetViewOrientation (PixbufOrientation.TopLeft);
-//					thumb = ThumbnailFactory.LoadThumbnail (uri);
-//					thumb = PixbufUtils.TransformOrientation (thumb, thumb_orientation);
-//					
-//					if (FSpot.ColorManagement.IsEnabled && !thumb.HasAlpha) {
-//						if (img.GetProfile () == null)
-//							FSpot.ColorManagement.PhotoImageView.Transform = FSpot.ColorManagement.StandardTransform ();
-//						else
-//							FSpot.ColorManagement.PhotoImageView.Transform = FSpot.ColorManagement.CreateTransform (thumb, img.GetProfile ());
-//					}
-//					else
-//						FSpot.ColorManagement.PhotoImageView.Transform = null;
-//				} catch (System.Exception e) {
-//					if (!(e is GLib.GException)) 
-//						Log.DebugException (e);
-//				}
-//
-//				System.IO.Stream nstream = img.PixbufStream ();
-//				if (nstream == null) {
-//					FileLoad (img);
-//					return;
-//				} else
-//					stream = new StreamWrapper (nstream);
-//
-//				loader = new Gdk.PixbufLoader ();
-//				loader.AreaPrepared += ap;
-//				loader.AreaUpdated += au;
-//				loader.Closed += ev;
-//
-//				if (AreaPrepared != null && thumb != null) {
-//					pixbuf = thumb;
-//					AreaPrepared (this, new AreaPreparedEventArgs (true));
-//				}
-//
-//				ThumbnailGenerator.Default.PushBlock ();
-//				//AsyncIORead (null);
-//				if (nstream is IOChannel) {
-//					((IOChannel)nstream).DataReady += IOChannelRead;
-//				} else
-//					delay.Start ();
-//			}
-//
-//		}			
-//
-//		private void LoadToAreaPrepared ()
-//		{
-//			while (!area_prepared && delay.IsPending)
-//				Application.RunIteration ();
-//		}
-//
-//		public void LoadToDone ()
-//		{
-//			while (!done_reading && delay.IsPending)
-//				Application.RunIteration ();
-//		}
-//
-//		private void Close () {
-//			ThumbnailGenerator.Default.PopBlock ();
-//				
-//			try {
-//				result = null;
-//
-//				delay.Stop ();
-//				if (loader != null) { 
-//					loader.AreaPrepared -= ap;
-//					loader.AreaUpdated -= au;
-//					// this can throw exceptions
-//					loader.Close ();
-//				}
-//			} catch (System.Exception) {
-//				//System.Console.WriteLine (e.ToString ());
-//				if (pixbuf != null)
-//					pixbuf.Dispose ();
-//
-//				pixbuf = null;
-//			} finally {
-//				if (loader != null) {
-//					loader.Closed -= ev;
-//					loader.Dispose ();
-//				}
-//
-//				loader = null;
-//
-//				if (stream != null) 
-//					stream.Close ();
-//
-//				stream = null;
-//			}
-//		}
-//
-//		private void UpdateListeners ()
-//		{
-//			Gdk.Rectangle area = damage;
-//			
-//			if (pixbuf != null && loader.Pixbuf != null && loader.Pixbuf != pixbuf && damage != Gdk.Rectangle.Zero)
-//				area = PixbufUtils.TransformAndCopy (loader.Pixbuf, pixbuf, orientation, damage);
-//			
-//			if (area.Width != 0 && area.Height != 0 && AreaUpdated != null)
-//				AreaUpdated (this, new AreaUpdatedEventArgs (area));
-//
-//			//System.Console.WriteLine ("orig {0} tform {1}", damage.ToString (), area.ToString ());
-//			damage = Gdk.Rectangle.Zero;
-//		}
-//
-//		private class StreamWrapper {
-//			private delegate int ReadDelegate (byte [] buffer, int offset, int count);
-//			System.IO.Stream stream;
-//
-//			public System.IO.Stream Stream {
-//				get { return stream; }
-//			}
-//
-//			public StreamWrapper (System.IO.Stream stream)
-//			{
-//				this.stream = stream;
-//			}
-//
-//			public int Read (byte[] buffer, int offset, int count)
-//			{
-//				return stream.Read (buffer, offset, count);
-//			}
-//
-//			public IAsyncResult BeginRead (byte [] buffer, int offset, int count, AsyncCallback cb, object state)
-//			{
-//				ReadDelegate del = new ReadDelegate (Read);
-//				return del.BeginInvoke (buffer, offset, count, cb, state);
-//			}
-//			
-//			public int EndRead (IAsyncResult result)
-//			{
-//				AsyncResult art = result as AsyncResult;
-//				ReadDelegate del = art.AsyncDelegate as ReadDelegate;
-//				int i = del.EndInvoke (result);
-//				return i;
-//			}
-//			
-//			public void Close ()
-//			{
-//				stream.Close ();
-//			}
-//		}
-//
-//		private void IOChannelRead (object sender, DataReadEventArgs args)
-//		{
-//			//Console.WriteLine ("IO read {0}", args.Condition);
-//
-//			if ( (System.IO.Stream)sender == stream.Stream)				
-//				args.Continue = AsyncRead ();
-//			else {
-//				args.Continue = false;
-//				stream.Close ();
-//			}
-//
-//			return;
-//		}
-//
-//		private bool AsyncIORead ()
-//		{
-//			try {
-//				if (result == null) {
-//					//System.Console.WriteLine ("start read");
-//					result = stream.BeginRead (buffer, 0, buffer.Length, AsyncIOEnd, stream);
-//				} else {
-//					//Console.WriteLine ("not done");
-//					UpdateListeners ();
-//				}
-//			} catch (System.Exception e) {
-//				System.Console.WriteLine ("In read got {0}", e);
-//			}
-//			
-//			if (done_reading)
-//				delay.Stop ();
-//
-//			return ! done_reading;
-//		}
-//
-//		private void AsyncIOEnd (IAsyncResult iar)
-//		{
-//			//System.Console.WriteLine ("ioended");
-//			if (stream == (StreamWrapper)iar.AsyncState)
-//				Gtk.Application.Invoke (ReadDone);
-//		}
-//		
-//		public void ReadDone (object sender, System.EventArgs args)
-//		{
-//			if (result == null)
-//				return;
-//
-//			int len = 0;
-//			try {
-//				len = stream.EndRead (result);
-//				//System.Console.WriteLine ("read {0} bytes", len);
-//				loader.Write (buffer, (ulong)len);
-//			} catch (System.ObjectDisposedException od) {
-//				System.Console.WriteLine ("error in endread {0}", od);
-//				//delay.Start ();
-//				len = -1;
-//			} catch (GLib.GException e) {
-//				System.Console.WriteLine (e.ToString ());
-//				pixbuf = null;
-//				len = -1;
-//			}
-//			result = null;
-//
-//			if (len <= 0) {
-//				if (loader.Pixbuf == null) {
-//					if (pixbuf != null)
-//						pixbuf.Dispose ();
-//					
-//					pixbuf = null;
-//				}
-//				
-//				UpdateListeners ();
-//				done_reading = true;
-//				Close ();
-//				return;
-//			}
-//		}
-//	
-//		private bool AsyncRead () 
-//		{
-//#if false
-//			return AsyncIORead ();
-//#else	
-//			return NormalRead ();
-//#endif		
-//		}
-//
-//		private bool NormalRead ()
-//		{
-//			System.DateTime start_time = System.DateTime.Now;
-//			System.TimeSpan span = start_time - start_time;
-//
-//			do {
-//				span = System.DateTime.Now - start_time;
-//
-//				int len;
-//				try {
-//					len = stream.Read (buffer, 0, buffer.Length);
-//					loader.Write (buffer, (ulong)len);
-//				} catch (Exception) {
-//					len = -1;
-//				}
-//
-//				if (len <= 0) {
-//					if (loader.Pixbuf == null) {
-//						if (pixbuf != null)
-//							pixbuf.Dispose ();
-//
-//						pixbuf = null;
-//					}
-//
-//					UpdateListeners ();
-//					done_reading = true;
-//					Close ();
-//					return false;
-//				}
-//			} while (!done_reading && span.TotalMilliseconds <= chunk_timeout);
-//
-//			UpdateListeners ();
-//			return true;
-//		}
-//		
-//		private void HandleAreaPrepared (object sender, System.EventArgs args)
-//		{
-//			pixbuf = PixbufUtils.TransformOrientation (loader.Pixbuf, orientation, false);
-//
-//			if (thumb != null && pixbuf != null)
-//				thumb.Composite (pixbuf, 0, 0,
-//						 pixbuf.Width, pixbuf.Height,
-//						 0.0, 0.0,
-//						 pixbuf.Width/(double)thumb.Width, pixbuf.Height/(double)thumb.Height,
-//						 Gdk.InterpType.Bilinear, 0xff);
-//			
-//			if (thumb != null)
-//				if (!ThumbnailFactory.ThumbnailIsValid (thumb, uri))
-//					FSpot.ThumbnailGenerator.Default.Request (uri, 0, 256, 256);
-//
-//			area_prepared = true;			
-//			if (AreaUpdated != null)
-//				AreaPrepared (this, new AreaPreparedEventArgs (false));
-//
-//			if (thumb != null)
-//				thumb.Dispose ();
-//			thumb = null;
-//		}
-//
-//		private void HandleAreaUpdated (object sender, Gdk.AreaUpdatedArgs args)
-//		{
-//			Gdk.Rectangle area = new Gdk.Rectangle (args.X, args.Y, args.Width, args.Height);
-//			
-//			if (damage.Width == 0 || damage.Height == 0)
-//				damage = area;
-//			else 
-//				damage = area.Union (damage);
-//		}
-//
-//		private void HandleClosed (object sender, System.EventArgs args) 
-//		{
-//			// FIXME This should probably queue the
-//			// thumbnail regeneration to a worker thread
-//			if (validate_thumbnail && done_reading && pixbuf != null) {
-//				PhotoLoader.ValidateThumbnail (uri, pixbuf);
-//			}
-//
-//			if (Done != null)
-//				Done (this, System.EventArgs.Empty);
-//			
-//			ThumbnailGenerator.Default.PopBlock ();
-//		}
-//
-//		public void Dispose ()
-//		{
-//			Close ();
-//
-//			if (pixbuf != null)
-//				pixbuf.Dispose ();
-//		}
 	}
-}
-
-	       
+}	       
diff --git a/src/PhotoImageView.cs b/src/PhotoImageView.cs
index 989e109..697790a 100644
--- a/src/PhotoImageView.cs
+++ b/src/PhotoImageView.cs
@@ -115,20 +115,27 @@ namespace FSpot.Widgets {
 				Glx.Destroy ();
 		}
 
-		// Display.
-		private void HandlePixbufAreaUpdated (object sender, AreaUpdatedEventArgs args)
-		{
-			if (!ShowProgress)
-				return;
+#region loader		
+		ImageLoader loader;
 
-			Gdk.Rectangle area = this.ImageCoordsToWindow (args.Area);
-			this.QueueDrawArea (area.X, area.Y, area.Width, area.Height);
+		void Load (Uri uri)
+		{
+			if (loader != null)
+				loader.Dispose ();
+
+			loader = new ImageLoader ();
+			loader.AreaPrepared += HandlePixbufPrepared;
+			loader.AreaUpdated += HandlePixbufAreaUpdated;
+			loader.Completed += HandleDone;
+			loader.Load (uri);
 		}
-		
 
-		private void HandlePixbufPrepared (object sender, AreaPreparedEventArgs args)
+		void HandlePixbufPrepared (object sender, AreaPreparedEventArgs args)
 		{
 			ImageLoader loader = sender as ImageLoader;
+			if (loader != this.loader)
+				return;
+
 			if (!ShowProgress)
 				return;
 
@@ -141,9 +148,24 @@ namespace FSpot.Widgets {
 			this.ZoomFit (args.ReducedResolution);
 		}
 
-		private void HandleDone (object sender, System.EventArgs args)
+		void HandlePixbufAreaUpdated (object sender, AreaUpdatedEventArgs args)
+		{
+			if (loader != this.loader)
+				return;
+
+			if (!ShowProgress)
+				return;
+
+			Gdk.Rectangle area = this.ImageCoordsToWindow (args.Area);
+			this.QueueDrawArea (area.X, area.Y, area.Width, area.Height);
+		}
+
+		void HandleDone (object sender, System.EventArgs args)
 		{
 			ImageLoader loader = sender as ImageLoader;
+			if (loader != this.loader)
+				return;
+
 			// FIXME the error hander here needs to provide proper information and we should
 			// pass the state and the write exception in the args
 			Gdk.Pixbuf prev = this.Pixbuf;
@@ -189,11 +211,10 @@ namespace FSpot.Widgets {
 			if (prev != this.Pixbuf && prev != null)
 				prev.Dispose ();
 		}
+#endregion
 		
 		private bool ShowProgress {
-			get {
-				return !(load_async != ProgressType.Full || !progressive_display);
-			}
+			get { return !(load_async != ProgressType.Full || !progressive_display); }
 		}
 	
 		// Zoom scaled between 0.0 and 1.0
@@ -253,11 +274,7 @@ namespace FSpot.Widgets {
 				try {
 					if (Item.IsValid) {
 						System.Uri uri = Item.Current.DefaultVersionUri;
-						ImageLoader loader = new ImageLoader ();
-						loader.AreaPrepared += HandlePixbufPrepared;
-						loader.AreaUpdated += HandlePixbufAreaUpdated;
-						loader.Completed += HandleDone;
-						loader.Load (uri);
+						Load (uri);
 					} else
 						LoadErrorImage (null);
 
@@ -395,6 +412,8 @@ namespace FSpot.Widgets {
 			//loader.AreaUpdated -= HandlePixbufAreaUpdated;
 			//loader.AreaPrepared -= HandlePixbufPrepared;
 			//loader.Dispose ();
+			if (loader != null)
+				loader.Dispose ();
 		}
 	}
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]