[baobab/wip/vala] introduce threaded scanner



commit fa65656fe5fa02c082775ab74ed7588f7bff2451
Author: Ryan Lortie <desrt desrt ca>
Date:   Thu Jan 5 17:54:58 2012 -0500

    introduce threaded scanner

 src/Makefile.am                  |    1 +
 src/baobab-threaded-scanner.vala |  208 ++++++++++++++++++++++++++++++++++++++
 src/baobab-window.vala           |    2 +-
 3 files changed, 210 insertions(+), 1 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 8435101..4fa7d0b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,6 +20,7 @@ baobab_SOURCES = \
 	baobab-chart.c			\
 	baobab-treemap.c		\
 	baobab-ringschart.c		\
+	baobab-threaded-scanner.vala	\
 	baobab-sync-scanner.vala	\
 	baobab-scanner.vala		\
 	baobab-cellrenderers.vala	\
diff --git a/src/baobab-threaded-scanner.vala b/src/baobab-threaded-scanner.vala
new file mode 100644
index 0000000..15fa36f
--- /dev/null
+++ b/src/baobab-threaded-scanner.vala
@@ -0,0 +1,208 @@
+namespace Baobab {
+	class ThreadedScanner : Scanner {
+		Queue<ResultsArray> results_queue;
+		ThreadedScanner? self;
+		Mutex mutex;
+
+		File directory;
+
+		[Compact]
+		class ResultsArray {
+			internal Results[] results;
+		}
+
+		[Compact]
+		class Results {
+			// written in the worker thread on creation
+			// read from the main thread at any time
+			internal unowned Results? parent;
+			internal string display_name;
+			internal string parse_name;
+
+			// written in the worker thread before dispatch
+			// read from the main thread only after dispatch
+			internal uint64 size;
+			internal uint64 alloc_size;
+			internal uint64 elements;
+			internal double percent;
+			internal int max_depth;
+
+			// accessed only by the main thread
+			internal Gtk.TreeIter iter;
+			internal bool iter_is_set;
+		}
+
+		Results? add_directory (File directory, FileInfo info, Results? parent = null) {
+			var results_array = new ResultsArray ();
+
+			if (Application.is_excluded_location (directory)) {
+				return null;
+			}
+
+			var results = new Results ();
+			results.display_name = info.get_display_name ();
+			results.parse_name = directory.get_parse_name ();
+			results.parent = parent;
+
+			if (info.has_attribute (FILE_ATTRIBUTE_STANDARD_SIZE)) {
+				results.size = info.get_size ();
+			}
+
+			if (info.has_attribute (FILE_ATTRIBUTE_UNIX_BLOCKS)) {
+				results.alloc_size = 512 * info.get_attribute_uint64 (FILE_ATTRIBUTE_UNIX_BLOCKS);
+			}
+
+			results.elements = 1;
+
+			try {
+				var children = directory.enumerate_children (ATTRIBUTES, FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
+				FileInfo? child_info;
+				while ((child_info = children.next_file (cancellable)) != null) {
+					if (cancellable.is_cancelled ()) {
+						break;
+					}
+
+					switch (child_info.get_file_type ()) {
+						case FileType.DIRECTORY:
+							var child = directory.get_child (child_info.get_name ());
+							var child_results = add_directory (child, child_info, results);
+
+							if (child_results != null) {
+								results.size += child_results.size;
+								results.alloc_size += child_results.size;
+								results.elements += child_results.elements;
+								results.max_depth = int.max (results.max_depth, child_results.max_depth + 1);
+								results_array.results += (owned) child_results;
+							}
+							break;
+
+						case FileType.REGULAR:
+							if (child_info.has_attribute (FILE_ATTRIBUTE_UNIX_NLINK)) {
+								if (child_info.get_attribute_uint32 (FILE_ATTRIBUTE_UNIX_NLINK) > 1) {
+									var hl = HardLink (child_info);
+
+									/* check if we've already encountered this file */
+									if (hl in hardlinks) {
+										continue;
+									}
+
+									hardlinks += hl;
+								}
+							}
+
+							if (child_info.has_attribute (FILE_ATTRIBUTE_UNIX_BLOCKS)) {
+								results.alloc_size += 512 * child_info.get_attribute_uint64 (FILE_ATTRIBUTE_UNIX_BLOCKS);
+							}
+							results.size += child_info.get_size ();
+							results.elements++;
+							break;
+
+						default:
+							/* ignore other types (symlinks, sockets, devices, etc) */
+							break;
+					}
+				}
+			} catch (IOError.PERMISSION_DENIED e) {
+			} catch (Error e) {
+				warning ("couldn't iterate %s: %s", results.parse_name, e.message);
+			}
+
+			foreach (unowned Results child_results in results_array.results) {
+				child_results.percent = 100 * ((double) child_results.size) / ((double) results.size);
+			}
+
+			mutex.lock ();
+			results_queue.push_tail ((owned) results_array);
+			mutex.unlock ();
+
+			return results;
+		}
+
+		void* scan_in_thread () {
+			try {
+				var array = new ResultsArray ();
+
+				var info = directory.query_info (ATTRIBUTES, 0, cancellable);
+				var results = add_directory (directory, info);
+				results.percent = 100.0;
+				array.results += (owned) results;
+				//max_depth = results.max_depth;
+			} catch { }
+
+			// drop the thread's reference on the Scanner object
+			this.self = null;
+			return null;
+		}
+
+		void ensure_iter_exists (Results results) {
+			Gtk.TreeIter? parent_iter;
+
+			if (results.iter_is_set) {
+				return;
+			}
+
+			if (results.parent != null) {
+				ensure_iter_exists (results.parent);
+				parent_iter = results.parent.iter;
+			} else {
+				parent_iter = null;
+			}
+
+			append (out results.iter, parent_iter);
+			set (results.iter,
+			     Columns.STATE,        State.SCANNING,
+			     Columns.DISPLAY_NAME, results.display_name,
+			     Columns.PARSE_NAME,   results.parse_name);
+			results.iter_is_set = true;
+		}
+
+		bool process_results () {
+			while (true) {
+				mutex.lock ();
+				var results_array = results_queue.pop_head ();
+				mutex.unlock ();
+
+				if (results_array == null) {
+					break;
+				}
+
+				foreach (unowned Results results in results_array.results) {
+					ensure_iter_exists (results);
+
+					set (results.iter,
+					     Columns.SIZE,       results.size,
+					     Columns.ALLOC_SIZE, results.alloc_size,
+					     Columns.PERCENT,    results.percent,
+					     Columns.ELEMENTS,   results.elements,
+					     Columns.STATE,      State.DONE);
+
+					if (results.max_depth > max_depth) {
+						max_depth = results.max_depth;
+					}
+				}
+
+			}
+
+			return this.self != null;
+		}
+
+		protected override void scan (File directory) {
+			this.directory = directory;
+
+			// the thread owns a reference on the Scanner object
+			this.self = this;
+
+			try {
+				Thread.create<void*> (scan_in_thread, false);
+			} catch {
+			}
+
+			Timeout.add (100, process_results);
+		}
+
+		public ThreadedScanner () {
+			results_queue = new Queue<ResultsArray> ();
+			mutex = new Mutex ();
+		}
+	}
+}
diff --git a/src/baobab-window.vala b/src/baobab-window.vala
index 475df0f..135a1f0 100644
--- a/src/baobab-window.vala
+++ b/src/baobab-window.vala
@@ -67,7 +67,7 @@ namespace Baobab {
 				return;
 			}
 
-			var scanner = new SyncScanner ();
+			var scanner = new ThreadedScanner ();
 			scanner.scan (directory);
 			model = scanner;
 			var rings_chart = builder.get_object ("rings-chart") as Chart;



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