diff --git a/libguile/fluids.c b/libguile/fluids.c
index bcd04c4..e532e39 100644
--- a/libguile/fluids.c
+++ b/libguile/fluids.c
@@ -87,6 +87,27 @@ static scm_t_bits tc16_dynamic_state;
 static SCM all_dynamic_states = SCM_EOL;
 static SCM all_fluids = SCM_EOL;
 
+static void
+update_all_states (void *p)
+{
+  SCM new_vectors = *(SCM *)p;
+  SCM state;
+
+  for (state = all_dynamic_states; !scm_is_null (state);
+       state = DYNAMIC_STATE_NEXT (state))
+    {
+      SCM old_fluids = DYNAMIC_STATE_FLUIDS (state);
+      SCM new_fluids = SCM_CAR (new_vectors);
+      size_t i, old_len = SCM_SIMPLE_VECTOR_LENGTH (old_fluids);
+
+      for (i = 0; i < old_len; i++)
+	SCM_SIMPLE_VECTOR_SET (new_fluids, i,
+			       SCM_SIMPLE_VECTOR_REF (old_fluids, i));
+      SET_DYNAMIC_STATE_FLUIDS (state, new_fluids);
+      new_vectors = SCM_CDR (new_vectors);
+    }
+}
+
 /* Make sure that all states have the right size.  This must be called
    while fluid_admin_mutex is held.
 */
@@ -112,21 +133,7 @@ resize_all_states ()
 					       SCM_BOOL_F),
 			    new_vectors);
 
-  scm_i_thread_put_to_sleep ();
-  for (state = all_dynamic_states; !scm_is_null (state);
-       state = DYNAMIC_STATE_NEXT (state))
-    {
-      SCM old_fluids = DYNAMIC_STATE_FLUIDS (state);
-      SCM new_fluids = SCM_CAR (new_vectors);
-      size_t i, old_len = SCM_SIMPLE_VECTOR_LENGTH (old_fluids);
-
-      for (i = 0; i < old_len; i++)
-	SCM_SIMPLE_VECTOR_SET (new_fluids, i,
-			       SCM_SIMPLE_VECTOR_REF (old_fluids, i));
-      SET_DYNAMIC_STATE_FLUIDS (state, new_fluids);
-      new_vectors = SCM_CDR (new_vectors);
-    }
-  scm_i_thread_wake_up ();
+  scm_i_with_threads_sleeping (update_all_states, &new_vectors);
 }
 
 /* This is called during GC, that is, while being single threaded.
diff --git a/libguile/gc.c b/libguile/gc.c
index b7a3bf0..818aec6 100644
--- a/libguile/gc.c
+++ b/libguile/gc.c
@@ -561,18 +561,20 @@ scm_check_deprecated_memory_return ()
 
 long int scm_i_last_marked_cell_count;
 
-/* Must be called while holding scm_i_sweep_mutex.
+/* Must be called while holding scm_i_sweep_mutex and with all threads
+   sleeping.
 
    This function is fairly long, but it touches various global
    variables. To not obscure the side effects on global variables,
    this function has not been split up.
  */
-void
-scm_i_gc (const char *what)
+static void
+scm_i_gc_1 (void *p)
 {
   unsigned long t_before_gc = 0;
-  
-  scm_i_thread_put_to_sleep ();
+#ifdef DEBUGINFO
+  const char *what = *(const char *)p;
+#endif
   
   scm_c_hook_run (&scm_before_gc_c_hook, 0);
 
@@ -678,8 +680,6 @@ scm_i_gc (const char *what)
      the time taken.
   */
   scm_gc_time_taken += (scm_c_get_internal_run_time () - t_before_gc);
-    
-  scm_i_thread_wake_up ();
   /*
     For debugging purposes, you could do
     scm_i_sweep_all_segments ("debug"), but then the remains of the
@@ -687,6 +687,11 @@ scm_i_gc (const char *what)
    */
 }
 
