Index: libgnomevfs/gnome-vfs-thread-pool.c
===================================================================
--- libgnomevfs/gnome-vfs-thread-pool.c	(revision 424)
+++ libgnomevfs/gnome-vfs-thread-pool.c	(working copy)
@@ -32,7 +32,7 @@
 
 #define GNOME_VFS_THREAD_STACK_SIZE 256*1024
 
-#if 0
+#if 1
 #define DEBUG_PRINT(x) g_print x
 #else
 #define DEBUG_PRINT(x)
@@ -45,6 +45,8 @@ typedef struct {
 	
 	void *(* entry_point) (void *);
 	void *entry_data;
+
+	guint idle_timeout_id;
 	
 	volatile gboolean exit_requested;
 } GnomeVFSThreadState;
@@ -52,6 +54,8 @@ typedef struct {
 static GStaticMutex thread_list_lock = G_STATIC_MUTEX_INIT;
 
 static const int MAX_AVAILABLE_THREADS = 20; 
+static const int MAX_THREAD_IDLE_TIME = 15;  /* seconds */
+
 static GList *available_threads;
 static int thread_count;
 
@@ -99,10 +103,46 @@ destroy_thread_state (GnomeVFSThreadStat
 {
 	g_mutex_free (state->waiting_for_work_lock);
 	g_cond_free (state->waiting_for_work_lock_condition);
+
+	if (state->idle_timeout_id != 0) {
+		g_source_remove (state->idle_timeout_id);
+		state->idle_timeout_id = 0;
+	}
+
 	g_free (state);
 }
 
 static gboolean
+exit_thread_timeout (GnomeVFSThreadState *state)
+{
+	/* Remove thread from list. */
+	g_static_mutex_lock (&thread_list_lock);
+
+	available_threads = g_list_remove (available_threads, state);
+	thread_count--;
+
+	DEBUG_PRINT (("exiting thread %p, removing from the pool, %d threads left\n",
+		      state->thread, thread_count));
+	
+	g_static_mutex_unlock (&thread_list_lock);
+
+	g_mutex_lock (state->waiting_for_work_lock);
+
+	DEBUG_PRINT (("exiting thread %p, timeout reached and no pending jobs\n",
+		      state->thread));
+
+	state->idle_timeout_id = 0;
+
+	/* Tell the thread to expire. */
+	state->exit_requested = TRUE;
+	g_cond_signal (state->waiting_for_work_lock_condition);
+
+	g_mutex_unlock (state->waiting_for_work_lock);
+
+	return FALSE;
+}
+
+static gboolean
 make_thread_available (GnomeVFSThreadState *state)
 {
 	/* thread is done with it's work, add it to the available pool */
@@ -124,8 +164,22 @@ make_thread_available (GnomeVFSThreadSta
 		available_threads = g_list_prepend (available_threads, state);
 		thread_count++;
 		delete_thread = FALSE;
-		DEBUG_PRINT (("adding thread %p the pool, %d threads\n",
-			      state->thread, thread_count));
+		DEBUG_PRINT (("adding thread %p the pool, %d threads (job limit:%d, max threads:%d)\n",
+			      state->thread, thread_count, job_limit, MAX_AVAILABLE_THREADS));
+
+		/* add timeout if there is no work we exit the thread */
+		g_mutex_lock (state->waiting_for_work_lock);
+		if (state->idle_timeout_id != 0) {
+			g_source_remove (state->idle_timeout_id);
+		}
+
+		state->idle_timeout_id = g_timeout_add (MAX_THREAD_IDLE_TIME * 1000,
+							(GSourceFunc)exit_thread_timeout,
+							state);
+		g_mutex_unlock (state->waiting_for_work_lock);
+
+		DEBUG_PRINT (("adding thread %p a timeout to exit thread after %d seconds if no jobs\n",
+			      state->thread, MAX_THREAD_IDLE_TIME));
 	}
 
 	g_static_mutex_unlock (&thread_list_lock);
Index: libgnomevfs/gnome-vfs-job-queue.c
===================================================================
--- libgnomevfs/gnome-vfs-job-queue.c	(revision 424)
+++ libgnomevfs/gnome-vfs-job-queue.c	(working copy)
@@ -29,7 +29,7 @@
 #include <glib/gtree.h>
 #include <unistd.h>
 
-#undef QUEUE_DEBUG
+#define QUEUE_DEBUG
 
 #ifdef QUEUE_DEBUG
 #define Q_DEBUG(x) g_print x
@@ -40,7 +40,7 @@
 /* See the comment at job_can_start () for 
    an explanation of the following macros */
 #ifndef DEFAULT_THREAD_COUNT_LIMIT
-#define DEFAULT_THREAD_COUNT_LIMIT 10
+#define DEFAULT_THREAD_COUNT_LIMIT 2
 #endif
 
 #define LIMIT_FUNCTION_LOWER_BOUND 2 /* must NOT be more than DEFAULT_THREAD_COUNT_LIMIT */
Index: programs/gnomevfs-async.c
===================================================================
--- programs/gnomevfs-async.c	(revision 0)
+++ programs/gnomevfs-async.c	(revision 0)
@@ -0,0 +1,249 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gnomevfs-monitor.c - Test for file/directory monitoring in gnome-vfs
+
+   Copyright (C) 2005 Martyn Russell 
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Martyn Russell <martyn@imendio.com>
+*/
+
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+#define d(x)
+
+#define START_WAIT_TIME             5
+#define CONTINUE_WAIT_TIME          20
+#define MAX_THREADS                 30 /* default is 10, min is 2, max is 20 */
+#define MAX_ITEMS_PER_NOTIFICATION -1
+
+
+static gboolean test_start              (gpointer             user_data);
+static gboolean test_continue           (gpointer             user_data);
+static void     directory_load_callback (GnomeVFSAsyncHandle *handle,
+					 GnomeVFSResult       result,
+					 GList               *list,
+					 guint                entries_read,
+					 const gchar         *text_uri);
+
+
+static GMainLoop   *mainloop = NULL;
+
+static gint         read_entry_count = 0;
+static gint         uri_count = 0;
+
+static gboolean     set_max_threads = FALSE;
+static gboolean     quit_when_finished = FALSE;
+
+static GTimer      *timer = NULL;
+
+static const gchar *text_uris[] = {
+	"/dev",
+	"/etc",
+	"/usr/share",
+	"/usr/lib",
+	"/usr/bin",
+	"/usr/include",
+	"/lib",
+	"/",
+	"/proc",
+	"/bin",
+	"/tmp",
+	"/media/Music",
+	"/home/martyn",
+	"/usr/local",
+	"/home/martyn/Ibuild/nokia-2006/build",
+	"/home/martyn/Ibuild/nokia-2006/build/etc",
+	"/home/martyn/Ibuild/nokia-2006/build/lib",
+	"/home/martyn/Ibuild/nokia-2006/build/bin",
+	"/home/martyn/Ibuild/nokia-2006/build/include",
+	"/home/martyn/Ibuild/nokia-2006/build/share",
+	NULL
+};
+
+
+static void
+directory_load_callback (GnomeVFSAsyncHandle *handle,
+			 GnomeVFSResult       result,
+			 GList               *list,
+			 guint                entries_read,
+			 const gchar         *text_uri)
+{
+	static gboolean  first_stage = TRUE;
+
+ 	GnomeVFSURI     *parent_uri;
+	GList           *node;
+	guint            i;
+	gboolean         finished = FALSE;
+
+	d(g_print ("Directory:'%s' (%s), %d entries - ",
+		   text_uri,
+		   gnome_vfs_result_to_string (result),
+		   entries_read));
+
+	parent_uri = gnome_vfs_uri_new (text_uri);
+	for (i = 0, node = list; i < entries_read && node != NULL; i++, node = node->next) {
+		GnomeVFSFileInfo *info;
+
+		info = node->data;
+
+		d(g_print ("File '%s'%s (%s), "
+			   "size %"GNOME_VFS_SIZE_FORMAT_STR", mode %04o\n",
+			   info->name,
+			   (info->flags & GNOME_VFS_FILE_FLAGS_SYMLINK) ? " [link]" : "",
+			   gnome_vfs_file_info_get_mime_type (info),
+			   info->size, 
+			   info->permissions));
+	}
+	
+	read_entry_count += entries_read;
+
+	gnome_vfs_uri_unref (parent_uri);
+
+	finished |= (MAX_ITEMS_PER_NOTIFICATION < 1);
+	finished |= (entries_read < MAX_ITEMS_PER_NOTIFICATION);
+
+	if (finished == FALSE) {
+		return;
+	}
+
+	if (--uri_count > 0) {
+		return;
+	}
+
+	d(g_print ("\n\nFinished!\n"));
+
+	g_timer_stop (timer);
+		
+	if (quit_when_finished && !first_stage) {
+		g_main_loop_quit (mainloop);  
+	}
+
+	if (first_stage) {
+		g_timeout_add (1000, test_continue, NULL);
+		first_stage = FALSE;
+	}
+}
+
+static gboolean
+test_start (gpointer user_data)
+{
+ 	GnomeVFSAsyncHandle *handle; 
+	gint                 i;
+
+	static guint         seconds_left = START_WAIT_TIME;
+
+	g_print ("Starting async operations test in %d seconds...\n", seconds_left--); 
+
+	if (seconds_left > 0) {
+		return TRUE;
+	}
+
+	g_timer_start (timer);
+
+	for (i = 0; text_uris[i]; i++, uri_count++) {
+		gnome_vfs_async_load_directory (&handle,
+						text_uris[i],
+						GNOME_VFS_FILE_INFO_GET_MIME_TYPE | 
+						GNOME_VFS_FILE_INFO_FOLLOW_LINKS,
+						MAX_ITEMS_PER_NOTIFICATION,
+						0,
+						(GnomeVFSAsyncDirectoryLoadCallback)directory_load_callback,
+						(gchar*)text_uris[i]);
+	}
+	
+	return FALSE;
+}
+
+static gboolean
+test_continue (gpointer user_data)
+{
+ 	GnomeVFSAsyncHandle *handle; 
+
+	static guint         seconds_left = CONTINUE_WAIT_TIME;
+
+	g_print ("Continuing async operations test in %d seconds...\n", seconds_left--); 
+
+	if (seconds_left > 0) {
+		return TRUE;
+	}
+
+	g_timer_continue (timer);
+
+	uri_count++;
+	gnome_vfs_async_load_directory (&handle,
+					text_uris[0],
+					GNOME_VFS_FILE_INFO_GET_MIME_TYPE | 
+					GNOME_VFS_FILE_INFO_FOLLOW_LINKS,
+					MAX_ITEMS_PER_NOTIFICATION,
+					0,
+					(GnomeVFSAsyncDirectoryLoadCallback)directory_load_callback,
+					(gchar*)text_uris[0]);
+	
+	return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+	gdouble secs;
+	gulong  ms;
+	
+	if (!gnome_vfs_init ()) {
+		fprintf (stderr, "Cannot initialize gnome-vfs.\n"); 
+		return EXIT_FAILURE;
+	}
+
+	if (set_max_threads) {
+		g_print ("Using %d maximum thread(s)\n", MAX_THREADS);
+		gnome_vfs_async_set_job_limit (MAX_THREADS);
+	}
+
+	g_print ("Using %d item(s) per notification\n", MAX_ITEMS_PER_NOTIFICATION);
+
+	if (quit_when_finished) {
+		g_print ("Note: The application will quit after the tests are finished\n");
+	} else {
+		g_print ("Note: The application will *NOT* quit after the tests are finished\n");
+	}
+
+	timer = g_timer_new ();
+
+	mainloop = g_main_loop_new (NULL, FALSE);
+
+	g_timeout_add (1000, test_start, NULL);
+
+	g_main_loop_run (mainloop);
+
+	g_timer_stop (timer);
+
+ 	gnome_vfs_shutdown (); 
+
+	secs = g_timer_elapsed (timer, &ms);
+	
+	g_print ("Traversed %d entries in %4.4f\n", 
+		 read_entry_count, secs);
+
+	g_timer_destroy (timer);
+
+	return EXIT_SUCCESS;
+}
+	
Index: programs/Makefile.am
===================================================================
--- programs/Makefile.am	(revision 424)
+++ programs/Makefile.am	(working copy)
@@ -22,6 +22,7 @@ bin_PROGRAMS =						\
 	gnomevfs-mkdir					\
 	gnomevfs-mv					\
 	gnomevfs-rm					\
+	gnomevfs-async					\
 	$(NULL)
 
 gnomevfs_cat_SOURCES = gnomevfs-cat.c
@@ -48,3 +49,6 @@ gnomevfs_mv_LDADD = $(libraries)
 gnomevfs_rm_SOURCES = gnomevfs-rm.c
 gnomevfs_rm_LDADD = $(libraries)
 
+gnomevfs_async_SOURCES = gnomevfs-async.c
+gnomevfs_async_LDADD = $(libraries)
+
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 424)
+++ Makefile.am	(working copy)
@@ -7,7 +7,7 @@ COMMON_SUBDIRS =                        
 	doc					\
 	programs				\
 	po                                      \
-	dbus-daemon                 
+	dbus-daemon 
 
 SUBDIRS =					\
 	$(COMMON_SUBDIRS)
