diff --git a/lib/master.pike.in b/lib/master.pike.in
index 243ee40807ebe31607272d0b909532b095edb53f..61fec6ea3dc918dd72aafb6011c2f124fb03c759 100644
--- a/lib/master.pike.in
+++ b/lib/master.pike.in
@@ -4656,6 +4656,14 @@ array get_backtrace (object|array err)
   return bt;
 }
 
+void thread_quanta_exceeded(object thread, int ns)
+{
+  werror("Thread quanta exceeded for %O (%d ns):\n"
+	 "%s\n",
+	 thread, ns,
+	 describe_backtrace(thread->backtrace()));
+}
+
 
 #ifdef ENCODE_DEBUG
 #  define ENC_MSG(X...) do werror (X); while (0)
diff --git a/src/threads.c b/src/threads.c
index 415af8ac847272a2e5d97c6ff93bfc1c130080a6..d3554c8e034dc7b35a4a5929781c1197678ee0bf 100644
--- a/src/threads.c
+++ b/src/threads.c
@@ -59,6 +59,8 @@
  */
 static struct Pike_interpreter_struct static_pike_interpreter;
 
+static cpu_time_t thread_quanta = 0;
+
 PMOD_EXPORT struct Pike_interpreter_struct *
 #if defined(__GNUC__) && __GNUC__ >= 3
     __restrict
@@ -572,6 +574,7 @@ PMOD_EXPORT void pike_init_thread_state (struct thread_state *ts)
   ts->id = th_self();
   ts->status = THREAD_RUNNING;
   ts->swapped = 0;
+  ts->interval_start = get_real_time();
 #ifdef PIKE_DEBUG
   ts->debug_flags = 0;
   thread_swaps++;
@@ -711,6 +714,34 @@ PMOD_EXPORT void pike_debug_check_thread (DLOC_DECL)
 
 #endif	/* PIKE_DEBUG */
 
+/*! @class MasterObject
+ */
+
+/*! @decl void thread_quanta_exceeded(Thread.Thread thread, int ns)
+ *!
+ *! Function called when a thread has exceeded the thread quanta.
+ *!
+ *! @param thread
+ *!   Thread that exceeded the thread quanta.
+ *!
+ *! @param ns
+ *!   Number of nanoseconds that the thread executed before allowing
+ *!   other threads to run.
+ *!
+ *! The default master prints a diagnostic and the thread backtrace
+ *! to @[Stdio.stderr].
+ *!
+ *! @note
+ *!   This function runs in a signal handler context, and should thus
+ *!   avoid handling of mutexes, etc.
+ *!
+ *! @seealso
+ *!   @[get_thread_quanta()], @[set_thread_quanta()]
+ */
+
+/*! @endclass
+ */
+
 PMOD_EXPORT void pike_threads_allow (struct thread_state *ts COMMA_DLOC_DECL)
 {
 #ifdef DO_PIKE_CLEANUP
@@ -718,6 +749,23 @@ PMOD_EXPORT void pike_threads_allow (struct thread_state *ts COMMA_DLOC_DECL)
   if (!ts) return;
 #endif
 
+  if (UNLIKELY(thread_quanta > 0)) {
+    cpu_time_t now = get_real_time();
+
+    if (UNLIKELY((now - ts->interval_start) > thread_quanta) &&
+	LIKELY(ts->thread_obj)) {
+      ref_push_object(ts->thread_obj);
+      push_int64(now - ts->interval_start);
+      ts->interval_start = now;
+#ifndef LONG_CPU_TIME
+      push_int(1000000000 / CPU_TIME_TICKS);
+      o_multiply();
+#endif
+      SAFE_APPLY_MASTER("thread_quanta_exceeded", 2);
+      pop_stack();
+    }
+  }
+
 #ifdef PIKE_DEBUG
   pike_debug_check_thread (DLOC_ARGS_OPT);
   if (Pike_in_gc > 50 && Pike_in_gc < 300)
@@ -758,6 +806,10 @@ PMOD_EXPORT void pike_threads_disallow (struct thread_state *ts COMMA_DLOC_DECL)
     pike_swap_in_thread (ts COMMA_DLOC_ARGS_OPT);
   }
 
+  if (UNLIKELY(thread_quanta)) {
+    ts->interval_start = get_real_time();
+  }
+
 #ifdef PIKE_DEBUG
   ts->debug_flags &= ~THREAD_DEBUG_LOOSE;
   pike_debug_check_thread (DLOC_ARGS_OPT);