+void
+scm_i_gc (const char *what)
+{
+  scm_i_with_threads_sleeping (scm_i_gc_1, &what);
+}
 
 
 /* {GC Protection Helper Functions}
diff --git a/libguile/strings.c b/libguile/strings.c
index 4e21f3e..ab8cd14 100644
--- a/libguile/strings.c
+++ b/libguile/strings.c
@@ -339,6 +339,21 @@ scm_i_string_chars (SCM str)
   return STRINGBUF_CHARS (buf) + start;
 }
 
+struct replace_data {
+  SCM str, new_buf;
+  size_t start;
+};
+
+static void
+replace_stringbuf_data (void *p)
+{
+  struct replace_data *r = p;
+
+  SET_STRING_STRINGBUF (r->str, r->new_buf);
+  r->start = STRING_START (r->str);
+  SET_STRING_START (r->str, 0);
+}
+
 char *
 scm_i_string_writable_chars (SCM orig_str)
 {
@@ -357,19 +372,17 @@ scm_i_string_writable_chars (SCM orig_str)
 
       size_t len = STRING_LENGTH (str);
       SCM new_buf;
+      struct replace_data r;
 
       scm_i_pthread_mutex_unlock (&stringbuf_write_mutex);
 
       new_buf = make_stringbuf (len);
       memcpy (STRINGBUF_CHARS (new_buf),
 	      STRINGBUF_CHARS (buf) + STRING_START (str), len);
-
-      scm_i_thread_put_to_sleep ();
-      SET_STRING_STRINGBUF (str, new_buf);
-      start -= STRING_START (str);
-      SET_STRING_START (str, 0);
-      scm_i_thread_wake_up ();
-
+      r.str = str;
+      r.new_buf = new_buf;
+      scm_i_with_threads_sleeping (replace_stringbuf_data, &r);
+      start -= r.start;
       buf = new_buf;
 
       scm_i_pthread_mutex_lock (&stringbuf_write_mutex);
diff --git a/libguile/threads.c b/libguile/threads.c
index 8458a60..a2a98f3 100644
--- a/libguile/threads.c
+++ b/libguile/threads.c
@@ -428,6 +428,25 @@ suspend (void)
   setjmp (t->regs);
   return t;
 }
+  
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+/* For paranoia's sake, make sure the stack slot we take the address
+   of is allocated after (deeper on the stack) than anything from the
+   parent, by not permitting inline expansion and thus possible
+   rearranging of things within the caller's stack frame.  */
+static void
+while_suspended (void (*func)(void *, scm_i_thread *), void *data)
+  __attribute__((noinline));
+#endif
+
+static void
+while_suspended (void (*func)(void *, scm_i_thread *), void *data)
+{
+  scm_i_thread *t = suspend ();
+  func (data, t);
+  resume (t);
+}
 
 static scm_t_guile_ticket
 scm_leave_guile ()
@@ -870,6 +889,20 @@ scm_without_guile (void *(*func)(void *), void *data)
   return res;
 }
 
