? modules/image/t.patch
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1255
diff -u -p -r1.1255 common.inc
--- includes/common.inc	17 Nov 2010 04:15:37 -0000	1.1255
+++ includes/common.inc	18 Nov 2010 19:37:39 -0000
@@ -3060,7 +3060,12 @@ function drupal_aggregate_css(&$css_grou
       // the group's data property to the file path of the aggregate file.
       case 'file':
         if ($group['preprocess'] && $preprocess_css) {
-          $css_groups[$key]['data'] = drupal_build_css_cache($group['items']);
+          $uri = drupal_build_css_cache($group['items']);
+          // Only include the file if was written successfully. Errors are logged
+          // using watchdog.
+          if ($uri) {
+            $css_groups[$key]['data'] = $xxx;
+          }
         }
         break;
       // Aggregate all inline CSS content into the group's data property.
@@ -3316,69 +3321,100 @@ function drupal_build_css_cache($css) {
   $data = '';
   $uri = '';
   $map = variable_get('drupal_css_cache_files', array());
-  $key = hash('sha256', serialize($css));
+  $version = variable_get('drupal_css_cache_version', 1);
+  $key = hash('sha256', $version . serialize($css));
   if (isset($map[$key])) {
     $uri = $map[$key];
   }
 
-  if (empty($uri) || !file_exists($uri)) {
-    // Build aggregate CSS file.
-    foreach ($css as $stylesheet) {
-      // Only 'file' stylesheets can be aggregated.
-      if ($stylesheet['type'] == 'file') {
-        $contents = drupal_load_stylesheet($stylesheet['data'], TRUE);
-
-        // Build the base URL of this CSS file: start with the full URL.
-        $css_base_url = file_create_url($stylesheet['data']);
-        // Move to the parent.
-        $css_base_url = substr($css_base_url, 0, strrpos($css_base_url, '/'));
-        // Simplify to a relative URL if the stylesheet URL starts with the
-        // base URL of the website.
-        if (substr($css_base_url, 0, strlen($GLOBALS['base_root'])) == $GLOBALS['base_root']) {
-          $css_base_url = substr($css_base_url, strlen($GLOBALS['base_root']));
+  $lock_acquire_attempts = 0;
+  $lock_name = 'css_cache:' . $key;
+  $mtime = $uri ? filemtime($uri) : 0;
+  do {
+    if (empty($uri) || !$mtime || (isset($map['clear']) && $mtime < $map['clear'])) {
+      if ($lock_acquired = lock_acquire($lock_name)) {
+        // Build aggregate CSS file.
+        foreach ($css as $stylesheet) {
+          // Only 'file' stylesheets can be aggregated.
+          if ($stylesheet['type'] == 'file') {
+            $contents = drupal_load_stylesheet($stylesheet['data'], TRUE);
+
+            // Build the base URL of this CSS file: start with the full URL.
+            $css_base_url = file_create_url($stylesheet['data']);
+            // Move to the parent.
+            $css_base_url = substr($css_base_url, 0, strrpos($css_base_url, '/'));
+            // Simplify to a relative URL if the stylesheet URL starts with the
+            // base URL of the website.
+            if (substr($css_base_url, 0, strlen($GLOBALS['base_root'])) == $GLOBALS['base_root']) {
+              $css_base_url = substr($css_base_url, strlen($GLOBALS['base_root']));
+            }
+
+            _drupal_build_css_path(NULL, $css_base_url . '/');
+            // Anchor all paths in the CSS with its base URL, ignoring external and absolute paths.
+            $data .= preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', '_drupal_build_css_path', $contents);
+          }
         }
 
-        _drupal_build_css_path(NULL, $css_base_url . '/');
-        // Anchor all paths in the CSS with its base URL, ignoring external and absolute paths.
-        $data .= preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', '_drupal_build_css_path', $contents);
+        // Per the W3C specification at http://www.w3.org/TR/REC-CSS2/cascade.html#at-import,
+        // @import rules must proceed any other style, so we move those to the top.
+        $regexp = '/@import[^;]+;/i';
+        preg_match_all($regexp, $data, $matches);
+        $data = preg_replace($regexp, '', $data);
+        $data = implode('', $matches[0]) . $data;
+
+        // Prefix filename to prevent blocking by firewalls which reject files
+        // starting with "ad*".
+        $filename = 'css_' . drupal_hash_base64($data) . '.css';
+        // Create the css/ within the files folder.
+        $csspath = 'public://css';
+        $uri = $csspath . '/' . $filename;
+        // Create the CSS file.
+        file_prepare_directory($csspath, FILE_CREATE_DIRECTORY);
+        if (!file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) {
+          lock_release($lock_name);
+          return $mtime ? $uri : FALSE;
+        }
+        // Update the map atomically.
+        _drupal_update_css_js_map('css', $key, $uri);
+        lock_release($lock_name);
       }
-    }
-
-    // Per the W3C specification at http://www.w3.org/TR/REC-CSS2/cascade.html#at-import,
-    // @import rules must proceed any other style, so we move those to the top.
-    $regexp = '/@import[^;]+;/i';
-    preg_match_all($regexp, $data, $matches);
-    $data = preg_replace($regexp, '', $data);
-    $data = implode('', $matches[0]) . $data;
-
-    // Prefix filename to prevent blocking by firewalls which reject files
-    // starting with "ad*".
-    $filename = 'css_' . drupal_hash_base64($data) . '.css';
-    // Create the css/ within the files folder.
-    $csspath = 'public://css';
-    $uri = $csspath . '/' . $filename;
-    // Create the CSS file.
-    file_prepare_directory($csspath, FILE_CREATE_DIRECTORY);
-    if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) {
-      return FALSE;
-    }
-    // If CSS gzip compression is enabled, clean URLs are enabled (which means
-    // that rewrite rules are working) and the zlib extension is available then
-    // create a gzipped version of this file. This file is served conditionally
-    // to browsers that accept gzip using .htaccess rules.
-    if (variable_get('css_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
-      if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
-        return FALSE;
+      else {
+        if ($mtime) {
+          break;
+        }
+        if ($lock_acquire_attempts == 0) {
+          $prev_key = hash('sha256', ($version - 1) . serialize($css));
+          if (isset($map[$prev_key]) && filemtime($map[$prev_key])) {
+            return $map[$prev_key];
+          }
+        }
+        lock_wait($lock_name);
       }
+      $mtime = $uri ? filemtime($uri) : 0;
     }
-    // Save the updated map.
-    $map[$key] = $uri;
-    variable_set('drupal_css_cache_files', $map);
   }
+  while (!$mtime && $lock_acquired === FALSE && ++$lock_acquire_attempts < 5);
   return $uri;
 }
 
 /**
+ * Helper function to atomically set the css and js cache maps.
+ */
+function _drupal_update_css_js_map($type, $key, $uri) {
+  $lock_name = $type . '_cache';
+  $var_name = 'drupal_' . $type . '_cache_files';
+  $lock_acquire_attempts = 0;
+  while (!lock_acquire($lock_name) && ++$lock_acquire_attempts < 5) {
+    lock_wait($lock_name);
+  }
+  $vars = variable_initialize(array());
+  $map = isset($vars[$var_name]) ? $vars[$var_name] : array();
+  $map[$key] = $uri;
+  variable_set($var_name, $map);
+  lock_release($lock_name);
+}
+
+/**
  * Helper function for drupal_build_css_cache().
  *
  * This function will prefix all paths within a CSS file.
@@ -4653,42 +4689,67 @@ function drupal_build_js_cache($files) {
   $contents = '';
   $uri = '';
   $map = variable_get('drupal_js_cache_files', array());
-  $key = hash('sha256', serialize($files));
+  $version = variable_get('drupal_js_cache_version', 1);
+  $key = hash('sha256', $version . serialize($files));
   if (isset($map[$key])) {
     $uri = $map[$key];
   }
 
-  if (empty($uri) || !file_exists($uri)) {
-    // Build aggregate JS file.
-    foreach ($files as $path => $info) {
-      if ($info['preprocess']) {
-        // Append a ';' and a newline after each JS file to prevent them from running together.
-        $contents .= file_get_contents($path) . ";\n";
+  $lock_acquire_attempts = 0;
+  $lock_name = 'js_cache:' . $uri;
+  $mtime = $uri ? filemtime($uri) : 0;
+  do {
+    if (empty($uri) || !$mtime || (isset($map['clear']) && $mtime < $map['clear'])) {
+      if ($lock_acquired = lock_acquire($lock_name)) {
+        // Build aggregate JS file.
+        foreach ($files as $path => $info) {
+          if ($info['preprocess']) {
+            // Append a ';' and a newline after each JS file to prevent them from running together.
+            $contents .= file_get_contents($path) . ";\n";
+          }
+        }
+        // Prefix filename to prevent blocking by firewalls which reject files
+        // starting with "ad*".
+        $filename = 'js_' . drupal_hash_base64($contents) . '.js';
+        // Create the js/ within the files folder.
+        $jspath = 'public://js';
+        $uri = $jspath . '/' . $filename;
+        // Create the JS file.
+        file_prepare_directory($jspath, FILE_CREATE_DIRECTORY);
+        if (!file_unmanaged_save_data($contents, $uri, FILE_EXISTS_REPLACE)) {
+          lock_release($lock_name);
+          return $mtime ? $uri : FALSE;
+        }
+        // If JS gzip compression is enabled, clean URLs are enabled (which means
+        // that rewrite rules are working) and the zlib extension is available then
+        // create a gzipped version of this file. This file is served conditionally
+        // to browsers that accept gzip using .htaccess rules.
+        if (variable_get('js_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
+          if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($contents, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
+            lock_release($lock_name);
+            return $mtime ? $uri : FALSE;
+          }
+        }
+        // Update the map atomically.
+        _drupal_update_css_js_map('js', $key, $uri);
+        lock_release($lock_name);
       }
-    }
-    // Prefix filename to prevent blocking by firewalls which reject files
-    // starting with "ad*".
-    $filename = 'js_' . drupal_hash_base64($contents) . '.js';
-    // Create the js/ within the files folder.
-    $jspath = 'public://js';
-    $uri = $jspath . '/' . $filename;
-    // Create the JS file.
-    file_prepare_directory($jspath, FILE_CREATE_DIRECTORY);
-    if (!file_exists($uri) && !file_unmanaged_save_data($contents, $uri, FILE_EXISTS_REPLACE)) {
-      return FALSE;
-    }
-    // If JS gzip compression is enabled, clean URLs are enabled (which means
-    // that rewrite rules are working) and the zlib extension is available then
-    // create a gzipped version of this file. This file is served conditionally
-    // to browsers that accept gzip using .htaccess rules.
-    if (variable_get('js_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
-      if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($contents, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
-        return FALSE;
+      else {
+        if ($mtime) {
+          break;
+        }
+        if ($lock_acquire_attempts == 0) {
+          $prev_key = hash('sha256', ($version - 1) . serialize($css));
+          if (isset($map[$prev_key]) && filemtime($map[$prev_key])) {
+            return $map[$prev_key];
+          }
+        }
+        lock_wait($lock_name);
       }
+      $mtime = $uri ? filemtime($uri) : 0;
     }
-    $map[$key] = $uri;
-    variable_set('drupal_js_cache_files', $map);
   }
+  while (!$mtime && $lock_acquired === FALSE && ++$lock_acquire_attempts < 5);
   return $uri;
 }
 
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.988
diff -u -p -r1.988 system.module
--- modules/system/system.module	12 Nov 2010 01:36:08 -0000	1.988
+++ modules/system/system.module	18 Nov 2010 19:37:40 -0000
@@ -2949,6 +2949,8 @@ function system_cron() {
   foreach ($cache_tables as $table) {
     cache_clear_all(NULL, $table);
   }
+  system_cleanup_cache_map('css');
+  system_cleanup_cache_map('js');
 
   // Cleanup the batch table and the queue for failed batches.
   db_delete('batch')
@@ -2980,6 +2982,34 @@ function system_flush_caches() {
 }
 
 /**
+ * Helper function to remove stale css and js from the cache maps.
+ *
+ * @param $type
+ *   'css' or 'js'
+ */
+function system_cleanup_cache_map($type) {
+  $var = 'drupal_' . $type . '_cache_files';
+  $map = variable_get($var, array());
+  if (!empty($map['clear'])) {
+    $clear = $map['clear'];
+    foreach ($map as $key => $uri) {
+      if ($key == 'clear') {
+        continue;
+      }
+      $mtime = filemtime($uri);
+      if ($mtime && $mtime < $clear && $mtime < REQUEST_TIME - variable_get('drupal_stale_map_threshold', 3600)) {
+        unset($map[$key]);
+        $changed = TRUE;
+      }
+    }
+    if (!isset($changed)) {
+      unset($map['clear']);
+    }
+    variable_set($var, $map);
+  }
+}
+
+/**
  * Implements hook_action_info().
  */
 function system_action_info() {
