diff --git a/gdb/infrun.c b/gdb/infrun.c
index 23c79f2..eca8eec 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5403,7 +5403,7 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
 		 stepping, then scheduler locking can't be in effect,
 		 otherwise we wouldn't have resumed the current event
 		 thread in the first place.  */
-	      gdb_assert (!schedlock_applies (1));
+	      gdb_assert (!schedlock_applies (currently_stepping (tp)));
 
 	      stepping_thread = tp;
 	    }
diff --git a/gdb/testsuite/gdb.threads/next-bp-other-thread.c b/gdb/testsuite/gdb.threads/next-bp-other-thread.c
new file mode 100644
index 0000000..e931305
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/next-bp-other-thread.c
@@ -0,0 +1,45 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/* Always zero, used in breakpoint condition.  */
+volatile int global_zero;
+
+void *
+child_function (void *arg)
+{
+  while (1)
+    {
+      usleep (1); /* set breakpoint child here */
+    }
+
+  pthread_exit (NULL);
+}
+
+int
+main (void)
+{
+  pthread_t child_thread;
+  int res;
+
+  res = pthread_create (&child_thread, NULL, child_function, NULL);
+  sleep (2); /* set wait-thread breakpoint here */
+  exit (EXIT_SUCCESS);
+}
diff --git a/gdb/testsuite/gdb.threads/next-bp-other-thread.exp b/gdb/testsuite/gdb.threads/next-bp-other-thread.exp
new file mode 100644
index 0000000..c2361fa
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/next-bp-other-thread.exp
@@ -0,0 +1,54 @@
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test that GDB behaves correctly when a "next" steps over a call, and
+# another thread hits a breakpoint that doesn't cause a user visible
+# stop (and so needs to be stepped over).  GDB used to trip on an
+# invalid assertion - PR17408.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug pthreads}] == -1} {
+    return -1
+}
+
+# Test all "set scheduler-locking" variants.
+foreach schedlock {"off" "step" "on" } {
+    with_test_prefix "schedlock=$schedlock" {
+	clean_restart $binfile
+
+	if ![runto_main] {
+	    continue
+	}
+
+	gdb_breakpoint [gdb_get_line_number "set wait-thread breakpoint here"]
+	gdb_continue_to_breakpoint "run to wait-thread breakpoint"
+	gdb_test "info threads" "2 .*\\\* 1.*" "info threads shows all threads"
+
+	delete_breakpoints
+
+	gdb_breakpoint [gdb_get_line_number "set breakpoint child here"]
+	# Give it a condition that always fails.
+	gdb_test "condition \$bpnum global_zero == 1" ".*"
+
+	gdb_test_no_output "set scheduler-locking $schedlock"
+
+	# While stepping over the sleep call, the other thread hits a
+	# breakpoint that doesn't cause a user visible stop (and so
+	# needs to be stepped over).  The next should complete as if
+	# that breakpoint never triggered.
+	gdb_test "next" "EXIT_SUCCESS.*" "next over function call"
+    }
+}
diff --git a/gdb/testsuite/gdb.threads/schedlock.c b/gdb/testsuite/gdb.threads/schedlock.c
index 2d8c7ab..11e4bbc 100644
--- a/gdb/testsuite/gdb.threads/schedlock.c
+++ b/gdb/testsuite/gdb.threads/schedlock.c
@@ -48,6 +48,28 @@ int main() {
     exit(EXIT_SUCCESS);
 }
 