@@ -772,6 +824,23 @@ PMOD_EXPORT void pike_threads_allow_ext (struct thread_state *ts
   if (!ts) return;
 #endif
 
+  if (UNLIKELY(thread_quanta > 0)) {
+    cpu_time_t now = get_real_time();
+
+    if (UNLIKELY((now - ts->interval_start) > thread_quanta) &&
+	LIKELY(ts->thread_obj)) {
+      ref_push_object(ts->thread_obj);
+      push_int64(now - ts->interval_start);
+      ts->interval_start = now;
+#ifndef LONG_CPU_TIME
+      push_int(1000000000 / CPU_TIME_TICKS);
+      o_multiply();
+#endif
+      SAFE_APPLY_MASTER("thread_quanta_exceeded", 2);
+      pop_stack();
+    }
+  }
+
 #ifdef PIKE_DEBUG
   pike_debug_check_thread (DLOC_ARGS_OPT);
   if (Pike_in_gc > 50 && Pike_in_gc < 300)
@@ -821,6 +890,10 @@ PMOD_EXPORT void pike_threads_disallow_ext (struct thread_state *ts
     pike_swap_in_thread (ts COMMA_DLOC_ARGS_OPT);
   }
 
+  if (UNLIKELY(thread_quanta)) {
+    ts->interval_start = get_real_time();
+  }
+
 #ifdef PIKE_DEBUG
   ts->debug_flags &= ~THREAD_DEBUG_LOOSE;
   pike_debug_check_thread (DLOC_ARGS_OPT);
@@ -2025,6 +2098,76 @@ PMOD_EXPORT void f_this_thread(INT32 args)
   }
 }
 
+/*! @decl int(0..) get_thread_quanta()
+ *!
+ *! @returns
+ *!   Returns the current thread quanta in nanoseconds.
+ *!
+ *! @seealso
+ *!   @[set_thread_quanta()], @[gethrtime()]
+ */
+static void f_get_thread_quanta(INT32 args)
+{
+  pop_n_elems(args);
+  push_int64(thread_quanta);
+#ifndef LONG_CPU_TIME_T
+  /* Convert from ticks. */
+  push_int(1000000000 / CPU_TIME_TICKS);
+  o_multiply();
+#endif
+}
+
+/*! @decl int(0..) set_thread_quanta(int(0..) ns)
+ *!
+ *! Set the thread quanta.
+ *!
+ *! @param ns
+ *!   New thread quanta in nanoseconds.
+ *!   A value of zero (default) disables the thread quanta checks.
+ *!
+ *! When enabled @[MasterObject.thread_quanta_exceeded()] will
+ *! be called when a thread has spent more time than the quanta
+ *! without allowing another thread to run.
+ *!
+ *! @note
+ *!   Setting a non-zero value that is too small to allow for
+ *!   @[MasterObject.thread_quanta_exceeded()] to run is NOT a
+ *!   good idea.
+ *!
+ *! @returns
+ *!   Returns the previous thread quanta in nanoseconds.
+ *!
+ *! @seealso
+ *!   @[set_thread_quanta()], @[gethrtime()]
+ */
+static void f_set_thread_quanta(INT32 args)
+{
+  LONGEST ns = 0;
+
+#ifndef LONG_CPU_TIME_T
+  /* Convert to ticks. */
+  push_int(1000000000 / CPU_TIME_TICKS);
+  o_divide();
+#endif
+  get_all_args("set_thread_quanta", args, "%l", &ns);
+  pop_n_elems(args);
+
+  push_int64(thread_quanta);
+#ifndef LONG_CPU_TIME_T
+  /* Convert from ticks. */
+  push_int(1000000000 / CPU_TIME_TICKS);
+  o_multiply();
+#endif
+
+  if (ns <= 0) ns = 0;
+
+  thread_quanta = ns;
+
+  if (Pike_interpreter.thread_state) {
+    Pike_interpreter.thread_state->interval_start = get_real_time();
+  }
+}
+
 #define THIS_MUTEX ((struct mutex_storage *)(CURRENT_STORAGE))
 
 
@@ -3393,6 +3536,14 @@ void th_init(void)
 	   tFunc(tNone,tArr(tObjIs_THREAD_ID)),
 	   OPT_EXTERNAL_DEPEND);
 
+  ADD_EFUN("get_thread_quanta", f_get_thread_quanta,
+	   tFunc(tNone, tInt),
+	   OPT_EXTERNAL_DEPEND);
+
+  ADD_EFUN("set_thread_quanta", f_set_thread_quanta,
+	   tFunc(tInt, tInt),
+	   OPT_EXTERNAL_DEPEND);
+
   /* Some constants... */
   add_integer_constant("THREAD_NOT_STARTED", THREAD_NOT_STARTED, 0);
   add_integer_constant("THREAD_RUNNING", THREAD_RUNNING, 0);
diff --git a/src/threads.h b/src/threads.h
index 4e9f8b615a5ae5af1cdb6bb77ced1bc36e785d2b..70be968bd58c8d49ec1f74d588ca6f619a9e9003 100644
--- a/src/threads.h
+++ b/src/threads.h
@@ -44,6 +44,7 @@ struct thread_state {
   struct svalue result;
   COND_T status_change;
   THREAD_T id;
+  cpu_time_t interval_start;	/* real_time at THREADS_DISALLOW(). */
 #ifdef CPU_TIME_MIGHT_BE_THREAD_LOCAL
   cpu_time_t auto_gc_time;
 #endif