2010-02-04 Andras Becsi <abecsi@inf.u-szeged.hu>
authorabecsi@webkit.org <abecsi@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Feb 2010 14:52:10 +0000 (14:52 +0000)
committerabecsi@webkit.org <abecsi@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Feb 2010 14:52:10 +0000 (14:52 +0000)
        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

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@54342 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebKitTools/ChangeLog
WebKitTools/Scripts/run-webkit-tests
WebKitTools/Scripts/webkitperl/httpd.pm

index ed2e4bf..7ac1940 100644 (file)
@@ -1,3 +1,23 @@
+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.
index 6b21e48..7815488 100755 (executable)
@@ -154,6 +154,7 @@ my $treatSkipped = "default";
 my $useRemoteLinksToTests = 0;
 my $useValgrind = 0;
 my $verbose = 0;
+my $shouldWaitForHTTPD = 0;
 
 my @leaksFilenames;
 
@@ -224,6 +225,7 @@ Usage: $programName [options] [testdir|testpath ...]
   --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)
@@ -267,6 +269,7 @@ my $getOptionsResult = GetOptions(
     'guard-malloc|g' => \$guardMalloc,
     'help|h' => \$showHelp,
     'http!' => \$testHTTP,
+    'wait-for-httpd!' => \$shouldWaitForHTTPD,
     'ignore-metrics!' => \$ignoreMetrics,
     'ignore-tests|i=s' => \$ignoreTests,
     'iterations=i' => \$iterations,
@@ -308,6 +311,8 @@ my $skippedOnly = $treatSkipped eq "only";
 
 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) {
@@ -943,7 +948,14 @@ for my $test (@tests) {
         }
     }
 }
-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";
 
@@ -1360,6 +1372,7 @@ sub configureAndOpenHTTPDIfNeeded()
     my @defaultArgs = getDefaultConfigForTestDirectory($testDirectory);
     @args = (@defaultArgs, @args);
 
+    waitForHTTPDLock() if $shouldWaitForHTTPD;
     $isHttpdOpen = openHTTPD(@args);
 }
 
index d082870..b362e51 100644 (file)
@@ -34,6 +34,7 @@ use warnings;
 use File::Path;
 use File::Spec;
 use File::Spec::Functions;
+use Fcntl ':flock';
 use IPC::Open2;
 
 use webkitdirs;
@@ -43,20 +44,25 @@ BEGIN {
    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
 {
@@ -141,7 +147,10 @@ sub openHTTPD(@)
                 --$retryCount;
             }
 
-            die "Timed out waiting for httpd to quit" unless $retryCount;
+            if (!$retryCount) {
+                cleanUp();
+                die "Timed out waiting for httpd to quit";
+            }
         }
     }
 
@@ -156,7 +165,7 @@ sub openHTTPD(@)
     }
 
     if (!$retryCount) {
-        rmtree $httpdPidDir;
+        cleanUp();
         die "Timed out waiting for httpd to start";
     }
 
@@ -172,20 +181,19 @@ sub openHTTPD(@)
 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;
 }
 
@@ -194,9 +202,94 @@ sub setShouldWaitForUserInterrupt
     $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;
+}