+2010-02-04 Andras Becsi <abecsi@inf.u-szeged.hu>
+
+ Reviewed by Tor Arne Vestbø.
+
+ Implement a locking and scheduling mechanism for http testing sessions to be able
+ to run multiple instances of run-webkit-tests parallel on the same machine.
+ If a test session wants to run http tests and this feature is enabled, the pending
+ sessions create lockfiles with sequential lock numbers. These locks are used to schedule
+ the running test sessions in first come first served order. An exclusive lock ensures
+ that the lock numbers are sequential to avoid deadlocks and starvation.
+ Because the buildbot master specifies the flags used by slaves we need an environment
+ variable too to be able to use the feature per-slave.
+ Exporting WEBKIT_WAIT_FOR_HTTPD=1 before testing or using the --wait-for-httpd
+ flag enables this feature, otherwise this patch has no effect on the testing whatsoever.
+
+ https://bugs.webkit.org/show_bug.cgi?id=33153
+
+ * Scripts/run-webkit-tests:
+ * Scripts/webkitperl/httpd.pm:
+
2010-01-22 Tor Arne Vestbø <tor.arne.vestbo@nokia.com>
Reviewed by Simon Hausmann.
my $useRemoteLinksToTests = 0;
my $useValgrind = 0;
my $verbose = 0;
+my $shouldWaitForHTTPD = 0;
my @leaksFilenames;
--exit-after-n-failures N Exit after the first N failures instead of running all tests
-h|--help Show this help message
--[no-]http Run (or do not run) http tests (default: $httpDefault)
+ --[no-]wait-for-httpd Wait for httpd if some other test session is using it already (same as WEBKIT_WAIT_FOR_HTTPD=1). (default: $shouldWaitForHTTPD)
-i|--ignore-tests Comma-separated list of directories or tests to ignore
--iterations n Number of times to run the set of tests (e.g. ABCABCABC)
--[no-]launch-safari Launch (or do not launch) Safari to display test results (default: $launchSafariDefault)
'guard-malloc|g' => \$guardMalloc,
'help|h' => \$showHelp,
'http!' => \$testHTTP,
+ 'wait-for-httpd!' => \$shouldWaitForHTTPD,
'ignore-metrics!' => \$ignoreMetrics,
'ignore-tests|i=s' => \$ignoreTests,
'iterations=i' => \$iterations,
my $configuration = configuration();
+# We need an environment variable to be able to enable the feature per-slave
+$shouldWaitForHTTPD = $ENV{"WEBKIT_WAIT_FOR_HTTPD"} unless ($shouldWaitForHTTPD);
$verbose = 1 if $testsPerDumpTool == 1;
if ($shouldCheckLeaks && $testsPerDumpTool > 1000) {
}
}
}
-printf "\n%0.2fs total testing time\n", (time - $overallStartTime) . "";
+my $totalTestingTime = time - $overallStartTime;
+my $waitTime = getWaitTime();
+if ($waitTime > 0.1) {
+ my $normalizedTestingTime = $totalTestingTime - $waitTime;
+ printf "\n%0.2fs HTTPD waiting time\n", $waitTime . "";
+ printf "%0.2fs normalized testing time", $normalizedTestingTime . "";
+}
+printf "\n%0.2fs total testing time\n", $totalTestingTime . "";
!$isDumpToolOpen || die "Failed to close $dumpToolName.\n";
my @defaultArgs = getDefaultConfigForTestDirectory($testDirectory);
@args = (@defaultArgs, @args);
+ waitForHTTPDLock() if $shouldWaitForHTTPD;
$isHttpdOpen = openHTTPD(@args);
}
use File::Path;
use File::Spec;
use File::Spec::Functions;
+use Fcntl ':flock';
use IPC::Open2;
use webkitdirs;
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw(Exporter);
- @EXPORT = qw(&getHTTPDPath &getDefaultConfigForTestDirectory &openHTTPD &closeHTTPD &getHTTPDPid &setShouldWaitForUserInterrupt);
+ @EXPORT = qw(&getHTTPDPath &getDefaultConfigForTestDirectory &openHTTPD &closeHTTPD &setShouldWaitForUserInterrupt &waitForHTTPDLock &getWaitTime);
%EXPORT_TAGS = ( );
@EXPORT_OK = ();
}
my $tmpDir = "/tmp";
+my $httpdLockPrefix = "WebKitHttpd.lock.";
+my $myLockFile;
+my $exclusiveLockFile = File::Spec->catfile($tmpDir, "WebKit.lock");
my $httpdPath;
my $httpdPidDir = File::Spec->catfile($tmpDir, "WebKit");
my $httpdPidFile = File::Spec->catfile($httpdPidDir, "httpd.pid");
my $httpdPid;
my $waitForUserInterrupt = 0;
+my $waitBeginTime;
+my $waitEndTime;
-$SIG{'INT'} = 'cleanup';
-$SIG{'TERM'} = 'cleanup';
+$SIG{'INT'} = 'handleInterrupt';
+$SIG{'TERM'} = 'handleInterrupt';
sub getHTTPDPath
{
--$retryCount;
}
- die "Timed out waiting for httpd to quit" unless $retryCount;
+ if (!$retryCount) {
+ cleanUp();
+ die "Timed out waiting for httpd to quit";
+ }
}
}
}
if (!$retryCount) {
- rmtree $httpdPidDir;
+ cleanUp();
die "Timed out waiting for httpd to start";
}
sub closeHTTPD
{
close HTTPDIN;
+ my $retryCount = 20;
if ($httpdPid) {
kill 15, $httpdPid;
- my $retryCount = 20;
while (-f $httpdPidFile && $retryCount) {
sleep 1;
--$retryCount;
}
-
- if (!$retryCount) {
- print STDERR "Timed out waiting for httpd to terminate!\n";
- return 0;
- }
}
- rmdir $httpdPidDir;
+ cleanUp();
+ if (!$retryCount) {
+ print STDERR "Timed out waiting for httpd to terminate!\n";
+ return 0;
+ }
return 1;
}
$waitForUserInterrupt = 1;
}
-sub cleanup
+sub handleInterrupt
{
closeHTTPD();
print "\n";
exit(1);
}
+
+sub cleanUp
+{
+ rmdir $httpdPidDir;
+ unlink $exclusiveLockFile;
+ unlink $myLockFile if $myLockFile;
+}
+
+sub extractLockNumber
+{
+ my ($lockFile) = @_;
+ return -1 unless $lockFile;
+ return substr($lockFile, length($httpdLockPrefix));
+}
+
+sub getLockFiles
+{
+ opendir(TMPDIR, $tmpDir) or die "Could not open " . $tmpDir . ".";
+ my @lockFiles = grep {m/^$httpdLockPrefix\d+$/} readdir(TMPDIR);
+ @lockFiles = sort { extractLockNumber($a) <=> extractLockNumber($b) } @lockFiles;
+ closedir(TMPDIR);
+ return @lockFiles;
+}
+
+sub getNextAvailableLockNumber
+{
+ my @lockFiles = getLockFiles();
+ return 0 unless @lockFiles;
+ return extractLockNumber($lockFiles[-1]) + 1;
+}
+
+sub getLockNumberForCurrentRunning
+{
+ my @lockFiles = getLockFiles();
+ return 0 unless @lockFiles;
+ return extractLockNumber($lockFiles[0]);
+}
+
+sub waitForHTTPDLock
+{
+ $waitBeginTime = time;
+ scheduleHttpTesting();
+ # If we are the only one waiting for Apache just run the tests without any further checking
+ if (scalar getLockFiles() > 1) {
+ my $currentLockFile = File::Spec->catfile($tmpDir, "$httpdLockPrefix" . getLockNumberForCurrentRunning());
+ my $currentLockPid = <SCHEDULER_LOCK> if (-f $currentLockFile && open(SCHEDULER_LOCK, "<$currentLockFile"));
+ # Wait until we are allowed to run the http tests
+ while ($currentLockPid && $currentLockPid != $$) {
+ $currentLockFile = File::Spec->catfile($tmpDir, "$httpdLockPrefix" . getLockNumberForCurrentRunning());
+ if ($currentLockFile eq $myLockFile) {
+ $currentLockPid = <SCHEDULER_LOCK> if open(SCHEDULER_LOCK, "<$currentLockFile");
+ if ($currentLockPid != $$) {
+ print STDERR "\nPID mismatch.\n";
+ last;
+ }
+ } else {
+ sleep 1;
+ }
+ }
+ }
+ $waitEndTime = time;
+}
+
+sub scheduleHttpTesting
+{
+ # We need an exclusive lock file to avoid deadlocks and starvation and ensure that the scheduler lock numbers are sequential.
+ # The scheduler locks are used to schedule the running test sessions in first come first served order.
+ while (!(open(SEQUENTIAL_GUARD_LOCK, ">$exclusiveLockFile") && flock(SEQUENTIAL_GUARD_LOCK, LOCK_EX|LOCK_NB))) {}
+ $myLockFile = File::Spec->catfile($tmpDir, "$httpdLockPrefix" . getNextAvailableLockNumber());
+ open(SCHEDULER_LOCK, ">$myLockFile");
+ print SCHEDULER_LOCK "$$";
+ print SEQUENTIAL_GUARD_LOCK "$$";
+ close(SCHEDULER_LOCK);
+ close(SEQUENTIAL_GUARD_LOCK);
+ unlink $exclusiveLockFile;
+}
+
+sub getWaitTime
+{
+ my $waitTime = 0;
+ if ($waitBeginTime && $waitEndTime) {
+ $waitTime = $waitEndTime - $waitBeginTime;
+ }
+ return $waitTime;
+}