diff --git a/CHANGES b/CHANGES
index 2b0afb3ce1e1c7e8455fecc408d0a98f75b973a8..865cf83d68e41c4623e6f02faa641b71cb8b3404 100644
--- a/CHANGES
+++ b/CHANGES
@@ -758,8 +758,11 @@ New features
 
 o Thread
 
-  _sprintf() improvements: Thread.Mutex now prints the ID of the thread
-  holding the lock, and thread IDs are shown as hexadecimal numbers.
+  - _sprintf() improvements: Thread.Mutex now prints the ID of the thread
+    holding the lock, and thread IDs are shown as hexadecimal numbers.
+
+  - Thread.Farm now supports a callback for thread creation and termination
+    for the purpose of tracking thread names.
 
 Bug fixes
 ---------
diff --git a/lib/modules/Thread.pmod b/lib/modules/Thread.pmod
index 2d4992ebecc206a810b3e975d74f6124b3f9f5b9..40f4dcf7f178a8993fed60f1c6a0b50641c9344f 100644
--- a/lib/modules/Thread.pmod
+++ b/lib/modules/Thread.pmod
@@ -406,6 +406,9 @@ optional class Farm
   protected Mutex mutex = Mutex();
   protected Condition ft_cond = Condition();
   protected Queue job_queue = Queue();
+  protected object dispatcher_thread;
+  protected function(object, string:void) thread_name_cb;
+  protected string thread_name_prefix;
 
   //! An asynchronous result.
   class Result
@@ -514,6 +517,16 @@ optional class Farm
 
     protected int ready;
 
+    void update_thread_name(int is_exiting)
+    {
+      if (thread_name_cb) {
+        string th_name =
+          !is_exiting &&
+          sprintf("%s Handler 0x%x", thread_name_prefix, thread->id_number());
+        thread_name_cb(thread, th_name);
+      }
+    }
+
     void handler()
     {
       array(object|array(function|array)) q;
@@ -540,11 +553,12 @@ optional class Farm
           if( st > max_time )
             max_time = st;
           ft_cond->broadcast();
-        } else  {
+        } else {
           object lock = mutex->lock();
           threads -= ({ this });
           free_threads -= ({ this });
           lock = 0;
+          update_thread_name(1);
           destruct();
           return;
         }
@@ -580,6 +594,7 @@ optional class Farm
     protected void create()
     {
       thread = thread_create( handler );
+      update_thread_name(0);
     }
 
 
@@ -623,6 +638,8 @@ optional class Farm
   {
     while( array q = [array]job_queue->read() )
       aquire_thread()->run( q[1], q[0] );
+    if (thread_name_cb)
+      thread_name_cb(this_thread(), 0);
   }
 
   protected class ValueAdjuster( object r, object r2, int i, mapping v )
@@ -776,6 +793,34 @@ optional class Farm
     return omnt;
   }
 
+  //! Provide a callback function to track names of threads created by the
+  //! farm.
+  //!
+  //! @param cb
+  //!   The callback function. This will get invoked with the thread as the
+  //!   first parameter and the name as the second whenever a thread is
+  //!   created. When the same thread terminates the callback is invoked
+  //!   again with @[0] as the second parameter. Set @[cb] to @[0] to stop
+  //!   any previously registered callbacks from being called.
+  //!
+  //! @param prefix
+  //!   An optional name prefix to distinguish different farms. If not given
+  //!   a prefix will be generated automatically.
+  void set_thread_name_cb(function(object, string:void) cb, void|string prefix)
+  {
+    thread_name_cb = cb;
+    thread_name_prefix =
+      cb &&
+      (prefix || sprintf("Thread.Farm 0x%x", dispatcher_thread->id_number()));
+
+    //  Give a name to all existing threads
+    if (thread_name_cb) {
+      thread_name_cb(dispatcher_thread, thread_name_prefix + " Dispatcher");
+      foreach (threads, Handler t)
+        t->update_thread_name(0);
+    }
+  }
+
   //! Get some statistics for the thread farm.
   string debug_status()
   {
@@ -800,7 +845,7 @@ optional class Farm
 
   protected void create()
   {
-    thread_create( dispatcher );
+    dispatcher_thread = thread_create( dispatcher );
   }
 }