+/* Internal helper function -- like scm_without_guile, but for various
+   cases in this file where we actually want to return an int, not
+   a pointer.  */
+static int
+scm_without_guile_int (int (*func)(void *), void *data)
+{
+  int res;
+  scm_t_guile_ticket t;
+  t = scm_leave_guile ();
+  res = func (data);
+  scm_enter_guile (t);
+  return res;
+}
+
 /*** Thread creation */
 
 typedef struct {
@@ -1769,6 +1802,28 @@ scm_threads_mark_stacks (void)
 
 /*** Select */
 
+struct select_callback_data {
+  SELECT_TYPE *readfds, *writefds, *exceptfds;
+  struct timeval *timeout;
+  scm_i_thread *t;
+  int nfds, wakeup_fd, eno;
+};
+
+static int
+select_callback (void *p)
+{
+  struct select_callback_data *cb = p;
+  int res;
+
+  FD_SET (cb->wakeup_fd, cb->readfds);
+  if (cb->wakeup_fd >= cb->nfds)
+    cb->nfds = cb->wakeup_fd+1;
+  res = select (cb->nfds, cb->readfds, cb->writefds, cb->exceptfds, cb->timeout);
+  cb->t->sleep_fd = -1;
+  cb->eno = errno;
+  return res;
+}
+
 int
 scm_std_select (int nfds,
 		SELECT_TYPE *readfds,
@@ -1777,45 +1832,41 @@ scm_std_select (int nfds,
 		struct timeval *timeout)
 {
   fd_set my_readfds;
-  int res, eno, wakeup_fd;
+  int res;
   scm_i_thread *t = SCM_I_CURRENT_THREAD;
-  scm_t_guile_ticket ticket;
+  struct select_callback_data cb = {
+    readfds, writefds, exceptfds, timeout, t, nfds,
+  };
 
   if (readfds == NULL)
     {
       FD_ZERO (&my_readfds);
-      readfds = &my_readfds;
+      cb.readfds = &my_readfds;
     }
 
   while (scm_i_setup_sleep (t, SCM_BOOL_F, NULL, t->sleep_pipe[1]))
     SCM_TICK;
 
-  wakeup_fd = t->sleep_pipe[0];
-  ticket = scm_leave_guile ();
-  FD_SET (wakeup_fd, readfds);
-  if (wakeup_fd >= nfds)
-    nfds = wakeup_fd+1;
-  res = select (nfds, readfds, writefds, exceptfds, timeout);
-  t->sleep_fd = -1;
-  eno = errno;
-  scm_enter_guile (ticket);
+  cb.wakeup_fd = t->sleep_pipe[0];
+
+  res = scm_without_guile_int (select_callback, &cb);
 
   scm_i_reset_sleep (t);
 
-  if (res > 0 && FD_ISSET (wakeup_fd, readfds))
+  if (res > 0 && FD_ISSET (cb.wakeup_fd, cb.readfds))
     {
       char dummy;
-      full_read (wakeup_fd, &dummy, 1);
+      full_read (cb.wakeup_fd, &dummy, 1);
 
-      FD_CLR (wakeup_fd, readfds);
+      FD_CLR (cb.wakeup_fd, cb.readfds);
       res -= 1;
       if (res == 0)
 	{
-	  eno = EINTR;
+	  cb.eno = EINTR;
 	  res = -1;
 	}
     }
-  errno = eno;
+  errno = cb.eno;
   return res;
 }
 
@@ -1823,18 +1874,18 @@ scm_std_select (int nfds,
 
 #if SCM_USE_PTHREAD_THREADS
 
+static int
+mutex_lock_callback (void *p)
+{
+  return scm_i_pthread_mutex_lock (p);
+}
+
 int
 scm_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
 {
   if (scm_i_pthread_mutex_trylock (mutex) == 0)
     return 0;
-  else
-    {
-      scm_t_guile_ticket t = scm_leave_guile ();
-      int res = scm_i_pthread_mutex_lock (mutex);
-      scm_enter_guile (t);
-      return res;
-    }
+  return scm_without_guile_int (mutex_lock_callback, mutex);
 }
 
 static void
@@ -1850,14 +1901,45 @@ scm_dynwind_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
   scm_dynwind_unwind_handler (do_unlock, mutex, SCM_F_WIND_EXPLICITLY);
 }
 
+struct cond_wait_callback_data {
+  scm_i_pthread_cond_t *cond;
+  scm_i_pthread_mutex_t *mutex;
+};
+
+static int
+cond_wait_callback (void *p)
+{
+  struct cond_wait_callback_data *cb = p;
+  int res;
+  scm_i_thread *t = SCM_I_CURRENT_THREAD;
+  t->held_mutex = cb->mutex;
+  res = scm_i_pthread_cond_wait (cb->cond, cb->mutex);
+  t->held_mutex = NULL;
+  return res;
+}
+
 int
 scm_pthread_cond_wait (scm_i_pthread_cond_t *cond, scm_i_pthread_mutex_t *mutex)
 {
-  scm_t_guile_ticket t = scm_leave_guile ();
-  ((scm_i_thread *)t)->held_mutex = mutex;
-  int res = scm_i_pthread_cond_wait (cond, mutex);
-  ((scm_i_thread *)t)->held_mutex = NULL;
-  scm_enter_guile (t);
+  struct cond_wait_callback_data cb = { cond, mutex };
+  return scm_without_guile_int (cond_wait_callback, &cb);
+}
+
+struct cond_timedwait_callback_data {
+  scm_i_pthread_cond_t *cond;
+  scm_i_pthread_mutex_t *mutex;
+  const scm_t_timespec *wt;
+};
+
+static int
+cond_timedwait_callback (void *p)
+{
+  struct cond_timedwait_callback_data *cb = p;
+  int res;
+  scm_i_thread *t = SCM_I_CURRENT_THREAD;
+  t->held_mutex = cb->mutex;
+  res = scm_i_pthread_cond_timedwait (cb->cond, cb->mutex, cb->wt);
+  t->held_mutex = NULL;
   return res;
 }
 
@@ -1866,12 +1948,8 @@ scm_pthread_cond_timedwait (scm_i_pthread_cond_t *cond,
 			    scm_i_pthread_mutex_t *mutex,
 			    const scm_t_timespec *wt)
 {
-  scm_t_guile_ticket t = scm_leave_guile ();
-  ((scm_i_thread *)t)->held_mutex = mutex;
-  int res = scm_i_pthread_cond_timedwait (cond, mutex, wt);
-  ((scm_i_thread *)t)->held_mutex = NULL;
-  scm_enter_guile (t);
-  return res;
+  struct cond_timedwait_callback_data cb = { cond, mutex, wt };
+  return scm_without_guile_int (cond_timedwait_callback, &cb);
 }
 
 #endif
@@ -1970,25 +2048,6 @@ int scm_i_thread_go_to_sleep;
 static int threads_initialized_p = 0;
 
 void
-scm_i_thread_put_to_sleep ()
-{
-  if (threads_initialized_p)
-    {
-      scm_i_thread *t;
-
-      scm_leave_guile ();
-      scm_i_pthread_mutex_lock (&thread_admin_mutex);
-
-      /* Signal all threads to go to sleep
-       */
-      scm_i_thread_go_to_sleep = 1;
-      for (t = all_threads; t; t = t->next_thread)
-	scm_i_pthread_mutex_lock (&t->heap_mutex);
-      scm_i_thread_go_to_sleep = 0;
-    }
-}
-
-void
 scm_i_thread_invalidate_freelists ()
 {
   /* thread_admin_mutex is already locked. */
@@ -1999,34 +2058,68 @@ scm_i_thread_invalidate_freelists ()
       t->clear_freelists_p = 1;
 }
 
+struct sleeping_callback {
+  void (*func) (void *);
+  void *data;
+};
+
+static void *
+sleeping_callback (void *p)
+{
+  struct sleeping_callback *cb = p;
+  scm_i_thread *t;
+
+  scm_i_pthread_mutex_lock (&thread_admin_mutex);
+
+  /* Signal all threads to go to sleep
+   */
+  scm_i_thread_go_to_sleep = 1;
+  for (t = all_threads; t; t = t->next_thread)
+    scm_i_pthread_mutex_lock (&t->heap_mutex);
+  scm_i_thread_go_to_sleep = 0;
+
+  cb->func (cb->data);
+
+  scm_i_pthread_cond_broadcast (&wake_up_cond);
+  for (t = all_threads; t; t = t->next_thread)
+    scm_i_pthread_mutex_unlock (&t->heap_mutex);
+  scm_i_pthread_mutex_unlock (&thread_admin_mutex);
+
+  return NULL;
+}
+
 void
-scm_i_thread_wake_up ()
+scm_i_with_threads_sleeping (void (*func)(void *), void *data)
 {
   if (threads_initialized_p)
     {
-      scm_i_thread *t;
-
-      scm_i_pthread_cond_broadcast (&wake_up_cond);
-      for (t = all_threads; t; t = t->next_thread)
-	scm_i_pthread_mutex_unlock (&t->heap_mutex);
-      scm_i_pthread_mutex_unlock (&thread_admin_mutex);
-      scm_enter_guile ((scm_t_guile_ticket) SCM_I_CURRENT_THREAD);
+      struct sleeping_callback cb = { func, data };
+      scm_without_guile (sleeping_callback, &cb);
+    }
+  else
+    {
+      func (data);
+      /* Can func ever cause initialization to happen if it hadn't
+	 been done before?  */
+      assert (threads_initialized_p == 0);
     }
 }
 
-void
-scm_i_thread_sleep_for_gc ()
+static void
+sleep_for_gc_helper (void *p, scm_i_thread *t)
 {
-  scm_i_thread *t = suspend ();
-
   /* Don't put t->heap_mutex in t->held_mutex here, because if the
      thread is cancelled during the cond wait, the thread's cleanup
      function (scm_leave_guile_cleanup) will handle unlocking the
      heap_mutex, so we don't need to do that again in on_thread_exit.
   */
   scm_i_pthread_cond_wait (&wake_up_cond, &t->heap_mutex);
+}
 
-  resume (t);
+void
+scm_i_thread_sleep_for_gc ()
+{
+  while_suspended (sleep_for_gc_helper, NULL);
 }
 
 /* This mutex is used by SCM_CRITICAL_SECTION_START/END.
diff --git a/libguile/threads.h b/libguile/threads.h
index 32b0ea6..407acb7 100644
--- a/libguile/threads.h
+++ b/libguile/threads.h
@@ -153,8 +153,8 @@ SCM_INTERNAL void *scm_i_with_guile_and_parent (void *(*func)(void *),
 
 extern int scm_i_thread_go_to_sleep;
 
-SCM_INTERNAL void scm_i_thread_put_to_sleep (void);
-SCM_INTERNAL void scm_i_thread_wake_up (void);
+SCM_INTERNAL void scm_i_with_threads_sleeping (void (*func)(void *),
+					       void *data);
 SCM_INTERNAL void scm_i_thread_invalidate_freelists (void);
 void scm_i_thread_sleep_for_gc (void);
 
