]> granicus.if.org Git - php/commitdiff
Implement periodic PCRE compiled regexp cache cleanup, to avoid memory
authorAndrei Zmievski <andrei@php.net>
Tue, 24 Aug 2004 20:57:32 +0000 (20:57 +0000)
committerAndrei Zmievski <andrei@php.net>
Tue, 24 Aug 2004 20:57:32 +0000 (20:57 +0000)
exhaustion.

NEWS
ext/pcre/php_pcre.c

diff --git a/NEWS b/NEWS
index 5f310a9cc6ae6efa39d1fbf612164f2e9d098fc6..05ddc2ac693fdfb75c9b51af750dfa203b00e124 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 PHP 4                                                                      NEWS
 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 ?? ??? 2004, Version 4.3.9
+- Implemented periodic PCRE compiled regexp cache cleanup, to avoid memory
+  exhaustion. (Andrei)
 - Fixed bug with raw_post_data not getting set (Brian)
 - Fixed a file-descriptor leak with phpinfo() and other 'special' URLs (Zeev)
 - Fixed bug #29753 (mcal_fetch_event() allows 2nd argument to be optional).
index 59c480d730bc5dcac470558646a1707ce59c413c..aac64c8fbe8ba8fb84be643c6b0750be6da93b50 100644 (file)
@@ -108,6 +108,63 @@ static PHP_MSHUTDOWN_FUNCTION(pcre)
 }
 /* }}} */
 
+#define PCRE_CACHE_SIZE 4096
+
+/* {{{ static pcre_clean_cache */
+static void pcre_clean_cache()
+{
+       HashTable *ht = &PCRE_G(pcre_cache);
+       Bucket *p = NULL;
+       int clean_size = PCRE_CACHE_SIZE / 8;
+
+       HANDLE_BLOCK_INTERRUPTIONS();
+
+       do {
+               p = ht->pListHead;
+
+               if (ht->pDestructor) {
+                       ht->pDestructor(p->pData);
+               }
+               if (!p->pDataPtr) {
+                       pefree(p->pData, ht->persistent);
+               }
+
+               if (p->pLast) {
+                       p->pLast->pNext = p->pNext;
+               } else {
+                       uint nIndex;
+
+                       nIndex = p->h & ht->nTableMask;
+                       ht->arBuckets[nIndex] = p->pNext;
+               }
+               if (p->pNext) {
+                       p->pNext->pLast = p->pLast;
+               } else {
+                       /* Nothing to do as this list doesn't have a tail */
+               }
+
+               if (p->pListLast != NULL) {
+                       p->pListLast->pListNext = p->pListNext;
+               } else {
+                       /* Deleting the head of the list */
+                       ht->pListHead = p->pListNext;
+               }
+               if (p->pListNext != NULL) {
+                       p->pListNext->pListLast = p->pListLast;
+               } else {
+                       ht->pListTail = p->pListLast;
+               }
+               if (ht->pInternalPointer == p) {
+                       ht->pInternalPointer = p->pListNext;
+               }
+               pefree(p, ht->persistent);
+               ht->nNumOfElements--;
+       } while (ht->nNumOfElements > PCRE_CACHE_SIZE - clean_size);
+
+       HANDLE_UNBLOCK_INTERRUPTIONS();
+}
+/* }}} */
+
 /* {{{ pcre_get_compiled_regex
  */
 PHPAPI pcre* pcre_get_compiled_regex(char *regex, pcre_extra **extra, int *preg_options) {
@@ -279,6 +336,15 @@ PHPAPI pcre* pcre_get_compiled_regex(char *regex, pcre_extra **extra, int *preg_
        
        efree(pattern);
 
+       /*
+        * If we reached cache limit, clean out the items from the head of the list;
+        * these are supposedly the oldest ones (but not necessarily the least used
+        * ones).
+        */
+       if (zend_hash_num_elements(&PCRE_G(pcre_cache)) == PCRE_CACHE_SIZE) {
+               pcre_clean_cache();
+       }
+
        /* Store the compiled pattern and extra info in the cache. */
        new_entry.re = re;
        new_entry.extra = *extra;