+void some_function (void) {
+  /* Sleep a bit to give the other threads a chance to run, if not
+     locked.  This also ensure that even if the compiler optimizes out
+     or inlines some_function, there's still be some function that
+     needs to be stepped over.  */
+  usleep (1);
+}
+
+/* When testing "next", this is set to have the loop call
+   some_function, which GDB should step over.  When testing "step",
+   that would step into the function, which is not what we want.  */
+volatile int call_function = 0;
+
+/* Call some_function if CALL_FUNCTION is set.  This is wrapped in a
+   macro so that it's a single source line in the main loop.  */
+#define MAYBE_CALL_SOME_FUNCTION()		\
+  do						\
+    {						\
+      if (call_function)			\
+	some_function ();			\
+    } while (0)
+
 void *thread_function(void *arg) {
     int my_number =  (long) arg;
     int *myp = (int *) &args[my_number];
@@ -56,7 +78,7 @@ void *thread_function(void *arg) {
     while (*myp > 0)
       {
 	/* schedlock.exp: main loop.  */
-	(*myp) ++;
+	MAYBE_CALL_SOME_FUNCTION(); (*myp) ++;
       }
 
     pthread_exit(NULL);
diff --git a/gdb/testsuite/gdb.threads/schedlock.exp b/gdb/testsuite/gdb.threads/schedlock.exp
index e105689..cd99844 100644
--- a/gdb/testsuite/gdb.threads/schedlock.exp
+++ b/gdb/testsuite/gdb.threads/schedlock.exp
@@ -30,8 +30,10 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
 
 # Now we can proceed with the real testing.
 
-proc get_args { } {
-    global list_count
+# Get the current contents of the `args` array in the test program.
+# Description is appended to the test message.
+
+proc get_args { description } {
     global gdb_prompt
     global NUM
 
@@ -40,10 +42,10 @@ proc get_args { } {
 	append pattern ", (\[0-9\]+)"
     }
 
-    gdb_test_multiple "print args" "listed args ($list_count)" {
+    set test "listed args ($description)"
+    gdb_test_multiple "print args" $test {
 	-re "\\\$\[0-9\]+ = {$pattern}.*$gdb_prompt $" {
-	    set list_count [expr $list_count + 1]
-	    pass "listed args ($list_count)"
+	    pass $test
 
 	    set result ""
 	    for {set i 1} {[expr $i <= $NUM]} {incr i} {
@@ -75,46 +77,62 @@ proc stop_process { description } {
 proc get_current_thread { description } {
     global gdb_prompt
 
-    gdb_test_multiple "bt" "$description" {
+    set test "find current thread ($description)"
+
+    gdb_test_multiple "bt" $test {
 	-re "thread_function \\(arg=0x(\[0-9\])\\).*$gdb_prompt $" {
-	    pass $description
+	    pass $test
 	    return $expect_out(1,string)
 	}
     }
     return ""
 }
 
+# Make sure we're stopped in the loop, in one of the non-main threads.
+
+proc goto_loop { msg } {
+    gdb_breakpoint [concat [gdb_get_line_number "schedlock.exp: main loop"] " if arg != 0"]
+
+    set test "return to loop"
+    if {$msg != ""} {
+	set test "$test ($msg)"
+    }
+    gdb_continue_to_breakpoint $test
+    delete_breakpoints
+}
+
 proc my_continue { msg } {
-    gdb_test_multiple "continue" "continuing ($msg)" {
+    set test "continue ($msg)"
+    gdb_test_multiple "continue" $test {
 	-re "Continuing" {
-	    pass "continue ($msg)"
+	    pass $test
 	}
     }
 
     stop_process "stop all threads ($msg)"
 
-    # Make sure we're in one of the non-main looping threads.
-    gdb_breakpoint [concat [gdb_get_line_number "schedlock.exp: main loop"] " if arg != 0"]
-    gdb_continue_to_breakpoint "return to loop ($msg)"
-    delete_breakpoints
+    goto_loop $msg
 }
 
-proc step_ten_loops { msg } {
+# Use CMD to step the loop 10 times.  CMD may be "step" or "next".
+
+proc step_ten_loops { cmd } {
     global gdb_prompt
 
     for {set i 0} {[expr $i < 10]} {set i [expr $i + 1]} {
 	set other_step 0
-	gdb_test_multiple "step" "step to increment ($msg $i)" {
+	set test "$cmd to increment ($i)"
+	gdb_test_multiple $cmd $test {
 	    -re ".*myp\\) \\+\\+;\[\r\n\]+$gdb_prompt $" {
-		pass "step to increment ($msg $i)"
+		pass $test
 	    }
 	    -re "$gdb_prompt $" {
 		if {$other_step == 0} {
 		    set other_step 1
-		    send_gdb "step\n"
+		    send_gdb "$cmd\n"
 		    exp_continue
 		} else {
-		    fail "step to increment ($msg $i)"
+		    fail $test
 		    # FIXME cascade?
 		}
 	    }
@@ -162,15 +180,12 @@ gdb_test_multiple "set scheduler-locking off" "scheduler locking set to none" {
 gdb_breakpoint [gdb_get_line_number "schedlock.exp: last thread start"]
 gdb_continue_to_breakpoint "all threads started"
 
-global list_count
-set list_count 0
-
-set start_args [get_args]
+set start_args [get_args "before initial"]
 
 # First make sure that all threads are alive.
 my_continue "initial"
 
-set cont_args [get_args]
+set cont_args [get_args "after initial"]
 
 set bad 0
 for {set i 0} {[expr $i < $NUM]} {set i [expr $i + 1]} {
@@ -184,125 +199,122 @@ if { $bad == 0 } {
   fail "all threads alive ($bad/$NUM did not run)"
 }
 
-# We can't change threads, unfortunately, in current GDB.  Use
-# whichever we stopped in.
-set curthread [get_current_thread "find current thread (1)"]
-
-
-
+# Compare the previous thread and args with the current thread and
+# args.  Check that we didn't switch threads, and that the threads
+# incremented their args counter the amounts expected.  CMD is the
+# command being tested.  BEFORE_THREAD is the thread that was selected
+# before the command was run.  BEFORE_ARGS is the value of the
+# thread's args before the command was run.  LOCKED indicates whether
+# we expect threads other than the selected thread remained locked.
 
-# Test stepping without scheduler locking.
-gdb_test_no_output "set scheduler-locking off"
+proc check_result { cmd before_thread before_args locked } {
+    global NUM
 
-step_ten_loops "unlocked"
+    # Make sure we're still in the same thread.
+    set newthread [get_current_thread "after"]
 
-# Make sure we're still in the same thread.
-set newthread [get_current_thread "find current thread (2)"]
-if {$curthread == $newthread} {
-    pass "step without lock does not change thread"
-} else {
-    fail "step without lock does not change thread (switched to thread $newthread)"
-}
+    set test "$cmd does not change thread"
+    if {$before_thread == $newthread} {
+	pass "$test"
+    } else {
+	fail "$test (switched to thread $newthread)"
+    }
 
-set start_args $cont_args
-set cont_args [get_args]
+    set after_args [get_args "after"]
 
-set num_other_threads 0
-for {set i 0} {[expr $i < $NUM]} {set i [expr $i + 1]} {
-  if {[lindex $start_args $i] == [lindex $cont_args $i]} {
-    if {$i == $curthread} {
-      fail "current thread stepped (didn't run)"
+    set test "current thread advanced"
+    if { $locked } {
+	set test "$test - locked"
+    } else {
+	set test "$test - unlocked"
     }
-  } else {
-    if {$i == $curthread} {
-	if {[lindex $start_args $i] == [expr [lindex $cont_args $i] - 10]} {
-	    pass "current thread stepped"
+
+    set num_other_threads 0
+    for {set i 0} {$i < $NUM} {incr i} {
+	if {[lindex $before_args $i] == [lindex $after_args $i]} {
+	    if {$i == $before_thread} {
+		fail "$test (didn't run)"
+	    }
 	} else {
-	    fail "current thread stepped (wrong amount)"
+	    if {$i == $before_thread} {
+		if {$cmd == "continue"
+		    || [lindex $before_args $i] == [expr [lindex $after_args $i] - 10]} {
+		    pass "$test"
+		} else {
+		    fail "$test (wrong amount)"
+		}
+	    } else {
+		incr num_other_threads
+	    }
 	}
+    }
+
+    if { $locked } {
+	gdb_assert {$num_other_threads == 0} "other threads didn't run - locked"
     } else {
-      set num_other_threads [expr $num_other_threads + 1]
+	gdb_assert {$num_other_threads > 0} "other threads ran - unlocked"
     }
-  }
-}
-if {$num_other_threads > 0} {
-  pass "other threads ran - unlocked"
-} else {
-  fail "other threads ran - unlocked"
 }
 
-# Test continue with scheduler locking
-gdb_test "set scheduler-locking on" ""
+with_test_prefix "schedlock=on: cmd=continue" {
+    # Use whichever we stopped in.
+    set curthread [get_current_thread "before"]
 
-my_continue "with lock"
+    # Test continue with scheduler locking.
+    gdb_test "set scheduler-locking on" ""
 
-# Make sure we're still in the same thread.
-set newthread [get_current_thread "find current thread (3)"]
-if {$curthread == $newthread} {
-    pass "continue with lock does not change thread"
-} else {
-    fail "continue with lock does not change thread (switched to thread $newthread)"
+    my_continue "with lock"
+
+    check_result "continue" $curthread $cont_args 1
 }
 
-set start_args $cont_args
-set cont_args [get_args]
+# Test stepping/nexting with different modes of scheduler locking.
+proc test_step { schedlock cmd call_function } {
+    global NUM
 
-set num_other_threads 0
-for {set i 0} {[expr $i < $NUM]} {set i [expr $i + 1]} {
-  if {[lindex $start_args $i] == [lindex $cont_args $i]} {
-    if {$i == $curthread} {
-      fail "current thread ran (didn't run)"
-    }
-  } else {
-    if {$i == $curthread} {
-      pass "current thread ran"
-    } else {
-      incr num_other_threads
+    gdb_test_no_output "set scheduler-locking off"
+    goto_loop ""
+
+    set curthread [get_current_thread "before"]
+
+    # No need to set to off again.  This avoids a duplicate message.
+    if {$schedlock != "off"} {
+	gdb_test_no_output "set scheduler-locking $schedlock"
     }
-  }
-}
-if {$num_other_threads > 0} {
-  fail "other threads didn't run - locked"
-} else {
-  pass "other threads didn't run - locked"
-}
 
-# Test stepping with scheduler locking
-step_ten_loops "locked"
+    gdb_test "print call_function = $call_function" \
+	" = $call_function"
 
-# Make sure we're still in the same thread.
-set newthread [get_current_thread "find current thread (2)"]
-if {$curthread == $newthread} {
-    pass "step with lock does not change thread"
-} else {
-    fail "step with lock does not change thread (switched to thread $newthread)"
-}
+    set before_args [get_args "before"]
 
-set start_args $cont_args
-set cont_args [get_args]
+    step_ten_loops $cmd
 
-set num_other_threads 0
-for {set i 0} {[expr $i < $NUM]} {set i [expr $i + 1]} {
-  if {[lindex $start_args $i] == [lindex $cont_args $i]} {
-    if {$i == $curthread} {
-      fail "current thread stepped locked (didn't run)"
-    }
-  } else {
-    if {$i == $curthread} {
-	if {[lindex $start_args $i] == [expr [lindex $cont_args $i] - 10]} {
-	    pass "current thread stepped locked"
-	} else {
-	    fail "current thread stepped locked (wrong amount)"
-	}
+    # "next" lets other threads run while stepping over functions.
+    if { $schedlock == "on" || ($schedlock == "step" && !$call_function) } {
+	set locked 1
     } else {
-      incr num_other_threads
+	set locked 0
     }
-  }
-}
-if {$num_other_threads > 0} {
-  fail "other threads didn't run - step locked"
-} else {
-  pass "other threads didn't run - step locked"
+
+    check_result $cmd $curthread $before_args $locked
 }
 
-return 0
+# Test stepping/nexting with different modes of scheduler locking.
+foreach schedlock {"off" "step" "on"} {
+    with_test_prefix "schedlock=$schedlock" {
+	with_test_prefix "cmd=step" {
+	    test_step $schedlock "step" 0
+	}
+	with_test_prefix "cmd=next" {
+	    # With "next", and schedlock "step", threads run unlocked
+	    # when stepping over a function call.  This exercises both
+	    # with and without a function call.  Without a function
+	    # call "next" should behave just like "step".
+	    foreach call_function {0 1} {
+		with_test_prefix "call_function=$call_function" {
+		    test_step $schedlock "next" $call_function
+		}
+	    }
+	}
+    }
+}
