diff --git a/Zend/tests/@CAN_BE_PARALLELISED b/Zend/tests/@CAN_BE_PARALLELISED deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/Zend/tests/traits/@CAN_BE_PARALLELISED b/Zend/tests/traits/@CAN_BE_PARALLELISED deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ext/curl/tests/CONFLICTS b/ext/curl/tests/CONFLICTS new file mode 100644 index 00000000000..254defddb53 --- /dev/null +++ b/ext/curl/tests/CONFLICTS @@ -0,0 +1 @@ +server diff --git a/ext/date/tests/@CAN_BE_PARALLELISED b/ext/date/tests/@CAN_BE_PARALLELISED deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ext/dom/tests/@CAN_BE_PARALLELISED b/ext/dom/tests/@CAN_BE_PARALLELISED deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ext/ftp/tests/CONFLICTS b/ext/ftp/tests/CONFLICTS new file mode 100644 index 00000000000..254defddb53 --- /dev/null +++ b/ext/ftp/tests/CONFLICTS @@ -0,0 +1 @@ +server diff --git a/ext/mysqli/tests/CONFLICTS b/ext/mysqli/tests/CONFLICTS new file mode 100644 index 00000000000..0eaebf1275b --- /dev/null +++ b/ext/mysqli/tests/CONFLICTS @@ -0,0 +1 @@ +mysql diff --git a/ext/opcache/tests/bug66338.phpt b/ext/opcache/tests/bug66338.phpt index 8b30391c4ff..5cd96937936 100644 --- a/ext/opcache/tests/bug66338.phpt +++ b/ext/opcache/tests/bug66338.phpt @@ -4,6 +4,8 @@ Bug #66338 (Optimization binding of class constants is not safely opcacheable) opcache.enable=0 --SKIPIF-- +--CONFLICTS-- +server --FILE-- +--CONFLICTS-- +server --FILE-- +--CONFLICTS-- +server --FILE-- +--CONFLICTS-- +server --FILE-- +--CONFLICTS-- +server --FILE-- +--CONFLICTS-- +server --FILE-- $tests) { - if (count($tests) < 64 || !is_string($dir)) { - continue; - } - if (file_exists($dir . DIRECTORY_SEPARATOR . '@CAN_BE_PARALLELISED')) { - foreach (array_chunk($tests, 64) as $testsChunk) { - $testDirsToGo[] = $testsChunk; + // Cache per-directory conflicts in a separate map, so we compute these only once. + $dir = dirname($file); + if (!isset($dirConflictsWith[$dir])) { + $dirConflicts = []; + if (file_exists($dir . '/CONFLICTS')) { + $contents = file_get_contents($dir . '/CONFLICTS'); + $dirConflicts = array_map('trim', explode("\n", trim($contents))); + } + $dirConflictsWith[$dir] = $dirConflicts; } - unset($testDirsToGo[$dir]); - } - } - - // Sort test dirs so the biggest ones are handled first, so we spend less - // time waiting on workers tasked with very large dirs. - // This is an ascending sort because items are popped off the end. - // Thank you Rasmus for this idea :) - uasort($testDirsToGo, function ($a, $b) { - return count($a) <=> count($b); - }); - - $testDirsInProgress = 0; - - echo "Isolated ", count($testDirsToGo), " directories to be tested in parallel.\n"; - - $shamedDirs = array_reverse(array_filter($testDirsToGo, function ($files) { - return count($files) > 100; - }), true); - - if ($shamedDirs) { - $shameList = ""; - foreach ($shamedDirs as $dir => $shame) { - $shameList .= "\n$dir: " . count($shame) . " files"; + $conflicts = $dirConflictsWith[$dir]; } - echo << 0)) { + while ($test_files || $testsInProgress > 0) { $toRead = array_values($workerSocks); $toWrite = NULL; $toExcept = NULL; @@ -1532,15 +1502,43 @@ escape: } switch ($message["type"]) { - case "dir_finished": - $testDirsInProgress--; + case "tests_finished": + $testsInProgress--; + foreach ($activeConflicts as $key => $workerId) { + if ($workerId === $i) { + unset($activeConflicts[$key]); + if (isset($waitingTests[$key])) { + while ($test = array_pop($waitingTests[$key])) { + $test_files[] = $test; + } + unset($waitingTests[$key]); + } + } + } // intentional fall-through case "ready": - if ($testDir = array_pop($testDirsToGo)) { - $testDirsInProgress++; + // Batch multiple tests to reduce communication overhead. + $files = []; + $batchSize = 32; + while (count($files) <= $batchSize && $file = array_pop($test_files)) { + foreach ($fileConflictsWith[$file] as $conflictKey) { + if (isset($activeConflicts[$conflictKey])) { + $waitingTests[$conflictKey][] = $file; + continue 2; + } + } + $files[] = $file; + } + if ($files) { + foreach ($files as $file) { + foreach ($fileConflictsWith[$file] as $conflictKey) { + $activeConflicts[$conflictKey] = $i; + } + } + $testsInProgress++; send_message($workerSocks[$i], [ "type" => "run_tests", - "test_files" => $testDir, + "test_files" => $files, "env" => $env, "redir_tested" => $redir_tested ]); @@ -1609,8 +1607,8 @@ escape: kill_children($workerProcs); - if ($testDirsInProgress < 0) { - error("$testDirsInProgress test directories “in progress”, which is less than zero. THIS SHOULD NOT HAPPEN."); + if ($testsInProgress < 0) { + error("$testsInProgress test batches “in progress”, which is less than zero. THIS SHOULD NOT HAPPEN."); } } @@ -1677,7 +1675,7 @@ function run_worker() { case "run_tests": run_all_tests($command["test_files"], $command["env"], $command["redir_tested"]); send_message($workerSock, [ - "type" => "dir_finished" + "type" => "tests_finished" ]); break; default: @@ -1793,7 +1791,7 @@ TEST $file 'CAPTURE_STDIO', 'STDIN', 'CGI', 'PHPDBG', 'INI', 'ENV', 'EXTENSIONS', 'SKIPIF', 'XFAIL', 'CLEAN', - 'CREDITS', 'DESCRIPTION', + 'CREDITS', 'DESCRIPTION', 'CONFLICTS', ))) { $bork_info = 'Unknown section "' . $section . '"'; } diff --git a/sapi/cli/tests/CONFLICTS b/sapi/cli/tests/CONFLICTS new file mode 100644 index 00000000000..254defddb53 --- /dev/null +++ b/sapi/cli/tests/CONFLICTS @@ -0,0 +1 @@ +server diff --git a/tests/basic/bug67198.phpt b/tests/basic/bug67198.phpt index 4c2322b6dec..184916197b8 100644 --- a/tests/basic/bug67198.phpt +++ b/tests/basic/bug67198.phpt @@ -2,6 +2,8 @@ php://input is empty when enable_post_data_reading=Off --INI-- allow_url_fopen=1 +--CONFLICTS-- +server --SKIPIF--