+ while (1) {
+ my @copy_conn = @conn; # avoid alias problems after splice()
+ my $nconn = scalar(@copy_conn);
+ foreach my $i0 (0 .. $#copy_conn) {
+ my $conn = $copy_conn[$i0];
+ #print "connection $i0 of $nconn/", scalar(@conn), " is $conn\n";
+ if (!$conn->current_task()) {
+ if (!$conn->next_task()) {
+ # Out of tasks: we need a new test
+ NEXT_TEST:
+ my $address = $conn->option("current_test_address");
+ my $nextaddr;
+ if (!defined $address) {
+ $nextaddr = "";
+ } else {
+ $this->log("irspy_test",
+ "checking for next test after '$address'");
+ $nextaddr = $this->_next_test($address);
+ }
+ if (!defined $nextaddr) {
+ $conn->log("irspy", "has no more tests: removing");
+ splice @conn, $i0, 1;
+ $this->_rewrite_record($conn);
+ next;
+ }
+
+ my $node = $this->{tree}->select($nextaddr)
+ or die "invalid nextaddr '$nextaddr'";
+ $conn->option(current_test_address => $nextaddr);
+ my $tname = $node->name();
+ $conn->log("irspy_test",
+ "starting test '$nextaddr' = $tname");
+ my $tasks = $conn->tasks();
+ my $oldcount = @$tasks;
+ "ZOOM::IRSpy::Test::$tname"->start($conn);
+ $tasks = $conn->tasks();
+ if (@$tasks > $oldcount) {
+ # Prepare to start the first of the newly added tasks
+ $conn->next_task($tasks->[$oldcount]);
+ } else {
+ $conn->log("irspy_task",
+ "no tasks added by new test $tname");
+ goto NEXT_TEST;
+ }
+ }
+
+ my $task = $conn->next_task();
+ die "no next task queued for $conn" if !defined $task;
+ $conn->log("irspy_task", "preparing task $task");
+ $conn->next_task(0);
+ $conn->current_task($task);
+ $task->run();
+ }
+
+ # Do we need to test $conn->is_idle()? I don't think so!
+ }
+
+ my $i0 = ZOOM::event(\@conn);
+ $this->log("irspy_event",
+ "ZOOM_event(", scalar(@conn), " connections) = $i0");
+ last if $i0 == 0 || $i0 == -3; # no events or no connections