From 3682e0a7a26931aabca2b6e54eb08efd7dc0430b Mon Sep 17 00:00:00 2001 From: Dragan Dosen Date: Tue, 1 Apr 2008 21:40:51 +0200 Subject: [PATCH] New PHP5 APC - version 3.0.18, using PHP5 5.2.0-8+etch10, 20060613+lfs. --- CHANGELOG | 10 + INSTALL | 417 ++++++++ LICENSE | 68 ++ NOTICE | 43 + TECHNOTES.txt | 361 +++++++ TODO | 30 + apc.c | 64 +- apc.dsp | 207 ++++ apc.h | 121 +++ apc.php | 1341 ++++++++++++++++++++++++++ apc_cache.c | 1129 ++++++++++++++++++++++ apc_cache.h | 343 +++++++ apc_compile.c | 2572 ++++++++++++++++++++++++++++++++++++++++++++++++++ apc_compile.h | 134 +++ apc_debug.c | 57 ++ apc_debug.h | 1 + apc_fcntl.c | 118 +++ apc_fcntl.h | 50 + apc_fcntl_win32.c | 117 +++ apc_futex.c | 116 +++ apc_futex.h | 55 ++ apc_globals.h | 8 +- apc_lock.h | 105 +++ apc_main.c | 142 ++- apc_main.h | 57 ++ apc_mmap.c | 4 +- apc_php.h | 71 ++ apc_pool.c | 344 +++++++ apc_pool.h | 58 ++ apc_pthreadmutex.h | 48 + apc_rfc1867.c | 195 ++++ apc_sem.c | 177 ++++ apc_sem.h | 51 + apc_shm.c | 110 +++ apc_shm.h | 54 ++ apc_signal.c | 18 +- apc_signal.h | 3 +- apc_sma.c | 32 +- apc_sma.h | 88 ++ apc_spin.c | 65 ++ apc_spin.h | 48 + apc_stack.c | 105 +++ apc_stack.h | 58 ++ apc_zend.c | 277 ++++++ apc_zend.h | 53 ++ arch/atomic.h | 38 + arch/i386/atomic.h | 79 ++ arch/x86_64/atomic.h | 80 ++ config.m4 | 24 +- debian/changelog | 7 + debian/rules | 2 +- pgsql_s_lock.c | 481 ++++++++++ pgsql_s_lock.h | 928 ++++++++++++++++++ php_apc.c | 64 +- php_apc.h | 52 + tests/apc_001.phpt | 27 + tests/apc_002.phpt | 34 + tests/apc_003.phpt | 112 +++ tests/skipif.inc | 6 + 59 files changed, 11301 insertions(+), 158 deletions(-) create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 TECHNOTES.txt create mode 100644 TODO create mode 100644 apc.dsp create mode 100644 apc.h create mode 100644 apc.php create mode 100644 apc_cache.c create mode 100644 apc_cache.h create mode 100644 apc_compile.c create mode 100644 apc_compile.h create mode 100644 apc_debug.c create mode 100644 apc_debug.h create mode 100644 apc_fcntl.c create mode 100644 apc_fcntl.h create mode 100644 apc_fcntl_win32.c create mode 100644 apc_futex.c create mode 100644 apc_futex.h create mode 100644 apc_lock.h create mode 100644 apc_main.h create mode 100644 apc_php.h create mode 100644 apc_pool.c create mode 100644 apc_pool.h create mode 100644 apc_pthreadmutex.h create mode 100644 apc_rfc1867.c create mode 100644 apc_sem.c create mode 100644 apc_sem.h create mode 100644 apc_shm.c create mode 100644 apc_shm.h create mode 100644 apc_sma.h create mode 100644 apc_spin.c create mode 100644 apc_spin.h create mode 100644 apc_stack.c create mode 100644 apc_stack.h create mode 100644 apc_zend.c create mode 100644 apc_zend.h create mode 100644 arch/atomic.h create mode 100644 arch/i386/atomic.h create mode 100644 arch/x86_64/atomic.h create mode 100644 pgsql_s_lock.c create mode 100644 pgsql_s_lock.h create mode 100644 php_apc.h create mode 100644 tests/apc_001.phpt create mode 100644 tests/apc_002.phpt create mode 100644 tests/apc_003.phpt create mode 100644 tests/skipif.inc diff --git a/CHANGELOG b/CHANGELOG index c9de553..c140244 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +3.0.18: 2008-03-28 +- Revert memleak patch (Gopal) +- Fix for bug #13504 (David Fraser) + +3.0.17: 2008-03-25 +- Fix for CVE-2008-1488 (Daniel Papasian, Rasmus) +- Fix apc_add() cache expunge bug (Rasmus) +- Added parameter to apc_fetch to determine success/failure when fetching booleans (shire) +- Fix misc. memleaks (shire) + 3.0.16: 2007-12-26 - Fix for longstanding cache-full crash (Christian Seiler) http://news.php.net/php.pecl.dev/4951 for the details diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..23ec35f --- /dev/null +++ b/INSTALL @@ -0,0 +1,417 @@ +Installation Instructions for APC +--------------------------------- + +This version of APC should work on PHP 4.3.0 - 4.4.x and +5.1.0 - 5.2.x. Yes, that means PHP 5.0.x is no longer +supported. Upgrade to PHP 5.1.x or 5.2.x and you will +notice all sorts of performance increases. + +CVS Instructions +---------------- +Building from CVS can be done like this: + + cvs -d :pserver:cvsread@cvs.php.net:/repository login + Password: phpfi + cvs -d :pserver:cvsread@cvs.php.net:/repository co pecl/apc + cd pecl/apc + phpize + ./configure --enable-apc-mmap --with-apxs --with-php-config=/usr/local/php/bin/php-config + make + make install + +Suggested Configuration (in your php.ini file) +---------------------------------------------- + extension=apc.so + apc.enabled=1 + apc.shm_segments=1 + apc.shm_size=128 + apc.ttl=7200 + apc.user_ttl=7200 + apc.num_files_hint=1024 + apc.mmap_file_mask=/tmp/apc.XXXXXX + apc.enable_cli=1 + +These are fully described at the bottom of this file. + +PHP 4 Optimization +------------------ +If you are trying to get every little bit of speed out of PHP4+APC, you need +to tell APC where to find your httpd.h file and also add -DAPC_PHP4_STAT to +your CPPFLAGS. (if you don't have httpd.h, install the apache_dev package +for your OS) and do: + export CPPFLAGS="-I/usr/include/apache-1.3 -DAPC_PHP4_STAT" (for bash on Debian) + setenv CPPFLAGS "-I/usr/include/apache-1.3 -DAPC_PHP4_STAT" (for tsch on Debian) +and then re-run your configure script. + +This optimization saves a stat syscall on the main script file. In PHP5 this +optimization is automatic and doesn't need any special build flags. + +The second thing you are going to want to do to save another syscall is to +compile using the --with-apxs configure switch. This should work for both +Apache1 and Apache2. Point it directly at your apxs2 script for Apache2. +eg. --with-apxs=/usr/local/bin/apxs2 + ++---------------------+ +| QUICK INSTALL (DSO) | ++---------------------+ + +These instructions assume your PHP installation is located in /usr/local/php and you +want Apache optimizations (--with-apxs). + +$ gunzip -c apc_x.y.tar.gz | tar xf - +$ cd apc_x.y +$ /usr/local/php/bin/phpize +$ ./configure --enable-apc --enable-apc-mmap --with-apxs --with-php-config=/usr/local/php/bin/php-config +$ make +$ make install + +You will probably need to run the final command (make install) as root. + +The above sequence of commands will install a .so file in your PHP +installation extension directory. The output of make install should display +that path to the screen. + +Next you must edit your php.ini file, which is normally located in +/usr/local/php/lib/php.ini, and add the following line: + + extension="apc.so" + +Replace "/path/to/php/extensions" with whatever path was displayed when you +ran make install above. + +Then restart your web server and consult the output of phpinfo(). If there is +an informational section for APC, the installation was successful. + ++------------------------+ +| QUICK INSTALL (Static) | ++------------------------+ + +APC will not successfully compile on all systems as a DSO. If you run into +problems using the DSO quick install, you can try to compile it statically +into PHP. (The DSO install is recommended, though.) + +These instructions assume the current directory is the root of the PHP source +tree, and that you have already configured PHP by running its bundled +configure script. + +$ cd ext +$ gunzip -c apc_x.y.tar.gz | tar xf - +$ cd .. +$ ./buildconf +$ ./config.nice +$ make +$ make install + +Once this is complete, simply restart your web server. You do not need to +modify your php.ini file to enable APC. + ++-----------------+ +| VERBOSE INSTALL | ++-----------------+ + +These instructions assume your PHP installation is located in /usr/local/php. + +1. Unpack your distribution file. + + You will have downloaded a file named something like apc_x.y.tar.gz. + Unzip this file with a command like + + gunzip apc_x.y.tar.gz + + Next you have to untar it with + + tar xvf apc_x.y.tar + + This will create an apc_x.y directory. cd into this new directory: + + cd apc_x.y + +2. Run phpize. + + phpize is a script that should have been installed with PHP, and is + normally located in /usr/local/php/bin assuming you installed PHP in + /usr/local/php. (If you do not have the phpize script, you must reinstall + PHP and be sure not to disable PEAR.) + + Run the phpize command: + + /usr/local/php/bin/phpize + + Its output should resemble this: + + autoheader: `config.h.in' is created + You should update your `aclocal.m4' by running aclocal. + Configuring for: + PHP Api Version: 20020918 + Zend Module Api No: 20020429 + Zend Extension Api No: 20021010 + + phpize should create a configure script in the current directory. If you + get errors instead, you might be missing some required development tools, + such as autoconf or libtool. You can try downloading the latest versions + of those tools and running phpize again. + +3. Run the configure script. + + phpize creates a configure script. The only option you need to specify is + the location of your php-config script: + + ./configure --enable-apc + + php-config should be located in the same directory as phpize. + + If you prefer to use mmap instead of the default IPC shared memory support, + add --enable-apc-mmap to your configure line. + + If you prefer to use sysv IPC semaphores over the safer fcntl() locks, add + --enable-sem to your configure line. If you don't have a problem + with your server segaulting, or any other unnatural accumulation of + semaphores on your system, the semaphore based locking is slightly faster. + +4. Compile and install the files. Simply type: make install + + (You may need to be root in order to install) + + If you encounter errors from libtool or gcc during this step, please + contact the project maintainer (dcowgill@php.net). + +5. Edit your php.ini + + make install should have printed a line resembling the following: + + Installing shared extensions: /path/to/extension/ + + Copy the path /path/to/extension/ and add the following line to your + php.ini file (normally located in /usr/local/php/lib/php.ini): + + extension="apc.so" + + If you don't have a php.ini file in that location, you can create it now. + +6. Restart the web server and test the installation. + + Restart your web server now (for apache, it's apachectl restart) and + create a small test PHP file in your document root. The file should + contain just the following line: + + + + Request that file in a web browser. If there is an entry for APC in the + list of installed modules, the installation was successful. + + If APC is not listed, consult your web server error log. If it contains an + error message saying that it can't load the APC extension, your system + might not be able to load shared libraries created with PHP's build + system. One alternative would be to compile APC statically into PHP. See + the Quick Install (Static) instructions above. + + You should consult your error log anyway to see if APC generated any + errors. On BSD-based platforms, it is typical for APC to be unable to + allocate the default-sized shared memory segment. See below for hints on + raising your system's shared memory limitations. + ++-----------------+ +| CONFIGURING APC | ++-----------------+ + +Although the default APC settings are fine for many installations, serious +users should consider tuning the following parameters: + + OPTION DESCRIPTION + ------------------ -------------------------------------------------- + apc.enabled This can be set to 0 to disable APC. This is + primarily useful when APC is statically compiled + into PHP, since there is no other way to disable + it (when compiled as a DSO, the zend_extension + line can just be commented-out). + (Default: 1) + + apc.shm_segments The number of shared memory segments to allocate + for the compiler cache. If APC is running out of + shared memory but you have already set + apc.shm_size as high as your system allows, you + can try raising this value. Setting this to a + value other than 1 has no effect in mmap mode + since mmap'ed shm segments don't have size limits. + (Default: 1) + + apc.shm_size The size of each shared memory segment in MB. + By default, some systems (including most BSD + variants) have very low limits on the size of a + shared memory segment. + (Default: 30) + + apc.optimization This option has been deprecated. + (Default: 0) + + apc.num_files_hint A "hint" about the number of distinct source files + that will be included or requested on your web + server. Set to zero or omit if you're not sure; + this setting is mainly useful for sites that have + many thousands of source files. + (Default: 1000) + + apc.user_entries_hint Just like num_files_hint, a "hint" about the number + of distinct user cache variables to store. + Set to zero or omit if you're not sure; + (Default: 4096) + + apc.ttl The number of seconds a cache entry is allowed to + idle in a slot in case this cache entry slot is + needed by another entry. Leaving this at zero + means that your cache could potentially fill up + with stale entries while newer entries won't be + cached. + (Default: 0) + + apc.user_ttl The number of seconds a user cache entry is allowed + to idle in a slot in case this cache entry slot is + needed by another entry. Leaving this at zero + means that your cache could potentially fill up + with stale entries while newer entries won't be + cached. + (Default: 0) + + + apc.gc_ttl The number of seconds that a cache entry may + remain on the garbage-collection list. This value + provides a failsafe in the event that a server + process dies while executing a cached source file; + if that source file is modified, the memory + allocated for the old version will not be + reclaimed until this TTL reached. Set to zero to + disable this feature. + (Default: 3600) + + apc.cache_by_default On by default, but can be set to off and used in + conjunction with positive apc.filters so that files + are only cached if matched by a positive filter. + (Default: On) + + apc.filters A comma-separated list of POSIX extended regular + expressions. If any pattern matches the source + filename, the file will not be cached. Note that + the filename used for matching is the one passed + to include/require, not the absolute path. If the + first character of the expression is a + then the + expression will be additive in the sense that any + files matched by the expression will be cached, and + if the first character is a - then anything matched + will not be cached. The - case is the default, so + it can be left off. + (Default: "") + + apc.mmap_file_mask If compiled with MMAP support by using --enable-mmap + this is the mktemp-style file_mask to pass to the + mmap module for determing whether your mmap'ed memory + region is going to be file-backed or shared memory + backed. For straight file-backed mmap, set it to + something like /tmp/apc.XXXXXX (exactly 6 X's). + To use POSIX-style shm_open/mmap put a ".shm" + somewhere in your mask. eg. "/apc.shm.XXXXXX" + You can also set it to "/dev/zero" to use your + kernel's /dev/zero interface to anonymous mmap'ed + memory. Leaving it undefined will force an + anonymous mmap. + (Default: "") + + apc.slam_defense ** DEPRECATED - Use apc.write_lock instead ** + On very busy servers whenever you start the server or + modify files you can create a race of many processes + all trying to cache the same file at the same time. + This option sets the percentage of processes that will + skip trying to cache an uncached file. Or think of it + as the probability of a single process to skip caching. + For example, setting this to 75 would mean that there is + a 75% chance that the process will not cache an uncached + file. So the higher the setting the greater the defense + against cache slams. Setting this to 0 disables this + feature. + (Default: 0) + + apc.file_update_protection + When you modify a file on a live web server you really + should do so in an atomic manner. That is, write to a + temporary file and rename (mv) the file into its permanent + position when it is ready. Many text editors, cp, tar and + other such programs don't do this. This means that there + is a chance that a file is accessed (and cached) while it + is still being written to. This file_update_protection + setting puts a delay on caching brand new files. The + default is 2 seconds which means that if the modification + timestamp (mtime) on a file shows that it is less than 2 + seconds old when it is accessed, it will not be cached. + The unfortunate person who accessed this half-written file + will still see weirdness, but at least it won't persist. + If you are certain you always atomically update your files + by using something like rsync which does this correctly, you + can turn this protection off by setting it to 0. If you + have a system that is flooded with io causing some update + procedure to take longer than 2 seconds, you may want to + increase this a bit. + (Default: 2) + + apc.enable_cli Mostly for testing and debugging. Setting this enables APC + for the CLI version of PHP. Normally you wouldn't want to + create, populate and tear down the APC cache on every CLI + request, but for various test scenarios it is handy to be + able to enable APC for the CLI version of APC easily. + (Default: 0) + + apc.max_file_size Prevents large files from being cached. + (Default: 1M) + + apc.stat Whether to stat the main script file and the fullpath + includes. If you turn this off you will need to restart + your server in order to update scripts. + (Default: 1) + + apc.write_lock On busy servers when you first start up the server, or when + many files are modified, you can end up with all your processes + trying to compile and cache the same files. With write_lock + enabled, only one process at a time will try to compile an + uncached script while the other processes will run uncached + instead of sitting around waiting on a lock. + (Default: 1) + + apc.report_autofilter Logs any scripts that were automatically excluded from being + cached due to early/late binding issues. + (Default: 0) + + apc.rfc1867 RFC1867 File Upload Progress hook handler is only available + if you compiled APC against PHP 5.2.0 or later. When enabled + any file uploads which includes a field called + APC_UPLOAD_PROGRESS before the file field in an upload form + will cause APC to automatically create an upload_ + user cache entry where is the value of the + APC_UPLOAD_PROGRESS form entry. + + Note that the file upload tracking is not threadsafe at this + point, so new uploads that happen while a previous one is + still going will disable the tracking for the previous. + (Default: 0) + + apc.rfc1867_prefix Key prefix to use for the user cache entry generated by + rfc1867 upload progress functionality. + (Default: "upload_") + + apc.rfc1867_name Specify the hidden form entry name that activates APC upload + progress and specifies the user cache key suffix. + (Default: "APC_UPLOAD_PROGRESS") + + apc.rfc1867_freq The frequency that updates should be made to the user cache + entry for upload progress. This can take the form of a + percentage of the total file size or a size in bytes + optionally suffixed with 'k', 'm', or 'g' for kilobytes, + megabytes, or gigabytes respectively (case insensitive). + A setting of 0 updates as often as possible, which may cause + slower uploads. + (Default: 0) + + apc.localcache ** REMOVED + apc.localcache.size ** REMOVED + + apc.include_once_override + Optimize include_once and require_once calls and avoid the + expensive system calls used. + (Default: 0) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8d3fa07 --- /dev/null +++ b/LICENSE @@ -0,0 +1,68 @@ +-------------------------------------------------------------------- + The PHP License, version 3.01 +Copyright (c) 1999 - 2006 The PHP Group. All rights reserved. +-------------------------------------------------------------------- + +Redistribution and use in source and binary forms, with or without +modification, is permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP +DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------- + +This software consists of voluntary contributions made by many +individuals on behalf of the PHP Group. + +The PHP Group can be contacted via Email at group@php.net. + +For more information on the PHP Group and the PHP project, +please see . + +PHP includes the Zend Engine, freely available at +. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..59e686b --- /dev/null +++ b/NOTICE @@ -0,0 +1,43 @@ +This is the NOTICE file that holds acknowledgements and stuff. + +The Alternative PHP Cache (APC) is a free and open opcode cache for PHP. +This extension is being released under the PHP License for complete compliance +with PHP and to encourage wide-spread use. It is our intention that this +project be kept open source and that all commercial spin-offs contribute their +modifications back into the public source-tree. + +Creators: + Daniel Cowgill + George Schlossnagle + +PHP5 support and major features by: + Arun C. Murthy + Gopal Vijayaraghavan + Rasmus Lerdorf + +This software was contributed to PHP by Community Connect Inc. in 2002 +and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. +Future revisions and derivatives of this source code must acknowledge +Community Connect Inc. as the original contributor of this module by +leaving this note intact in the source code. + +All other licensing and usage conditions are those of the PHP Group. + +We would like to thank Community Connect Inc. and Yahoo! Inc. for supporting +this project and providing a challenging and stimulating environment in +which exciting projects can happen. + +Contributors: + Mike Bretz bug fixes, GUI, and lots of work + Ricardo Galli changed read-write locks to prefer readers + Yann Grossel bug fixes + Thies Arntzen bug fixes + Sara Golemon optimizer work + +Special Thanks: + Florian Baumert help debugging phplib problems + Thomas Duffey help debugging inheritance issues + Vibol Hou help debugging phplib problems + Angel Li diffs for ANSI comment compliance + Christian Rishøj help debugging phplib problems + Sascha Schumann memory error bug fix diff --git a/TECHNOTES.txt b/TECHNOTES.txt new file mode 100644 index 0000000..66d0fb7 --- /dev/null +++ b/TECHNOTES.txt @@ -0,0 +1,361 @@ +APC Quick-Start Braindump + +This is a rapidly written braindump of how APC currently works in the +form of a quick-start guide to start hacking on APC. + +1. Install and use APC a bit so you know what it does from the end-user's + perspective. + user-space functions are all explained here: + +2. Grab the current APC code from CVS: + + cvs -d:pserver:cvsread@cvs.php.net:/repository login + Password: phpfi + cvs -d:pserver:cvsread@cvs.php.net:/repository co pecl/apc + + apc/php_apc.c has most of the code for the user-visible stuff. It is + also a regular PHP extension in the sense that there are MINIT, MINFO, + MSHUTDOWN, RSHUTDOWN, etc. functions. + +3. Build it. + + cd pecl/apc + phpize + ./configure --enable-apc --enable-mmap + make + cp modules/apc.so /usr/local/lib/php + apachectl restart + +4. Debugging Hints + + apachectl stop + gdb /usr/bin/httpd + break ?? + run -X + + Grab the .gdbinit from the PHP source tree and have a look at the macros. + +5. Look through apc/apc_sma.c + It is a pretty standard memory allocator. + + apc_sma_malloc, apc_sma_realloc, apc_sma_strdup and apc_sma_free behave to the + caller just like malloc, realloc, strdup and free + + On server startup the MINIT hook in php_apc.c calls apc_module_init() in + apc_main.c which in turn calls apc_sma_init(). apc_sma_init calls into + apc_mmap.c to mmap the specified sized segment (I tend to just use a single + segment). apc_mmap.c should be self-explanatory. It mmaps a temp file and + then unlinks that file right after the mmap to provide automatic shared memory + cleanup in case the process dies. + + Once the region has been initialized we stick a header_t at the beginning + of the region. It contains the total size in header->segsize and the number + of bytes available in header->avail. + + After the header comes a bit of a hack. A zero-sized block is inserted just + to make things easier later on. And then a huge block that is basically + the size of the entire segment minus the two (for the 0-sized block, and this one) + block headers. + + The code for this is: + + header = (header_t*) shmaddr; + header->segsize = sma_segsize; + header->avail = sma_segsize - sizeof(header_t) - sizeof(block_t) - alignword(sizeof(int)); + memset(&header->lock,0,sizeof(header->lock)); + sma_lock = &header->lock; + block = BLOCKAT(sizeof(header_t)); + block->size = 0; + block->next = sizeof(header_t) + sizeof(block_t); + block = BLOCKAT(block->next); + block->size = header->avail; + block->next = 0; + + So the shared memory looks like this: + + +--------+-------+---------------------------------+ + | header | block | block | + +--------+-------+---------------------------------+ + + sma_shmaddrs[0] gives you the address of header + + The blocks are just a simple offset-based linked list (so no pointers): + + typedef struct block_t block_t; + struct block_t { + size_t size; /* size of this block */ + size_t next; /* offset in segment of next free block */ + size_t canary; /* canary to check for memory overwrites */ +#ifdef __APC_SMA_DEBUG__ + int id; /* identifier for the memory block */ +#endif + }; + + The BLOCKAT macro turns an offset into an actual address for you: + + #define BLOCKAT(offset) ((block_t*)((char *)shmaddr + offset)) + + where shmaddr = sma_shaddrs[0] + + And the OFFSET macro goes the other way: + + #define OFFSET(block) ((int)(((char*)block) - (char*)shmaddr)) + + Allocating a block with a call to apc_sma_allocate() walks through the + linked list of blocks until it finds one that is >= to the requested size. + The first call to apc_sma_allocate() will hit the second block. We then + chop up that block so it looks like this: + + +--------+-------+-------+-------------------------+ + | header | block | block | block | + +--------+-------+-------+-------------------------+ + + Then we unlink that block from the linked list so it won't show up + as an available block on the next allocate. So we actually have: + + +--------+-------+ +-------------------------+ + | header | block |------>| block | + +--------+-------+ +-------------------------+ + + And header->avail along with block->size of the remaining large + block are updated accordingly. The arrow there representing the + link which now points to a block with an offset further along in + the segment. + + When the block is freed using apc_sma_deallocate() the steps are + basically just reversed. The block is put back and then the deallocate + code looks at the block before and after to see if the block immediately + before and after are free and if so the blocks are combined. So you never + have 2 free blocks next to each other, apart from at the front with that + 0-sized dummy block. This mostly prevents fragmentation. I have been + toying with the idea of always allocating block at 2^n boundaries to make + it more likely that they will be re-used to cut down on fragmentation further. + That's what the POWER_OF_TWO_BLOCKSIZE you see in apc_sma.c is all about. + + Of course, anytime we fiddle with our shared memory segment we lock using + the locking macros, LOCK() and UNLOCK(). + + That should mostly take care of the low-level shared memory handling. + +6. Next up is apc_main.c and apc_cache.c which implement the meat of the + cache logic. + + The apc_main.c file mostly calls functions in apc_sma.c to allocate memory + and apc_cache.c for actual cache manipulation. + + After the shared memory segment is created and the caches are initialized, + apc_module_init() installs the my_compile_file() function overriding Zend's + version. I'll talk about my_compile_file() and the rest of apc_compile.c + in the next section. For now I will stick with apc_main.c and apc_cache.c + and talk about the actual caches. A cache consists of a block of shared + memory returned by apc_sma_allocate() via apc_sma_malloc(). You will + notice references to apc_emalloc(). apc_emalloc() is just a thin wrapper + around PHP's own emalloc() function which allocates per-process memory from + PHP's pool-based memory allocator. Don't confuse apc_emalloc() and + apc_sma_malloc() as the first is per-process and the second is shared memory. + + The cache is stored in/described by this struct allocated locally using + emalloc(): + + struct apc_cache_t { + void* shmaddr; /* process (local) address of shared cache */ + header_t* header; /* cache header (stored in SHM) */ + slot_t** slots; /* array of cache slots (stored in SHM) */ + int num_slots; /* number of slots in cache */ + int gc_ttl; /* maximum time on GC list for a slot */ + int ttl; /* if slot is needed and entry's access time is older than this ttl, remove it */ + }; + + Whenever you see functions that take a 'cache' argument, this is what they + take. And apc_cache_create() returns a pointer to this populated struct. + + At the beginning of the cache we have a header. Remember, we are down a level now + from the sma stuff. The sma stuff is the low-level shared-memory allocator which + has its own header which is completely separate and invisible to apc_cache.c. + As far as apc_cache.c is concerned the block of memory it is working with could + have come from a call to malloc(). + + The header looks like this: + + typedef struct header_t header_t; + struct header_t { + int num_hits; /* total successful hits in cache */ + int num_misses; /* total unsuccessful hits in cache */ + slot_t* deleted_list; /* linked list of to-be-deleted slots */ + }; + + Since this is at the start of the shared memory segment, these values are accessible + across all the yapache processes and hence access to them has to be locked. + + After the header we have an array of slots. The number of slots is user-defined + through the apc.num_slots ini hint. Each slot is described by: + + typedef struct slot_t slot_t; + struct slot_t { + apc_cache_key_t key; /* slot key */ + apc_cache_entry_t* value; /* slot value */ + slot_t* next; /* next slot in linked list */ + int num_hits; /* number of hits to this bucket */ + time_t creation_time; /* time slot was initialized */ + time_t deletion_time; /* time slot was removed from cache */ + time_t access_time; /* time slot was last accessed */ + }; + + The slot_t *next there is a linked list to other slots that happened to hash to the + same array position. + + apc_cache_insert() shows what happens on a new cache insert. + + slot = &cache->slots[hash(key) % cache->num_slots]; + + cache->slots is our array of slots in the segment. hash() is simply: + + static unsigned int hash(apc_cache_key_t key) + { + return key.data.file.device + key.data.file.inode; + } + + That is, we use the file's device and inode to uniquely identify it. Initially + we had used the file's full path, but getting that requires a realpath() call which + is amazingly expensive since it has to stat each component of the path to resolve + symlinks and get rid of relative path components. By using the device+inode we + can uniquely identify a file with a single stat. + + So, on an insert we find the array position in the slots array by hasing the device+inode. + If there are currently no other slots there, we just create the slot and stick it into + the array: + + *slot = make_slot(key, value, *slot, t) + + If there are other slots already at this position we walk the link list to get to + the end. Here is the loop: + + while (*slot) { + if (key_equals((*slot)->key.data.file, key.data.file)) { + /* If existing slot for the same device+inode is different, remove it and insert the new version */ + if ((*slot)->key.mtime != key.mtime) { + remove_slot(cache, slot); + break; + } + UNLOCK(cache); + return 0; + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { + remove_slot(cache, slot); + continue; + } + slot = &(*slot)->next; + } + + That first key_equals() check sees if we have an exact match meaning the file + is already in the cache. Since we try to find the file in the cache before doing + an insert, this will generally only happen if another process managed to beat us + to inserting it. If we have a newer version of the file at this point we remove + it an insert the new version. If our version is not newer we just return without + doing anything. + + While walking the linked list we also check to see if the cache has a TTL defined. + If while walking the linked list we see a slot that has expired, we remove it + since we are right there looking at it. This is the only place we remove stale + entries unless the shared memory segment fills up and we force a full expunge via + apc_cache_expunge(). apc_cache_expunge() walks the entire slots array and walks + down every linked list removing stale slots to free up room. This is obviously + slow and thus only happens when we have run out of room. + + apc_cache_find() simply hashes and returns the entry if it is there. If it is there + but older than the mtime in the entry we are looking for, we delete the one that is + there and return indicating we didn't find it. + + Next we need to understand what an actual cache entry looks like. Have a look at + apc_cache.h for the structs. I sort of glossed over the key part earlier saying + that we just used the device+inode to find a hash slot. It is actually a bit more + complex than that because we have two kinds of caches. We have the standard file + cache containing opcode arrays, but we also have a user-controlled cache that the + user can insert whatever they want into via apc_store(). For the user cache we + obviously don't have a device+inode. The actual identifier is provided by the user + as a char *. So the key is actually a union that looks like this: + + typedef union _apc_cache_key_data_t { + struct { + int device; /* the filesystem device */ + int inode; /* the filesystem inode */ + } file; + struct { + char *identifier; + } user; + } apc_cache_key_data_t; + + struct apc_cache_key_t { + apc_cache_key_data_t data; + int mtime; /* the mtime of this cached entry */ + }; + + And we have two sets of functions to do inserts and finds. apc_cache_user_find() + and apc_cache_user_insert() operate on the user cache. + + Ok, on to the actual cache entry. Again, because we have two kinds of caches, we + also have the corresponding two kinds of cache entries described by this union: + + typedef union _apc_cache_entry_value_t { + struct { + char *filename; /* absolute path to source file */ + zend_op_array* op_array; /* op_array allocated in shared memory */ + apc_function_t* functions; /* array of apc_function_t's */ + apc_class_t* classes; /* array of apc_class_t's */ + } file; + struct { + char *info; + zval *val; + unsigned int ttl; + } user; + } apc_cache_entry_value_t; + + And then the actual cache entry: + + struct apc_cache_entry_t { + apc_cache_entry_value_t data; + unsigned char type; + int ref_count; + }; + + The user entry is pretty simple and not all that important for now. I will + concentrate on the file entries since that is what holds the actual compiled + opcode arrays along with the functions and classes required by the executor. + + apc_cache_make_file_entry() in apc_cache.c shows how an entry is constructed. + The main thing to understand here is that we need more than just the opcode + array, we also need the functions and classes created by the compiler when it + created the opcode array. As far as the executor is concerned, it doesn't know + that it isn't operating in normal mode being called right after the parse/compile + phase, so we need to recreate everything so it looks exactly like it would at + that point. + +7. my_compile_file() and apc_compile.c + + my_compile_file() in apc_main.c controls where we get the opcodes from. If + the user-specified filters exclude the file from being cached, then we just + call the original compile function and return. Otherwise we fetch the request + time from Apache to avoid an extra syscall, create the key so we can look up + the file in the cache. If we find it we stick it on a local stack which we + use at cleanup time to make sure we return everything back to normal after a + request and call cached_compile() which installs the functions and classes + associated with the op_array in this entry and then copy the op_array down + into our memory space for execution. + + If we didn't find the file in the cache, we need to compile it and insert it. + To compile it we simply call the original compile function: + + op_array = old_compile_file(h, type TSRMLS_CC); + + To do the insert we need to copy the functions, classes and the opcode array + the compile phase created into shared memory. This all happens in apc_compile.c + in the apc_copy_op_array(), apc_copy_new_functions() and apc_copy_new_classes() + functions. Then we make the file entry and do the insert. Both of these + operations were described in the previous section. + +8. The Optimizer + + The optimizer has been deprecated. + +If you made it to the end of this, you should have a pretty good idea of where things are in +the code. I skimmed over a lot of things, so plan on spending some time reading through the code. + diff --git a/TODO b/TODO new file mode 100644 index 0000000..7bf1b54 --- /dev/null +++ b/TODO @@ -0,0 +1,30 @@ +Known Bugs + +1. Gallery2 doesn't work with PHP5+APC. There is something wrong + with the way methods are restored in some edge case I haven't + been able to figure out yet. + To reproduce install gallery2 and click down to an individual photo. + +2. apc_store() probably needs some checks to skip trying to store + internal classes. Something along the lines of: + + if(Z_TYPE_P(val) == IS_OBJECT) { + zend_class_entry *ce = Z_OBJCE_P(val); + if(ce->type == ZEND_INTERNAL_CLASS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot cache internal objects"); + RETURN_FALSE; + } + } + + in the apc_store() function in php_apc.c but I am wondering if it needs to do more + than that. + +Enhancements + +1. Some faster platform-specific locking mechanisms wouldd be nice. futex support + for the 2.6 Linux kernels, and/or x86-specific spinlock support. + +2. The optimizer needs a lot of work. + +3. Assert() elimination in the optimizer when some debug flag somewhere isn't set. + diff --git a/apc.c b/apc.c index ad770f8..14fc3ea 100644 --- a/apc.c +++ b/apc.c @@ -29,7 +29,7 @@ */ -/* $Id: apc.c,v 3.18 2007/11/29 22:15:53 shire Exp $ */ +/* $Id: apc.c,v 3.18.2.1 2008/03/25 18:04:53 gopalv Exp $ */ #include "apc.h" #include /* for POSIX regular expressions */ @@ -270,26 +270,13 @@ char** apc_tokenize(const char* s, char delim) /* }}} */ -/* {{{ filesystem functions */ - -#ifdef PHP_WIN32 -int apc_win32_stat(const char *path, struct stat *buf TSRMLS_DC) -{ - char rpath[MAXPATHLEN]; - BY_HANDLE_FILE_INFORMATION fi; - HANDLE f; - - if (VCWD_STAT(path, buf)) { - return -1; - } - - VCWD_REALPATH(path, rpath); - f = CreateFile(rpath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL); - GetFileInformationByHandle(f, &fi); - buf->st_ino = (ino_t)fi.nFileIndexLow; - CloseHandle (f); - return 0; -} +/* similar to php_stream_stat_path */ +#ifdef ZEND_ENGINE_2 +#define APC_URL_STAT(wrapper, filename, pstatbuf) \ + ((wrapper)->wops->url_stat((wrapper), (filename), 0, (pstatbuf), NULL TSRMLS_CC)) +#else +#define APC_URL_STAT(wrapper, filename, pstatbuf) \ + ((wrapper)->wops->url_stat((wrapper), (filename), (pstatbuf) TSRMLS_CC)) #endif int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo) @@ -299,12 +286,33 @@ int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fil int exec_fname_length; int found = 0; int i; + php_stream_wrapper *wrapper = NULL; + char *path_for_open = NULL; + TSRMLS_FETCH(); assert(filename && fileinfo); - if (IS_ABSOLUTE_PATH(filename, strlen(filename)) && apc_stat(filename, &fileinfo->st_buf) == 0) { - strncpy(fileinfo->fullpath, filename, MAXPATHLEN); + + wrapper = php_stream_locate_url_wrapper(filename, &path_for_open, 0 TSRMLS_CC); + + if(!wrapper || !wrapper->wops || !wrapper->wops->url_stat) { + return -1; + } + +#ifdef ZEND_ENGINE_2 + if(wrapper != &php_plain_files_wrapper) { + if(APC_URL_STAT(wrapper, path_for_open, &fileinfo->st_buf) == 0) { + strncpy(fileinfo->fullpath, path_for_open, MAXPATHLEN); + return 0; + } + return -1; /* cannot stat */ + } +#endif + + if (IS_ABSOLUTE_PATH(path_for_open, strlen(path_for_open)) && + APC_URL_STAT(wrapper, path_for_open, &fileinfo->st_buf) == 0) { + strncpy(fileinfo->fullpath, path_for_open, MAXPATHLEN); return 0; } @@ -314,8 +322,8 @@ int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fil /* for each directory in paths, look for filename inside */ for (i = 0; paths[i]; i++) { - snprintf(fileinfo->fullpath, sizeof(fileinfo->fullpath), "%s%c%s", paths[i], DEFAULT_SLASH, filename); - if (apc_stat(fileinfo->fullpath, &fileinfo->st_buf) == 0) { + snprintf(fileinfo->fullpath, sizeof(fileinfo->fullpath), "%s%c%s", paths[i], DEFAULT_SLASH, path_for_open); + if (APC_URL_STAT(wrapper, fileinfo->fullpath, &fileinfo->st_buf) == 0) { found = 1; break; } @@ -331,9 +339,9 @@ int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fil /* not: [no active file] or no path */ memcpy(fileinfo->fullpath, exec_fname, exec_fname_length); fileinfo->fullpath[exec_fname_length] = DEFAULT_SLASH; - strcpy(fileinfo->fullpath +exec_fname_length +1, filename); - /* apc_wprint("filename: %s, exec_fname: %s, fileinfo->fullpath: %s", filename, exec_fname, fileinfo->fullpath); */ - if (apc_stat(fileinfo->fullpath, &fileinfo->st_buf) == 0) { + strlcpy(fileinfo->fullpath +exec_fname_length +1, path_for_open,sizeof(fileinfo->fullpath)-exec_fname_length-1); + /* apc_wprint("filename: %s, exec_fname: %s, fileinfo->fullpath: %s", path_for_open, exec_fname, fileinfo->fullpath); */ + if (APC_URL_STAT(wrapper, fileinfo->fullpath, &fileinfo->st_buf) == 0) { found = 1; } } diff --git a/apc.dsp b/apc.dsp new file mode 100644 index 0000000..d921b5f --- /dev/null +++ b/apc.dsp @@ -0,0 +1,207 @@ +# Microsoft Developer Studio Project File - Name="apc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=apc - Win32 Debug_TS +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "apc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "apc.mak" CFG="apc - Win32 Debug_TS" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "apc - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "apc - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "apc - Win32 Debug_TS" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug_TS" +# PROP BASE Intermediate_Dir "Debug_TS" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug_TS" +# PROP Intermediate_Dir "Debug_TS" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\php4" /I "..\..\..\php4\main" /I "..\..\..\php4\Zend" /I "..\..\..\php4\TSRM" /I "..\..\..\php4\win32" /I "..\..\..\php4\regex" /D "TSRM_LOCKS" /D HAVE_APC=1 /D "COMPILE_DL_APC" /D ZEND_DEBUG=1 /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APC_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 php4ts_debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"Debug_TS/php_apc.dll" /pdbtype:sept /libpath:"..\..\..\php4\Debug_TS" + +!ELSEIF "$(CFG)" == "apc - Win32 Release_TS" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release_TS" +# PROP BASE Intermediate_Dir "Release_TS" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release_TS" +# PROP Intermediate_Dir "Release_TS" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\php4" /I "..\..\..\php4\main" /I "..\..\..\php4\Zend" /I "..\..\..\php4\TSRM" /I "..\..\..\php4\win32" /I "..\..\..\php4\regex" /D "TSRM_LOCKS" /D HAVE_APC=1 /D "COMPILE_DL_APC" /D ZEND_DEBUG=0 /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APC_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 php4ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../Release_TS/php_apc.dll" /libpath:"..\..\..\php4\Release_TS" /libpath:"..\..\..\php4\Release_TS_Inline" + +!ENDIF + +# Begin Target + +# Name "apc - Win32 Debug_TS" +# Name "apc - Win32 Release_TS" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\apc.c +# End Source File +# Begin Source File + +SOURCE=.\apc_cache.c +# End Source File +# Begin Source File + +SOURCE=.\apc_compile.c +# End Source File +# Begin Source File + +SOURCE=.\apc_debug.c +# End Source File +# Begin Source File + +SOURCE=.\apc_fcntl_win32.c +# End Source File +# Begin Source File + +SOURCE=.\apc_main.c +# End Source File +# Begin Source File + +SOURCE=.\apc_rfc1867.c +# End Source File +# Begin Source File + +SOURCE=.\apc_shm.c +# End Source File +# Begin Source File + +SOURCE=.\apc_sma.c +# End Source File +# Begin Source File + +SOURCE=.\apc_stack.c +# End Source File +# Begin Source File + +SOURCE=.\apc_zend.c +# End Source File +# Begin Source File + +SOURCE=.\php_apc.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\apc.h +# End Source File +# Begin Source File + +SOURCE=.\apc_cache.h +# End Source File +# Begin Source File + +SOURCE=.\apc_compile.h +# End Source File +# Begin Source File + +SOURCE=.\apc_debug.h +# End Source File +# Begin Source File + +SOURCE=.\apc_fcntl.h +# End Source File +# Begin Source File + +SOURCE=.\apc_globals.h +# End Source File +# Begin Source File + +SOURCE=.\apc_lock.h +# End Source File +# Begin Source File + +SOURCE=.\apc_main.h +# End Source File +# Begin Source File + +SOURCE=.\apc_php.h +# End Source File +# Begin Source File + +SOURCE=.\apc_shm.h +# End Source File +# Begin Source File + +SOURCE=.\apc_sma.h +# End Source File +# Begin Source File + +SOURCE=.\apc_stack.h +# End Source File +# Begin Source File + +SOURCE=.\apc_zend.h +# End Source File +# Begin Source File + +SOURCE=.\php_apc.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/apc.h b/apc.h new file mode 100644 index 0000000..d71036e --- /dev/null +++ b/apc.h @@ -0,0 +1,121 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc.h,v 3.14.2.1 2008/03/25 18:04:53 gopalv Exp $ */ + +#ifndef APC_H +#define APC_H + +/* + * This module defines utilities and helper functions used elsewhere in APC. + */ + +/* Commonly needed C library headers. */ +#include +#include +#include +#include +#include +#include +#include + +/* UNIX headers (needed for struct stat) */ +#include +#include +#ifndef PHP_WIN32 +#include +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "php.h" +#include "main/php_streams.h" + +/* log levels constants (see apc_log) */ +enum { APC_DBG, APC_NOTICE, APC_WARNING, APC_ERROR }; + +/* typedefs for extensible memory allocators */ +typedef void* (*apc_malloc_t)(size_t); +typedef void (*apc_free_t) (void*); + +/* wrappers for memory allocation routines */ +extern void* apc_emalloc(size_t n); +extern void* apc_erealloc(void* p, size_t n); +extern void apc_efree(void* p); +extern char* apc_estrdup(const char* s); +extern void* apc_xstrdup(const char* s, apc_malloc_t f); +extern void* apc_xmemcpy(const void* p, size_t n, apc_malloc_t f); + +/* console display functions */ +extern void apc_log(int level, const char* fmt, ...); +extern void apc_eprint(const char* fmt, ...); +extern void apc_wprint(const char* fmt, ...); +extern void apc_dprint(const char* fmt, ...); +extern void apc_nprint(const char* fmt, ...); + +/* string and text manipulation */ +extern char* apc_append(const char* s, const char* t); +extern char* apc_substr(const char* s, int start, int length); +extern char** apc_tokenize(const char* s, char delim); + +/* filesystem functions */ + +typedef struct apc_fileinfo_t +{ + char fullpath[MAXPATHLEN+1]; + php_stream_statbuf st_buf; +} apc_fileinfo_t; + +extern int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo); + +/* regular expression wrapper functions */ +extern void* apc_regex_compile_array(char* patterns[]); +extern void apc_regex_destroy_array(void* p); +extern int apc_regex_match_array(void* p, const char* input); + +/* apc_crc32: returns the CRC-32 checksum of the first len bytes in buf */ +extern unsigned int apc_crc32(const char* buf, int len); + +#define APC_NEGATIVE_MATCH 1 +#define APC_POSITIVE_MATCH 2 + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc.php b/apc.php new file mode 100644 index 0000000..3b45aae --- /dev/null +++ b/apc.php @@ -0,0 +1,1341 @@ + | + | Rasmus Lerdorf | + | Ilia Alshanetsky | + +----------------------------------------------------------------------+ + + All other licensing and usage conditions are those of the PHP Group. + + */ + +$VERSION='$Id: apc.php,v 3.68.2.1 2008/03/25 18:04:53 gopalv Exp $'; + +////////// READ OPTIONAL CONFIGURATION FILE //////////// +if (file_exists("apc.conf.php")) include("apc.conf.php"); +//////////////////////////////////////////////////////// + +////////// BEGIN OF DEFAULT CONFIG AREA /////////////////////////////////////////////////////////// + +defaults('USE_AUTHENTICATION',1); // Use (internal) authentication - best choice if + // no other authentication is available + // If set to 0: + // There will be no further authentication. You + // will have to handle this by yourself! + // If set to 1: + // You need to change ADMIN_PASSWORD to make + // this work! +defaults('ADMIN_USERNAME','apc'); // Admin Username +defaults('ADMIN_PASSWORD','password'); // Admin Password - CHANGE THIS TO ENABLE!!! + +// (beckerr) I'm using a clear text password here, because I've no good idea how to let +// users generate a md5 or crypt password in a easy way to fill it in above + +//defaults('DATE_FORMAT', "d.m.Y H:i:s"); // German +defaults('DATE_FORMAT', 'Y/m/d H:i:s'); // US + +defaults('GRAPH_SIZE',200); // Image size + +////////// END OF DEFAULT CONFIG AREA ///////////////////////////////////////////////////////////// + + +// "define if not defined" +function defaults($d,$v) { + if (!defined($d)) define($d,$v); // or just @define(...) +} + +// rewrite $PHP_SELF to block XSS attacks +// +$PHP_SELF= isset($_SERVER['PHP_SELF']) ? htmlentities(strip_tags($_SERVER['PHP_SELF'],''), ENT_QUOTES) : ''; +$time = time(); +$host = getenv('HOSTNAME'); +if($host) { $host = '('.$host.')'; } + +// operation constants +define('OB_HOST_STATS',1); +define('OB_SYS_CACHE',2); +define('OB_USER_CACHE',3); +define('OB_SYS_CACHE_DIR',4); +define('OB_VERSION_CHECK',9); + +// check validity of input variables +$vardom=array( + 'OB' => '/^\d+$/', // operational mode switch + 'CC' => '/^[01]$/', // clear cache requested + 'DU' => '/^.*$/', // Delete User Key + 'SH' => '/^[a-z0-9]+$/', // shared object description + + 'IMG' => '/^[123]$/', // image to generate + 'LO' => '/^1$/', // login requested + + 'COUNT' => '/^\d+$/', // number of line displayed in list + 'SCOPE' => '/^[AD]$/', // list view scope + 'SORT1' => '/^[AHSMCDTZ]$/', // first sort key + 'SORT2' => '/^[DA]$/', // second sort key + 'AGGR' => '/^\d+$/', // aggregation by dir level + 'SEARCH' => '~^[a-zA-Z0-1/_.-]*$~' // aggregation by dir level +); + +// default cache mode +$cache_mode='opcode'; + +// cache scope +$scope_list=array( + 'A' => 'cache_list', + 'D' => 'deleted_list' +); + +// handle POST and GET requests +if (empty($_REQUEST)) { + if (!empty($_GET) && !empty($_POST)) { + $_REQUEST = array_merge($_GET, $_POST); + } else if (!empty($_GET)) { + $_REQUEST = $_GET; + } else if (!empty($_POST)) { + $_REQUEST = $_POST; + } else { + $_REQUEST = array(); + } +} + +// check parameter syntax +foreach($vardom as $var => $dom) { + if (!isset($_REQUEST[$var])) { + $MYREQUEST[$var]=NULL; + } else if (!is_array($_REQUEST[$var]) && preg_match($dom.'D',$_REQUEST[$var])) { + $MYREQUEST[$var]=$_REQUEST[$var]; + } else { + $MYREQUEST[$var]=$_REQUEST[$var]=NULL; + } +} + +// check parameter sematics +if (empty($MYREQUEST['SCOPE'])) $MYREQUEST['SCOPE']="A"; +if (empty($MYREQUEST['SORT1'])) $MYREQUEST['SORT1']="H"; +if (empty($MYREQUEST['SORT2'])) $MYREQUEST['SORT2']="D"; +if (empty($MYREQUEST['OB'])) $MYREQUEST['OB']=OB_HOST_STATS; +if (!isset($MYREQUEST['COUNT'])) $MYREQUEST['COUNT']=20; +if (!isset($scope_list[$MYREQUEST['SCOPE']])) $MYREQUEST['SCOPE']='A'; + +$MY_SELF= + "$PHP_SELF". + "?SCOPE=".$MYREQUEST['SCOPE']. + "&SORT1=".$MYREQUEST['SORT1']. + "&SORT2=".$MYREQUEST['SORT2']. + "&COUNT=".$MYREQUEST['COUNT']; +$MY_SELF_WO_SORT= + "$PHP_SELF". + "?SCOPE=".$MYREQUEST['SCOPE']. + "&COUNT=".$MYREQUEST['COUNT']; + +// authentication needed? +// +if (!USE_AUTHENTICATION) { + $AUTHENTICATED=1; +} else { + $AUTHENTICATED=0; + if (ADMIN_PASSWORD!='password' && ($MYREQUEST['LO'] == 1 || isset($_SERVER['PHP_AUTH_USER']))) { + + if (!isset($_SERVER['PHP_AUTH_USER']) || + !isset($_SERVER['PHP_AUTH_PW']) || + $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME || + $_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) { + Header("WWW-Authenticate: Basic realm=\"APC Login\""); + Header("HTTP/1.0 401 Unauthorized"); + + echo << +

Rejected!

+ Wrong Username or Password!
 
  + Continue... + +EOB; + exit; + + } else { + $AUTHENTICATED=1; + } + } +} + +// select cache mode +if ($AUTHENTICATED && $MYREQUEST['OB'] == OB_USER_CACHE) { + $cache_mode='user'; +} +// clear cache +if ($AUTHENTICATED && isset($MYREQUEST['CC']) && $MYREQUEST['CC']) { + apc_clear_cache($cache_mode); +} + +if ($AUTHENTICATED && !empty($MYREQUEST['DU'])) { + apc_delete($MYREQUEST['DU']); +} + +if(!function_exists('apc_cache_info') || !($cache=@apc_cache_info($cache_mode))) { + echo "No cache info available. APC does not appear to be running."; + exit; +} + +$cache_user = apc_cache_info('user', 1); +$mem=apc_sma_info(); +if(!$cache['num_hits']) { $cache['num_hits']=1; $time++; } // Avoid division by 0 errors on a cache clear + +// don't cache this page +// +header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); // HTTP/1.0 + +function duration($ts) { + global $time; + $years = (int)((($time - $ts)/(7*86400))/52.177457); + $rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400)); + $weeks = (int)(($rem)/(7*86400)); + $days = (int)(($rem)/86400) - $weeks*7; + $hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24; + $mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60; + $str = ''; + if($years==1) $str .= "$years year, "; + if($years>1) $str .= "$years years, "; + if($weeks==1) $str .= "$weeks week, "; + if($weeks>1) $str .= "$weeks weeks, "; + if($days==1) $str .= "$days day,"; + if($days>1) $str .= "$days days,"; + if($hours == 1) $str .= " $hours hour and"; + if($hours>1) $str .= " $hours hours and"; + if($mins == 1) $str .= " 1 minute"; + else $str .= " $mins minutes"; + return $str; +} + +// create graphics +// +function graphics_avail() { + return extension_loaded('gd'); +} +if (isset($MYREQUEST['IMG'])) +{ + if (!graphics_avail()) { + exit(0); + } + + function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$color2,$text='',$placeindex=0) { + $r=$diameter/2; + $w=deg2rad((360+$start+($end-$start)/2)%360); + + + if (function_exists("imagefilledarc")) { + // exists only if GD 2.0.1 is avaliable + imagefilledarc($im, $centerX+1, $centerY+1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE); + imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE); + imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL|IMG_ARC_EDGED); + } else { + imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start+1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end-1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2); + imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2); + imagefill($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2, $color2); + } + if ($text) { + if ($placeindex>0) { + imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1); + imagestring($im,4,$diameter, $placeindex*12,$text,$color1); + + } else { + imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1); + } + } + } + + function text_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$text,$placeindex=0) { + $r=$diameter/2; + $w=deg2rad((360+$start+($end-$start)/2)%360); + + if ($placeindex>0) { + imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1); + imagestring($im,4,$diameter, $placeindex*12,$text,$color1); + + } else { + imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1); + } + } + + function fill_box($im, $x, $y, $w, $h, $color1, $color2,$text='',$placeindex='') { + global $col_black; + $x1=$x+$w-1; + $y1=$y+$h-1; + + imagerectangle($im, $x, $y1, $x1+1, $y+1, $col_black); + if($y1>$y) imagefilledrectangle($im, $x, $y, $x1, $y1, $color2); + else imagefilledrectangle($im, $x, $y1, $x1, $y, $color2); + imagerectangle($im, $x, $y1, $x1, $y, $color1); + if ($text) { + if ($placeindex>0) { + + if ($placeindex<16) + { + $px=5; + $py=$placeindex*12+6; + imagefilledrectangle($im, $px+90, $py+3, $px+90-4, $py-3, $color2); + imageline($im,$x,$y+$h/2,$px+90,$py,$color2); + imagestring($im,2,$px,$py-6,$text,$color1); + + } else { + if ($placeindex<31) { + $px=$x+40*2; + $py=($placeindex-15)*12+6; + } else { + $px=$x+40*2+100*intval(($placeindex-15)/15); + $py=($placeindex%15)*12+6; + } + imagefilledrectangle($im, $px, $py+3, $px-4, $py-3, $color2); + imageline($im,$x+$w,$y+$h/2,$px,$py,$color2); + imagestring($im,2,$px+2,$py-6,$text,$color1); + } + } else { + imagestring($im,4,$x+5,$y1-16,$text,$color1); + } + } + } + + + $size = GRAPH_SIZE; // image size + if ($MYREQUEST['IMG']==3) + $image = imagecreate(2*$size+150, $size+10); + else + $image = imagecreate($size+50, $size+10); + + $col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF); + $col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30); + $col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60); + $col_black = imagecolorallocate($image, 0, 0, 0); + imagecolortransparent($image,$col_white); + + switch ($MYREQUEST['IMG']) { + + case 1: + $s=$mem['num_seg']*$mem['seg_size']; + $a=$mem['avail_mem']; + $x=$y=$size/2; + $fuzz = 0.000001; + + // This block of code creates the pie chart. It is a lot more complex than you + // would expect because we try to visualize any memory fragmentation as well. + $angle_from = 0; + $string_placement=array(); + for($i=0; $i<$mem['num_seg']; $i++) { + $ptr = 0; + $free = $mem['block_lists'][$i]; + foreach($free as $block) { + if($block['offset']!=$ptr) { // Used block + $angle_to = $angle_from+($block['offset']-$ptr)/$s; + if(($angle_to+$fuzz)>1) $angle_to = 1; + if( ($angle_to*360) - ($angle_from*360) >= 1) { + fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red); + if (($angle_to-$angle_from)>0.05) { + array_push($string_placement, array($angle_from,$angle_to)); + } + } + $angle_from = $angle_to; + } + $angle_to = $angle_from+($block['size'])/$s; + if(($angle_to+$fuzz)>1) $angle_to = 1; + if( ($angle_to*360) - ($angle_from*360) >= 1) { + fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_green); + if (($angle_to-$angle_from)>0.05) { + array_push($string_placement, array($angle_from,$angle_to)); + } + } + $angle_from = $angle_to; + $ptr = $block['offset']+$block['size']; + } + if ($ptr < $mem['seg_size']) { // memory at the end + $angle_to = $angle_from + ($mem['seg_size'] - $ptr)/$s; + if(($angle_to+$fuzz)>1) $angle_to = 1; + fill_arc($image,$x,$y,$size,$angle_from*360,$angle_to*360,$col_black,$col_red); + if (($angle_to-$angle_from)>0.05) { + array_push($string_placement, array($angle_from,$angle_to)); + } + } + } + foreach ($string_placement as $angle) { + text_arc($image,$x,$y,$size,$angle[0]*360,$angle[1]*360,$col_black,bsize($s*($angle[1]-$angle[0]))); + } + break; + + case 2: + $s=$cache['num_hits']+$cache['num_misses']; + $a=$cache['num_hits']; + + fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s)); + fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s)); + break; + + case 3: + $s=$mem['num_seg']*$mem['seg_size']; + $a=$mem['avail_mem']; + $x=130; + $y=1; + $j=1; + + // This block of code creates the bar chart. It is a lot more complex than you + // would expect because we try to visualize any memory fragmentation as well. + for($i=0; $i<$mem['num_seg']; $i++) { + $ptr = 0; + $free = $mem['block_lists'][$i]; + foreach($free as $block) { + if($block['offset']!=$ptr) { // Used block + $h=(GRAPH_SIZE-5)*($block['offset']-$ptr)/$s; + if ($h>0) { + $j++; + if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($block['offset']-$ptr),$j); + else fill_box($image,$x,$y,50,$h,$col_black,$col_red); + } + $y+=$h; + } + $h=(GRAPH_SIZE-5)*($block['size'])/$s; + if ($h>0) { + $j++; + if($j<75) fill_box($image,$x,$y,50,$h,$col_black,$col_green,bsize($block['size']),$j); + else fill_box($image,$x,$y,50,$h,$col_black,$col_green); + } + $y+=$h; + $ptr = $block['offset']+$block['size']; + } + if ($ptr < $mem['seg_size']) { // memory at the end + $h = (GRAPH_SIZE-5) * ($mem['seg_size'] - $ptr) / $s; + if ($h > 0) { + fill_box($image,$x,$y,50,$h,$col_black,$col_red,bsize($mem['seg_size']-$ptr),$j++); + } + } + } + break; + case 4: + $s=$cache['num_hits']+$cache['num_misses']; + $a=$cache['num_hits']; + + fill_box($image, 30,$size,50,-$a*($size-21)/$s,$col_black,$col_green,sprintf("%.1f%%",$cache['num_hits']*100/$s)); + fill_box($image,130,$size,50,-max(4,($s-$a)*($size-21)/$s),$col_black,$col_red,sprintf("%.1f%%",$cache['num_misses']*100/$s)); + break; + + } + header("Content-type: image/png"); + imagepng($image); + exit; +} + +// pretty printer for byte values +// +function bsize($s) { + foreach (array('','K','M','G') as $i => $k) { + if ($s < 1024) break; + $s/=1024; + } + return sprintf("%5.1f %sBytes",$s,$k); +} + +// sortable table header in "scripts for this host" view +function sortheader($key,$name,$extra='') { + global $MYREQUEST, $MY_SELF_WO_SORT; + + if ($MYREQUEST['SORT1']==$key) { + $MYREQUEST['SORT2'] = $MYREQUEST['SORT2']=='A' ? 'D' : 'A'; + } + return "$name"; + +} + +// create menu entry +function menu_entry($ob,$title) { + global $MYREQUEST,$MY_SELF; + if ($MYREQUEST['OB']!=$ob) { + return "
  • $title
  • "; + } else if (empty($MYREQUEST['SH'])) { + return "
  • $title
  • "; + } else { + return "
  • $title
  • "; + } +} + +function put_login_link($s="Login") +{ + global $MY_SELF,$MYREQUEST,$AUTHENTICATED; + // needs ADMIN_PASSWORD to be changed! + // + if (!USE_AUTHENTICATION) { + return; + } else if (ADMIN_PASSWORD=='password') + { + print <<$s +EOB; + } else if ($AUTHENTICATED) { + print <<$s +EOB; + } +} + + +?> + + +APC INFO <?php echo $host ?> + + + +
    +

    + +
    Opcode Cache
    +

    + +
    +
    + +
  • Refresh Data
  • +EOB; +echo + menu_entry(1,'View Host Stats'), + menu_entry(2,'System Cache Entries'); +if ($AUTHENTICATED) { + echo menu_entry(4,'Per-Directory Entries'); +} +echo + menu_entry(3,'User Cache Entries'), + menu_entry(9,'Version Check'); + +if ($AUTHENTICATED) { + echo <<Clear $cache_mode Cache +EOB; +} +echo << +EOB; + + +// CONTENT +echo << +EOB; + +// MAIN SWITCH STATEMENT + +switch ($MYREQUEST['OB']) { + + + + + +// ----------------------------------------------- +// Host Stats +// ----------------------------------------------- +case OB_HOST_STATS: + $mem_size = $mem['num_seg']*$mem['seg_size']; + $mem_avail= $mem['avail_mem']; + $mem_used = $mem_size-$mem_avail; + $seg_size = bsize($mem['seg_size']); + $req_rate = sprintf("%.2f",($cache['num_hits']+$cache['num_misses'])/($time-$cache['start_time'])); + $hit_rate = sprintf("%.2f",($cache['num_hits'])/($time-$cache['start_time'])); + $miss_rate = sprintf("%.2f",($cache['num_misses'])/($time-$cache['start_time'])); + $insert_rate = sprintf("%.2f",($cache['num_inserts'])/($time-$cache['start_time'])); + $req_rate_user = sprintf("%.2f",($cache_user['num_hits']+$cache_user['num_misses'])/($time-$cache_user['start_time'])); + $hit_rate_user = sprintf("%.2f",($cache_user['num_hits'])/($time-$cache_user['start_time'])); + $miss_rate_user = sprintf("%.2f",($cache_user['num_misses'])/($time-$cache_user['start_time'])); + $insert_rate_user = sprintf("%.2f",($cache_user['num_inserts'])/($time-$cache_user['start_time'])); + $apcversion = phpversion('apc'); + $phpversion = phpversion(); + $number_files = $cache['num_entries']; + $size_files = bsize($cache['mem_size']); + $number_vars = $cache_user['num_entries']; + $size_vars = bsize($cache_user['mem_size']); + $i=0; + echo <<< EOB +

    General Cache Information

    + + + +EOB; + + if(!empty($_SERVER['SERVER_NAME'])) + echo "\n"; + if(!empty($_SERVER['SERVER_SOFTWARE'])) + echo "\n"; + + echo << +EOB; + echo ''; + echo ''; + echo ''; + echo <<
    APC Version$apcversion
    PHP Version$phpversion
    APC Host{$_SERVER['SERVER_NAME']} $host
    Server Software{$_SERVER['SERVER_SOFTWARE']}
    Shared Memory{$mem['num_seg']} Segment(s) with $seg_size +
    ({$cache['memory_type']} memory, {$cache['locking_type']} locking) +
    Start Time',date(DATE_FORMAT,$cache['start_time']),'
    Uptime',duration($cache['start_time']),'
    File Upload Support',$cache['file_upload_progress'],'
    +
    + +

    File Cache Information

    + + + + + + + + + +
    Cached Files$number_files ($size_files)
    Hits{$cache['num_hits']}
    Misses{$cache['num_misses']}
    Request Rate (hits, misses)$req_rate cache requests/second
    Hit Rate$hit_rate cache requests/second
    Miss Rate$miss_rate cache requests/second
    Insert Rate$insert_rate cache requests/second
    Cache full count{$cache['expunges']}
    +
    + +

    User Cache Information

    + + + + + + + + + + +
    Cached Variables$number_vars ($size_vars)
    Hits{$cache_user['num_hits']}
    Misses{$cache_user['num_misses']}
    Request Rate (hits, misses)$req_rate_user cache requests/second
    Hit Rate$hit_rate_user cache requests/second
    Miss Rate$miss_rate_user cache requests/second
    Insert Rate$insert_rate_user cache requests/second
    Cache full count{$cache_user['expunges']}
    +
    + +

    Runtime Settings

    +EOB; + + $j = 0; + foreach (ini_get_all('apc') as $k => $v) { + echo "\n"; + $j = 1 - $j; + } + + if($mem['num_seg']>1 || $mem['num_seg']==1 && count($mem['block_lists'][0])>1) + $mem_note = "Memory Usage
    (multiple slices indicate fragments)"; + else + $mem_note = "Memory Usage"; + + echo <<< EOB +
    ",$k,"",str_replace(',',',
    ',$v['local_value']),"
    +
    + +

    Host Status Diagrams

    + +EOB; + $size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10); + echo << + + + +EOB; + + echo + graphics_avail() ? + ''. + "". + "\n" + : "", + '', + '\n", + '\n", + '', + '', + '\n", + '\n"; + echo <<< EOB + +
    $mem_noteHits & Misses
    \"\"\"\"
     Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size)," Hits: ',$cache['num_hits'].sprintf(" (%.1f%%)",$cache['num_hits']*100/($cache['num_hits']+$cache['num_misses'])),"
     Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size)," Misses: ',$cache['num_misses'].sprintf(" (%.1f%%)",$cache['num_misses']*100/($cache['num_hits']+$cache['num_misses'])),"
    + +
    +

    Detailed Memory Usage and Fragmentation

    + + + + +EOB; + if(isset($mem['adist'])) { + foreach($mem['adist'] as $i=>$v) { + $cur = pow(2,$i); $nxt = pow(2,$i+1)-1; + if($i==0) $range = "1"; + else $range = "$cur - $nxt"; + echo "\n"; + } + } + echo <<

    +EOB; + + // Fragementation: (freeseg - 1) / total_seg + $nseg = $freeseg = $fragsize = $freetotal = 0; + for($i=0; $i<$mem['num_seg']; $i++) { + $ptr = 0; + foreach($mem['block_lists'][$i] as $block) { + if ($block['offset'] != $ptr) { + ++$nseg; + } + $ptr = $block['offset'] + $block['size']; + /* Only consider blocks <5M for the fragmentation % */ + if($block['size']<(5*1024*1024)) $fragsize+=$block['size']; + $freetotal+=$block['size']; + } + $freeseg += count($mem['block_lists'][$i]); + } + + if ($freeseg > 1) { + $frag = sprintf("%.2f%% (%s out of %s in %d fragments)", ($fragsize/$freetotal)*100,bsize($fragsize),bsize($freetotal),$freeseg); + } else { + $frag = "0%"; + } + + if (graphics_avail()) { + $size='width='.(2*GRAPH_SIZE+150).' height='.(GRAPH_SIZE+10); + echo << +EOB; + } + echo <<Fragmentation: $frag +
    $range$v
    +
    +EOB; + + break; + + +// ----------------------------------------------- +// User Cache Entries +// ----------------------------------------------- +case OB_USER_CACHE: + if (!$AUTHENTICATED) { + echo '
    You need to login to see the user values here!
     
    '; + put_login_link("Login now!"); + echo '
    '; + break; + } + $fieldname='info'; + $fieldheading='User Entry Label'; + $fieldkey='info'; + +// ----------------------------------------------- +// System Cache Entries +// ----------------------------------------------- +case OB_SYS_CACHE: + if (!isset($fieldname)) + { + $fieldname='filename'; + $fieldheading='Script Filename'; + if(ini_get("apc.stat")) $fieldkey='inode'; + else $fieldkey='filename'; + } + if (!empty($MYREQUEST['SH'])) + { + echo <<< EOB +
    + +EOB; + + $m=0; + foreach($scope_list as $j => $list) { + foreach($cache[$list] as $i => $entry) { + if (md5($entry[$fieldkey])!=$MYREQUEST['SH']) continue; + foreach($entry as $k => $value) { + if (!$AUTHENTICATED) { + // hide all path entries if not logged in + $value=preg_replace('/^.*(\\/|\\\\)/','<hidden>/',$value); + } + + if ($k == "num_hits") { + $value=sprintf("%s (%.2f%%)",$value,$value*100/$cache['num_hits']); + } + if ($k == 'deletion_time') { + if(!$entry['deletion_time']) $value = "None"; + } + echo + "", + "", + "", + ""; + $m=1-$m; + } + if($fieldkey=='info') { + echo "\n"; + } + break; + } + } + + echo <<
    AttributeValue
    ",ucwords(preg_replace("/_/"," ",$k)),"",(preg_match("/time/",$k) && $value!='None') ? date(DATE_FORMAT,$value) : $value,"
    Stored Value
    ";
    +					$output = var_export(apc_fetch($entry[$fieldkey]),true);
    +					echo htmlspecialchars($output);
    +					echo "
    +
    +EOB; + break; + } + + $cols=6; + echo <<
    Scope: + + ", + ", Sorting:', + '', + '', + '  Search: ', + ' ', + '
    '; + + if (isset($MYREQUEST['SEARCH'])) { + // Don't use preg_quote because we want the user to be able to specify a + // regular expression subpattern. + $MYREQUEST['SEARCH'] = '/'.str_replace('/', '\\/', $MYREQUEST['SEARCH']).'/i'; + if (preg_match($MYREQUEST['SEARCH'], 'test') === false) { + echo '
    Error: enter a valid regular expression as a search query.
    '; + break; + } + } + + echo + '
    ', + '', + '', + '', + '', + '', + '', + ''; + + if($fieldname=='info') { + $cols+=2; + echo ''; + } + echo ''; + + // builds list with alpha numeric sortable keys + // + $list = array(); + foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $i => $entry) { + switch($MYREQUEST['SORT1']) { + case 'A': $k=sprintf('%015d-',$entry['access_time']); break; + case 'H': $k=sprintf('%015d-',$entry['num_hits']); break; + case 'Z': $k=sprintf('%015d-',$entry['mem_size']); break; + case 'M': $k=sprintf('%015d-',$entry['mtime']); break; + case 'C': $k=sprintf('%015d-',$entry['creation_time']); break; + case 'T': $k=sprintf('%015d-',$entry['ttl']); break; + case 'D': $k=sprintf('%015d-',$entry['deletion_time']); break; + case 'S': $k=''; break; + } + if (!$AUTHENTICATED) { + // hide all path entries if not logged in + $list[$k.$entry[$fieldname]]=preg_replace('/^.*(\\/|\\\\)/','<hidden>/',$entry); + } else { + $list[$k.$entry[$fieldname]]=$entry; + } + } + + if ($list) { + + // sort list + // + switch ($MYREQUEST['SORT2']) { + case "A": krsort($list); break; + case "D": ksort($list); break; + } + + // output list + $i=0; + foreach($list as $k => $entry) { + if(!$MYREQUEST['SEARCH'] || preg_match($MYREQUEST['SEARCH'], $entry[$fieldname]) != 0) { + echo + '', + "', + '', + '', + '', + '', + ''; + + if($fieldname=='info') { + if($entry['ttl']) + echo ''; + else + echo ''; + } + if ($entry['deletion_time']) { + + echo ''; + } else if ($MYREQUEST['OB'] == OB_USER_CACHE) { + + echo ''; + } else { + echo ''; + } + echo ''; + $i++; + if ($i == $MYREQUEST['COUNT']) + break; + } + } + + } else { + echo ''; + } + echo <<< EOB +
    ',sortheader('S',$fieldheading, "&OB=".$MYREQUEST['OB']),'',sortheader('H','Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('Z','Size', "&OB=".$MYREQUEST['OB']),'',sortheader('A','Last accessed',"&OB=".$MYREQUEST['OB']),'',sortheader('M','Last modified',"&OB=".$MYREQUEST['OB']),'',sortheader('C','Created at', "&OB=".$MYREQUEST['OB']),'',sortheader('T','Timeout',"&OB=".$MYREQUEST['OB']),'',sortheader('D','Deleted at',"&OB=".$MYREQUEST['OB']),'
    ",$entry[$fieldname],'',$entry['num_hits'],'',$entry['mem_size'],'',date(DATE_FORMAT,$entry['access_time']),'',date(DATE_FORMAT,$entry['mtime']),'',date(DATE_FORMAT,$entry['creation_time']),''.$entry['ttl'].' secondsNone', date(DATE_FORMAT,$entry['deletion_time']), ''; + echo '[Delete Now]'; + echo '  
    No data
    +EOB; + + if ($list && $i < count($list)) { + echo "",count($list)-$i,' more available...'; + } + + echo <<< EOB +
    +EOB; + break; + + +// ----------------------------------------------- +// Per-Directory System Cache Entries +// ----------------------------------------------- +case OB_SYS_CACHE_DIR: + if (!$AUTHENTICATED) { + break; + } + + echo <<
    Scope: + + ", + ", Sorting:', + '', + '', + ", Group By Dir Level:', + ' ', + '
    ', + + '
    ', + '', + '', + '', + '', + '', + '', + '', + ''; + + // builds list with alpha numeric sortable keys + // + $tmp = $list = array(); + foreach($cache[$scope_list[$MYREQUEST['SCOPE']]] as $entry) { + $n = dirname($entry['filename']); + if ($MYREQUEST['AGGR'] > 0) { + $n = preg_replace("!^(/?(?:[^/\\\\]+[/\\\\]){".($MYREQUEST['AGGR']-1)."}[^/\\\\]*).*!", "$1", $n); + } + if (!isset($tmp[$n])) { + $tmp[$n] = array('hits'=>0,'size'=>0,'ents'=>0); + } + $tmp[$n]['hits'] += $entry['num_hits']; + $tmp[$n]['size'] += $entry['mem_size']; + ++$tmp[$n]['ents']; + } + + foreach ($tmp as $k => $v) { + switch($MYREQUEST['SORT1']) { + case 'A': $kn=sprintf('%015d-',$v['size'] / $v['ents']);break; + case 'T': $kn=sprintf('%015d-',$v['ents']); break; + case 'H': $kn=sprintf('%015d-',$v['hits']); break; + case 'Z': $kn=sprintf('%015d-',$v['size']); break; + case 'C': $kn=sprintf('%015d-',$v['hits'] / $v['ents']);break; + case 'S': $kn = $k; break; + } + $list[$kn.$k] = array($k, $v['ents'], $v['hits'], $v['size']); + } + + if ($list) { + + // sort list + // + switch ($MYREQUEST['SORT2']) { + case "A": krsort($list); break; + case "D": ksort($list); break; + } + + // output list + $i = 0; + foreach($list as $entry) { + echo + '', + "', + '', + '', + '', + '', + '', + ''; + + if (++$i == $MYREQUEST['COUNT']) break; + } + + } else { + echo ''; + } + echo <<< EOB +
    ',sortheader('S','Directory Name', "&OB=".$MYREQUEST['OB']),'',sortheader('T','Number of Files',"&OB=".$MYREQUEST['OB']),'',sortheader('H','Total Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('Z','Total Size', "&OB=".$MYREQUEST['OB']),'',sortheader('C','Avg. Hits', "&OB=".$MYREQUEST['OB']),'',sortheader('A','Avg. Size', "&OB=".$MYREQUEST['OB']),'
    ",$entry[0],'',$entry[1],'',$entry[2],'',$entry[3],'',round($entry[2] / $entry[1]),'',round($entry[3] / $entry[1]),'
    No data
    +EOB; + + if ($list && $i < count($list)) { + echo "",count($list)-$i,' more available...'; + } + + echo <<< EOB +
    +EOB; + break; + +// ----------------------------------------------- +// Version check +// ----------------------------------------------- +case OB_VERSION_CHECK: + echo <<

    APC Version Information

    + + + + +EOB; + + $rss = @file_get_contents("http://pecl.php.net/feeds/pkg_apc.rss"); + if (!$rss) { + echo ''; + } else { + $apcversion = phpversion('apc'); + + preg_match('!APC ([0-9.]+)!', $rss, $match); + echo ''; + echo ''; + } + echo <<< EOB +
    Unable to fetch version information.
    '; + if (version_compare($apcversion, $match[1], '>=')) { + echo '
    You are running the latest version of APC ('.$apcversion.')
    '; + $i = 3; + } else { + echo '
    You are running an older version of APC ('.$apcversion.'), + newer version '.$match[1].' is available at + http://pecl.php.net/package/APC/'.$match[1].' +
    '; + $i = -1; + } + echo '

    Change Log:


    '; + + preg_match_all('!<(title|description)>([^<]+)!', $rss, $match); + next($match[2]); next($match[2]); + + while (list(,$v) = each($match[2])) { + list(,$ver) = explode(' ', $v, 2); + if ($i < 0 && version_compare($apcversion, $ver, '>=')) { + break; + } else if (!$i--) { + break; + } + echo "".htmlspecialchars($v)."
    "; + echo nl2br(htmlspecialchars(current($match[2])))."
    "; + next($match[2]); + } + echo '
    + +EOB; + break; + +} + +echo <<< EOB + +EOB; + +?> + + + + diff --git a/apc_cache.c b/apc_cache.c new file mode 100644 index 0000000..62d5f8b --- /dev/null +++ b/apc_cache.c @@ -0,0 +1,1129 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_cache.c,v 3.145.2.2 2008/03/25 18:24:57 gopalv Exp $ */ + +#include "apc_cache.h" +#include "apc_lock.h" +#include "apc_sma.h" +#include "apc_globals.h" +#include "SAPI.h" +#include "ext/standard/php_var.h" +#include "ext/standard/php_smart_str.h" + +/* TODO: rehash when load factor exceeds threshold */ + +#define CHECK(p) { if ((p) == NULL) return NULL; } + +/* {{{ locking macros */ +#define CREATE_LOCK(lock) apc_lck_create(NULL, 0, 1, lock) +#define DESTROY_LOCK(c) apc_lck_destroy(c->header->lock) +#define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c->header->lock); } +#define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c->header->lock); } +#define UNLOCK(c) { apc_lck_unlock(c->header->lock); HANDLE_UNBLOCK_INTERRUPTIONS(); } +/* }}} */ + +/* {{{ key_equals */ +#define key_equals(a, b) (a.inode==b.inode && a.device==b.device) +/* }}} */ + +static void apc_cache_expunge(apc_cache_t* cache, size_t size); + +/* {{{ hash */ +static unsigned int hash(apc_cache_key_t key) +{ + return key.data.file.device + key.data.file.inode; +} +/* }}} */ + +/* {{{ string_nhash_8 */ +static unsigned int string_nhash_8(const char *s, size_t len) +{ + register const unsigned int *iv = (const unsigned int *)s; + register unsigned int h = 0; + register const unsigned int *e = (const unsigned int *)(s + len - (len % sizeof(unsigned int))); + + for(;iv> ((8*sizeof(unsigned int)) - 7)); + } + s = (const char *)iv; + for(len %= sizeof(unsigned int);len;len--) { + h += *(s++); + } + h ^= (h >> 13); + h ^= (h >> 7); + return h; +} +/* }}} */ + +/* {{{ make_slot */ +slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next, time_t t) +{ + slot_t* p = apc_sma_malloc(sizeof(slot_t)); + if (!p) return NULL; + + if(value->type == APC_CACHE_ENTRY_USER) { + char *identifier = (char*) apc_xstrdup(key.data.user.identifier, apc_sma_malloc); + if (!identifier) { + apc_sma_free(p); + return NULL; + } + key.data.user.identifier = identifier; + } else if(key.type == APC_CACHE_KEY_FPFILE) { + char *fullpath = (char*) apc_xstrdup(key.data.fpfile.fullpath, apc_sma_malloc); + if (!fullpath) { + apc_sma_free(p); + return NULL; + } + key.data.fpfile.fullpath = fullpath; + } + p->key = key; + p->value = value; + p->next = next; + p->num_hits = 0; + p->creation_time = t; + p->access_time = t; + p->deletion_time = 0; + return p; +} +/* }}} */ + +/* {{{ free_slot */ +static void free_slot(slot_t* slot) +{ + if(slot->value->type == APC_CACHE_ENTRY_USER) { + apc_sma_free((char *)slot->key.data.user.identifier); + } else if(slot->key.type == APC_CACHE_KEY_FPFILE) { + apc_sma_free((char *)slot->key.data.fpfile.fullpath); + } + apc_cache_free_entry(slot->value); + apc_sma_free(slot); +} +/* }}} */ + +/* {{{ remove_slot */ +static void remove_slot(apc_cache_t* cache, slot_t** slot) +{ + slot_t* dead = *slot; + *slot = (*slot)->next; + + cache->header->mem_size -= dead->value->mem_size; + cache->header->num_entries--; + if (dead->value->ref_count <= 0) { + free_slot(dead); + } + else { + dead->next = cache->header->deleted_list; + dead->deletion_time = time(0); + cache->header->deleted_list = dead; + } +} +/* }}} */ + +/* {{{ process_pending_removals */ +static void process_pending_removals(apc_cache_t* cache) +{ + slot_t** slot; + time_t now; + + /* This function scans the list of removed cache entries and deletes any + * entry whose reference count is zero (indicating that it is no longer + * being executed) or that has been on the pending list for more than + * cache->gc_ttl seconds (we issue a warning in the latter case). + */ + + if (!cache->header->deleted_list) + return; + + slot = &cache->header->deleted_list; + now = time(0); + + while (*slot != NULL) { + int gc_sec = cache->gc_ttl ? (now - (*slot)->deletion_time) : 0; + + if ((*slot)->value->ref_count <= 0 || gc_sec > cache->gc_ttl) { + slot_t* dead = *slot; + + if (dead->value->ref_count > 0) { + switch(dead->value->type) { + case APC_CACHE_ENTRY_FILE: + apc_log(APC_WARNING, "GC cache entry '%s' (dev=%d ino=%d) " + "was on gc-list for %d seconds", dead->value->data.file.filename, + dead->key.data.file.device, dead->key.data.file.inode, gc_sec); + break; + case APC_CACHE_ENTRY_USER: + apc_log(APC_WARNING, "GC cache entry '%s' " + "was on gc-list for %d seconds", dead->value->data.user.info, gc_sec); + break; + } + } + *slot = dead->next; + free_slot(dead); + } + else { + slot = &(*slot)->next; + } + } +} +/* }}} */ + +/* {{{ prevent_garbage_collection */ +static void prevent_garbage_collection(apc_cache_entry_t* entry) +{ + /* set reference counts on zend objects to an arbitrarily high value to + * prevent garbage collection after execution */ + + enum { BIG_VALUE = 1000 }; + + if(entry->data.file.op_array) { + entry->data.file.op_array->refcount[0] = BIG_VALUE; + } + if (entry->data.file.functions) { + int i; + apc_function_t* fns = entry->data.file.functions; + for (i=0; fns[i].function != NULL; i++) { + *(fns[i].function->op_array.refcount) = BIG_VALUE; + } + } + if (entry->data.file.classes) { + int i; + apc_class_t* classes = entry->data.file.classes; + for (i=0; classes[i].class_entry != NULL; i++) { +#ifdef ZEND_ENGINE_2 + classes[i].class_entry->refcount = BIG_VALUE; +#else + classes[i].class_entry->refcount[0] = BIG_VALUE; +#endif + } + } +} +/* }}} */ + +/* {{{ apc_cache_create */ +apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl) +{ + apc_cache_t* cache; + int cache_size; + int num_slots; + int i; + + num_slots = size_hint > 0 ? size_hint*2 : 2000; + + cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t)); + cache_size = sizeof(cache_header_t) + num_slots*sizeof(slot_t*); + + cache->shmaddr = apc_sma_malloc(cache_size); + if(!cache->shmaddr) { + apc_eprint("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). "); + } + memset(cache->shmaddr, 0, cache_size); + + cache->header = (cache_header_t*) cache->shmaddr; + cache->header->num_hits = 0; + cache->header->num_misses = 0; + cache->header->deleted_list = NULL; + cache->header->start_time = time(NULL); + cache->header->expunges = 0; + cache->header->busy = 0; + + cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(cache_header_t)); + cache->num_slots = num_slots; + cache->gc_ttl = gc_ttl; + cache->ttl = ttl; + CREATE_LOCK(cache->header->lock); +#if NONBLOCKING_LOCK_AVAILABLE + CREATE_LOCK(cache->header->wrlock); +#endif + for (i = 0; i < num_slots; i++) { + cache->slots[i] = NULL; + } + cache->expunge_cb = apc_cache_expunge; + + return cache; +} +/* }}} */ + +/* {{{ apc_cache_destroy */ +void apc_cache_destroy(apc_cache_t* cache) +{ + DESTROY_LOCK(cache); + apc_efree(cache); +} +/* }}} */ + +/* {{{ apc_cache_clear */ +void apc_cache_clear(apc_cache_t* cache) +{ + int i; + + if(!cache) return; + + LOCK(cache); + cache->header->busy = 1; + cache->header->num_hits = 0; + cache->header->num_misses = 0; + cache->header->start_time = time(NULL); + cache->header->expunges = 0; + + for (i = 0; i < cache->num_slots; i++) { + slot_t* p = cache->slots[i]; + while (p) { + remove_slot(cache, &p); + } + cache->slots[i] = NULL; + } + + cache->header->busy = 0; + UNLOCK(cache); +} +/* }}} */ + +/* {{{ apc_cache_expunge */ +static void apc_cache_expunge(apc_cache_t* cache, size_t size) +{ + int i; + time_t t; + TSRMLS_FETCH(); + +#if PHP_API_VERSION < 20041225 +#if HAVE_APACHE && defined(APC_PHP4_STAT) + t = ((request_rec *)SG(server_context))->request_time; +#else + t = time(0); +#endif +#else + t = sapi_get_request_time(TSRMLS_C); +#endif + + if(!cache) return; + + if(!cache->ttl) { + /* + * If cache->ttl is not set, we wipe out the entire cache when + * we run out of space. + */ + LOCK(cache); + cache->header->busy = 1; + cache->header->expunges++; + for (i = 0; i < cache->num_slots; i++) { + slot_t* p = cache->slots[i]; + while (p) { + remove_slot(cache, &p); + } + cache->slots[i] = NULL; + } + cache->header->busy = 0; + UNLOCK(cache); + } else { + slot_t **p; + + /* + * If the ttl for the cache is set we walk through and delete stale + * entries. For the user cache that is slightly confusing since + * we have the individual entry ttl's we can look at, but that would be + * too much work. So if you want the user cache expunged, set a high + * default apc.user_ttl and still provide a specific ttl for each entry + * on insert + */ + + LOCK(cache); + cache->header->busy = 1; + cache->header->expunges++; + for (i = 0; i < cache->num_slots; i++) { + p = &cache->slots[i]; + while(*p) { + /* + * For the user cache we look at the individual entry ttl values + * and if not set fall back to the default ttl for the user cache + */ + if((*p)->value->type == APC_CACHE_ENTRY_USER) { + if((*p)->value->data.user.ttl) { + if((*p)->creation_time + (*p)->value->data.user.ttl < t) { + remove_slot(cache, p); + continue; + } + } else if(cache->ttl) { + if((*p)->creation_time + cache->ttl < t) { + remove_slot(cache, p); + continue; + } + } + } else if((*p)->access_time < (t - cache->ttl)) { + remove_slot(cache, p); + continue; + } + p = &(*p)->next; + } + } + cache->header->busy = 0; + UNLOCK(cache); + } +} +/* }}} */ + +/* {{{ apc_cache_insert */ +int apc_cache_insert(apc_cache_t* cache, + apc_cache_key_t key, + apc_cache_entry_t* value, + time_t t) +{ + slot_t** slot; + + if (!value) { + return 0; + } + +#ifdef __DEBUG_APC__ + fprintf(stderr,"Inserting [%s]\n", value->data.file.filename); +#endif + + LOCK(cache); + process_pending_removals(cache); + + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots]; + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots]; + + while(*slot) { + if(key.type == (*slot)->key.type) { + if(key.type == APC_CACHE_KEY_FILE) { + if(key_equals((*slot)->key.data.file, key.data.file)) { + /* If existing slot for the same device+inode is different, remove it and insert the new version */ + if ((*slot)->key.mtime != key.mtime) { + remove_slot(cache, slot); + break; + } + UNLOCK(cache); + return 0; + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { + remove_slot(cache, slot); + continue; + } + } else { /* APC_CACHE_KEY_FPFILE */ + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) { + /* Hrm.. it's already here, remove it and insert new one */ + remove_slot(cache, slot); + break; + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { + remove_slot(cache, slot); + continue; + } + } + } + slot = &(*slot)->next; + } + + if ((*slot = make_slot(key, value, *slot, t)) == NULL) { + UNLOCK(cache); + return -1; + } + + cache->header->mem_size += value->mem_size; + cache->header->num_entries++; + cache->header->num_inserts++; + + UNLOCK(cache); + return 1; +} +/* }}} */ + +/* {{{ apc_cache_user_insert */ +int apc_cache_user_insert(apc_cache_t* cache, apc_cache_key_t key, apc_cache_entry_t* value, time_t t, int exclusive TSRMLS_DC) +{ + slot_t** slot; + size_t* mem_size_ptr = NULL; + + if (!value) { + return 0; + } + + LOCK(cache); + process_pending_removals(cache); + + slot = &cache->slots[string_nhash_8(key.data.user.identifier, key.data.user.identifier_len) % cache->num_slots]; + + if (APCG(mem_size_ptr) != NULL) { + mem_size_ptr = APCG(mem_size_ptr); + APCG(mem_size_ptr) = NULL; + } + + while (*slot) { + if (!memcmp((*slot)->key.data.user.identifier, key.data.user.identifier, key.data.user.identifier_len)) { + /* + * At this point we have found the user cache entry. If we are doing + * an exclusive insert (apc_add) we are going to bail right away if + * the user entry already exists and it has no ttl, or + * there is a ttl and the entry has not timed out yet. + */ + if(exclusive && ( !(*slot)->value->data.user.ttl || + ( (*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) >= t ) + ) ) { + UNLOCK(cache); + return 0; + } + remove_slot(cache, slot); + break; + } else + /* + * This is a bit nasty. The idea here is to do runtime cleanup of the linked list of + * slot entries so we don't always have to skip past a bunch of stale entries. We check + * for staleness here and get rid of them by first checking to see if the cache has a global + * access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly + * we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl + */ + if((cache->ttl && (*slot)->access_time < (t - cache->ttl)) || + ((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t)) { + remove_slot(cache, slot); + continue; + } + slot = &(*slot)->next; + } + + if (mem_size_ptr != NULL) { + APCG(mem_size_ptr) = mem_size_ptr; + } + + if ((*slot = make_slot(key, value, *slot, t)) == NULL) { + UNLOCK(cache); + return 0; + } + if (APCG(mem_size_ptr) != NULL) { + value->mem_size = *APCG(mem_size_ptr); + cache->header->mem_size += *APCG(mem_size_ptr); + } + cache->header->num_entries++; + cache->header->num_inserts++; + + UNLOCK(cache); + return 1; +} +/* }}} */ + +/* {{{ apc_cache_find_slot */ +slot_t* apc_cache_find_slot(apc_cache_t* cache, apc_cache_key_t key, time_t t) +{ + slot_t** slot; + volatile slot_t* retval = NULL; + + LOCK(cache); + if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots]; + else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots]; + + while (*slot) { + if(key.type == (*slot)->key.type) { + if(key.type == APC_CACHE_KEY_FILE) { + if(key_equals((*slot)->key.data.file, key.data.file)) { + if((*slot)->key.mtime != key.mtime) { + remove_slot(cache, slot); + cache->header->num_misses++; + UNLOCK(cache); + return NULL; + } + (*slot)->num_hits++; + (*slot)->value->ref_count++; + (*slot)->access_time = t; + prevent_garbage_collection((*slot)->value); + cache->header->num_hits++; + retval = *slot; + UNLOCK(cache); + return (slot_t*)retval; + } + } else { /* APC_CACHE_KEY_FPFILE */ + if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) { + /* TTL Check ? */ + (*slot)->num_hits++; + (*slot)->value->ref_count++; + (*slot)->access_time = t; + prevent_garbage_collection((*slot)->value); + cache->header->num_hits++; + retval = *slot; + UNLOCK(cache); + return (slot_t*)retval; + } + } + } + slot = &(*slot)->next; + } + cache->header->num_misses++; + UNLOCK(cache); + return NULL; +} +/* }}} */ + +/* {{{ apc_cache_find */ +apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key, time_t t) +{ + slot_t * slot = apc_cache_find_slot(cache, key, t); + return (slot) ? slot->value : NULL; +} +/* }}} */ + +/* {{{ apc_cache_user_find */ +apc_cache_entry_t* apc_cache_user_find(apc_cache_t* cache, char *strkey, int keylen, time_t t) +{ + slot_t** slot; + volatile apc_cache_entry_t* value = NULL; + + LOCK(cache); + + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots]; + + while (*slot) { + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) { + /* Check to make sure this entry isn't expired by a hard TTL */ + if((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) { + remove_slot(cache, slot); + UNLOCK(cache); + return NULL; + } + /* Otherwise we are fine, increase counters and return the cache entry */ + (*slot)->num_hits++; + (*slot)->value->ref_count++; + (*slot)->access_time = t; + + cache->header->num_hits++; + value = (*slot)->value; + UNLOCK(cache); + return (apc_cache_entry_t*)value; + } + slot = &(*slot)->next; + } + + UNLOCK(cache); + return NULL; +} +/* }}} */ + +/* {{{ apc_cache_user_delete */ +int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen) +{ + slot_t** slot; + + LOCK(cache); + + slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots]; + + while (*slot) { + if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) { + remove_slot(cache, slot); + UNLOCK(cache); + return 1; + } + slot = &(*slot)->next; + } + + UNLOCK(cache); + return 0; +} +/* }}} */ + +/* {{{ apc_cache_release */ +void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry) +{ + LOCK(cache); + entry->ref_count--; + UNLOCK(cache); +} +/* }}} */ + +/* {{{ apc_cache_make_file_key */ +int apc_cache_make_file_key(apc_cache_key_t* key, + const char* filename, + const char* include_path, + time_t t + TSRMLS_DC) +{ + struct stat *tmp_buf=NULL; + struct apc_fileinfo_t fileinfo = { {0}, }; + int len; + + assert(key != NULL); + + if (!filename || !SG(request_info).path_translated) { +#ifdef __DEBUG_APC__ + fprintf(stderr,"No filename and no path_translated - bailing\n"); +#endif + return 0; + } + + len = strlen(filename); + if(APCG(fpstat)==0) { + if(IS_ABSOLUTE_PATH(filename,len)) { + key->data.fpfile.fullpath = filename; + key->data.fpfile.fullpath_len = len; + key->mtime = t; + key->type = APC_CACHE_KEY_FPFILE; + } else { + if (apc_search_paths(filename, include_path, &fileinfo) != 0) { + apc_wprint("apc failed to locate %s - bailing", filename); + return 0; + } + + if(!realpath(fileinfo.fullpath, APCG(canon_path))) { + apc_wprint("realpath failed to canonicalize %s - bailing", filename); + return 0; + } + + key->data.fpfile.fullpath = APCG(canon_path); + key->data.fpfile.fullpath_len = strlen(APCG(canon_path)); + key->mtime = t; + key->type = APC_CACHE_KEY_FPFILE; + } + return 1; + } + + if(!strcmp(SG(request_info).path_translated, filename)) { + tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */ + } + if(tmp_buf) { + fileinfo.st_buf.sb = *tmp_buf; + } else { + if (apc_search_paths(filename, include_path, &fileinfo) != 0) { +#ifdef __DEBUG_APC__ + fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated); +#endif + return 0; + } + } + + if(APCG(max_file_size) < fileinfo.st_buf.sb.st_size) { +#ifdef __DEBUG_APC__ + fprintf(stderr,"File is too big %s (%d - %ld) - bailing\n",filename,t,fileinfo.st_buf.sb.st_size); +#endif + return 0; + } + + /* + * This is a bit of a hack. + * + * Here I am checking to see if the file is at least 2 seconds old. + * The idea is that if the file is currently being written to then its + * mtime is going to match or at most be 1 second off of the current + * request time and we want to avoid caching files that have not been + * completely written. Of course, people should be using atomic + * mechanisms to push files onto live web servers, but adding this + * tiny safety is easier than educating the world. This is now + * configurable, but the default is still 2 seconds. + */ + if(APCG(file_update_protection) && (t - fileinfo.st_buf.sb.st_mtime < APCG(file_update_protection))) { +#ifdef __DEBUG_APC__ + fprintf(stderr,"File is too new %s (%d - %d) - bailing\n",filename,t,fileinfo.st_buf.sb.st_mtime); +#endif + return 0; + } + + key->data.file.device = fileinfo.st_buf.sb.st_dev; + key->data.file.inode = fileinfo.st_buf.sb.st_ino; + /* + * If working with content management systems that like to munge the mtime, + * it might be appropriate to key off of the ctime to be immune to systems + * that try to backdate a template. If the mtime is set to something older + * than the previous mtime of a template we will obviously never see this + * "older" template. At some point the Smarty templating system did this. + * I generally disagree with using the ctime here because you lose the + * ability to warm up new content by saving it to a temporary file, hitting + * it once to cache it and then renaming it into its permanent location so + * set the apc.stat_ctime=true to enable this check. + */ + if(APCG(stat_ctime)) { + key->mtime = (fileinfo.st_buf.sb.st_ctime > fileinfo.st_buf.sb.st_mtime) ? fileinfo.st_buf.sb.st_ctime : fileinfo.st_buf.sb.st_mtime; + } else { + key->mtime = fileinfo.st_buf.sb.st_mtime; + } + key->type = APC_CACHE_KEY_FILE; + return 1; +} +/* }}} */ + +/* {{{ apc_cache_make_user_key */ +int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t) +{ + assert(key != NULL); + + if (!identifier) + return 0; + + key->data.user.identifier = identifier; + key->data.user.identifier_len = identifier_len; + key->mtime = t; + key->type = APC_CACHE_KEY_USER; + return 1; +} +/* }}} */ + +/* {{{ apc_cache_make_file_entry */ +apc_cache_entry_t* apc_cache_make_file_entry(const char* filename, + zend_op_array* op_array, + apc_function_t* functions, + apc_class_t* classes) +{ + apc_cache_entry_t* entry; + + entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t)); + if (!entry) return NULL; + + entry->data.file.filename = apc_xstrdup(filename, apc_sma_malloc); + if(!entry->data.file.filename) { +#ifdef __DEBUG_APC__ + fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is NULL - bailing\n"); +#endif + apc_sma_free(entry); + return NULL; + } +#ifdef __DEBUG_APC__ + fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is [%s]\n",entry->data.file.filename); +#endif + entry->data.file.op_array = op_array; + entry->data.file.functions = functions; + entry->data.file.classes = classes; + entry->type = APC_CACHE_ENTRY_FILE; + entry->ref_count = 0; + entry->mem_size = 0; + return entry; +} +/* }}} */ + +/* {{{ apc_cache_store_zval */ +zval* apc_cache_store_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + smart_str buf = {0}; + php_serialize_data_t var_hash; + TSRMLS_FETCH(); + + if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) { + if(!dst) { + CHECK(dst = (zval*) allocate(sizeof(zval))); + } + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&buf, (zval**)&src, &var_hash TSRMLS_CC); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + dst->type = IS_NULL; /* in case we fail */ + if(buf.c) { + dst->type = src->type & ~IS_CONSTANT_INDEX; + dst->value.str.len = buf.len; + CHECK(dst->value.str.val = apc_xmemcpy(buf.c, buf.len+1, allocate)); + dst->type = src->type; + smart_str_free(&buf); + } + return dst; + } else { + + /* Maintain a list of zvals we've copied to properly handle recursive structures */ + HashTable *old = APCG(copied_zvals); + APCG(copied_zvals) = emalloc(sizeof(HashTable)); + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0); + + dst = apc_copy_zval(dst, src, allocate, deallocate); + + if(APCG(copied_zvals)) { + zend_hash_destroy(APCG(copied_zvals)); + efree(APCG(copied_zvals)); + } + + APCG(copied_zvals) = old; + + return dst; + } +} +/* }}} */ + +/* {{{ apc_cache_fetch_zval */ +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + TSRMLS_FETCH(); + if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) { + php_unserialize_data_t var_hash; + const unsigned char *p = (unsigned char*)Z_STRVAL_P(src); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + if(!php_var_unserialize(&dst, &p, p + Z_STRLEN_P(src), &var_hash TSRMLS_CC)) { + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + zval_dtor(dst); + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - Z_STRVAL_P(src)), Z_STRLEN_P(src)); + dst->type = IS_NULL; + } + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return dst; + } else { + + /* Maintain a list of zvals we've copied to properly handle recursive structures */ + HashTable *old = APCG(copied_zvals); + APCG(copied_zvals) = emalloc(sizeof(HashTable)); + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0); + + dst = apc_copy_zval(dst, src, allocate, deallocate); + + if(APCG(copied_zvals)) { + zend_hash_destroy(APCG(copied_zvals)); + efree(APCG(copied_zvals)); + } + + APCG(copied_zvals) = old; + + return dst; + } +} +/* }}} */ + +/* {{{ apc_cache_free_zval */ +void apc_cache_free_zval(zval* src, apc_free_t deallocate) +{ + TSRMLS_FETCH(); + if ((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) { + if (src->value.str.val) { + deallocate(src->value.str.val); + } + deallocate(src); + } else { + /* Maintain a list of zvals we've copied to properly handle recursive structures */ + HashTable *old = APCG(copied_zvals); + APCG(copied_zvals) = emalloc(sizeof(HashTable)); + zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0); + + apc_free_zval(src, deallocate); + + if(APCG(copied_zvals)) { + zend_hash_destroy(APCG(copied_zvals)); + efree(APCG(copied_zvals)); + } + + APCG(copied_zvals) = old; + } +} +/* }}} */ + +/* {{{ apc_cache_make_user_entry */ +apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval* val, const unsigned int ttl) +{ + apc_cache_entry_t* entry; + + entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t)); + if (!entry) return NULL; + + entry->data.user.info = apc_xmemcpy(info, info_len, apc_sma_malloc); + entry->data.user.info_len = info_len; + if(!entry->data.user.info) { + apc_sma_free(entry); + return NULL; + } + entry->data.user.val = apc_cache_store_zval(NULL, val, apc_sma_malloc, apc_sma_free); + if(!entry->data.user.val) { + apc_sma_free(entry->data.user.info); + apc_sma_free(entry); + return NULL; + } + INIT_PZVAL(entry->data.user.val); + entry->data.user.ttl = ttl; + entry->type = APC_CACHE_ENTRY_USER; + entry->ref_count = 0; + entry->mem_size = 0; + return entry; +} +/* }}} */ + +/* {{{ apc_cache_free_entry */ +void apc_cache_free_entry(apc_cache_entry_t* entry) +{ + if (entry != NULL) { + assert(entry->ref_count == 0); + switch(entry->type) { + case APC_CACHE_ENTRY_FILE: + apc_sma_free(entry->data.file.filename); + apc_free_op_array(entry->data.file.op_array, apc_sma_free); + apc_free_functions(entry->data.file.functions, apc_sma_free); + apc_free_classes(entry->data.file.classes, apc_sma_free); + break; + case APC_CACHE_ENTRY_USER: + apc_sma_free(entry->data.user.info); + apc_cache_free_zval(entry->data.user.val, apc_sma_free); + break; + } + apc_sma_free(entry); + } +} +/* }}} */ + +/* {{{ apc_cache_info */ +apc_cache_info_t* apc_cache_info(apc_cache_t* cache, zend_bool limited) +{ + apc_cache_info_t* info; + slot_t* p; + int i; + + if(!cache) return NULL; + + LOCK(cache); + + info = (apc_cache_info_t*) apc_emalloc(sizeof(apc_cache_info_t)); + if(!info) { + UNLOCK(cache); + return NULL; + } + info->num_slots = cache->num_slots; + info->ttl = cache->ttl; + info->num_hits = cache->header->num_hits; + info->num_misses = cache->header->num_misses; + info->list = NULL; + info->deleted_list = NULL; + info->start_time = cache->header->start_time; + info->expunges = cache->header->expunges; + info->mem_size = cache->header->mem_size; + info->num_entries = cache->header->num_entries; + info->num_inserts = cache->header->num_inserts; + + if(!limited) { + /* For each hashtable slot */ + for (i = 0; i < info->num_slots; i++) { + p = cache->slots[i]; + for (; p != NULL; p = p->next) { + apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t)); + + if(p->value->type == APC_CACHE_ENTRY_FILE) { + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc); + link->data.file.device = p->key.data.file.device; + link->data.file.inode = p->key.data.file.inode; + link->type = APC_CACHE_ENTRY_FILE; + } else if(p->value->type == APC_CACHE_ENTRY_USER) { + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc); + link->data.user.ttl = p->value->data.user.ttl; + link->type = APC_CACHE_ENTRY_USER; + } + link->num_hits = p->num_hits; + link->mtime = p->key.mtime; + link->creation_time = p->creation_time; + link->deletion_time = p->deletion_time; + link->access_time = p->access_time; + link->ref_count = p->value->ref_count; + link->mem_size = p->value->mem_size; + link->next = info->list; + info->list = link; + } + } + + /* For each slot pending deletion */ + for (p = cache->header->deleted_list; p != NULL; p = p->next) { + apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t)); + + if(p->value->type == APC_CACHE_ENTRY_FILE) { + link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc); + if(p->key.type == APC_CACHE_KEY_FILE) { + link->data.file.device = p->key.data.file.device; + link->data.file.inode = p->key.data.file.inode; + } else { /* This is a no-stat fullpath file entry */ + link->data.file.device = 0; + link->data.file.inode = 0; + } + link->type = APC_CACHE_ENTRY_FILE; + } else if(p->value->type == APC_CACHE_ENTRY_USER) { + link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc); + link->data.user.ttl = p->value->data.user.ttl; + link->type = APC_CACHE_ENTRY_USER; + } + link->num_hits = p->num_hits; + link->mtime = p->key.mtime; + link->creation_time = p->creation_time; + link->deletion_time = p->deletion_time; + link->access_time = p->access_time; + link->ref_count = p->value->ref_count; + link->mem_size = p->value->mem_size; + link->next = info->deleted_list; + info->deleted_list = link; + } + } + + UNLOCK(cache); + return info; +} +/* }}} */ + +/* {{{ apc_cache_free_info */ +void apc_cache_free_info(apc_cache_info_t* info) +{ + apc_cache_link_t* p = info->list; + apc_cache_link_t* q = NULL; + while (p != NULL) { + q = p; + p = p->next; + if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename); + else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info); + apc_efree(q); + } + p = info->deleted_list; + while (p != NULL) { + q = p; + p = p->next; + if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename); + else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info); + apc_efree(q); + } + apc_efree(info); +} +/* }}} */ + +/* {{{ apc_cache_unlock */ +void apc_cache_unlock(apc_cache_t* cache) +{ + UNLOCK(cache); +} +/* }}} */ + +/* {{{ apc_cache_busy */ +zend_bool apc_cache_busy(apc_cache_t* cache) +{ + return cache->header->busy; +} +/* }}} */ + +#if NONBLOCKING_LOCK_AVAILABLE +/* {{{ apc_cache_write_lock */ +zend_bool apc_cache_write_lock(apc_cache_t* cache) +{ + return apc_lck_nb_lock(cache->header->wrlock); +} +/* }}} */ + +/* {{{ apc_cache_write_unlock */ +void apc_cache_write_unlock(apc_cache_t* cache) +{ + apc_lck_unlock(cache->header->wrlock); +} +/* }}} */ +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_cache.h b/apc_cache.h new file mode 100644 index 0000000..a36c46f --- /dev/null +++ b/apc_cache.h @@ -0,0 +1,343 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_cache.h,v 3.46.2.2 2008/03/25 18:24:57 gopalv Exp $ */ + +#ifndef APC_CACHE_H +#define APC_CACHE_H + +/* + * This module defines the shared memory file cache. Basically all of the + * logic for storing and retrieving cache entries lives here. + */ + +#include "apc.h" +#include "apc_compile.h" +#include "apc_lock.h" + +#define APC_CACHE_ENTRY_FILE 1 +#define APC_CACHE_ENTRY_USER 2 + +#define APC_CACHE_KEY_FILE 1 +#define APC_CACHE_KEY_USER 2 +#define APC_CACHE_KEY_FPFILE 3 + +/* {{{ struct definition: apc_cache_key_t */ +#define T apc_cache_t* +typedef struct apc_cache_t apc_cache_t; /* opaque cache type */ + +typedef union _apc_cache_key_data_t { + struct { + dev_t device; /* the filesystem device */ + ino_t inode; /* the filesystem inode */ + } file; + struct { + const char *identifier; + int identifier_len; + } user; + struct { + const char *fullpath; + int fullpath_len; + } fpfile; +} apc_cache_key_data_t; + +typedef struct apc_cache_key_t apc_cache_key_t; +struct apc_cache_key_t { + apc_cache_key_data_t data; + time_t mtime; /* the mtime of this cached entry */ + unsigned char type; +}; +/* }}} */ + +/* {{{ struct definition: apc_cache_entry_t */ +typedef union _apc_cache_entry_value_t { + struct { + char *filename; /* absolute path to source file */ + zend_op_array* op_array; /* op_array allocated in shared memory */ + apc_function_t* functions; /* array of apc_function_t's */ + apc_class_t* classes; /* array of apc_class_t's */ + } file; + struct { + char *info; + int info_len; + zval *val; + unsigned int ttl; + } user; +} apc_cache_entry_value_t; + +typedef struct apc_cache_entry_t apc_cache_entry_t; +struct apc_cache_entry_t { + apc_cache_entry_value_t data; + unsigned char type; + int ref_count; + size_t mem_size; +}; +/* }}} */ + +/* + * apc_cache_create creates the shared memory compiler cache. This function + * should be called just once (ideally in the web server parent process, e.g. + * in apache), otherwise you will end up with multiple caches (which won't + * necessarily break anything). Returns a pointer to the cache object. + * + * size_hint is a "hint" at the total number of source files that will be + * cached. It determines the physical size of the hash table. Passing 0 for + * this argument will use a reasonable default value. + * + * gc_ttl is the maximum time a cache entry may speed on the garbage + * collection list. This is basically a work around for the inherent + * unreliability of our reference counting mechanism (see apc_cache_release). + * + * ttl is the maximum time a cache entry can idle in a slot in case the slot + * is needed. This helps in cleaning up the cache and ensuring that entries + * hit frequently stay cached and ones not hit very often eventually disappear. + */ +extern T apc_cache_create(int size_hint, int gc_ttl, int ttl); + +/* + * apc_cache_destroy releases any OS resources associated with a cache object. + * Under apache, this function can be safely called by the child processes + * when they exit. + */ +extern void apc_cache_destroy(T cache); + +/* + * apc_cache_clear empties a cache. This can safely be called at any time, + * even while other server processes are executing cached source files. + */ +extern void apc_cache_clear(T cache); + +/* + * apc_cache_insert adds an entry to the cache, using a filename as a key. + * Internally, the filename is translated to a canonical representation, so + * that relative and absolute filenames will map to a single key. Returns + * non-zero if the file was successfully inserted, 0 otherwise. If 0 is + * returned, the caller must free the cache entry by calling + * apc_cache_free_entry (see below). + * + * key is the value created by apc_cache_make_file_key for file keys. + * + * value is a cache entry returned by apc_cache_make_entry (see below). + */ +extern int apc_cache_insert(T cache, apc_cache_key_t key, + apc_cache_entry_t* value, time_t t); + +extern int apc_cache_user_insert(T cache, apc_cache_key_t key, + apc_cache_entry_t* value, time_t t, int exclusive TSRMLS_DC); + +/* + * apc_cache_find searches for a cache entry by filename, and returns a + * pointer to the entry if found, NULL otherwise. + * + * key is a value created by apc_cache_make_file_key for file keys. + */ +extern apc_cache_entry_t* apc_cache_find(T cache, apc_cache_key_t key, time_t t); + +/* + * apc_cache_user_find searches for a cache entry by its hashed identifier, + * and returns a pointer to the entry if found, NULL otherwise. + * + */ +extern apc_cache_entry_t* apc_cache_user_find(T cache, char* strkey, int keylen, time_t t); + +/* + * apc_cache_user_delete finds an entry in the user cache and deletes it. + */ +extern int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen); + +/* apc_cach_fetch_zval takes a zval in the cache and reconstructs a runtime + * zval from it. + * + */ +zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate); + +/* + * apc_cache_release decrements the reference count associated with a cache + * entry. Calling apc_cache_find automatically increments the reference count, + * and this function must be called post-execution to return the count to its + * original value. Failing to do so will prevent the entry from being + * garbage-collected. + * + * entry is the cache entry whose ref count you want to decrement. + */ +extern void apc_cache_release(T cache, apc_cache_entry_t* entry); + +/* + * apc_cache_make_file_key creates a key object given a relative or absolute + * filename and an optional list of auxillary paths to search. include_path is + * searched if the filename cannot be found relative to the current working + * directory. + * + * key points to caller-allocated storage (must not be null). + * + * filename is the path to the source file. + * + * include_path is a colon-separated list of directories to search. + * + * and finally we pass in the current request time so we can avoid + * caching files with a current mtime which tends to indicate that + * they are still being written to. + */ +extern int apc_cache_make_file_key(apc_cache_key_t* key, + const char* filename, + const char* include_path, + time_t t + TSRMLS_DC); + +/* + * apc_cache_make_file_entry creates an apc_cache_entry_t object given a filename + * and the compilation results returned by the PHP compiler. + */ +extern apc_cache_entry_t* apc_cache_make_file_entry(const char* filename, + zend_op_array* op_array, + apc_function_t* functions, + apc_class_t* classes); +/* + * apc_cache_make_user_entry creates an apc_cache_entry_t object given an info string + * and the zval to be stored. + */ +extern apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval *val, const unsigned int ttl); + +extern int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t); + +/* + * Frees all memory associated with an object returned by apc_cache_make_entry + * (see above). + */ +extern void apc_cache_free_entry(apc_cache_entry_t* entry); + +/* {{{ struct definition: apc_cache_link_data_t */ +typedef union _apc_cache_link_data_t { + struct { + char *filename; + dev_t device; + ino_t inode; + } file; + struct { + char *info; + unsigned int ttl; + } user; +} apc_cache_link_data_t; +/* }}} */ + +/* {{{ struct definition: apc_cache_link_t */ +typedef struct apc_cache_link_t apc_cache_link_t; +struct apc_cache_link_t { + apc_cache_link_data_t data; + unsigned char type; + int num_hits; + time_t mtime; + time_t creation_time; + time_t deletion_time; + time_t access_time; + int ref_count; + size_t mem_size; + apc_cache_link_t* next; +}; +/* }}} */ + +/* {{{ struct definition: apc_cache_info_t */ +typedef struct apc_cache_info_t apc_cache_info_t; +struct apc_cache_info_t { + int num_slots; + int num_hits; + int num_misses; + int ttl; + apc_cache_link_t* list; + apc_cache_link_t* deleted_list; + time_t start_time; + int expunges; + int num_entries; + int num_inserts; + size_t mem_size; +}; +/* }}} */ + +/* {{{ struct definition: slot_t */ +typedef struct slot_t slot_t; +struct slot_t { + apc_cache_key_t key; /* slot key */ + apc_cache_entry_t* value; /* slot value */ + slot_t* next; /* next slot in linked list */ + int num_hits; /* number of hits to this bucket */ + time_t creation_time; /* time slot was initialized */ + time_t deletion_time; /* time slot was removed from cache */ + time_t access_time; /* time slot was last accessed */ +}; +/* }}} */ + +/* {{{ struct definition: cache_header_t + Any values that must be shared among processes should go in here. */ +typedef struct cache_header_t cache_header_t; +struct cache_header_t { + apc_lck_t lock; /* read/write lock (exclusive blocking cache lock) */ + apc_lck_t wrlock; /* write lock (non-blocking used to prevent cache slams) */ + int num_hits; /* total successful hits in cache */ + int num_misses; /* total unsuccessful hits in cache */ + int num_inserts; /* total successful inserts in cache */ + slot_t* deleted_list; /* linked list of to-be-deleted slots */ + time_t start_time; /* time the above counters were reset */ + int expunges; /* total number of expunges */ + zend_bool busy; /* Flag to tell clients when we are busy cleaning the cache */ + int num_entries; /* Statistic on the number of entries */ + size_t mem_size; /* Statistic on the memory size used by this cache */ +}; +/* }}} */ + +typedef void (*apc_expunge_cb_t)(T cache, size_t n); + +/* {{{ struct definition: apc_cache_t */ +struct apc_cache_t { + void* shmaddr; /* process (local) address of shared cache */ + cache_header_t* header; /* cache header (stored in SHM) */ + slot_t** slots; /* array of cache slots (stored in SHM) */ + int num_slots; /* number of slots in cache */ + int gc_ttl; /* maximum time on GC list for a slot */ + int ttl; /* if slot is needed and entry's access time is older than this ttl, remove it */ + apc_expunge_cb_t expunge_cb; /* cache specific expunge callback to free up sma memory */ +}; +/* }}} */ + +extern apc_cache_info_t* apc_cache_info(T cache, zend_bool limited); +extern void apc_cache_free_info(apc_cache_info_t* info); +extern void apc_cache_unlock(apc_cache_t* cache); +extern zend_bool apc_cache_busy(apc_cache_t* cache); +extern zend_bool apc_cache_write_lock(apc_cache_t* cache); +extern void apc_cache_write_unlock(apc_cache_t* cache); + +#undef T +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_compile.c b/apc_compile.c new file mode 100644 index 0000000..7627a55 --- /dev/null +++ b/apc_compile.c @@ -0,0 +1,2572 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_compile.c,v 3.87.2.4 2008/03/28 21:05:14 rasmus Exp $ */ + +#include "apc_compile.h" +#include "apc_globals.h" +#include "apc_zend.h" + +#ifndef Z_REFCOUNT_P +#define Z_REFCOUNT_P(pz) (pz)->refcount +#define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz)) +#endif + +#ifndef Z_SET_REFCOUNT_P +#define Z_SET_REFCOUNT_P(pz, rc) (pz)->refcount = rc +#define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc) +#endif + +#ifndef Z_ADDREF_P +#define Z_ADDREF_P(pz) (pz)->refcount++ +#define Z_ADDREF_PP(ppz) Z_ADDREF_P(*(ppz)) +#endif + +#ifndef Z_DELREF_P +#define Z_DELREF_P(pz) (pz)->refcount-- +#define Z_DELREF_PP(ppz) Z_DELREF_P(*(ppz)) +#endif + +#ifndef Z_ISREF_P +#define Z_ISREF_P(pz) (pz)->is_ref +#define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) +#endif + +#ifndef Z_SET_ISREF_P +#define Z_SET_ISREF_P(pz) (pz)->is_ref = 1 +#define Z_SET_ISREF_PP(ppz) Z_SET_ISREF_P(*(ppz)) +#endif + +#ifndef Z_UNSET_ISREF_P +#define Z_UNSET_ISREF_P(pz) (pz)->is_ref = 0 +#define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz)) +#endif + +#ifndef Z_SET_ISREF_TO_P +#define Z_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = isref +#define Z_SET_ISREF_TO_PP(ppz, isref) Z_SET_ISREF_TO_P(*(ppz), isref) +#endif + +typedef void* (*ht_copy_fun_t)(void*, void*, apc_malloc_t, apc_free_t); +typedef void (*ht_free_fun_t)(void*, apc_free_t); +typedef int (*ht_check_copy_fun_t)(Bucket*, va_list); + +#ifdef ZEND_ENGINE_2 +typedef void (*ht_fixup_fun_t)(Bucket*, zend_class_entry*, zend_class_entry*); +#endif + +#define CHECK(p) { if ((p) == NULL) return NULL; } + +/* {{{ internal function declarations */ + +static int is_derived_class(zend_op_array* op_array, const char* key, int key_size); + +static zend_function* my_bitwise_copy_function(zend_function*, zend_function*, apc_malloc_t); + +/* + * The "copy" functions perform deep-copies on a particular data structure + * (passed as the second argument). They also optionally allocate space for + * the destination data structure if the first argument is null. + */ +static zval** my_copy_zval_ptr(zval**, const zval**, apc_malloc_t, apc_free_t); +static zval* my_copy_zval(zval*, const zval*, apc_malloc_t, apc_free_t); +static znode* my_copy_znode(znode*, znode*, apc_malloc_t, apc_free_t); +static zend_op* my_copy_zend_op(zend_op*, zend_op*, apc_malloc_t, apc_free_t); +static zend_function* my_copy_function(zend_function*, zend_function*, apc_malloc_t, apc_free_t); +static zend_function_entry* my_copy_function_entry(zend_function_entry*, zend_function_entry*, apc_malloc_t, apc_free_t); +static zend_class_entry* my_copy_class_entry(zend_class_entry*, zend_class_entry*, apc_malloc_t, apc_free_t); +static HashTable* my_copy_hashtable_ex(HashTable*, HashTable*, ht_copy_fun_t, ht_free_fun_t, int, apc_malloc_t, apc_free_t, ht_check_copy_fun_t, ...); +#define my_copy_hashtable( dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate) \ + my_copy_hashtable_ex(dst, src, copy_fn, free_fn, holds_ptr, allocate, deallocate, NULL) +static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate); +#ifdef ZEND_ENGINE_2 +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate); +static zend_arg_info* my_copy_arg_info_array(zend_arg_info*, zend_arg_info*, uint, apc_malloc_t, apc_free_t); +static zend_arg_info* my_copy_arg_info(zend_arg_info*, zend_arg_info*, apc_malloc_t, apc_free_t); +#endif +/* + * The "destroy" functions free the memory associated with a particular data + * structure but do not free the pointer to the data structure. + * + * my_destroy_zval() returns SUCCESS or FAILURE, FAILURE means that + * the zval* has other references elsewhere + */ +static int my_destroy_zval(zval*, apc_free_t); +static void my_destroy_zval_ptr(zval**, apc_free_t); +static void my_destroy_zend_op(zend_op*, apc_free_t); +static void my_destroy_znode(znode*, apc_free_t); +static void my_destroy_function(zend_function*, apc_free_t); +static void my_destroy_function_entry(zend_function_entry*, apc_free_t); +static void my_destroy_class_entry(zend_class_entry*, apc_free_t); +static void my_destroy_hashtable(HashTable*, ht_free_fun_t, apc_free_t); +static void my_destroy_op_array(zend_op_array*, apc_free_t); +#ifdef ZEND_ENGINE_2 +static void my_destroy_property_info(zend_property_info*, apc_free_t); +static void my_destroy_arg_info_array(zend_arg_info* src, uint, apc_free_t); +static void my_destroy_arg_info(zend_arg_info*, apc_free_t); +#endif + +/* + * The "free" functions work exactly like their "destroy" counterparts (see + * above) but also free the pointer to the data structure. + */ +static void my_free_zval_ptr(zval**, apc_free_t); +static void my_free_function(zend_function*, apc_free_t); +static void my_free_hashtable(HashTable*, ht_free_fun_t, apc_free_t); +#ifdef ZEND_ENGINE_2 +static void my_free_property_info(zend_property_info* src, apc_free_t); +static void my_free_arg_info_array(zend_arg_info*, uint, apc_free_t); +static void my_free_arg_info(zend_arg_info*, apc_free_t); +#endif + +/* + * The "fixup" functions need for ZEND_ENGINE_2 + */ +#ifdef ZEND_ENGINE_2 +static void my_fixup_function( Bucket *p, zend_class_entry *src, zend_class_entry *dst ); +static void my_fixup_hashtable( HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst ); +/* my_fixup_function_for_execution is the same as my_fixup_function + * but named differently for clarity + */ +#define my_fixup_function_for_execution my_fixup_function + +#ifdef ZEND_ENGINE_2_2 +static void my_fixup_property_info( Bucket *p, zend_class_entry *src, zend_class_entry *dst ); +#define my_fixup_property_info_for_execution my_fixup_property_info +#endif + +#endif + +/* + * These functions return "1" if the member/function is + * defined/overridden in the 'current' class and not inherited. + */ +static int my_check_copy_function(Bucket* src, va_list args); +static int my_check_copy_default_property(Bucket* p, va_list args); +#ifdef ZEND_ENGINE_2 +static int my_check_copy_property_info(Bucket* src, va_list args); +static int my_check_copy_static_member(Bucket* src, va_list args); +#endif + +/* }}} */ + +/* {{{ check_op_array_integrity */ +#if 0 +static void check_op_array_integrity(zend_op_array* src) +{ + int i, j; + + /* These sorts of checks really aren't particularly effective, but they + * can provide a welcome sanity check when debugging. Just don't enable + * for production use! */ + + assert(src->refcount != NULL); + assert(src->opcodes != NULL); + assert(src->last > 0); + + for (i = 0; i < src->last; i++) { + zend_op* op = &src->opcodes[i]; + znode* nodes[] = { &op->result, &op->op1, &op->op2 }; + for (j = 0; j < 3; j++) { + assert(nodes[j]->op_type == IS_CONST || + nodes[j]->op_type == IS_VAR || + nodes[j]->op_type == IS_TMP_VAR || + nodes[j]->op_type == IS_UNUSED); + + if (nodes[j]->op_type == IS_CONST) { + int type = nodes[j]->u.constant.type; + assert(type == IS_RESOURCE || + type == IS_BOOL || + type == IS_LONG || + type == IS_DOUBLE || + type == IS_NULL || + type == IS_CONSTANT || + type == IS_STRING || + type == FLAG_IS_BC || + type == IS_ARRAY || + type == IS_CONSTANT_ARRAY || + type == IS_OBJECT); + } + } + } +} +#endif +/* }}} */ + +/* {{{ is_derived_class */ +static int is_derived_class(zend_op_array* op_array, const char* key, int key_size) +{ + int i; + + /* + * Scan the op_array for execution-time class declarations of derived + * classes. If we find one whose key matches our current class key, we + * know the current class is a derived class. + * + * This check is exceedingly inefficient (fortunately it only has to occur + * once, when the source file is first compiled and cached), but the + * compiler should save this information for us -- definitely a candidate + * for a Zend Engine patch. + * + * XXX checking for derived classes provides a minimal (albeit measurable) + * speed up. It may not be worth the added complexity -- considere + * removing this optimization. + */ + + for (i = 0; i < op_array->last; i++) { + zend_op* op = &op_array->opcodes[i]; + +#ifdef ZEND_ENGINE_2 + if (op->opcode == ZEND_DECLARE_CLASS && + op->extended_value == ZEND_DECLARE_INHERITED_CLASS) +#else + if (op->opcode == ZEND_DECLARE_FUNCTION_OR_CLASS && + op->extended_value == ZEND_DECLARE_INHERITED_CLASS) +#endif + { + if (op->op1.u.constant.value.str.len == key_size && + !memcmp(op->op1.u.constant.value.str.val, key, key_size)) + { + return 1; + } + } + } + + return 0; +} +/* }}} */ + +/* {{{ my_bitwise_copy_function */ +static zend_function* my_bitwise_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate) +{ + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_function*) allocate(sizeof(src[0]))); + } + + /* We only need to do a bitwise copy */ + memcpy(dst, src, sizeof(src[0])); + + return dst; +} +/* }}} */ + +/* {{{ my_copy_zval_ptr */ +static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + zval* dst_new; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zval**) allocate(sizeof(zval*))); + local_dst_alloc = 1; + } + + if(!(dst[0] = (zval*) allocate(sizeof(zval)))) { + if(local_dst_alloc) deallocate(dst); + return NULL; + } + if(!(dst_new = my_copy_zval(*dst, *src, allocate, deallocate))) { + if(local_dst_alloc) deallocate(dst); + return NULL; + } + if(dst_new != *dst) { + deallocate(*dst); + *dst = dst_new; + } + + Z_SET_REFCOUNT_PP(dst, Z_REFCOUNT_PP(src)); + Z_SET_ISREF_TO_PP(dst, Z_ISREF_PP(src)); + + return dst; +} +/* }}} */ + +/* {{{ my_copy_zval */ +static zval* my_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + zval **tmp; + TSRMLS_FETCH(); + + assert(dst != NULL); + assert(src != NULL); + + memcpy(dst, src, sizeof(src[0])); + + switch (src->type & ~IS_CONSTANT_INDEX) { + case IS_RESOURCE: + case IS_BOOL: + case IS_LONG: + case IS_DOUBLE: + case IS_NULL: + break; + + case IS_CONSTANT: + case IS_STRING: +#ifndef ZEND_ENGINE_2 + case FLAG_IS_BC: +#endif + if (src->value.str.val) { + CHECK(dst->value.str.val = apc_xmemcpy(src->value.str.val, + src->value.str.len+1, + allocate)); + } + break; + + case IS_ARRAY: + + if(APCG(copied_zvals)) { + if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) { + Z_ADDREF_PP(tmp); + return *tmp; + } + + zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&dst, sizeof(zval*), NULL); + } + /* fall through */ + + case IS_CONSTANT_ARRAY: + + CHECK(dst->value.ht = + my_copy_hashtable(NULL, + src->value.ht, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + allocate, deallocate)); + break; + + case IS_OBJECT: +#ifndef ZEND_ENGINE_2 + CHECK(dst->value.obj.ce = + my_copy_class_entry(NULL, src->value.obj.ce, allocate, deallocate)); + + if(!(dst->value.obj.properties = my_copy_hashtable(NULL, + src->value.obj.properties, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + allocate, deallocate))) { + my_destroy_class_entry(dst->value.obj.ce, deallocate); + return NULL; + } + break; +#else + dst->type = IS_NULL; +#endif + break; + + default: + assert(0); + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_znode */ +static znode* my_copy_znode(znode* dst, znode* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + assert(dst != NULL); + assert(src != NULL); + + memcpy(dst, src, sizeof(src[0])); + +#ifdef IS_CV + assert(dst ->op_type == IS_CONST || + dst ->op_type == IS_VAR || + dst ->op_type == IS_CV || + dst ->op_type == IS_TMP_VAR || + dst ->op_type == IS_UNUSED); +#else + assert(dst ->op_type == IS_CONST || + dst ->op_type == IS_VAR || + dst ->op_type == IS_TMP_VAR || + dst ->op_type == IS_UNUSED); +#endif + + if (src->op_type == IS_CONST) { + if(!my_copy_zval(&dst->u.constant, &src->u.constant, allocate, deallocate)) { + return NULL; + } + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_zend_op */ +static zend_op* my_copy_zend_op(zend_op* dst, zend_op* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + assert(dst != NULL); + assert(src != NULL); + + memcpy(dst, src, sizeof(src[0])); + + if( my_copy_znode(&dst->result, &src->result, allocate, deallocate) == NULL + || my_copy_znode(&dst->op1, &src->op1, allocate, deallocate) == NULL + || my_copy_znode(&dst->op2, &src->op2, allocate, deallocate) == NULL) + { + return NULL; + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_function */ +static zend_function* my_copy_function(zend_function* dst, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + TSRMLS_FETCH(); + + assert(src != NULL); + + if(!dst) local_dst_alloc = 1; + CHECK(dst = my_bitwise_copy_function(dst, src, allocate)); + + switch (src->type) { + case ZEND_INTERNAL_FUNCTION: + case ZEND_OVERLOADED_FUNCTION: + /* shallow copy because op_array is internal */ + dst->op_array = src->op_array; + break; + + case ZEND_USER_FUNCTION: + case ZEND_EVAL_CODE: + if(!apc_copy_op_array(&dst->op_array, + &src->op_array, + allocate, deallocate TSRMLS_CC)) { + if(local_dst_alloc) deallocate(dst); + return NULL; + } + break; + + default: + assert(0); + } +#ifdef ZEND_ENGINE_2 + /* + * op_array bitwise copying overwrites what ever you modified + * before apc_copy_op_array - which is why this code is outside + * my_bitwise_copy_function. + */ + + /* zend_do_inheritance will re-look this up, because the pointers + * in prototype are from a function table of another class. It just + * helps if that one is from EG(class_table). + */ + dst->common.prototype = NULL; + + /* once a method is marked as ZEND_ACC_IMPLEMENTED_ABSTRACT then you + * have to carry around a prototype. Thankfully zend_do_inheritance + * sets this properly as well + */ + dst->common.fn_flags = src->common.fn_flags & (~ZEND_ACC_IMPLEMENTED_ABSTRACT); +#endif + + + return dst; +} +/* }}} */ + +/* {{{ my_copy_function_entry */ +static zend_function_entry* my_copy_function_entry(zend_function_entry* dst, zend_function_entry* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_function_entry*) allocate(sizeof(src[0]))); + local_dst_alloc = 1; + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(src[0])); + + dst->fname = NULL; +#ifdef ZEND_ENGINE_2 + dst->arg_info = NULL; +#else + dst->func_arg_types = NULL; +#endif + + if (src->fname) { + if(!(dst->fname = apc_xstrdup(src->fname, allocate))) { + goto cleanup; + } + } + +#ifdef ZEND_ENGINE_2 + if (src->arg_info) { + if(!(dst->arg_info = my_copy_arg_info_array(NULL, + src->arg_info, + src->num_args, + allocate, + deallocate))) { + goto cleanup; + } + } +#else + if (src->func_arg_types) { + if(!(dst->func_arg_types = apc_xmemcpy(src->func_arg_types, + src->func_arg_types[0]+1, + allocate))) { + goto cleanup; + } + } +#endif + + return dst; + +cleanup: + if(dst->fname) deallocate(dst->fname); + if(local_dst_alloc) deallocate(dst); + return NULL; +} +/* }}} */ + +#ifdef ZEND_ENGINE_2 +/* {{{ my_copy_property_info */ +static zend_property_info* my_copy_property_info(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_property_info*) allocate(sizeof(*src))); + local_dst_alloc = 1; + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)); + + dst->name = NULL; +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0 + dst->doc_comment = NULL; +#endif + + if (src->name) { + /* private members are stored inside property_info as a mangled + * string of the form: + * \0\0\0 + */ + if(!(dst->name = + apc_xmemcpy(src->name, src->name_length+1, allocate))) { + goto cleanup; + } + } + +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0 + if (src->doc_comment) { + if( !(dst->doc_comment = + apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) { + goto cleanup; + } + } +#endif + + return dst; + +cleanup: + if(dst->name) deallocate(dst->name); +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0 + if(dst->doc_comment) deallocate(dst->doc_comment); +#endif + if(local_dst_alloc) deallocate(dst); + return NULL; +} +/* }}} */ + +/* {{{ my_copy_property_info_for_execution */ +static zend_property_info* my_copy_property_info_for_execution(zend_property_info* dst, zend_property_info* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_property_info*) allocate(sizeof(*src))); + local_dst_alloc = 1; + } + + /* We need only a shallow copy */ + memcpy(dst, src, sizeof(*src)); + + return dst; +} +/* }}} */ + +/* {{{ my_copy_arg_info_array */ +static zend_arg_info* my_copy_arg_info_array(zend_arg_info* dst, zend_arg_info* src, uint num_args, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + int i = 0; + + + if (!dst) { + CHECK(dst = (zend_arg_info*) allocate(sizeof(*src)*num_args)); + local_dst_alloc = 1; + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)*num_args); + + for(i=0; i < num_args; i++) { + if(!(my_copy_arg_info( &dst[i], &src[i], allocate, deallocate))) { + if(i) my_destroy_arg_info_array(dst, i-1, deallocate); + if(local_dst_alloc) deallocate(dst); + return NULL; + } + } + + return dst; +} +/* }}} */ + +/* {{{ my_copy_arg_info */ +static zend_arg_info* my_copy_arg_info(zend_arg_info* dst, zend_arg_info* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_arg_info*) allocate(sizeof(*src))); + local_dst_alloc = 1; + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)); + + dst->name = NULL; + dst->class_name = NULL; + + if (src->name) { + if(!(dst->name = + apc_xmemcpy(src->name, src->name_len+1, allocate))) { + goto cleanup; + } + } + + if (src->class_name) { + if(!(dst->class_name = + apc_xmemcpy(src->class_name, src->class_name_len+1, allocate))) { + goto cleanup; + } + } + + return dst; + +cleanup: + if(dst->name) deallocate(dst->name); + if(dst->class_name) deallocate(dst->name); + if(local_dst_alloc) deallocate(dst); + return NULL; +} +/* }}} */ +#endif + +/* {{{ my_copy_class_entry */ +static zend_class_entry* my_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + int i = 0; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_class_entry*) allocate(sizeof(*src))); + local_dst_alloc = 1; + } + + /* Start with a bitwise copy */ + memcpy(dst, src, sizeof(*src)); + + dst->name = NULL; + dst->builtin_functions = NULL; + memset(&dst->function_table, 0, sizeof(dst->function_table)); + memset(&dst->default_properties, 0, sizeof(dst->default_properties)); +#ifndef ZEND_ENGINE_2 + dst->refcount = NULL; +#else + dst->static_members = NULL; + dst->doc_comment = NULL; + dst->filename = NULL; + memset(&dst->properties_info, 0, sizeof(dst->properties_info)); + memset(&dst->constants_table, 0, sizeof(dst->constants_table)); + memset(&dst->default_static_members, 0, sizeof(dst->default_static_members)); +#endif + + if (src->name) { + if(!(dst->name = apc_xstrdup(src->name, allocate))) { + goto cleanup; + } + } + +#ifndef ZEND_ENGINE_2 + if(!(dst->refcount = apc_xmemcpy(src->refcount, + sizeof(src->refcount[0]), + allocate))) { + goto cleanup; + } +#endif + + if(!(my_copy_hashtable_ex(&dst->function_table, + &src->function_table, + (ht_copy_fun_t) my_copy_function, + (ht_free_fun_t) my_free_function, + 0, + allocate, deallocate, + (ht_check_copy_fun_t) my_check_copy_function, + src))) { + goto cleanup; + } + +#ifdef ZEND_ENGINE_2 + + /* the interfaces are populated at runtime using ADD_INTERFACE */ + dst->interfaces = NULL; + + /* the current count includes inherited interfaces as well, + the real dynamic ones are the first which are zero'd + out in zend_do_end_class_declaration */ + for(i = 0 ; i < src->num_interfaces ; i++) { + if(src->interfaces[i]) + { + dst->num_interfaces = i; + break; + } + } + + /* these will either be set inside my_fixup_hashtable or + * they will be copied out from parent inside zend_do_inheritance + */ + dst->constructor = NULL; + dst->destructor = NULL; + dst->clone = NULL; + dst->__get = NULL; + dst->__set = NULL; + dst->__unset = NULL; + dst->__isset = NULL; + dst->__call = NULL; +#ifdef ZEND_ENGINE_2_2 + dst->__tostring = NULL; +#endif + + /* unset function proxies */ + dst->serialize_func = NULL; + dst->unserialize_func = NULL; + + my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function, src, dst); +#endif + + if(!(my_copy_hashtable_ex(&dst->default_properties, + &src->default_properties, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + allocate,deallocate, + (ht_check_copy_fun_t) my_check_copy_default_property, + src))) { + goto cleanup; + } + +#ifdef ZEND_ENGINE_2 + + if(!(my_copy_hashtable_ex(&dst->properties_info, + &src->properties_info, + (ht_copy_fun_t) my_copy_property_info, + (ht_free_fun_t) my_free_property_info, + 0, + allocate, deallocate, + (ht_check_copy_fun_t) my_check_copy_property_info, + src))) { + goto cleanup; + } + +#ifdef ZEND_ENGINE_2_2 + /* php5.2 introduced a scope attribute for property info */ + my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst); +#endif + + if(!my_copy_hashtable_ex(&dst->default_static_members, + &src->default_static_members, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + allocate, deallocate, + (ht_check_copy_fun_t) my_check_copy_static_member, + src, + &src->default_static_members)) { + goto cleanup; + } + if(src->static_members != &src->default_static_members) + { + if(!(dst->static_members = my_copy_hashtable_ex(NULL, + src->static_members, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + allocate, deallocate, + (ht_check_copy_fun_t) my_check_copy_static_member, + src, + src->static_members))) { + goto cleanup; + } + } + else + { + dst->static_members = &dst->default_static_members; + } + + if(!(my_copy_hashtable(&dst->constants_table, + &src->constants_table, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + allocate, deallocate))) { + goto cleanup; + } + + if (src->doc_comment) { + if(!(dst->doc_comment = + apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) { + goto cleanup; + } + } +#endif + + if (src->builtin_functions) { + int i, n; + + for (n = 0; src->type == ZEND_INTERNAL_CLASS && src->builtin_functions[n].fname != NULL; n++) {} + + if(!(dst->builtin_functions = + (zend_function_entry*) + allocate((n + 1) * sizeof(zend_function_entry)))) { + goto cleanup; + } + + + for (i = 0; i < n; i++) { + if(!my_copy_function_entry(&dst->builtin_functions[i], + &src->builtin_functions[i], + allocate, deallocate)) { + int ii; + + for(ii=i-1; i>=0; i--) my_destroy_function_entry(&dst->builtin_functions[ii], deallocate); + goto cleanup; + } + } + *(char**)&(dst->builtin_functions[n].fname) = NULL; + } + +#ifdef ZEND_ENGINE_2 + if (src->filename) { + if(!(dst->filename = apc_xstrdup(src->filename, allocate))) { + goto cleanup; + } + } +#endif + + return dst; + + +cleanup: + if(dst->name) deallocate(dst->name); +#ifdef ZEND_ENGINE_2 + if(dst->doc_comment) deallocate(dst->doc_comment); + if(dst->filename) deallocate(dst->filename); +#else + if(dst->refcount) deallocate(dst->refcount); +#endif + + if(dst->builtin_functions) deallocate(dst->builtin_functions); + if(dst->function_table.arBuckets) my_destroy_hashtable(&dst->function_table, (ht_free_fun_t) my_free_function, deallocate); + if(dst->default_properties.arBuckets) my_destroy_hashtable(&dst->default_properties, (ht_free_fun_t) my_free_zval_ptr, deallocate); + +#ifdef ZEND_ENGINE_2 + if(dst->properties_info.arBuckets) my_destroy_hashtable(&dst->properties_info, (ht_free_fun_t) my_free_property_info, deallocate); + if(dst->default_static_members.arBuckets) + { + my_destroy_hashtable(&dst->default_static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate); + } + if(dst->static_members && dst->static_members != &(dst->default_static_members)) + { + my_destroy_hashtable(dst->static_members, (ht_free_fun_t) my_free_zval_ptr, deallocate); + deallocate(dst->static_members); + } + if(dst->constants_table.arBuckets) my_destroy_hashtable(&dst->constants_table, (ht_free_fun_t) my_free_zval_ptr, deallocate); +#endif + if(local_dst_alloc) deallocate(dst); + + return NULL; +} +/* }}} */ + +/* {{{ my_copy_hashtable */ +static HashTable* my_copy_hashtable_ex(HashTable* dst, + HashTable* src, + ht_copy_fun_t copy_fn, + ht_free_fun_t free_fn, + int holds_ptrs, + apc_malloc_t allocate, + apc_free_t deallocate, + ht_check_copy_fun_t check_fn, + ...) +{ + Bucket* curr = NULL; + Bucket* prev = NULL; + Bucket* newp = NULL; + int first = 1; + int local_dst_alloc = 0; + int index = 0; + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (HashTable*) allocate(sizeof(src[0]))); + local_dst_alloc = 1; + } + + memcpy(dst, src, sizeof(src[0])); + + /* allocate buckets for the new hashtable */ + if(!(dst->arBuckets = allocate(dst->nTableSize * sizeof(Bucket*)))) { + if(local_dst_alloc) deallocate(dst); + return NULL; + } + + memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*)); + dst->pInternalPointer = NULL; + dst->pListHead = NULL; + + for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) { + int n = curr->h % dst->nTableSize; + + if(check_fn) { + va_list args; + va_start(args, check_fn); + + /* Call the check_fn to see if the current bucket + * needs to be copied out + */ + if(!check_fn(curr, args)) { + dst->nNumOfElements--; + continue; + } + + va_end(args); + } + + /* create a copy of the bucket 'curr' */ + if(!(newp = + (Bucket*) apc_xmemcpy(curr, + sizeof(Bucket) + curr->nKeyLength - 1, + allocate))) { + goto cleanup; + } + + /* insert 'newp' into the linked list at its hashed index */ + if (dst->arBuckets[n]) { + newp->pNext = dst->arBuckets[n]; + newp->pLast = NULL; + newp->pNext->pLast = newp; + } + else { + newp->pNext = newp->pLast = NULL; + } + + dst->arBuckets[n] = newp; + + /* copy the bucket data using our 'copy_fn' callback function */ + if(!(newp->pData = copy_fn(NULL, curr->pData, allocate, deallocate))) { + goto cleanup; + } + + if (holds_ptrs) { + memcpy(&newp->pDataPtr, newp->pData, sizeof(void*)); + } + else { + newp->pDataPtr = NULL; + } + + /* insert 'newp' into the table-thread linked list */ + newp->pListLast = prev; + newp->pListNext = NULL; + + if (prev) { + prev->pListNext = newp; + } + + if (first) { + dst->pListHead = newp; + first = 0; + } + + prev = newp; + } + + dst->pListTail = newp; + + return dst; + + cleanup: + for(index = 0; index < dst->nTableSize; index++) + { + curr = dst->arBuckets[index]; + while(curr != NULL) + { + Bucket * tmp = curr; + if(curr->pData && free_fn) + { + free_fn(curr->pData, deallocate); + } + curr = curr->pNext; + deallocate(tmp); + } + } + deallocate(dst->arBuckets); + if(local_dst_alloc) deallocate(dst); + else dst->arBuckets = NULL; + + return NULL; +} +/* }}} */ + +/* {{{ my_copy_static_variables */ +static HashTable* my_copy_static_variables(zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + if (src->static_variables == NULL) { + return NULL; + } + + return my_copy_hashtable(NULL, + src->static_variables, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + allocate, deallocate); +} +/* }}} */ + +/* {{{ apc_copy_zval */ +zval* apc_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + int local_dst_alloc = 0; + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zval*) allocate(sizeof(zval))); + local_dst_alloc = 1; + } + + dst = my_copy_zval(dst, src, allocate, deallocate); + if(!dst) { + if(local_dst_alloc) deallocate(dst); + return NULL; + } + return dst; +} +/* }}} */ + +#ifdef ZEND_ENGINE_2 +/* {{{ apc_fixup_op_array_jumps */ +static void apc_fixup_op_array_jumps(zend_op_array *dst, zend_op_array *src ) +{ + int i; + + for (i=0; i < dst->last; ++i) { + zend_op *zo = &(dst->opcodes[i]); + /*convert opline number to jump address*/ + switch (zo->opcode) { + case ZEND_JMP: + /*Note: if src->opcodes != dst->opcodes then we need to the opline according to src*/ + zo->op1.u.jmp_addr = dst->opcodes + (zo->op1.u.jmp_addr - src->opcodes); + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + zo->op2.u.jmp_addr = dst->opcodes + (zo->op2.u.jmp_addr - src->opcodes); + break; + default: + break; + } + } +} +/* }}} */ +#endif + +/* {{{ apc_copy_op_array */ +zend_op_array* apc_copy_op_array(zend_op_array* dst, zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC) +{ + int i; + int local_dst_alloc = 0; + apc_fileinfo_t fileinfo; + char canon_path[MAXPATHLEN]; + char *fullpath = NULL; +#ifdef ZEND_ENGINE_2 + apc_opflags_t * flags = NULL; +#endif + + assert(src != NULL); + + if (!dst) { + CHECK(dst = (zend_op_array*) allocate(sizeof(src[0]))); + local_dst_alloc = 1; + } + + if(APCG(apc_optimize_function)) { + APCG(apc_optimize_function)(src TSRMLS_CC); + } + + /* start with a bitwise copy of the array */ + memcpy(dst, src, sizeof(src[0])); + + dst->function_name = NULL; + dst->filename = NULL; + dst->refcount = NULL; + dst->opcodes = NULL; + dst->brk_cont_array = NULL; + dst->static_variables = NULL; +#ifdef ZEND_ENGINE_2 + dst->try_catch_array = NULL; + dst->arg_info = NULL; + dst->doc_comment = NULL; +#else + dst->arg_types = NULL; +#endif +#ifdef ZEND_ENGINE_2_1 + dst->vars = NULL; +#endif + + /* copy the arg types array (if set) */ +#ifdef ZEND_ENGINE_2 + if (src->arg_info) { + if(!(dst->arg_info = my_copy_arg_info_array(NULL, + src->arg_info, + src->num_args, + allocate, + deallocate))) { + goto cleanup; + } + } +#else + if (src->arg_types) { + if(!(dst->arg_types = apc_xmemcpy(src->arg_types, + sizeof(src->arg_types[0]) * (src->arg_types[0]+1), + allocate))) { + goto cleanup; + } + } +#endif + + if (src->function_name) { + if(!(dst->function_name = apc_xstrdup(src->function_name, allocate))) { + goto cleanup; + } + } + if (src->filename) { + if(!(dst->filename = apc_xstrdup(src->filename, allocate))) { + goto cleanup; + } + } + + if(!(dst->refcount = apc_xmemcpy(src->refcount, + sizeof(src->refcount[0]), + allocate))) { + goto cleanup; + } + + /* deep-copy the opcodes */ + if(!(dst->opcodes = (zend_op*) allocate(sizeof(zend_op) * src->last))) { + goto cleanup; + } + +#ifdef ZEND_ENGINE_2 + if(APCG(reserved_offset) != -1) { + /* Insanity alert: the void* pointer is cast into an apc_opflags_t + * struct. apc_zend_init() checks to ensure that it fits in a void* */ + flags = (apc_opflags_t*) & (dst->reserved[APCG(reserved_offset)]); + memset(flags, 0, sizeof(apc_opflags_t)); + /* assert(sizeof(apc_opflags_t) < sizeof(dst->reserved)); */ + } +#endif + + for (i = 0; i < src->last; i++) { +#ifdef ZEND_ENGINE_2 + zend_op *zo = &(src->opcodes[i]); + /* a lot of files are merely constant arrays with no jumps */ + switch (zo->opcode) { + case ZEND_JMP: + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + if(flags != NULL) { + flags->has_jumps = 1; + } + break; +#ifdef ZEND_ENGINE_2 + /* auto_globals_jit was not in php-4.3.* */ + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_IS: + case ZEND_FETCH_FUNC_ARG: + if(PG(auto_globals_jit) && flags != NULL) + { + /* The fetch is only required if auto_globals_jit=1 */ + if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL && + zo->op1.op_type == IS_CONST && + zo->op1.u.constant.type == IS_STRING) { + znode * varname = &zo->op1; + if (varname->u.constant.value.str.val[0] == '_') { +#define SET_IF_AUTOGLOBAL(member) \ + if(!strcmp(varname->u.constant.value.str.val, #member)) \ + flags->member = 1 /* no ';' here */ + SET_IF_AUTOGLOBAL(_GET); + else SET_IF_AUTOGLOBAL(_POST); + else SET_IF_AUTOGLOBAL(_COOKIE); + else SET_IF_AUTOGLOBAL(_SERVER); + else SET_IF_AUTOGLOBAL(_ENV); + else SET_IF_AUTOGLOBAL(_FILES); + else SET_IF_AUTOGLOBAL(_REQUEST); + else if(zend_is_auto_global( + varname->u.constant.value.str.val, + varname->u.constant.value.str.len + TSRMLS_CC)) + { + flags->unknown_global = 1; + } + } + } + } + break; +#endif + case ZEND_RECV_INIT: + if(zo->op2.op_type == IS_CONST && + zo->op2.u.constant.type == IS_CONSTANT_ARRAY) { + if(flags != NULL) { + flags->deep_copy = 1; + } + } + break; + default: + if((zo->op1.op_type == IS_CONST && + zo->op1.u.constant.type == IS_CONSTANT_ARRAY) || + (zo->op2.op_type == IS_CONST && + zo->op2.u.constant.type == IS_CONSTANT_ARRAY)) { + if(flags != NULL) { + flags->deep_copy = 1; + } + } + break; + } +#endif + if(!(my_copy_zend_op(dst->opcodes+i, src->opcodes+i, allocate, deallocate))) { + int ii; + for(ii = i-1; ii>=0; ii--) { + my_destroy_zend_op(dst->opcodes+ii, deallocate); + } + goto cleanup; + } +#ifdef ZEND_ENGINE_2 +/* This code breaks apc's rule#1 - cache what you compile */ + if(APCG(fpstat)==0) { + if((zo->opcode == ZEND_INCLUDE_OR_EVAL) && + (zo->op1.op_type == IS_CONST && zo->op1.u.constant.type == IS_STRING)) { + /* constant includes */ + if(!IS_ABSOLUTE_PATH(Z_STRVAL_P(&zo->op1.u.constant),Z_STRLEN_P(&zo->op1.u.constant))) { + if (apc_search_paths(Z_STRVAL_P(&zo->op1.u.constant), PG(include_path), &fileinfo) == 0) { + if((fullpath = realpath(fileinfo.fullpath, canon_path))) { + /* everything has to go through a realpath() */ + zend_op *dzo = &(dst->opcodes[i]); + deallocate(dzo->op1.u.constant.value.str.val); + dzo->op1.u.constant.value.str.len = strlen(fullpath); + dzo->op1.u.constant.value.str.val = apc_xstrdup(fullpath, allocate); + } + } + } + } + } +#endif + } + +#ifdef ZEND_ENGINE_2 + if(flags == NULL || flags->has_jumps) { + apc_fixup_op_array_jumps(dst,src); + } +#endif + + /* copy the break-continue array */ + if (src->brk_cont_array) { + if(!(dst->brk_cont_array = + apc_xmemcpy(src->brk_cont_array, + sizeof(src->brk_cont_array[0]) * src->last_brk_cont, + allocate))) { + goto cleanup_opcodes; + } + } + + /* copy the table of static variables */ + if (src->static_variables) { + if(!(dst->static_variables = my_copy_static_variables(src, allocate, deallocate))) { + goto cleanup_opcodes; + } + } + +#ifdef ZEND_ENGINE_2 + if (src->try_catch_array) { + if(!(dst->try_catch_array = + apc_xmemcpy(src->try_catch_array, + sizeof(src->try_catch_array[0]) * src->last_try_catch, + allocate))) { + goto cleanup_opcodes; + } + } +#endif + +#ifdef ZEND_ENGINE_2_1 /* PHP 5.1 */ + if (src->vars) { + if(!(dst->vars = apc_xmemcpy(src->vars, + sizeof(src->vars[0]) * src->last_var, + allocate))) { + goto cleanup_opcodes; + } + + for(i = 0; i < src->last_var; i++) dst->vars[i].name = NULL; + + for(i = 0; i < src->last_var; i++) { + if(!(dst->vars[i].name = apc_xmemcpy(src->vars[i].name, + src->vars[i].name_len + 1, + allocate))) { + dst->last_var = i; + goto cleanup_opcodes; + } + } + } +#endif + +#ifdef ZEND_ENGINE_2 + if (src->doc_comment) { + if (!(dst->doc_comment + = apc_xmemcpy(src->doc_comment, src->doc_comment_len+1, allocate))) { + goto cleanup_opcodes; + } + } +#endif + + return dst; + +cleanup_opcodes: + if(dst->opcodes) { + for(i=0; i < src->last; i++) my_destroy_zend_op(dst->opcodes+i, deallocate); + } +cleanup: + if(dst->function_name) deallocate(dst->function_name); + if(dst->refcount) deallocate(dst->refcount); + if(dst->filename) deallocate(dst->filename); +#ifdef ZEND_ENGINE_2 + if(dst->arg_info) my_free_arg_info_array(dst->arg_info, dst->num_args, deallocate); + if(dst->try_catch_array) deallocate(dst->try_catch_array); + if(dst->doc_comment) deallocate(dst->doc_comment); +#else + if(dst->arg_types) deallocate(dst->arg_types); +#endif + if(dst->opcodes) deallocate(dst->opcodes); + if(dst->brk_cont_array) deallocate(dst->brk_cont_array); + if(dst->static_variables) my_free_hashtable(dst->static_variables, (ht_free_fun_t)my_free_zval_ptr, (apc_free_t)deallocate); +#ifdef ZEND_ENGINE_2_1 + if (dst->vars) { + for(i=0; i < dst->last_var; i++) { + if(dst->vars[i].name) deallocate(dst->vars[i].name); + } + deallocate(dst->vars); + } +#endif + if(local_dst_alloc) deallocate(dst); + return NULL; +} +/* }}} */ + +/* {{{ apc_copy_new_functions */ +apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC) +{ + apc_function_t* array; + int new_count; /* number of new functions in table */ + int i; + + new_count = zend_hash_num_elements(CG(function_table)) - old_count; + assert(new_count >= 0); + + CHECK(array = + (apc_function_t*) + allocate(sizeof(apc_function_t) * (new_count+1))); + + if (new_count == 0) { + array[0].function = NULL; + return array; + } + + /* Skip the first `old_count` functions in the table */ + zend_hash_internal_pointer_reset(CG(function_table)); + for (i = 0; i < old_count; i++) { + zend_hash_move_forward(CG(function_table)); + } + + /* Add the next `new_count` functions to our array */ + for (i = 0; i < new_count; i++) { + char* key; + uint key_size; + zend_function* fun; + + zend_hash_get_current_key_ex(CG(function_table), + &key, + &key_size, + NULL, + 0, + NULL); + + zend_hash_get_current_data(CG(function_table), (void**) &fun); + + if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) { + int ii; + for(ii=i-1; ii>=0; ii--) { + deallocate(array[ii].name); + my_free_function(array[ii].function, deallocate); + } + deallocate(array); + return NULL; + } + array[i].name_len = (int) key_size-1; + if(!(array[i].function = my_copy_function(NULL, fun, allocate, deallocate))) { + int ii; + deallocate(array[i].name); + for(ii=i-1; ii>=0; ii--) { + deallocate(array[ii].name); + my_free_function(array[ii].function, deallocate); + } + deallocate(array); + return NULL; + } + zend_hash_move_forward(CG(function_table)); + } + + array[i].function = NULL; + return array; +} +/* }}} */ + +/* {{{ apc_copy_new_classes */ +apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC) +{ + apc_class_t* array; + int new_count; /* number of new classes in table */ + int i; + + new_count = zend_hash_num_elements(CG(class_table)) - old_count; + assert(new_count >= 0); + + CHECK(array = + (apc_class_t*) + allocate(sizeof(apc_class_t)*(new_count+1))); + + if (new_count == 0) { + array[0].class_entry = NULL; + return array; + } + + /* Skip the first `old_count` classes in the table */ + zend_hash_internal_pointer_reset(CG(class_table)); + for (i = 0; i < old_count; i++) { + zend_hash_move_forward(CG(class_table)); + } + + /* Add the next `new_count` classes to our array */ + for (i = 0; i < new_count; i++) { + char* key; + uint key_size; + zend_class_entry* elem = NULL; + + array[i].class_entry = NULL; + + zend_hash_get_current_key_ex(CG(class_table), + &key, + &key_size, + NULL, + 0, + NULL); + + zend_hash_get_current_data(CG(class_table), (void**) &elem); + + +#ifdef ZEND_ENGINE_2 + elem = *((zend_class_entry**)elem); +#endif + + if(!(array[i].name = apc_xmemcpy(key, (int) key_size, allocate))) { + int ii; + + for(ii=i-1; ii>=0; ii--) { + deallocate(array[ii].name); + my_destroy_class_entry(array[ii].class_entry, deallocate); + deallocate(array[ii].class_entry); + } + deallocate(array); + return NULL; + } + array[i].name_len = (int) key_size-1; + if(!(array[i].class_entry = my_copy_class_entry(NULL, elem, allocate, deallocate))) { + int ii; + + deallocate(array[i].name); + for(ii=i-1; ii>=0; ii--) { + deallocate(array[ii].name); + my_destroy_class_entry(array[ii].class_entry, deallocate); + deallocate(array[ii].class_entry); + } + deallocate(array); + return NULL; + } + + /* + * If the class has a pointer to its parent class, save the parent + * name so that we can enable compile-time inheritance when we reload + * the child class; otherwise, set the parent name to null and scan + * the op_array to determine if this class inherits from some base + * class at execution-time. + */ + + if (elem->parent) { + if(!(array[i].parent_name = + apc_xstrdup(elem->parent->name, allocate))) { + int ii; + + for(ii=i; ii>=0; ii--) { + deallocate(array[ii].name); + my_destroy_class_entry(array[ii].class_entry, deallocate); + deallocate(array[ii].class_entry); + if(ii==i) continue; + if(array[ii].parent_name) deallocate(array[ii].parent_name); + } + deallocate(array); + return NULL; + } + array[i].is_derived = 1; + } + else { + array[i].parent_name = NULL; + array[i].is_derived = is_derived_class(op_array, key, key_size); + } + + zend_hash_move_forward(CG(class_table)); + } + + array[i].class_entry = NULL; + return array; +} +/* }}} */ + +/* {{{ my_destroy_zval_ptr */ +static void my_destroy_zval_ptr(zval** src, apc_free_t deallocate) +{ + assert(src != NULL); + if(my_destroy_zval(src[0], deallocate) == SUCCESS) { + deallocate(src[0]); + } +} +/* }}} */ + +/* {{{ my_destroy_zval */ +static int my_destroy_zval(zval* src, apc_free_t deallocate) +{ + zval **tmp; + TSRMLS_FETCH(); + + switch (src->type & ~IS_CONSTANT_INDEX) { + case IS_RESOURCE: + case IS_BOOL: + case IS_LONG: + case IS_DOUBLE: + case IS_NULL: + break; + + case IS_CONSTANT: + case IS_STRING: +#ifndef ZEND_ENGINE_2 + case FLAG_IS_BC: +#endif + deallocate(src->value.str.val); + break; + + case IS_ARRAY: + + /* Maintain a list of zvals we've copied to properly handle recursive structures */ + if(APCG(copied_zvals)) { + if(zend_hash_index_find(APCG(copied_zvals), (ulong)src, (void**)&tmp) == SUCCESS) { + Z_DELREF_PP(tmp); + return FAILURE; + } + zend_hash_index_update(APCG(copied_zvals), (ulong)src, (void**)&src, sizeof(zval*), NULL); + } + /* fall through */ + + case IS_CONSTANT_ARRAY: + my_free_hashtable(src->value.ht, + (ht_free_fun_t) my_free_zval_ptr, + deallocate); + break; + + case IS_OBJECT: +#ifndef ZEND_ENGINE_2 + my_destroy_class_entry(src->value.obj.ce, deallocate); + deallocate(src->value.obj.ce); + my_free_hashtable(src->value.obj.properties, + (ht_free_fun_t) my_free_zval_ptr, + deallocate); +#endif + break; + + default: + assert(0); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ my_destroy_znode */ +static void my_destroy_znode(znode* src, apc_free_t deallocate) +{ + if (src->op_type == IS_CONST) { + my_destroy_zval(&src->u.constant, deallocate); + } +} +/* }}} */ + +/* {{{ my_destroy_zend_op */ +static void my_destroy_zend_op(zend_op* src, apc_free_t deallocate) +{ + my_destroy_znode(&src->result, deallocate); + my_destroy_znode(&src->op1, deallocate); + my_destroy_znode(&src->op2, deallocate); +} +/* }}} */ + +/* {{{ my_destroy_function */ +static void my_destroy_function(zend_function* src, apc_free_t deallocate) +{ + assert(src != NULL); + + switch (src->type) { + case ZEND_INTERNAL_FUNCTION: + case ZEND_OVERLOADED_FUNCTION: + break; + + case ZEND_USER_FUNCTION: + case ZEND_EVAL_CODE: + my_destroy_op_array(&src->op_array, deallocate); + break; + + default: + assert(0); + } +} +/* }}} */ + +/* {{{ my_destroy_function_entry */ +static void my_destroy_function_entry(zend_function_entry* src, apc_free_t deallocate) +{ + assert(src != NULL); + + deallocate(src->fname); +#ifdef ZEND_ENGINE_2 + if (src->arg_info) { + my_free_arg_info_array(src->arg_info, src->num_args, deallocate); + } +#else + if (src->func_arg_types) { + deallocate(src->func_arg_types); + } +#endif +} +/* }}} */ + +#ifdef ZEND_ENGINE_2 +/* {{{ my_destroy_property_info*/ +static void my_destroy_property_info(zend_property_info* src, apc_free_t deallocate) +{ + assert(src != NULL); + + deallocate(src->name); +#if defined(ZEND_ENGINE_2) && PHP_MINOR_VERSION > 0 + if(src->doc_comment) deallocate(src->doc_comment); +#endif +} +/* }}} */ + +/* {{{ my_destroy_arg_info_array */ +static void my_destroy_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate) +{ + int i = 0; + + assert(src != NULL); + + for(i=0; i < num_args; i++) { + my_destroy_arg_info(&src[i], deallocate); + } +} +/* }}} */ + +/* {{{ my_destroy_arg_info */ +static void my_destroy_arg_info(zend_arg_info* src, apc_free_t deallocate) +{ + assert(src != NULL); + + deallocate(src->name); + deallocate(src->class_name); +} +/* }}} */ +#endif + +/* {{{ my_destroy_class_entry */ +static void my_destroy_class_entry(zend_class_entry* src, apc_free_t deallocate) +{ + uint i; + + assert(src != NULL); + + deallocate(src->name); +#ifndef ZEND_ENGINE_2 + deallocate(src->refcount); +#else + if(src->doc_comment) deallocate(src->doc_comment); + if(src->filename) deallocate(src->filename); +#endif + + my_destroy_hashtable(&src->function_table, + (ht_free_fun_t) my_free_function, + deallocate); + + my_destroy_hashtable(&src->default_properties, + (ht_free_fun_t) my_free_zval_ptr, + deallocate); + +#ifdef ZEND_ENGINE_2 + my_destroy_hashtable(&src->properties_info, + (ht_free_fun_t) my_free_property_info, + deallocate); + if(src->static_members) + { + my_destroy_hashtable(src->static_members, + (ht_free_fun_t) my_free_zval_ptr, + deallocate); + if(src->static_members != &(src->default_static_members)) + { + deallocate(src->static_members); + } + } + + my_destroy_hashtable(&src->constants_table, + (ht_free_fun_t) my_free_zval_ptr, + deallocate); +#endif + + if (src->builtin_functions) { + for (i = 0; src->builtin_functions[i].fname != NULL; i++) { + my_destroy_function_entry(&src->builtin_functions[i], deallocate); + } + deallocate(src->builtin_functions); + } +} +/* }}} */ + +/* {{{ my_destroy_hashtable */ +static void my_destroy_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate) +{ + int i; + + assert(src != NULL); + + for (i = 0; i < src->nTableSize; i++) { + Bucket* p = src->arBuckets[i]; + while (p != NULL) { + Bucket* q = p; + p = p->pNext; + free_fn(q->pData, deallocate); + deallocate(q); + } + } + + deallocate(src->arBuckets); +} +/* }}} */ + +/* {{{ my_destroy_op_array */ +static void my_destroy_op_array(zend_op_array* src, apc_free_t deallocate) +{ + int i; + + assert(src != NULL); + +#ifdef ZEND_ENGINE_2 + if (src->arg_info) { + my_free_arg_info_array(src->arg_info, src->num_args, deallocate); + } +#else + if (src->arg_types) { + deallocate(src->arg_types); + } +#endif + + deallocate(src->function_name); + deallocate(src->filename); + deallocate(src->refcount); + + for (i = 0; i < src->last; i++) { + my_destroy_zend_op(src->opcodes + i, deallocate); + } + deallocate(src->opcodes); + + if (src->brk_cont_array) { + deallocate(src->brk_cont_array); + } + + if (src->static_variables) { + my_free_hashtable(src->static_variables, + (ht_free_fun_t) my_free_zval_ptr, + deallocate); + } + +#ifdef ZEND_ENGINE_2_1 + if (src->vars) { + for(i=0; i < src->last_var; i++) { + if(src->vars[i].name) deallocate(src->vars[i].name); + } + deallocate(src->vars); + } +#endif +#ifdef ZEND_ENGINE_2 + if(src->try_catch_array) { + deallocate(src->try_catch_array); + } + if (src->doc_comment) { + deallocate(src->doc_comment); + } +#endif +} +/* }}} */ + +/* {{{ my_free_zval_ptr */ +static void my_free_zval_ptr(zval** src, apc_free_t deallocate) +{ + my_destroy_zval_ptr(src, deallocate); + deallocate(src); +} +/* }}} */ + +#ifdef ZEND_ENGINE_2 +/* {{{ my_free_property_info */ +static void my_free_property_info(zend_property_info* src, apc_free_t deallocate) +{ + my_destroy_property_info(src, deallocate); + deallocate(src); +} +/* }}} */ + +/* {{{ my_free_arg_info_array */ +static void my_free_arg_info_array(zend_arg_info* src, uint num_args, apc_free_t deallocate) +{ + my_destroy_arg_info_array(src, num_args, deallocate); + deallocate(src); +} +/* }}} */ + +/* {{{ my_free_arg_info */ +static void my_free_arg_info(zend_arg_info* src, apc_free_t deallocate) +{ + my_destroy_arg_info(src, deallocate); + deallocate(src); +} +/* }}} */ +#endif + +/* {{{ my_free_function */ +static void my_free_function(zend_function* src, apc_free_t deallocate) +{ + my_destroy_function(src, deallocate); + deallocate(src); +} +/* }}} */ + +/* {{{ my_free_hashtable */ +static void my_free_hashtable(HashTable* src, ht_free_fun_t free_fn, apc_free_t deallocate) +{ + my_destroy_hashtable(src, free_fn, deallocate); + deallocate(src); +} +/* }}} */ + +/* {{{ apc_free_op_array */ +void apc_free_op_array(zend_op_array* src, apc_free_t deallocate) +{ + if (src != NULL) { + my_destroy_op_array(src, deallocate); + deallocate(src); + } +} +/* }}} */ + +/* {{{ apc_free_functions */ +void apc_free_functions(apc_function_t* src, apc_free_t deallocate) +{ + int i; + + if (src != NULL) { + for (i = 0; src[i].function != NULL; i++) { + deallocate(src[i].name); + my_destroy_function(src[i].function, deallocate); + deallocate(src[i].function); + } + deallocate(src); + } +} +/* }}} */ + +/* {{{ apc_free_classes */ +void apc_free_classes(apc_class_t* src, apc_free_t deallocate) +{ + int i; + + if (src != NULL) { + for (i = 0; src[i].class_entry != NULL; i++) { + deallocate(src[i].name); + deallocate(src[i].parent_name); + my_destroy_class_entry(src[i].class_entry, deallocate); + deallocate(src[i].class_entry); + } + deallocate(src); + } +} +/* }}} */ + +/* {{{ apc_free_zval */ +void apc_free_zval(zval* src, apc_free_t deallocate) +{ + if (src != NULL) { + if(my_destroy_zval(src, deallocate) == SUCCESS) { + deallocate(src); + } + } +} +/* }}} */ + + +/* Used only by my_prepare_op_array_for_execution */ +#define APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION() \ + /* The fetch is only required if auto_globals_jit=1 */ \ + if(zo->op2.u.EA.type == ZEND_FETCH_GLOBAL && \ + zo->op1.op_type == IS_CONST && \ + zo->op1.u.constant.type == IS_STRING && \ + zo->op1.u.constant.value.str.val[0] == '_') { \ + \ + znode* varname = &zo->op1; \ + (void)zend_is_auto_global(varname->u.constant.value.str.val, \ + varname->u.constant.value.str.len \ + TSRMLS_CC); \ + } \ + +/* {{{ my_prepare_op_array_for_execution */ +static int my_prepare_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC) +{ + /* combine my_fetch_global_vars and my_copy_data_exceptions. + * - Pre-fetch superglobals which would've been pre-fetched in parse phase. + * - If the opcode stream contain mutable data, ensure a copy. + * - Fixup array jumps in the same loop. + */ + int i=src->last; + zend_op *zo; + zend_op *dzo; +#ifdef ZEND_ENGINE_2 + apc_opflags_t * flags = APCG(reserved_offset) != -1 ? + (apc_opflags_t*) & (src->reserved[APCG(reserved_offset)]) : NULL; + int needcopy = flags ? flags->deep_copy : 1; + /* auto_globals_jit was not in php4 */ + int do_prepare_fetch_global = PG(auto_globals_jit) && (flags == NULL || flags->unknown_global); + +#define FETCH_AUTOGLOBAL(member) do { \ + if(flags && flags->member == 1) { \ + zend_is_auto_global(#member,\ + (sizeof(#member) - 1)\ + TSRMLS_CC);\ + } \ +}while(0); + + FETCH_AUTOGLOBAL(_GET); + FETCH_AUTOGLOBAL(_POST); + FETCH_AUTOGLOBAL(_COOKIE); + FETCH_AUTOGLOBAL(_SERVER); + FETCH_AUTOGLOBAL(_ENV); + FETCH_AUTOGLOBAL(_FILES); + FETCH_AUTOGLOBAL(_REQUEST); + +#else + int needcopy = 0; + int do_prepare_fetch_global = 0; + int j = 0; + + for(j = 0; j < src->last; j++) { + zo = &src->opcodes[j]; + + if( ((zo->op1.op_type == IS_CONST && + zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) || + ((zo->op2.op_type == IS_CONST && + zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) { + needcopy = 1; + } + } +#endif + + if(needcopy) { + + dst->opcodes = (zend_op*) apc_xmemcpy(src->opcodes, + sizeof(zend_op) * src->last, + apc_php_malloc); + zo = src->opcodes; + dzo = dst->opcodes; + while(i > 0) { + + if( ((zo->op1.op_type == IS_CONST && + zo->op1.u.constant.type == IS_CONSTANT_ARRAY)) || + ((zo->op2.op_type == IS_CONST && + zo->op2.u.constant.type == IS_CONSTANT_ARRAY))) { + + if(!(my_copy_zend_op(dzo, zo, apc_php_malloc, apc_php_free))) { + assert(0); /* emalloc failed or a bad constant array */ + } + } + +#ifdef ZEND_ENGINE_2 + switch(zo->opcode) { + case ZEND_JMP: + dzo->op1.u.jmp_addr = dst->opcodes + + (zo->op1.u.jmp_addr - src->opcodes); + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + dzo->op2.u.jmp_addr = dst->opcodes + + (zo->op2.u.jmp_addr - src->opcodes); + break; + case ZEND_FETCH_R: + case ZEND_FETCH_W: + case ZEND_FETCH_IS: + case ZEND_FETCH_FUNC_ARG: + if(do_prepare_fetch_global) + { + APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION(); + } + break; + default: + break; + } +#endif + i--; + zo++; + dzo++; + } +#ifdef ZEND_ENGINE_2 + } else { /* !needcopy */ + /* The fetch is only required if auto_globals_jit=1 */ + if(do_prepare_fetch_global) + { + zo = src->opcodes; + while(i > 0) { + + if(zo->opcode == ZEND_FETCH_R || + zo->opcode == ZEND_FETCH_W || + zo->opcode == ZEND_FETCH_IS || + zo->opcode == ZEND_FETCH_FUNC_ARG + ) { + APC_PREPARE_FETCH_GLOBAL_FOR_EXECUTION(); + } + + i--; + zo++; + } + } +#endif + } + return 1; +} +/* }}} */ + +/* {{{ apc_copy_op_array_for_execution */ +zend_op_array* apc_copy_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC) +{ + if(dst == NULL) { + dst = (zend_op_array*) emalloc(sizeof(src[0])); + } + memcpy(dst, src, sizeof(src[0])); + dst->static_variables = my_copy_static_variables(src, apc_php_malloc, apc_php_free); + + dst->refcount = apc_xmemcpy(src->refcount, + sizeof(src->refcount[0]), + apc_php_malloc); + + my_prepare_op_array_for_execution(dst,src TSRMLS_CC); + + return dst; +} +/* }}} */ + +/* {{{ apc_copy_function_for_execution */ +zend_function* apc_copy_function_for_execution(zend_function* src) +{ + zend_function* dst; + TSRMLS_FETCH(); + + dst = (zend_function*) emalloc(sizeof(src[0])); + memcpy(dst, src, sizeof(src[0])); + apc_copy_op_array_for_execution(&(dst->op_array), &(src->op_array) TSRMLS_CC); + return dst; +} +/* }}} */ + +/* {{{ apc_copy_function_for_execution_ex */ +zend_function* apc_copy_function_for_execution_ex(void *dummy, zend_function* src, apc_malloc_t allocate, apc_free_t deallocate) +{ + if(src->type==ZEND_INTERNAL_FUNCTION || src->type==ZEND_OVERLOADED_FUNCTION) return src; + return apc_copy_function_for_execution(src); +} +/* }}} */ + +/* {{{ apc_copy_class_entry_for_execution */ +zend_class_entry* apc_copy_class_entry_for_execution(zend_class_entry* src, int is_derived) +{ + zend_class_entry* dst = (zend_class_entry*) emalloc(sizeof(src[0])); + memcpy(dst, src, sizeof(src[0])); + +#ifdef ZEND_ENGINE_2 + if(src->num_interfaces) + { + /* These are slots to be populated later by ADD_INTERFACE insns */ + dst->interfaces = apc_php_malloc( + sizeof(zend_class_entry*) * src->num_interfaces); + memset(dst->interfaces, 0, + sizeof(zend_class_entry*) * src->num_interfaces); + } + else + { + /* assert(dst->interfaces == NULL); */ + } +#endif + +#ifndef ZEND_ENGINE_2 + dst->refcount = apc_xmemcpy(src->refcount, + sizeof(src->refcount[0]), + apc_php_malloc); +#endif + + /* Deep-copy the class properties, because they will be modified */ + + my_copy_hashtable(&dst->default_properties, + &src->default_properties, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + apc_php_malloc, apc_php_free); + + /* For derived classes, we must also copy the function hashtable (although + * we can merely bitwise copy the functions it contains) */ + + my_copy_hashtable(&dst->function_table, + &src->function_table, + (ht_copy_fun_t) apc_copy_function_for_execution_ex, + NULL, + 0, + apc_php_malloc, apc_php_free); +#ifdef ZEND_ENGINE_2 + my_fixup_hashtable(&dst->function_table, (ht_fixup_fun_t)my_fixup_function_for_execution, src, dst); + + /* zend_do_inheritance merges properties_info. + * Need only shallow copying as it doesn't hold the pointers. + */ + my_copy_hashtable(&dst->properties_info, + &src->properties_info, + (ht_copy_fun_t) my_copy_property_info_for_execution, + NULL, + 0, + apc_php_malloc, apc_php_free); + +#ifdef ZEND_ENGINE_2_2 + /* php5.2 introduced a scope attribute for property info */ + my_fixup_hashtable(&dst->properties_info, (ht_fixup_fun_t)my_fixup_property_info_for_execution, src, dst); +#endif + + /* if inheritance results in a hash_del, it might result in + * a pefree() of the pointers here. Deep copying required. + */ + + my_copy_hashtable(&dst->constants_table, + &src->constants_table, + (ht_copy_fun_t) my_copy_zval_ptr, + NULL, + 1, + apc_php_malloc, apc_php_free); + + my_copy_hashtable(&dst->default_static_members, + &src->default_static_members, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + apc_php_malloc, apc_php_free); + + if(src->static_members != &(src->default_static_members)) + { + dst->static_members = my_copy_hashtable(NULL, + src->static_members, + (ht_copy_fun_t) my_copy_zval_ptr, + (ht_free_fun_t) my_free_zval_ptr, + 1, + apc_php_malloc, apc_php_free); + } + else + { + dst->static_members = &(dst->default_static_members); + } + +#endif + + return dst; +} +/* }}} */ + +/* {{{ apc_free_class_entry_after_execution */ +void apc_free_class_entry_after_execution(zend_class_entry* src) +{ +#ifdef ZEND_ENGINE_2 + if(src->num_interfaces > 0 && src->interfaces) { + apc_php_free(src->interfaces); + src->interfaces = NULL; + src->num_interfaces = 0; + } + /* my_destroy_hashtable() does not play nice with refcounts */ + + zend_hash_clean(&src->default_static_members); + if(src->static_members != &(src->default_static_members)) + { + zend_hash_destroy(src->static_members); + apc_php_free(src->static_members); + src->static_members = NULL; + } + else + { + src->static_members = NULL; + } + zend_hash_clean(&src->default_properties); + zend_hash_clean(&src->constants_table); +#endif + + /* TODO: more cleanup */ +} +/* }}} */ + +#ifdef ZEND_ENGINE_2 + +/* {{{ my_fixup_function */ +static void my_fixup_function(Bucket *p, zend_class_entry *src, zend_class_entry *dst) +{ + zend_function* zf = p->pData; + + #define SET_IF_SAME_NAME(member) \ + do { \ + if(src->member && !strcmp(zf->common.function_name, src->member->common.function_name)) { \ + dst->member = zf; \ + } \ + } \ + while(0) + + if(zf->common.scope == src) + { + + /* Fixing up the default functions for objects here since + * we need to compare with the newly allocated functions + * + * caveat: a sub-class method can have the same name as the + * parent's constructor and create problems. + */ + + if(zf->common.fn_flags & ZEND_ACC_CTOR) dst->constructor = zf; + else if(zf->common.fn_flags & ZEND_ACC_DTOR) dst->destructor = zf; + else if(zf->common.fn_flags & ZEND_ACC_CLONE) dst->clone = zf; + else + { + SET_IF_SAME_NAME(__get); + SET_IF_SAME_NAME(__set); + SET_IF_SAME_NAME(__unset); + SET_IF_SAME_NAME(__isset); + SET_IF_SAME_NAME(__call); +#ifdef ZEND_ENGINE_2_2 + SET_IF_SAME_NAME(__tostring); +#endif + } + zf->common.scope = dst; + } + else + { + /* no other function should reach here */ + assert(0); + } + + #undef SET_IF_SAME_NAME +} +/* }}} */ + +#ifdef ZEND_ENGINE_2_2 +/* {{{ my_fixup_property_info */ +static void my_fixup_property_info(Bucket *p, zend_class_entry *src, zend_class_entry *dst) +{ + zend_property_info* property_info = (zend_property_info*)p->pData; + + if(property_info->ce == src) + { + property_info->ce = dst; + } + else + { + assert(0); /* should never happen */ + } +} +/* }}} */ +#endif + +/* {{{ my_fixup_hashtable */ +static void my_fixup_hashtable(HashTable *ht, ht_fixup_fun_t fixup, zend_class_entry *src, zend_class_entry *dst) +{ + Bucket *p; + + uint i; + + for (i = 0; i < ht->nTableSize; i++) { + if(!ht->arBuckets) break; + p = ht->arBuckets[i]; + while (p != NULL) { + fixup(p, src, dst); + p = p->pNext; + } + } +} +/* }}} */ + +#endif + +/* {{{ my_check_copy_function */ +static int my_check_copy_function(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + zend_function* zf = (zend_function*)p->pData; +#ifndef ZEND_ENGINE_2 + zend_class_entry* parent = src->parent; + zend_function* parent_fn = NULL; +#endif + +#ifdef ZEND_ENGINE_2 + return (zf->common.scope == src); +#else + if (parent && + zend_hash_quick_find(&parent->function_table, p->arKey, + p->nKeyLength, p->h, (void **) &parent_fn)==SUCCESS) { + + if((parent_fn && zf) && + (parent_fn->op_array.refcount == zf->op_array.refcount)) + { + return 0; + } + } + return 1; +#endif +} +/* }}} */ + +/* {{{ my_check_copy_default_property */ +static int my_check_copy_default_property(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + zend_class_entry* parent = src->parent; + zval ** child_prop = (zval**)p->pData; + zval ** parent_prop = NULL; + + if (parent && + zend_hash_quick_find(&parent->default_properties, p->arKey, + p->nKeyLength, p->h, (void **) &parent_prop)==SUCCESS) { + + if((parent_prop && child_prop) && (*parent_prop) == (*child_prop)) + { + return 0; + } + } + + /* possibly not in the parent */ + return 1; +} +/* }}} */ + +#ifdef ZEND_ENGINE_2 + +/* {{{ my_check_copy_property_info */ +static int my_check_copy_property_info(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + zend_class_entry* parent = src->parent; + zend_property_info* child_info = (zend_property_info*)p->pData; + zend_property_info* parent_info = NULL; + +#ifdef ZEND_ENGINE_2_2 + /* so much easier */ + return (child_info->ce == src); +#endif + + if (parent && + zend_hash_quick_find(&parent->properties_info, p->arKey, p->nKeyLength, + p->h, (void **) &parent_info)==SUCCESS) { + if(parent_info->flags & ZEND_ACC_PRIVATE) + { + return 1; + } + if((parent_info->flags & ZEND_ACC_PPP_MASK) != + (child_info->flags & ZEND_ACC_PPP_MASK)) + { + /* TODO: figure out whether ACC_CHANGED is more appropriate + * here */ + return 1; + } + return 0; + } + + /* property doesn't exist in parent, copy into cached child */ + return 1; +} +/* }}} */ + +/* {{{ my_check_copy_static_member */ +static int my_check_copy_static_member(Bucket* p, va_list args) +{ + zend_class_entry* src = va_arg(args, zend_class_entry*); + HashTable * ht = va_arg(args, HashTable*); + zend_class_entry* parent = src->parent; + HashTable * parent_ht = NULL; + char * member_name; + char * class_name = NULL; + + zend_property_info *parent_info = NULL; + zend_property_info *child_info = NULL; + zval ** parent_prop = NULL; + zval ** child_prop = (zval**)(p->pData); + + if(!parent) { + return 1; + } + + /* these do not need free'ing */ +#ifdef ZEND_ENGINE_2_2 + zend_unmangle_property_name(p->arKey, p->nKeyLength-1, &class_name, &member_name); +#else + zend_unmangle_property_name(p->arKey, &class_name, &member_name); +#endif + + /* please refer do_inherit_property_access_check in zend_compile.c + * to understand why we lookup in properties_info. + */ + if((zend_hash_find(&parent->properties_info, member_name, + strlen(member_name)+1, (void**)&parent_info) == SUCCESS) + && + (zend_hash_find(&src->properties_info, member_name, + strlen(member_name)+1, (void**)&child_info) == SUCCESS)) + { + if(child_info->flags & ZEND_ACC_STATIC && + (parent_info->flags & ZEND_ACC_PROTECTED && + child_info->flags & ZEND_ACC_PUBLIC)) + { + /* Do not copy into static_members. zend_do_inheritance + * will automatically insert a NULL value. + * TODO: decrement refcount or fixup when copying out for exec ? + */ + return 0; + } + if(ht == &(src->default_static_members)) + { + parent_ht = &parent->default_static_members; + } + else + { + parent_ht = parent->static_members; + } + + if(zend_hash_quick_find(parent_ht, p->arKey, + p->nKeyLength, p->h, (void**)&parent_prop) == SUCCESS) + { + /* they point to the same zval */ + if(*parent_prop == *child_prop) + { + return 0; + } + } + } + + return 1; +} +/* }}} */ +#endif + +/* {{{ apc_register_optimizer(apc_optimize_function_t optimizer) + * register a optimizer callback function, returns the previous callback + */ +apc_optimize_function_t apc_register_optimizer(apc_optimize_function_t optimizer TSRMLS_DC) { + apc_optimize_function_t old_optimizer = APCG(apc_optimize_function); + APCG(apc_optimize_function) = optimizer; + return old_optimizer; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_compile.h b/apc_compile.h new file mode 100644 index 0000000..20ff076 --- /dev/null +++ b/apc_compile.h @@ -0,0 +1,134 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_compile.h,v 3.19 2007/03/08 22:03:35 gopalv Exp $ */ + +#ifndef APC_COMPILE_H +#define APC_COMPILE_H + +/* + * This module encapsulates most of the complexity involved in deep-copying + * the Zend compiler data structures. The routines are allocator-agnostic, so + * the same function can be used for copying to and from shared memory. + */ + +#include "apc.h" +#include "apc_php.h" + +/* {{{ struct definition: apc_function_t */ +typedef struct apc_function_t apc_function_t; +struct apc_function_t { + char* name; /* the function name */ + int name_len; /* length of name */ + zend_function* function; /* the zend function data structure */ +}; +/* }}} */ + +/* {{{ struct definition: apc_class_t */ +typedef struct apc_class_t apc_class_t; +struct apc_class_t { + char* name; /* the class name */ + int name_len; /* length of name */ + int is_derived; /* true if this is a derived class */ + char* parent_name; /* the parent class name */ + zend_class_entry* class_entry; /* the zend class data structure */ +}; +/* }}} */ + +/* {{{ struct definition: apc_opflags_t */ +typedef struct apc_opflags_t apc_opflags_t; +struct apc_opflags_t { + unsigned int has_jumps : 1; /* has jump offsets */ + unsigned int deep_copy : 1; /* needs deep copy */ + + /* autoglobal bits */ + unsigned int _POST : 1; + unsigned int _GET : 1; + unsigned int _COOKIE : 1; + unsigned int _SERVER : 1; + unsigned int _ENV : 1; + unsigned int _FILES : 1; + unsigned int _REQUEST : 1; + unsigned int unknown_global : 1; +}; +/* }}} */ + +/* + * These are the top-level copy functions. + */ + +extern zend_op_array* apc_copy_op_array(zend_op_array* dst, zend_op_array* src, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC); +extern zend_class_entry* apc_copy_class_entry(zend_class_entry* dst, zend_class_entry* src, apc_malloc_t allocate, apc_free_t deallocate); +extern apc_function_t* apc_copy_new_functions(int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC); +extern apc_class_t* apc_copy_new_classes(zend_op_array* op_array, int old_count, apc_malloc_t allocate, apc_free_t deallocate TSRMLS_DC); +extern zval* apc_copy_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate); + +/* + * Deallocation functions corresponding to the copy functions above. + */ + +extern void apc_free_op_array(zend_op_array* src, apc_free_t deallocate); +extern void apc_free_functions(apc_function_t* src, apc_free_t deallocate); +extern void apc_free_classes(apc_class_t* src, apc_free_t deallocate); +extern void apc_free_zval(zval* src, apc_free_t deallocate); + +/* + * These "copy-for-execution" functions must be called after retrieving an + * object from the shared cache. They do the minimal amount of work necessary + * to allow multiple processes to concurrently execute the same VM data + * structures. + */ + +extern zend_op_array* apc_copy_op_array_for_execution(zend_op_array* dst, zend_op_array* src TSRMLS_DC); +extern zend_function* apc_copy_function_for_execution(zend_function* src); +extern zend_class_entry* apc_copy_class_entry_for_execution(zend_class_entry* src, int is_derived); + +/* + * The "free-after-execution" function performs a cursory clean up of the class data + * This is required to minimize memory leak warnings and to ensure correct destructor + * ordering of some variables. + */ +extern void apc_free_class_entry_after_execution(zend_class_entry* src); + +/* + * Optimization callback definition and registration function. + */ +typedef zend_op_array* (*apc_optimize_function_t) (zend_op_array* TSRMLS_DC); +extern apc_optimize_function_t apc_register_optimizer(apc_optimize_function_t optimizer TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_debug.c b/apc_debug.c new file mode 100644 index 0000000..0f47a0a --- /dev/null +++ b/apc_debug.c @@ -0,0 +1,57 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. +*/ + +/* $Id: apc_debug.c,v 3.6 2006/12/07 23:51:28 gopalv Exp $ */ +#include "apc.h" +#include +#include "zend_compile.h" + +#ifdef __DEBUG_APC__ + +#include + +/* keep track of vld_dump_oparray() signature */ +typedef void (*vld_dump_f) (zend_op_array * TSRMLS_DC); + +#endif + +void dump(zend_op_array *op_array TSRMLS_DC) +{ +#ifdef __DEBUG_APC__ + vld_dump_f dump_op_array = dlsym(NULL, "vld_dump_oparray"); + + if(dump_op_array) + { + dump_op_array(op_array TSRMLS_CC); + } + else + { + apc_wprint("vld is not installed or something even worse."); + } +#endif +} diff --git a/apc_debug.h b/apc_debug.h new file mode 100644 index 0000000..4e286e7 --- /dev/null +++ b/apc_debug.h @@ -0,0 +1 @@ +void dump(zend_op_array * TSRMLS_DC); diff --git a/apc_fcntl.c b/apc_fcntl.c new file mode 100644 index 0000000..550405e --- /dev/null +++ b/apc_fcntl.c @@ -0,0 +1,118 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_fcntl.c,v 3.25 2006/06/19 02:52:49 rasmus Exp $ */ + +#include "apc_fcntl.h" +#include "apc.h" +#include +#include + +int apc_fcntl_create(const char* pathname) +{ + int fd; + if(pathname == NULL) { + char lock_path[] = "/tmp/.apc.XXXXXX"; + mktemp(lock_path); + fd = open(lock_path, O_RDWR|O_CREAT, 0666); + if(fd > 0 ) { + unlink(lock_path); + return fd; + } else { + apc_eprint("apc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:", lock_path); + return -1; + } + } + fd = open(pathname, O_RDWR|O_CREAT, 0666); + if(fd > 0 ) { + unlink(pathname); + return fd; + } + apc_eprint("apc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:", pathname); + return -1; +} + +void apc_fcntl_destroy(int fd) +{ + close(fd); +} + +static int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) +{ + int ret; + struct flock lock; + + lock.l_type = type; + lock.l_start = offset; + lock.l_whence = whence; + lock.l_len = len; + lock.l_pid = 0; + + do { ret = fcntl(fd, cmd, &lock) ; } + while(ret < 0 && errno == EINTR); + return(ret); +} + +void apc_fcntl_lock(int fd) +{ + if(lock_reg(fd, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) < 0) { + apc_eprint("apc_fcntl_lock failed:"); + } +} + +void apc_fcntl_rdlock(int fd) +{ + if(lock_reg(fd, F_SETLKW, F_RDLCK, 0, SEEK_SET, 0) < 0) { + apc_eprint("apc_fcntl_rdlock failed:"); + } +} + +zend_bool apc_fcntl_nonblocking_lock(int fd) +{ + if(lock_reg(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 0) < 0) { + if(errno==EACCES||errno==EAGAIN) return 0; + else apc_eprint("apc_fcntl_lock failed:"); + } + return 1; +} + +void apc_fcntl_unlock(int fd) +{ + if(lock_reg(fd, F_SETLKW, F_UNLCK, 0, SEEK_SET, 0) < 0) { + apc_eprint("apc_fcntl_unlock failed:"); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_fcntl.h b/apc_fcntl.h new file mode 100644 index 0000000..1f71a46 --- /dev/null +++ b/apc_fcntl.h @@ -0,0 +1,50 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_fcntl.h,v 3.14 2006/05/31 22:24:48 rasmus Exp $ */ + +#ifndef APC_FCNTL_H +#define APC_FCNTL_H + + +extern int apc_fcntl_create(const char* pathname); +extern void apc_fcntl_destroy(int fd); +extern void apc_fcntl_lock(int fd); +extern void apc_fcntl_rdlock(int fd); +extern void apc_fcntl_unlock(int fd); +extern unsigned char apc_fcntl_nonblocking_lock(int fd); +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_fcntl_win32.c b/apc_fcntl_win32.c new file mode 100644 index 0000000..b3822d0 --- /dev/null +++ b/apc_fcntl_win32.c @@ -0,0 +1,117 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: George Schlossnagle | + | Edin Kadribasic | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_fcntl_win32.c,v 3.6 2006/03/12 00:31:45 rasmus Exp $ */ + +#include "apc_fcntl.h" +#include "apc.h" +#include +#include +#include +#include +#include +#include + +int apc_fcntl_create(const char* pathname) +{ + char *lock_file = emalloc(MAXPATHLEN); + HANDLE fd; + DWORD tmplen; + static int i=0; + + tmplen = GetTempPath(MAXPATHLEN, lock_file); + if (!tmplen) { + efree(lock_file); + return -1; + } + + snprintf(lock_file + tmplen, MAXPATHLEN - tmplen - 1, "apc.lock.%d", i++); + + fd = CreateFile(lock_file, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + + if (fd == INVALID_HANDLE_VALUE) { + apc_eprint("apc_fcntl_create: could not open %s", lock_file); + efree(lock_file); + return -1; + } + + efree(lock_file); + return (int)fd; +} + +void apc_fcntl_destroy(int fd) +{ + CloseHandle((HANDLE)fd); +} + +void apc_fcntl_lock(int fd) +{ + OVERLAPPED offset = {0, 0, 0, 0, NULL}; + + if (!LockFileEx((HANDLE)fd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset)) { + apc_eprint("apc_fcntl_lock failed errno:%d", GetLastError()); + } +} + +void apc_fcntl_rdlock(int fd) +{ + OVERLAPPED offset = {0, 0, 0, 0, NULL}; + + if (!LockFileEx((HANDLE)fd, 0, 0, 1, 0, &offset)) { + apc_eprint("apc_fcntl_rdlock failed errno:%d", GetLastError()); + } +} + +void apc_fcntl_unlock(int fd) +{ + OVERLAPPED offset = {0, 0, 0, 0, NULL}; + + if (!UnlockFileEx((HANDLE)fd, 0, 1, 0, &offset)) { + DWORD error_code = GetLastError(); + /* Ignore already unlocked error */ + if (error_code != ERROR_NOT_LOCKED) { + apc_eprint("apc_fcntl_unlock failed errno:%d", error_code); + } + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_futex.c b/apc_futex.c new file mode 100644 index 0000000..89069ff --- /dev/null +++ b/apc_futex.c @@ -0,0 +1,116 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_futex.c,v 3.2 2006/10/12 08:23:16 shire Exp $ */ + +/*************************************************************************** +* Futex (Fast Userspace Mutex) support for APC +* +* Futex support provides user space locking with system calls only +* for the contended cases. Some required reading for this functionality is: +* +* 'Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux' +* by Hubertus Franke, Rusty Russell, and Matthew Kirkwood +* http://www.realitydiluted.com/nptl-uclibc/docs/futex.pdf +* +* 'Futexes are Tricky' by Ulrich Drepper +* http://people.redhat.com/drepper/futex.pdf +* +* +* This implementation is optimized and designed for the i386 and x86_64 +* architectures. Other architectures may require additional design +* to efficiently and safely implement this functionality. +* +* Lock values are: +* 0 = Unlocked +* 1 = Locked without any waiting processes +* 2 = Locked with an unknown number of waiting processes +* +***************************************************************************/ + +#include "apc.h" +#include "apc_futex.h" + +#ifdef APC_FUTEX_LOCKS + + +inline int apc_futex_create() +{ + return 0; +} + +inline void apc_futex_destroy(volatile int* lock) +{ + return; +} + +void apc_futex_lock(volatile int* lock) +{ + int c; + + /* Attempt to obtain a lock if not currently locked. If the previous + * value was not 0 then we did not obtain the lock, and must wait. + * If the previous value was 1 (has no waiting processes) then we + * set the lock to 2 before blocking on the futex wait operation. + * This implementation suffers from the possible difficulty of + * efficently implementing the atomic xchg operation on some + * architectures, and could also cause unecessary wake operations by + * setting the lock to 2 when there are no additional waiters. + */ + if((c = apc_cmpxchg(lock, 0, 1)) != 0) { + if(c != 2) { + c = apc_xchg(lock, 2); + } + while(c != 0) { + apc_futex_wait(lock, 2); + c = apc_xchg(lock, 2); + } + } + +} + +/* non-blocking lock returns 1 when the lock has been obtained, 0 if it would block */ +inline zend_bool apc_futex_nonblocking_lock(volatile int* lock) +{ + return apc_cmpxchg(lock, 0, 1) == 0; +} + + +inline void apc_futex_unlock(volatile int* lock) +{ + /* set the lock to 0, if it's previous values was not 1 (no waiters) + * then perform a wake operation on one process letting it know the lock + * is available. This is an optimization to save wake calls if there + * are no waiting processes for the lock + */ + if(apc_xchg(lock,0) != 1) { + apc_futex_wake(lock, 1); + } +} + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_futex.h b/apc_futex.h new file mode 100644 index 0000000..3fca50f --- /dev/null +++ b/apc_futex.h @@ -0,0 +1,55 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_futex.h,v 3.2 2006/10/12 08:23:16 shire Exp $ */ + +#ifndef APC_FUTEX_H +#define APC_FUTEX_H + +#include "apc.h" + +#ifdef APC_FUTEX_LOCKS + +#include +#include +#include + +#include "arch/atomic.h" + +#define sys_futex(futex, op, val, timeout) syscall(SYS_futex, futex, op, val, timeout) +#define apc_futex_wait(val, oldval) sys_futex((void*)val, FUTEX_WAIT, oldval, NULL) +#define apc_futex_wake(val, count) sys_futex((void*)val, FUTEX_WAKE, count, NULL) + +int apc_futex_create(); +void apc_futex_destroy(volatile int* lock); +void apc_futex_lock(volatile int* lock); +void apc_futex_unlock(volatile int* lock); + +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_globals.h b/apc_globals.h index c2b2ce7..1a5e938 100644 --- a/apc_globals.h +++ b/apc_globals.h @@ -29,12 +29,12 @@ */ -/* $Id: apc_globals.h,v 3.70 2007/12/26 22:46:33 rasmus Exp $ */ +/* $Id: apc_globals.h,v 3.70.2.5 2008/03/28 19:41:23 rasmus Exp $ */ #ifndef APC_GLOBALS_H #define APC_GLOBALS_H -#define APC_VERSION "3.0.16" +#define APC_VERSION "3.0.18" #include "apc_cache.h" #include "apc_stack.h" @@ -82,15 +82,13 @@ ZEND_BEGIN_MODULE_GLOBALS(apc) #ifdef ZEND_ENGINE_2 int reserved_offset; /* offset for apc info in op_array->reserved[] */ #endif - zend_bool localcache; /* enable local cache */ - long localcache_size; /* size of fast cache */ - apc_local_cache_t* lcache; /* unlocked local cache */ zend_bool force_file_update; /* force files to be updated during apc_compile_file */ char canon_path[MAXPATHLEN]; /* canonical path for key data */ #if APC_FILEHITS zval *filehits; /* Files that came from the cache for this request */ #endif zend_bool coredump_unmap; /* Trap signals that coredump and unmap shared memory */ + apc_cache_t *current_cache; /* current cache being modified/read */ ZEND_END_MODULE_GLOBALS(apc) /* (the following declaration is defined in php_apc.c) */ diff --git a/apc_lock.h b/apc_lock.h new file mode 100644 index 0000000..089e1cb --- /dev/null +++ b/apc_lock.h @@ -0,0 +1,105 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_lock.h,v 3.20 2007/01/29 07:39:02 shire Exp $ */ + +#ifndef APC_LOCK +#define APC_LOCK + +#include "apc_sem.h" +#include "apc_fcntl.h" +#include "apc_pthreadmutex.h" +#include "apc_futex.h" +#include "apc_spin.h" +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef TSRM_LOCKS +#define RDLOCK_AVAILABLE 0 +#define NONBLOCKING_LOCK_AVAILABLE 0 +/* quick & dirty: use TSRM mutex locks for now */ +#define apc_lck_create(a,b,c,d) d=(int)tsrm_mutex_alloc() +#define apc_lck_destroy(a) tsrm_mutex_free((MUTEX_T)a) +#define apc_lck_lock(a) tsrm_mutex_lock((MUTEX_T)a) +#define apc_lck_rdlock(a) tsrm_mutex_lock((MUTEX_T)a) +#define apc_lck_unlock(a) tsrm_mutex_unlock((MUTEX_T)a) +#elif defined(APC_SEM_LOCKS) +#define RDLOCK_AVAILABLE 0 +#define NONBLOCKING_LOCK_AVAILABLE 0 +#define apc_lck_t int +#define apc_lck_create(a,b,c,d) d=apc_sem_create(NULL,(b),(c)) +#define apc_lck_destroy(a) apc_sem_destroy(a) +#define apc_lck_lock(a) apc_sem_lock(a) +#define apc_lck_rdlock(a) apc_sem_lock(a) +#define apc_lck_unlock(a) apc_sem_unlock(a) +#elif defined(APC_PTHREADMUTEX_LOCKS) +#define RDLOCK_AVAILABLE 0 +#define NONBLOCKING_LOCK_AVAILABLE 1 +#define apc_lck_t pthread_mutex_t +#define apc_lck_create(a,b,c,d) apc_pthreadmutex_create((pthread_mutex_t*)&d) +#define apc_lck_destroy(a) apc_pthreadmutex_destroy(&a) +#define apc_lck_lock(a) apc_pthreadmutex_lock(&a) +#define apc_lck_nb_lock(a) apc_pthreadmutex_nonblocking_lock(&a) +#define apc_lck_rdlock(a) apc_pthreadmutex_lock(&a) +#define apc_lck_unlock(a) apc_pthreadmutex_unlock(&a) +#elif defined(APC_FUTEX_LOCKS) +#define NONBLOCKING_LOCK_AVAILABLE 1 +#define apc_lck_t int +#define apc_lck_create(a,b,c,d) d=apc_futex_create() +#define apc_lck_destroy(a) apc_futex_destroy(&a) +#define apc_lck_lock(a) apc_futex_lock(&a) +#define apc_lck_nb_lock(a) apc_futex_nonblocking_lock(&a) +#define apc_lck_rdlock(a) apc_futex_lock(&a) +#define apc_lck_unlock(a) apc_futex_unlock(&a) +#elif defined(APC_SPIN_LOCKS) +#define NONBLOCKING_LOCK_AVAILABLE APC_SLOCK_NONBLOCKING_LOCK_AVAILABLE +#define apc_lck_t slock_t +#define apc_lck_create(a,b,c,d) apc_slock_create((slock_t*)&(d)) +#define apc_lck_destroy(a) apc_slock_destroy(&a) +#define apc_lck_lock(a) apc_slock_lock(&a) +#define apc_lck_nb_lock(a) apc_slock_nonblocking_lock(&a) +#define apc_lck_rdlock(a) apc_slock_lock(&a) +#define apc_lck_unlock(a) apc_slock_unlock(&a) +#else +#define RDLOCK_AVAILABLE 1 +#ifdef PHP_WIN32 +#define NONBLOCKING_LOCK_AVAILABLE 0 +#else +#define NONBLOCKING_LOCK_AVAILABLE 1 +#endif +#define apc_lck_t int +#define apc_lck_create(a,b,c,d) d=apc_fcntl_create((a)) +#define apc_lck_destroy(a) apc_fcntl_destroy(a) +#define apc_lck_lock(a) apc_fcntl_lock(a) +#define apc_lck_nb_lock(a) apc_fcntl_nonblocking_lock(a) +#define apc_lck_rdlock(a) apc_fcntl_rdlock(a) +#define apc_lck_unlock(a) apc_fcntl_unlock(a) +#endif + +#endif diff --git a/apc_main.c b/apc_main.c index 50b673d..3cfb279 100644 --- a/apc_main.c +++ b/apc_main.c @@ -28,7 +28,7 @@ */ -/* $Id: apc_main.c,v 3.103 2007/11/14 19:46:46 shire Exp $ */ +/* $Id: apc_main.c,v 3.103.2.2 2008/03/28 18:35:40 gopalv Exp $ */ #include "apc_php.h" #include "apc_main.h" @@ -71,14 +71,20 @@ static zend_compile_t* set_compile_hook(zend_compile_t *ptr) /* {{{ install_function */ static int install_function(apc_function_t fn TSRMLS_DC) { - int status = - zend_hash_add(EG(function_table), + zend_function *func; + int status; + + func = apc_copy_function_for_execution(fn.function); + + status = zend_hash_add(EG(function_table), fn.name, fn.name_len+1, - apc_copy_function_for_execution(fn.function), + func, sizeof(fn.function[0]), NULL); + efree(func); + if (status == FAILURE) { /* apc_eprint("Cannot redeclare %s()", fn.name); */ } @@ -274,8 +280,6 @@ default_compile: /* cannot free up cache data yet, it maybe in use */ - zend_llist_del_element(&CG(open_files), h, compare_file_handles); /* XXX: kludge */ - h->type = ZEND_HANDLE_FILENAME; return NULL; @@ -298,9 +302,9 @@ static zend_op_array* my_compile_file(zend_file_handle* h, char *path; size_t mem_size; - if (!APCG(enabled) || (apc_cache_busy(apc_cache) && !APCG(localcache))) { - return old_compile_file(h, type TSRMLS_CC); - } + if (!APCG(enabled) || apc_cache_busy(apc_cache)) { + return old_compile_file(h, type TSRMLS_CC); + } /* check our regular expression filters */ if (APCG(filters) && apc_compiled_filters) { @@ -333,24 +337,20 @@ static zend_op_array* my_compile_file(zend_file_handle* h, if(!APCG(force_file_update)) { - if(APCG(localcache)) { - /* search for the file in the local cache */ - cache_entry = apc_local_cache_find(APCG(lcache), key, t); - } else { - /* search for the file in the cache */ - cache_entry = apc_cache_find(apc_cache, key, t); - } + /* search for the file in the cache */ + cache_entry = apc_cache_find(apc_cache, key, t); } else { cache_entry = NULL; } if (cache_entry != NULL) { int dummy = 1; + int reset_opened_path = 0; if (h->opened_path == NULL) { - h->opened_path = estrdup(cache_entry->data.file.filename); + h->opened_path = cache_entry->data.file.filename; + reset_opened_path = 1; } zend_hash_add(&EG(included_files), h->opened_path, strlen(h->opened_path)+1, (void *)&dummy, sizeof(int), NULL); - zend_llist_add_element(&CG(open_files), h); /* XXX kludge */ apc_stack_push(APCG(cache_stack), cache_entry); op_array = cached_compile(h, type TSRMLS_CC); if(op_array) { @@ -363,14 +363,12 @@ static zend_op_array* my_compile_file(zend_file_handle* h, if(APCG(report_autofilter)) { apc_wprint("Recompiling %s", h->opened_path); } + if (reset_opened_path == 1) { + h->opened_path = NULL; + } /* TODO: check what happens with EG(included_files) */ } - if(apc_cache_busy(apc_cache) && APCG(localcache)) { - /* possibly local cache returned NULL because cache is busy */ - return old_compile_file(h, type TSRMLS_CC); - } - /* remember how many functions and classes existed before compilation */ num_functions = zend_hash_num_elements(CG(function_table)); num_classes = zend_hash_num_elements(CG(class_table)); @@ -407,7 +405,7 @@ static zend_op_array* my_compile_file(zend_file_handle* h, tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */ } if(tmp_buf) { - fileinfo.st_buf = *tmp_buf; + fileinfo.st_buf.sb = *tmp_buf; } else { if (apc_search_paths(h->filename, PG(include_path), &fileinfo) != 0) { #ifdef __DEBUG_APC__ @@ -416,7 +414,7 @@ static zend_op_array* my_compile_file(zend_file_handle* h, return op_array; } } - key.mtime = fileinfo.st_buf.st_mtime; + key.mtime = fileinfo.st_buf.sb.st_mtime; } HANDLE_BLOCK_INTERRUPTIONS(); @@ -432,10 +430,10 @@ static zend_op_array* my_compile_file(zend_file_handle* h, mem_size = 0; APCG(mem_size_ptr) = &mem_size; + APCG(current_cache) = apc_cache; if(!(alloc_op_array = apc_copy_op_array(NULL, op_array, apc_sma_malloc, apc_sma_free TSRMLS_CC))) { - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); APCG(mem_size_ptr) = NULL; + APCG(current_cache) = NULL; #if NONBLOCKING_LOCK_AVAILABLE if(APCG(write_lock)) { apc_cache_write_unlock(apc_cache); @@ -447,9 +445,8 @@ static zend_op_array* my_compile_file(zend_file_handle* h, if(!(alloc_functions = apc_copy_new_functions(num_functions, apc_sma_malloc, apc_sma_free TSRMLS_CC))) { apc_free_op_array(alloc_op_array, apc_sma_free); - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); APCG(mem_size_ptr) = NULL; + APCG(current_cache) = NULL; #if NONBLOCKING_LOCK_AVAILABLE if(APCG(write_lock)) { apc_cache_write_unlock(apc_cache); @@ -461,9 +458,8 @@ static zend_op_array* my_compile_file(zend_file_handle* h, if(!(alloc_classes = apc_copy_new_classes(op_array, num_classes, apc_sma_malloc, apc_sma_free TSRMLS_CC))) { apc_free_op_array(alloc_op_array, apc_sma_free); apc_free_functions(alloc_functions, apc_sma_free); - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); APCG(mem_size_ptr) = NULL; + APCG(current_cache) = NULL; #if NONBLOCKING_LOCK_AVAILABLE if(APCG(write_lock)) { apc_cache_write_unlock(apc_cache); @@ -484,9 +480,8 @@ static zend_op_array* my_compile_file(zend_file_handle* h, apc_free_op_array(alloc_op_array, apc_sma_free); apc_free_functions(alloc_functions, apc_sma_free); apc_free_classes(alloc_classes, apc_sma_free); - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); APCG(mem_size_ptr) = NULL; + APCG(current_cache) = NULL; #if NONBLOCKING_LOCK_AVAILABLE if(APCG(write_lock)) { apc_cache_write_unlock(apc_cache); @@ -500,12 +495,10 @@ static zend_op_array* my_compile_file(zend_file_handle* h, if ((ret = apc_cache_insert(apc_cache, key, cache_entry, t)) != 1) { apc_cache_free_entry(cache_entry); - if(ret==-1) { - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); - } } + APCG(current_cache) = NULL; + #if NONBLOCKING_LOCK_AVAILABLE if(APCG(write_lock)) { apc_cache_write_unlock(apc_cache); @@ -592,56 +585,17 @@ int apc_module_shutdown(TSRMLS_D) /* {{{ process init and shutdown */ int apc_process_init(int module_number TSRMLS_DC) { - int minttl = (APCG(gc_ttl) > APCG(ttl) ? APCG(ttl) : APCG(gc_ttl))/2; - int size = APCG(localcache_size); - if(APCG(initialized) && APCG(localcache)) { - /* TTL is 2 mins by default */ - APCG(lcache) = apc_local_cache_create(apc_cache, size, minttl ? minttl : 120); - } return 0; } int apc_process_shutdown(TSRMLS_D) { - if(APCG(initialized) && APCG(localcache) && APCG(lcache)) { - apc_local_cache_destroy(APCG(lcache)); - APCG(lcache) = NULL; - } - return 0; -} -/* }}} */ - -/* {{{ request init and shutdown */ - -int apc_request_init(TSRMLS_D) -{ - apc_stack_clear(APCG(cache_stack)); - APCG(slam_rand) = -1; - APCG(copied_zvals) = NULL; - -#ifdef APC_FILEHITS - ALLOC_INIT_ZVAL(APCG(filehits)); - array_init(APCG(filehits)); -#endif - return 0; } - -int apc_request_shutdown(TSRMLS_D) -{ - apc_deactivate(TSRMLS_C); - -#ifdef APC_FILEHITS - zval_ptr_dtor(&APCG(filehits)); -#endif - - return 0; -} - /* }}} */ /* {{{ apc_deactivate */ -void apc_deactivate(TSRMLS_D) +static void apc_deactivate(TSRMLS_D) { /* The execution stack was unwound, which prevented us from decrementing * the reference counts on active cache entries in `my_execute`. @@ -685,12 +639,40 @@ void apc_deactivate(TSRMLS_D) } apc_cache_release(apc_cache, cache_entry); } - if(APCG(localcache)) { - apc_local_cache_cleanup(APCG(lcache)); - } + } /* }}} */ +/* {{{ request init and shutdown */ + +int apc_request_init(TSRMLS_D) +{ + apc_stack_clear(APCG(cache_stack)); + APCG(slam_rand) = -1; + APCG(copied_zvals) = NULL; + +#ifdef APC_FILEHITS + ALLOC_INIT_ZVAL(APCG(filehits)); + array_init(APCG(filehits)); +#endif + + return 0; +} + +int apc_request_shutdown(TSRMLS_D) +{ + apc_deactivate(TSRMLS_C); + +#ifdef APC_FILEHITS + zval_ptr_dtor(&APCG(filehits)); +#endif + + return 0; +} + +/* }}} */ + + /* * Local variables: * tab-width: 4 diff --git a/apc_main.h b/apc_main.h new file mode 100644 index 0000000..2a32e44 --- /dev/null +++ b/apc_main.h @@ -0,0 +1,57 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_main.h,v 3.9.2.1 2008/03/25 18:04:53 gopalv Exp $ */ + +#ifndef APC_MAIN_H +#define APC_MAIN_H + +/* + * This module provides the primary interface between PHP and APC. + */ + +extern int apc_module_init(int module_number TSRMLS_DC); +extern int apc_module_shutdown(TSRMLS_D); +extern int apc_process_init(int module_number TSRMLS_DC); +extern int apc_process_shutdown(TSRMLS_D); +extern int apc_request_init(TSRMLS_D); +extern int apc_request_shutdown(TSRMLS_D); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_mmap.c b/apc_mmap.c index 8b9cccb..74d793f 100644 --- a/apc_mmap.c +++ b/apc_mmap.c @@ -25,7 +25,7 @@ */ -/* $Id: apc_mmap.c,v 3.7 2007/12/20 23:00:51 shire Exp $ */ +/* $Id: apc_mmap.c,v 3.7.2.1 2008/03/25 21:00:22 rasmus Exp $ */ #include "apc.h" @@ -114,7 +114,7 @@ void *apc_mmap(char *file_mask, size_t size) unlink(file_mask); } } - if((int)shmaddr == -1) { + if((long)shmaddr == -1) { apc_eprint("apc_mmap: mmap failed:"); } return shmaddr; diff --git a/apc_php.h b/apc_php.h new file mode 100644 index 0000000..fa1e4d4 --- /dev/null +++ b/apc_php.h @@ -0,0 +1,71 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | George Schlossnagle | + | Rasmus Lerdorf | + | Arun C. Murthy | + | Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_php.h,v 3.10 2006/11/16 20:24:48 gopalv Exp $ */ + +#ifndef APC_PHP_H +#define APC_PHP_H + +/* + * The purpose of this header file is to include all PHP and Zend headers that + * are typically needed elsewhere in APC. This makes it easy to insure that + * all required headers are available. + */ + +#include "php.h" +#include "zend.h" +#include "zend_API.h" +#include "zend_compile.h" +#include "zend_hash.h" +#include "zend_extensions.h" + +#if ZEND_MODULE_API_NO > 20050922 +#define ZEND_ENGINE_2_2 +#endif +#if ZEND_MODULE_API_NO > 20050921 +#define ZEND_ENGINE_2_1 +#endif +#ifdef ZEND_ENGINE_2_1 +#include "zend_vm.h" +#endif + +#include "rfc1867.h" + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_pool.c b/apc_pool.c new file mode 100644 index 0000000..1d12258 --- /dev/null +++ b/apc_pool.c @@ -0,0 +1,344 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2008 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Yahoo! Inc. in 2008. + + Future revisions and derivatives of this source code must acknowledge + Yahoo! Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_pool.c,v 3.3 2008/01/09 12:30:39 gopalv Exp $ */ + + +#include "apc_pool.h" +#include + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +/* {{{ typedefs */ +typedef struct _pool_block +{ + size_t avail; + size_t capacity; + unsigned char *mark; + struct _pool_block *next; + unsigned :0; /* this should align to word */ + unsigned char data[0]; +}pool_block; + +/* + parts in ? are optional and turned on for fun, memory loss, + and for something else that I forgot about ... ah, debugging + + |--------> data[0] |<-- non word boundary (too) + +-------------+--------------+-----------+-------------+-------------->>> + | pool_block | ?sizeinfo<1> | block<1> | ?redzone<1> | ?sizeinfo<2> + | | (size_t) | | padded left | + +-------------+--------------+-----------+-------------+-------------->>> + */ + +struct _apc_pool +{ + apc_malloc_t allocate; + apc_free_t deallocate; + + size_t dsize; + void *owner; + + struct + { + unsigned int redzones:1; + unsigned int sizeinfo:1; + } options; + + pool_block *head; +}; +/* }}} */ + +/* {{{ redzone code */ +static const unsigned char decaff[] = { + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad, + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad, + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad, + 0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad +}; + +/* a redzone is at least 4 (0xde,0xca,0xc0,0xff) bytes */ +#define REDZONE_SIZE(size) \ + ((ALIGNWORD((size)) > ((size) + 4)) ? \ + (ALIGNWORD((size)) - (size)) : /* does not change realsize */\ + ALIGNWORD((size)) - (size) + ALIGNWORD((sizeof(char)))) /* adds 1 word to realsize */ + +#define SIZEINFO_SIZE ALIGNWORD(sizeof(size_t)) + +#define MARK_REDZONE(block, redsize) do {\ + memcpy(block, decaff, redsize );\ + } while(0) + +#define CHECK_REDZONE(block, redsize) (memcmp(block, decaff, redsize) == 0) + +/* }}} */ + +#define APC_POOL_OPTION(pool, option) ((pool)->options.option) + +/* {{{ create_pool_block */ +static pool_block* create_pool_block(apc_pool *pool, size_t size) +{ + size_t realsize = sizeof(pool_block) + ALIGNWORD(size); + + pool_block* entry = pool->allocate(realsize); + + entry->avail = entry->capacity = size; + + entry->mark = entry->data; + + entry->next = pool->head; + + pool->head = entry; + + return entry; +} +/* }}} */ + +/* {{{ apc_pool_create */ +apc_pool* apc_pool_create(apc_pool_type pool_type, + apc_malloc_t allocate, + apc_free_t deallocate) +{ + apc_pool* pool = NULL; + size_t dsize = 0; + + /* sanity checks */ + assert(sizeof(decaff) > REDZONE_SIZE(ALIGNWORD(sizeof(char)))); + assert(sizeof(pool_block) == ALIGNWORD(sizeof(pool_block))); + + assert(APC_POOL_SIZE_MASK & (APC_POOL_SIZEINFO | APC_POOL_REDZONES) == 0); + + switch(pool_type & APC_POOL_SIZE_MASK) { + case APC_SMALL_POOL: + dsize = 512; + break; + + case APC_LARGE_POOL: + dsize = 8192; + break; + + case APC_MEDIUM_POOL: + dsize = 4096; + break; + + default: + return NULL; + } + + pool = (apc_pool*)allocate(sizeof(apc_pool)); + + if(!pool) { + return NULL; + } + + pool->allocate = allocate; + pool->deallocate = deallocate; + pool->dsize = dsize; + pool->head = NULL; + + APC_POOL_OPTION(pool, redzones) = (pool_type & APC_POOL_REDZONES) != 0; + APC_POOL_OPTION(pool, sizeinfo) = (pool_type & APC_POOL_SIZEINFO) != 0; + + if(!create_pool_block(pool, dsize)) { + deallocate(pool); + return NULL; + } + + return pool; +} +/* }}} */ + +/* {{{ apc_pool_destroy */ +void apc_pool_destroy(apc_pool *pool) +{ + + apc_free_t deallocate = pool->deallocate; + pool_block *entry; + pool_block *tmp; + + entry = pool->head; + + while(entry != NULL) { + tmp = entry->next; + deallocate(entry); + entry = tmp; + } + + deallocate(pool); +} +/* }}} */ + +/* {{{ apc_pool_alloc */ +void* apc_pool_alloc(apc_pool *pool, size_t size) +{ + unsigned char *p = NULL; + size_t realsize = ALIGNWORD(size); + size_t poolsize; + unsigned char *redzone = NULL; + size_t redsize = 0; + size_t *sizeinfo= NULL; + + pool_block *entry; + + + if(APC_POOL_OPTION(pool, redzones)) { + redsize = REDZONE_SIZE(size); /* redsize might be re-using word size padding */ + realsize = size + redsize; /* recalculating realsize */ + } else { + redsize = realsize - size; /* use padding space */ + } + + if(APC_POOL_OPTION(pool, sizeinfo)) { + realsize += ALIGNWORD(sizeof(size_t)); + } + + + for(entry = pool->head; entry != NULL; entry = entry->next) { + if(entry->avail >= realsize) { + goto found; + } + } + + poolsize = ALIGNSIZE(realsize, pool->dsize); + + entry = create_pool_block(pool, poolsize); + + if(!entry) { + return NULL; + } + +found: + p = entry->mark; + + if(APC_POOL_OPTION(pool, sizeinfo)) { + sizeinfo = (size_t*)p; + p += SIZEINFO_SIZE; + *sizeinfo = size; + } + + redzone = p + size; + + if(APC_POOL_OPTION(pool, redzones)) { + MARK_REDZONE(redzone, redsize); + } + +#ifdef VALGRIND_MAKE_MEM_NOACCESS + if(redsize != 0) { + VALGRIND_MAKE_MEM_NOACCESS(redzone, redsize); + } +#endif + + entry->avail -= realsize; + entry->mark += realsize; + +#ifdef VALGRIND_MAKE_MEM_UNDEFINED + /* need to write before reading data off this */ + VALGRIND_MAKE_MEM_UNDEFINED(p, size); +#endif + + return (void*)p; +} +/* }}} */ + +/* {{{ apc_pool_free */ +/* + * free does not do anything other than + * check for redzone values when free'ing + * data areas. + */ +void apc_pool_free(apc_pool *pool, void *p) +{ + if(!APC_POOL_OPTION(pool, sizeinfo) || + !APC_POOL_OPTION(pool, redzones)) { + } +} +/* }}} */ + +/* {{{ apc_pool_check_integrity */ +/* + * Checking integrity at runtime, does an + * overwrite check only when the sizeinfo + * is set. + */ +int apc_pool_check_integrity(apc_pool *pool) +{ + pool_block *entry; + size_t *sizeinfo = NULL; + unsigned char *start; + size_t realsize; + unsigned char *redzone; + size_t redsize; + + for(entry = pool->head; entry != NULL; entry = entry->next) { + start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block)); + if((entry->mark - start) != (entry->capacity - entry->avail)) { + return 0; + } + } + + if(!APC_POOL_OPTION(pool, sizeinfo) || + !APC_POOL_OPTION(pool, redzones)) { + return 1; + } + + for(entry = pool->head; entry != NULL; entry = entry->next) { + start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block)); + + while(start < entry->mark) { + sizeinfo = (size_t*)start; + /* redzone starts where real data ends, in a non-word boundary + * redsize is at least 4 bytes + whatever's needed to make it + * to another word boundary. + */ + redzone = start + SIZEINFO_SIZE + (*sizeinfo); + redsize = REDZONE_SIZE(*sizeinfo); + if(!CHECK_REDZONE(redzone, redsize)) + { + /* + fprintf(stderr, "Redzone check failed for %p\n", + start + ALIGNWORD(sizeof(size_t)));*/ + return 0; + } + realsize = SIZEINFO_SIZE + *sizeinfo + redsize; + start += realsize; + } + } + + return 1; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_pool.h b/apc_pool.h new file mode 100644 index 0000000..deec2c8 --- /dev/null +++ b/apc_pool.h @@ -0,0 +1,58 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2008 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gopal Vijayaraghavan | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Yahoo! Inc. in 2008. + + Future revisions and derivatives of this source code must acknowledge + Yahoo! Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_pool.h,v 3.3 2008/01/09 12:30:39 gopalv Exp $ */ + +#ifndef APC_POOL_H +#define APC_POOL_H + +#include "apc.h" +#include "apc_sma.h" + +typedef enum { + APC_SMALL_POOL = 0x1, + APC_MEDIUM_POOL = 0x2, + APC_LARGE_POOL = 0x3, + APC_POOL_SIZE_MASK = 0x7, /* waste a bit */ + APC_POOL_REDZONES = 0x08, + APC_POOL_SIZEINFO = 0x10, + APC_POOL_OPT_MASK = 0x18 +} apc_pool_type; + +typedef struct _apc_pool apc_pool; + +extern apc_pool* apc_pool_create(apc_pool_type pool_type, + apc_malloc_t allocate, + apc_free_t deallocate); + + +extern void apc_pool_destroy(apc_pool *pool); +extern void* apc_pool_alloc(apc_pool *pool, size_t size); +extern void apc_pool_free(apc_pool *pool, void *ptr); +extern int apc_pool_check_integrity(apc_pool *pool); + +#endif diff --git a/apc_pthreadmutex.h b/apc_pthreadmutex.h new file mode 100644 index 0000000..286d303 --- /dev/null +++ b/apc_pthreadmutex.h @@ -0,0 +1,48 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_pthreadmutex.h,v 3.3 2007/01/28 07:53:57 shire Exp $ */ + +#ifndef APC_PTHREADMUTEX_H +#define APC_PTHREADMUTEX_H + +#include "apc.h" + +#ifdef APC_PTHREADMUTEX_LOCKS + +#include + +pthread_mutex_t *apc_pthreadmutex_create(); +void apc_pthreadmutex_destroy(pthread_mutex_t *lock); +void apc_pthreadmutex_lock(pthread_mutex_t *lock); +void apc_pthreadmutex_unlock(pthread_mutex_t *lock); +zend_bool apc_pthreadmutex_nonblocking_lock(pthread_mutex_t *lock); + +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_rfc1867.c b/apc_rfc1867.c new file mode 100644 index 0000000..af01870 --- /dev/null +++ b/apc_rfc1867.c @@ -0,0 +1,195 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_rfc1867.c,v 3.12 2007/10/18 20:37:20 rasmus Exp $*/ + +#include "apc.h" +#include "apc_globals.h" +#include "rfc1867.h" + +#ifdef PHP_WIN32 +#include "win32/time.h" +#endif + +#ifdef MULTIPART_EVENT_FORMDATA +extern int _apc_store(char *strkey, int strkey_len, const zval *val, const unsigned int ttl, const int exclusive TSRMLS_DC); + +static double my_time() { + struct timeval a; + double t; + gettimeofday(&a, NULL); + t = a.tv_sec + (a.tv_usec/1000000.00); + return t; +} + +void apc_rfc1867_progress(unsigned int event, void *event_data, void **extra TSRMLS_DC) { + static char tracking_key[64]; + static int key_length = 0; + static size_t content_length = 0; + static char filename[128]; + static char name[64]; + static char *temp_filename=NULL; + static int cancel_upload = 0; + static double start_time; + static size_t bytes_processed = 0; + static size_t prev_bytes_processed = 0; + static int update_freq = 0; + static double rate; + zval *track = NULL; + + switch (event) { + case MULTIPART_EVENT_START: + { + multipart_event_start *data = (multipart_event_start *) event_data; + content_length = data->content_length; + *tracking_key = '\0'; + *name = '\0'; + cancel_upload = 0; + temp_filename = NULL; + *filename= '\0'; + key_length = 0; + start_time = my_time(); + bytes_processed = 0; + rate = 0; + update_freq = APCG(rfc1867_freq); + if(update_freq < 0) { // frequency is a percentage, not bytes + update_freq = content_length * APCG(rfc1867_freq) / 100; + } + } + break; + + case MULTIPART_EVENT_FORMDATA: + { + int prefix_len = strlen(APCG(rfc1867_prefix)); + multipart_event_formdata *data = (multipart_event_formdata *) event_data; + if(data->name && !strncasecmp(data->name, APCG(rfc1867_name), strlen(APCG(rfc1867_name))) && data->value && data->length && data->length < sizeof(tracking_key) - prefix_len) { + strlcat(tracking_key, APCG(rfc1867_prefix), 63); + strlcat(tracking_key, *data->value, 63); + key_length = data->length + prefix_len; + bytes_processed = data->post_bytes_processed; + } + } + break; + + case MULTIPART_EVENT_FILE_START: + if(*tracking_key) { + multipart_event_file_start *data = (multipart_event_file_start *) event_data; + + bytes_processed = data->post_bytes_processed; + strncpy(filename,*data->filename,127); + temp_filename = NULL; + strncpy(name,data->name,63); + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", content_length); + add_assoc_long(track, "current", bytes_processed); + add_assoc_string(track, "filename", filename, 1); + add_assoc_string(track, "name", name, 1); + add_assoc_long(track, "done", 0); + add_assoc_double(track, "start_time", start_time); + _apc_store(tracking_key, key_length, track, 3600, 0 TSRMLS_CC); + zval_ptr_dtor(&track); + } + break; + + case MULTIPART_EVENT_FILE_DATA: + if(*tracking_key) { + multipart_event_file_data *data = (multipart_event_file_data *) event_data; + bytes_processed = data->post_bytes_processed; + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", content_length); + add_assoc_long(track, "current", bytes_processed); + add_assoc_string(track, "filename", filename, 1); + add_assoc_string(track, "name", name, 1); + add_assoc_long(track, "done", 0); + add_assoc_double(track, "start_time", start_time); + if(bytes_processed - prev_bytes_processed > update_freq) { + _apc_store(tracking_key, key_length, track, 3600, 0 TSRMLS_CC); + prev_bytes_processed = bytes_processed; + } + zval_ptr_dtor(&track); + } + break; + + case MULTIPART_EVENT_FILE_END: + if(*tracking_key) { + multipart_event_file_end *data = (multipart_event_file_end *) event_data; + bytes_processed = data->post_bytes_processed; + cancel_upload = data->cancel_upload; + temp_filename = data->temp_filename; + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", content_length); + add_assoc_long(track, "current", bytes_processed); + add_assoc_string(track, "filename", filename, 1); + add_assoc_string(track, "name", name, 1); + add_assoc_string(track, "temp_filename", temp_filename, 1); + add_assoc_long(track, "cancel_upload", cancel_upload); + add_assoc_long(track, "done", 0); + add_assoc_double(track, "start_time", start_time); + _apc_store(tracking_key, key_length, track, 3600, 0 TSRMLS_CC); + zval_ptr_dtor(&track); + } + break; + + case MULTIPART_EVENT_END: + if(*tracking_key) { + double now = my_time(); + multipart_event_end *data = (multipart_event_end *) event_data; + bytes_processed = data->post_bytes_processed; + if(now>start_time) rate = 8.0*bytes_processed/(now-start_time); + else rate = 8.0*bytes_processed; /* Too quick */ + ALLOC_INIT_ZVAL(track); + array_init(track); + add_assoc_long(track, "total", content_length); + add_assoc_long(track, "current", bytes_processed); + add_assoc_double(track, "rate", rate); + add_assoc_string(track, "filename", filename, 1); + add_assoc_string(track, "name", name, 1); + if(temp_filename) { + add_assoc_string(track, "temp_filename", temp_filename, 1); + } + add_assoc_long(track, "cancel_upload", cancel_upload); + add_assoc_long(track, "done", 1); + add_assoc_double(track, "start_time", start_time); + _apc_store(tracking_key, key_length, track, 3600, 0 TSRMLS_CC); + zval_ptr_dtor(&track); + } + break; + } +} + +#endif +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_sem.c b/apc_sem.c new file mode 100644 index 0000000..5dada09 --- /dev/null +++ b/apc_sem.c @@ -0,0 +1,177 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_sem.c,v 3.16 2006/03/12 00:31:45 rasmus Exp $ */ + +#include "apc_sem.h" +#include "apc.h" +#include "php.h" +#include +#include +#include +#include +#include + +#if HAVE_SEMUN +/* we have semun, no need to define */ +#else +#undef HAVE_SEMUN +union semun { + int val; /* value for SETVAL */ + struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* array for GETALL, SETALL */ + /* Linux specific part: */ + struct seminfo *__buf; /* buffer for IPC_INFO */ +}; +#define HAVE_SEMUN 1 +#endif + +#ifndef SEM_R +# define SEM_R 0444 +#endif +#ifndef SEM_A +# define SEM_A 0222 +#endif + +/* always use SEM_UNDO, otherwise we risk deadlock */ +#define USE_SEM_UNDO + +#ifdef USE_SEM_UNDO +# define UNDO SEM_UNDO +#else +# define UNDO 0 +#endif + +int apc_sem_create(const char* pathname, int proj, int initval) +{ + int semid; + int perms; + union semun arg; + key_t key; + + perms = 0777; + + key = IPC_PRIVATE; + if (pathname != NULL) { + if ((key = ftok(pathname, proj)) < 0) { + apc_eprint("apc_sem_create: ftok(%s,%d) failed:", pathname, proj); + } + } + + if ((semid = semget(key, 1, IPC_CREAT | IPC_EXCL | perms)) >= 0) { + /* sempahore created for the first time, initialize now */ + arg.val = initval; + if (semctl(semid, 0, SETVAL, arg) < 0) { + apc_eprint("apc_sem_create: semctl(%d,...) failed:", semid); + } + } + else if (errno == EEXIST) { + /* sempahore already exists, don't initialize */ + if ((semid = semget(key, 1, perms)) < 0) { + apc_eprint("apc_sem_create: semget(%u,...) failed:", key); + } + /* insert here */ + } + else { + apc_eprint("apc_sem_create: semget(%u,...) failed:", key); + } + + return semid; +} + +void apc_sem_destroy(int semid) +{ + /* we expect this call to fail often, so we do not check */ + union semun arg; + semctl(semid, 0, IPC_RMID, arg); +} + +void apc_sem_lock(int semid) +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = -1; + op.sem_flg = UNDO; + + if (semop(semid, &op, 1) < 0) { + if (errno != EINTR) { + apc_eprint("apc_sem_lock: semop(%d) failed:", semid); + } + } +} + +void apc_sem_unlock(int semid) +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = 1; + op.sem_flg = UNDO; + + if (semop(semid, &op, 1) < 0) { + if (errno != EINTR) { + apc_eprint("apc_sem_unlock: semop(%d) failed:", semid); + } + } +} + +void apc_sem_wait_for_zero(int semid) +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = 0; + op.sem_flg = UNDO; + + if (semop(semid, &op, 1) < 0) { + if (errno != EINTR) { + apc_eprint("apc_sem_waitforzero: semop(%d) failed:", semid); + } + } +} + +int apc_sem_get_value(int semid) +{ + union semun arg; + unsigned short val[1]; + + arg.array = val; + if (semctl(semid, 0, GETALL, arg) < 0) { + apc_eprint("apc_sem_getvalue: semctl(%d,...) failed:", semid); + } + return val[0]; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_sem.h b/apc_sem.h new file mode 100644 index 0000000..c5dcb03 --- /dev/null +++ b/apc_sem.h @@ -0,0 +1,51 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_sem.h,v 3.6 2006/03/12 00:31:45 rasmus Exp $ */ + +#ifndef APC_SEM_H +#define APC_SEM_H + +/* Wrapper functions for SysV sempahores */ + +extern int apc_sem_create(const char* pathname, int proj, int initval); +extern void apc_sem_destroy(int semid); +extern void apc_sem_lock(int semid); +extern void apc_sem_unlock(int semid); +extern void apc_sem_wait_for_zero(int semid); +extern int apc_sem_get_value(int semid); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_shm.c b/apc_shm.c new file mode 100644 index 0000000..c94eb1e --- /dev/null +++ b/apc_shm.c @@ -0,0 +1,110 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_shm.c,v 3.11.2.1 2008/03/25 21:00:22 rasmus Exp $ */ + +#include "apc_shm.h" +#include "apc.h" +#ifdef PHP_WIN32 +/* shm functions are available in TSRM */ +#include +#define key_t long +#else +#include +#include +#include +#endif + +#ifndef SHM_R +# define SHM_R 0444 /* read permission */ +#endif +#ifndef SHM_A +# define SHM_A 0222 /* write permission */ +#endif + +int apc_shm_create(const char* pathname, int proj, size_t size) +{ + int shmid; /* shared memory id */ + int oflag; /* permissions on shm */ + key_t key; /* shm key returned by ftok */ + + key = IPC_PRIVATE; +#ifndef PHP_WIN32 + /* no ftok yet for win32 */ + if (pathname != NULL) { + if ((key = ftok(pathname, proj)) < 0) { + apc_eprint("apc_shm_create: ftok failed:"); + } + } +#endif + + oflag = IPC_CREAT | SHM_R | SHM_A; + if ((shmid = shmget(key, size, oflag)) < 0) { + apc_eprint("apc_shm_create: shmget(%d, %d, %d) failed: %s. It is possible that the chosen SHM segment size is higher than the operation system allows. Linux has usually a default limit of 32MB per segment.", key, size, oflag, strerror(errno)); + } + + return shmid; +} + +void apc_shm_destroy(int shmid) +{ + /* we expect this call to fail often, so we do not check */ + shmctl(shmid, IPC_RMID, 0); +} + +void* apc_shm_attach(int shmid) +{ + void* shmaddr; /* the shared memory address */ + + if ((long)(shmaddr = shmat(shmid, 0, 0)) == -1) { + apc_eprint("apc_shm_attach: shmat failed:"); + } + + /* + * We set the shmid for removal immediately after attaching to it. The + * segment won't disappear until all processes have detached from it. + */ + apc_shm_destroy(shmid); + return shmaddr; +} + +void apc_shm_detach(void* shmaddr) +{ + if (shmdt(shmaddr) < 0) { + apc_eprint("apc_shm_detach: shmdt failed:"); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_shm.h b/apc_shm.h new file mode 100644 index 0000000..81f8053 --- /dev/null +++ b/apc_shm.h @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_shm.h,v 3.8 2007/05/23 01:23:21 auroraeosrose Exp $ */ + +#ifndef APC_SHM_H +#define APC_SHM_H + +#include +#ifdef PHP_WIN32 +#include +#endif + +/* Wrapper functions for unix shared memory */ + +extern int apc_shm_create(const char* name, int proj, size_t size); +extern void apc_shm_destroy(int shmid); +extern void* apc_shm_attach(int shmid); +extern void apc_shm_detach(void* shmaddr); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_signal.c b/apc_signal.c index 406fc47..32627a3 100644 --- a/apc_signal.c +++ b/apc_signal.c @@ -24,7 +24,7 @@ All other licensing and usage conditions are those of the PHP Group. */ - /* $Id: apc_signal.c,v 1.2 2007/12/26 22:38:43 rasmus Exp $ */ + /* $Id: apc_signal.c,v 1.2.2.2 2008/03/25 18:24:57 gopalv Exp $ */ /* Allows apc to install signal handlers and maintain signalling to already registered handlers. Registers all signals that @@ -88,7 +88,7 @@ static void apc_rehandle_signal(int signo, siginfo_t *siginfo, void *context) static int apc_register_signal(int signo, void (*handler)(int, siginfo_t*, void*)) { #if HAVE_SIGACTION - struct sigaction sa = {0}; + struct sigaction sa = {{0}}; apc_signal_entry_t p_sig = {0}; if (sigaction(signo, NULL, &sa) == 0) { @@ -168,6 +168,20 @@ void apc_set_signals(TSRMLS_D) } } /* }}} */ +/* {{{ apc_set_signals + * cleanup signals for shutdown */ +void apc_shutdown_signals() +{ + int i=0; + if (apc_signal_info.installed > 0) { + for (i=0; (i < apc_signal_info.installed); i++) { + apc_efree(apc_signal_info.prev[i]); + } + apc_efree(apc_signal_info.prev); + apc_signal_info.installed = 0; /* just in case */ + } +} + /* * Local variables: * tab-width: 4 diff --git a/apc_signal.h b/apc_signal.h index 6503cdd..7494702 100644 --- a/apc_signal.h +++ b/apc_signal.h @@ -17,7 +17,7 @@ */ -/* $Id: apc_signal.h,v 1.1 2007/12/26 22:36:06 rasmus Exp $ */ +/* $Id: apc_signal.h,v 1.1.2.1 2008/03/25 18:24:57 gopalv Exp $ */ #ifndef APC_SIGNAL_H #define APC_SIGNAL_H @@ -37,6 +37,7 @@ typedef struct apc_signal_info_t { } apc_signal_info_t; void apc_set_signals(TSRMLS_D); +void apc_shutdown_signals(); #endif diff --git a/apc_sma.c b/apc_sma.c index 20b8e45..0ba8be8 100644 --- a/apc_sma.c +++ b/apc_sma.c @@ -26,19 +26,24 @@ */ -/* $Id: apc_sma.c,v 1.69 2007/12/26 21:35:39 gopalv Exp $ */ +/* $Id: apc_sma.c,v 1.69.2.2 2008/03/28 20:17:47 rasmus Exp $ */ #include "apc_sma.h" #include "apc.h" #include "apc_globals.h" #include "apc_lock.h" #include "apc_shm.h" +#include "apc_cache.h" #include #if APC_MMAP void *apc_mmap(char *file_mask, size_t size); void apc_unmap(void* shmaddr, size_t size); #endif +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + /* {{{ locking macros */ #define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c); } #define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c); } @@ -110,19 +115,12 @@ struct block_t { #endif -#ifdef max -#undef max -#endif -#define max(a, b) ((a) > (b) ? (a) : (b)) - -/* {{{ ALIGNWORD: pad up x, aligned to the system's word boundary */ -typedef union { void* p; int i; long l; double d; void (*f)(); } apc_word_t; -#define ALIGNWORD(x) (sizeof(apc_word_t) * (1 + (((x)-1)/sizeof(apc_word_t)))) +/* {{{ MINBLOCKSIZE */ #define MINBLOCKSIZE (ALIGNWORD(1) + ALIGNWORD(sizeof(block_t))) /* }}} */ /* {{{ sma_allocate: tries to allocate size bytes in a segment */ -static int sma_allocate(void* shmaddr, size_t size) +static size_t sma_allocate(void* shmaddr, size_t size) { header_t* header; /* header of shared memory segment */ block_t* prv; /* block prior to working block */ @@ -239,7 +237,7 @@ static int sma_allocate(void* shmaddr, size_t size) /* }}} */ /* {{{ sma_deallocate: deallocates the block at the given offset */ -static int sma_deallocate(void* shmaddr, int offset) +static size_t sma_deallocate(void* shmaddr, size_t offset) { header_t* header; /* header of shared memory segment */ block_t* cur; /* the new block to insert */ @@ -409,8 +407,9 @@ void apc_sma_cleanup() /* {{{ apc_sma_malloc */ void* apc_sma_malloc(size_t n) { - int off; + size_t off; int i; + size_t *orig_mem_size_ptr; TSRMLS_FETCH(); assert(sma_initialized); @@ -421,6 +420,9 @@ void* apc_sma_malloc(size_t n) void* p = (void *)(((char *)(sma_shmaddrs[sma_lastseg])) + off); if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; } UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); +#ifdef VALGRIND_MALLOCLIKE_BLOCK + VALGRIND_MALLOCLIKE_BLOCK(p, n, 0, 0); +#endif return p; } UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); @@ -436,6 +438,9 @@ void* apc_sma_malloc(size_t n) if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; } UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); sma_lastseg = i; +#ifdef VALGRIND_MALLOCLIKE_BLOCK + VALGRIND_MALLOCLIKE_BLOCK(p, n, 0, 0); +#endif return p; } UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); @@ -490,6 +495,9 @@ void apc_sma_free(void* p) d_size = sma_deallocate(sma_shmaddrs[i], offset); if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) -= d_size; } UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); +#ifdef VALGRIND_FREELIKE_BLOCK + VALGRIND_FREELIKE_BLOCK(p, 0); +#endif return; } UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); diff --git a/apc_sma.h b/apc_sma.h new file mode 100644 index 0000000..e0ba5f4 --- /dev/null +++ b/apc_sma.h @@ -0,0 +1,88 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_sma.h,v 1.18.2.1 2008/03/25 18:04:53 gopalv Exp $ */ + +#ifndef APC_SMA_H +#define APC_SMA_H + +#define ALLOC_DISTRIBUTION 0 + +#include "apc.h" + +/* Simple shared memory allocator */ + +extern void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask); +extern void apc_sma_cleanup(); +extern void* apc_sma_malloc(size_t size); +extern void* apc_sma_realloc(void* p, size_t size); +extern char* apc_sma_strdup(const char *s); +extern void apc_sma_free(void* p); +#if ALLOC_DISTRIBUTION +extern size_t *apc_sma_get_alloc_distribution(); +#endif + +/* {{{ struct definition: apc_sma_link_t */ +typedef struct apc_sma_link_t apc_sma_link_t; +struct apc_sma_link_t { + long size; /* size of this free block */ + long offset; /* offset in segment of this block */ + apc_sma_link_t* next; /* link to next free block */ +}; +/* }}} */ + +/* {{{ struct definition: apc_sma_info_t */ +typedef struct apc_sma_info_t apc_sma_info_t; +struct apc_sma_info_t { + int num_seg; /* number of shared memory segments */ + long seg_size; /* size of each shared memory segment */ + apc_sma_link_t** list; /* there is one list per segment */ +}; +/* }}} */ + +extern apc_sma_info_t* apc_sma_info(zend_bool limited); +extern void apc_sma_free_info(apc_sma_info_t* info); + +extern size_t apc_sma_get_avail_mem(); +extern void apc_sma_check_integrity(); + +/* {{{ ALIGNWORD: pad up x, aligned to the system's word boundary */ +typedef union { void* p; int i; long l; double d; void (*f)(); } apc_word_t; +#define ALIGNSIZE(x, size) ((size) * (1 + (((x)-1)/(size)))) +#define ALIGNWORD(x) ALIGNSIZE(x, sizeof(apc_word_t)) +/* }}} */ + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_spin.c b/apc_spin.c new file mode 100644 index 0000000..3ce65eb --- /dev/null +++ b/apc_spin.c @@ -0,0 +1,65 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_spin.c,v 3.1 2007/01/29 07:39:02 shire Exp $ */ + +#include "apc_spin.h" + +#ifdef APC_SPIN_LOCKS + +slock_t *apc_slock_create(slock_t *lock) +{ + S_INIT_LOCK(lock); +} + +void apc_slock_destroy(slock_t *lock) +{ + S_LOCK_FREE(lock); +} + +void apc_slock_lock(slock_t *lock) +{ + S_LOCK(lock); +} + +void apc_slock_unlock(slock_t *lock) +{ + S_UNLOCK(lock); +} + +zend_bool apc_slock_nonblocking_lock(slock_t *lock) +{ + /* Technically we aren't supposed to call this directly, but the original + * code provides no method for absolute non-blocking locks, so we'll call into + * the TAS (test and set) functionality directly + */ + return !(TAS(lock)); /* if TAS returns 0 we obtained the lock, otherwise we failed */ +} + + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_spin.h b/apc_spin.h new file mode 100644 index 0000000..46163d6 --- /dev/null +++ b/apc_spin.h @@ -0,0 +1,48 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: apc_spin.h,v 3.1 2007/01/29 07:39:02 shire Exp $ */ + +#ifndef APC_SPIN_H +#define APC_SPIN_H + +#include "apc.h" + +#ifdef APC_SPIN_LOCKS + +#include "pgsql_s_lock.h" + +pthread_mutex_t *apc_spin_create(); +void apc_spin_destroy(pthread_mutex_t *lock); +void apc_spin_lock(pthread_mutex_t *lock); +void apc_spin_unlock(pthread_mutex_t *lock); +zend_bool apc_spin_nonblocking_lock(pthread_mutex_t *lock); + +#endif + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_stack.c b/apc_stack.c new file mode 100644 index 0000000..c5f7865 --- /dev/null +++ b/apc_stack.c @@ -0,0 +1,105 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_stack.c,v 3.4 2006/03/12 00:31:45 rasmus Exp $ */ + +#include "apc_stack.h" +#include "apc.h" + +struct apc_stack_t { + void** data; + int capacity; + int size; +}; + +apc_stack_t* apc_stack_create(int size_hint) +{ + apc_stack_t* stack = (apc_stack_t*) apc_emalloc(sizeof(apc_stack_t)); + + stack->capacity = (size_hint > 0) ? size_hint : 10; + stack->size = 0; + stack->data = (void**) apc_emalloc(sizeof(void*) * stack->capacity); + + return stack; +} + +void apc_stack_destroy(apc_stack_t* stack) +{ + if (stack != NULL) { + apc_efree(stack->data); + apc_efree(stack); + } +} + +void apc_stack_clear(apc_stack_t* stack) +{ + assert(stack != NULL); + stack->size = 0; +} + +void apc_stack_push(apc_stack_t* stack, void* item) +{ + assert(stack != NULL); + if (stack->size == stack->capacity) { + stack->capacity *= 2; + stack->data = apc_erealloc(stack->data, sizeof(void*)*stack->capacity); + } + stack->data[stack->size++] = item; +} + +void* apc_stack_pop(apc_stack_t* stack) +{ + assert(stack != NULL && stack->size > 0); + return stack->data[--stack->size]; +} + +void* apc_stack_top(apc_stack_t* stack) +{ + assert(stack != NULL && stack->size > 0); + return stack->data[stack->size-1]; +} + +void* apc_stack_get(apc_stack_t* stack, int n) +{ + assert(stack != NULL && stack->size > n); + return stack->data[n]; +} + +int apc_stack_size(apc_stack_t* stack) +{ + assert(stack != NULL); + return stack->size; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_stack.h b/apc_stack.h new file mode 100644 index 0000000..8d2a5d2 --- /dev/null +++ b/apc_stack.h @@ -0,0 +1,58 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | George Schlossnagle | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_stack.h,v 3.4 2006/03/12 00:31:45 rasmus Exp $ */ + +#ifndef APC_STACK_H +#define APC_STACK_H + +/* Basic stack datatype */ + +#define T apc_stack_t* +typedef struct apc_stack_t apc_stack_t; /* opaque stack type */ + +extern T apc_stack_create(int size_hint); +extern void apc_stack_destroy(T stack); +extern void apc_stack_clear(T stack); +extern void apc_stack_push(T stack, void* item); +extern void* apc_stack_pop(T stack); +extern void* apc_stack_top(T stack); +extern void* apc_stack_get(T stack, int n); +extern int apc_stack_size(T stack); + +#undef T +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_zend.c b/apc_zend.c new file mode 100644 index 0000000..6cd21be --- /dev/null +++ b/apc_zend.c @@ -0,0 +1,277 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_zend.c,v 3.14 2007/04/02 22:57:10 rasmus Exp $ */ + +#include "apc_zend.h" +#include "apc_globals.h" + +void* apc_php_malloc(size_t n) +{ + return emalloc(n); +} + +void apc_php_free(void* p) +{ + efree(p); +} + +#ifndef ZEND_VM_KIND_CALL /* Not currently defined by any ZE version */ +# define ZEND_VM_KIND_CALL 1 +#endif + +#ifndef ZEND_VM_KIND /* Indicates PHP < 5.1 */ +# define ZEND_VM_KIND ZEND_VM_KIND_CALL +#endif + +#if defined(ZEND_ENGINE_2) && (ZEND_VM_KIND == ZEND_VM_KIND_CALL) +# define APC_OPCODE_OVERRIDE +#endif + +#ifdef APC_OPCODE_OVERRIDE + +#ifdef ZEND_ENGINE_2_1 +/* Taken from Zend/zend_vm_execute.h */ +#define _CONST_CODE 0 +#define _TMP_CODE 1 +#define _VAR_CODE 2 +#define _UNUSED_CODE 3 +#define _CV_CODE 4 +static inline int _apc_opcode_handler_decode(zend_op *opline) +{ + static const int apc_vm_decode[] = { + _UNUSED_CODE, /* 0 */ + _CONST_CODE, /* 1 = IS_CONST */ + _TMP_CODE, /* 2 = IS_TMP_VAR */ + _UNUSED_CODE, /* 3 */ + _VAR_CODE, /* 4 = IS_VAR */ + _UNUSED_CODE, /* 5 */ + _UNUSED_CODE, /* 6 */ + _UNUSED_CODE, /* 7 */ + _UNUSED_CODE, /* 8 = IS_UNUSED */ + _UNUSED_CODE, /* 9 */ + _UNUSED_CODE, /* 10 */ + _UNUSED_CODE, /* 11 */ + _UNUSED_CODE, /* 12 */ + _UNUSED_CODE, /* 13 */ + _UNUSED_CODE, /* 14 */ + _UNUSED_CODE, /* 15 */ + _CV_CODE /* 16 = IS_CV */ + }; + return (opline->opcode * 25) + (apc_vm_decode[opline->op1.op_type] * 5) + apc_vm_decode[opline->op2.op_type]; +} + +# define APC_ZEND_OPLINE zend_op *opline = execute_data->opline; +# define APC_OPCODE_HANDLER_DECODE(opline) _apc_opcode_handler_decode(opline) +# if PHP_MAJOR_VERSION >= 6 +# define APC_OPCODE_HANDLER_COUNT ((25 * 152) + 1) +# else +# define APC_OPCODE_HANDLER_COUNT ((25 * 151) + 1) +# endif +# define APC_REPLACE_OPCODE(opname) { int i; for(i = 0; i < 25; i++) if (zend_opcode_handlers[(opname*25) + i]) zend_opcode_handlers[(opname*25) + i] = apc_op_##opname; } + +#else /* ZE2.0 */ +# define APC_ZEND_ONLINE +# define APC_OPCODE_HANDLER_DECODE(opline) (opline->opcode) +# define APC_OPCODE_HANDLER_COUNT 512 +# define APC_REPLACE_OPCODE(opname) zend_opcode_handlers[opname] = apc_op_##opname; +#endif + +static opcode_handler_t *apc_original_opcode_handlers; +static opcode_handler_t apc_opcode_handlers[APC_OPCODE_HANDLER_COUNT]; + +#define APC_EX_T(offset) (*(temp_variable *)((char*)execute_data->Ts + offset)) + +static zval *apc_get_zval_ptr(znode *node, zval **freeval, zend_execute_data *execute_data TSRMLS_DC) +{ + *freeval = NULL; + + switch (node->op_type) { + case IS_CONST: + return &(node->u.constant); + case IS_VAR: + return APC_EX_T(node->u.var).var.ptr; + case IS_TMP_VAR: + return (*freeval = &APC_EX_T(node->u.var).tmp_var); +#ifdef ZEND_ENGINE_2_1 + case IS_CV: + { + zval ***ret = &execute_data->CVs[node->u.var]; + + if (!*ret) { + zend_compiled_variable *cv = &EG(active_op_array)->vars[node->u.var]; + + if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void**)ret)==FAILURE) { + apc_nprint("Undefined variable: %s", cv->name); + return &EG(uninitialized_zval); + } + } + return **ret; + } +#endif + case IS_UNUSED: + default: + return NULL; + } +} + +static int apc_op_ZEND_INCLUDE_OR_EVAL(ZEND_OPCODE_HANDLER_ARGS) +{ + APC_ZEND_OPLINE + zval *freeop1 = NULL; + zval *inc_filename = NULL, tmp_inc_filename; + char realpath[MAXPATHLEN]; + php_stream_wrapper *wrapper; + char *path_for_open; + int ret = 0; + #ifdef ZEND_ENGINE_2 + apc_opflags_t* flags = NULL; + #endif + + if (Z_LVAL(opline->op2.u.constant) != ZEND_INCLUDE_ONCE && + Z_LVAL(opline->op2.u.constant) != ZEND_REQUIRE_ONCE) { + return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + + inc_filename = apc_get_zval_ptr(&opline->op1, &freeop1, execute_data TSRMLS_CC); + if (Z_TYPE_P(inc_filename) != IS_STRING) { + tmp_inc_filename = *inc_filename; + zval_copy_ctor(&tmp_inc_filename); + convert_to_string(&tmp_inc_filename); + inc_filename = &tmp_inc_filename; + } + + wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(inc_filename), &path_for_open, 0 TSRMLS_CC); + if (wrapper != &php_plain_files_wrapper || + !IS_ABSOLUTE_PATH(path_for_open, strlen(path_for_open)) || + !expand_filepath(path_for_open, realpath TSRMLS_CC)) { + /* Fallback to original handler */ + if (inc_filename == &tmp_inc_filename) { + zval_dtor(&tmp_inc_filename); + } + return apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + + if (zend_hash_exists(&EG(included_files), realpath, strlen(realpath) + 1)) { + if (!(opline->result.u.EA.type & EXT_TYPE_UNUSED)) { + ALLOC_INIT_ZVAL(APC_EX_T(opline->result.u.var).var.ptr); + ZVAL_TRUE(APC_EX_T(opline->result.u.var).var.ptr); + } + if (inc_filename == &tmp_inc_filename) { + zval_dtor(&tmp_inc_filename); + } + if (freeop1) { + zval_dtor(freeop1); + } + execute_data->opline++; + return 0; + } + + if (inc_filename == &tmp_inc_filename) { + zval_dtor(&tmp_inc_filename); + } + + if(APCG(reserved_offset) != -1) { + /* Insanity alert: look into apc_compile.c for why a void** is cast to a apc_opflags_t* */ + flags = (apc_opflags_t*) & (execute_data->op_array->reserved[APCG(reserved_offset)]); + } + +#ifdef ZEND_ENGINE_2 + if(flags && flags->deep_copy == 1) { + /* Since the op array is a local copy, we can cheat our way through the file inclusion by temporarily + * changing the op to a plain require/include, calling its handler and finally restoring the opcode. + */ + Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE_ONCE) ? ZEND_INCLUDE : ZEND_REQUIRE; + ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + Z_LVAL(opline->op2.u.constant) = (Z_LVAL(opline->op2.u.constant) == ZEND_INCLUDE) ? ZEND_INCLUDE_ONCE : ZEND_REQUIRE_ONCE; +#else + if(0) { + /* do nothing, have nothing, be nothing */ +#endif + } else { + ret = apc_original_opcode_handlers[APC_OPCODE_HANDLER_DECODE(opline)](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + + return ret; +} + +void apc_zend_init(TSRMLS_D) +{ + zend_extension dummy_ext; +#ifdef ZEND_ENGINE_2 + APCG(reserved_offset) = zend_get_resource_handle(&dummy_ext); + assert(APCG(reserved_offset) == dummy_ext.resource_number); + assert(APCG(reserved_offset) != -1); + assert(sizeof(apc_opflags_t) <= sizeof(void*)); +#endif + if (!APCG(include_once)) { + /* If we're not overriding the INCLUDE_OR_EVAL handler, then just skip this malarkey */ + return; + } + + memcpy(apc_opcode_handlers, zend_opcode_handlers, sizeof(apc_opcode_handlers)); + + /* 5.0 exposes zend_opcode_handlers differently than 5.1 and later */ +#ifdef ZEND_ENGINE_2_1 + apc_original_opcode_handlers = zend_opcode_handlers; + zend_opcode_handlers = apc_opcode_handlers; +#else + apc_original_opcode_handlers = apc_opcode_handlers; +#endif + + APC_REPLACE_OPCODE(ZEND_INCLUDE_OR_EVAL); +} + +void apc_zend_shutdown(TSRMLS_D) +{ + if (!APCG(include_once)) { + /* Nothing changed, nothing to restore */ + return; + } + +#ifdef ZEND_ENGINE_2_1 + zend_opcode_handlers = apc_original_opcode_handlers; +#else + memcpy(zend_opcode_handlers, apc_original_opcode_handlers, sizeof(apc_opcode_handlers)); +#endif +} + +#else /* Opcode Overrides unavailable */ + +void apc_zend_init(TSRMLS_D) { } +void apc_zend_shutdown(TSRMLS_D) { } + +#endif /* APC_OPCODE_OVERRIDE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/apc_zend.h b/apc_zend.h new file mode 100644 index 0000000..5045d77 --- /dev/null +++ b/apc_zend.h @@ -0,0 +1,53 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: apc_zend.h,v 3.8 2006/09/01 21:59:16 pollita Exp $ */ + +#ifndef APC_ZEND_H +#define APC_ZEND_H + +/* Utilities for interfacing with the zend engine */ + +#include "apc.h" +#include "apc_php.h" + +extern void* apc_php_malloc(size_t n); +extern void apc_php_free(void* p); + +extern void apc_zend_init(TSRMLS_D); +extern void apc_zend_shutdown(TSRMLS_D); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/arch/atomic.h b/arch/atomic.h new file mode 100644 index 0000000..ac6feaf --- /dev/null +++ b/arch/atomic.h @@ -0,0 +1,38 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: atomic.h,v 1.1 2006/09/29 07:13:01 shire Exp $ */ + +#ifndef APC_ARCH_ATOMIC_H + +#define APC_ARCH_ATOMIC_H + +#if defined __x86_64__ +#include "x86_64/atomic.h" + +#elif defined __i386__ +#include "i386/atomic.h" + +#else +#error "Unknown or Unsupported Architecture. If you would like futex suupport for your architecture, please file a request at http://pecl.php.net/bugs/report.php?package=APC" + +#endif + + +#endif diff --git a/arch/i386/atomic.h b/arch/i386/atomic.h new file mode 100644 index 0000000..069a5b0 --- /dev/null +++ b/arch/i386/atomic.h @@ -0,0 +1,79 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: atomic.h,v 1.1 2006/09/29 07:13:01 shire Exp $ */ + + +#include +#include +#include + +/* int sys_futex (void *futex, int op, int val, const struct timespec *timeout); */ +static inline long int apc_sys_futex(void *futex, int op, int val, const struct timespec *timeout) { + + long int ret; + + /* i386 system calls are performed with nt 80h operation. + * the argument order is a, b, c, d, S, D + */ + asm volatile ("int $0x80" + : "=a" (ret) + : "0" (SYS_futex), + "b" (futex), + "c" (op), + "d" (val), + "S" (timeout) + : "memory" + ); + + return ret; + +} + + +static inline int apc_cmpxchg(volatile int *ptr, int old, int new) { + + int prev; + + asm volatile ("LOCK cmpxchgl %1, %2" + : "=a" (prev) + : "r" (new), + "m" (*(ptr)), + "0"(old) + : "memory", "cc" + ); + + return prev; +} + +static inline int apc_xchg(volatile int *ptr, int new) { + + int ret; + + asm volatile ("LOCK xchgl %[new], %[ptr]" + : "=a" (ret) + : [new] "0" (new), + [ptr] "m" (*(ptr)) + : "memory" + ); + + return ret; + +} + diff --git a/arch/x86_64/atomic.h b/arch/x86_64/atomic.h new file mode 100644 index 0000000..4b882d5 --- /dev/null +++ b/arch/x86_64/atomic.h @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: atomic.h,v 1.1 2006/09/29 07:13:01 shire Exp $ */ + + +#include +#include +#include + +/* int sys_futex (void *futex, int op, int val, const struct timespec *timeout); */ +static inline long int apc_sys_futex(void *futex, int op, int val, const struct timespec *timeout) { + + long int ret; + + /* x86_64 system calls are performed with the faster SYSCALL operation. + * the argument order is D, S, d, c, b, a rather than + * a, b, c, d, S, D as on the i386 int 80h call. + */ + asm volatile ("syscall" + : "=a" (ret) + : "0" (SYS_futex), + "D" (futex), + "S" (op), + "d" (val), + "c" (timeout) + : "r11", "rcx", "memory" + ); + + return ret; + +} + + +static inline int apc_cmpxchg(volatile int *ptr, int old, int new) { + + int prev; + + asm volatile ("LOCK cmpxchgl %1, %2" + : "=a" (prev) + : "r" (new), + "m" (*(ptr)), + "0"(old) + : "memory", "cc" + ); + + return prev; +} + +static inline int apc_xchg(volatile int *ptr, int new) { + + int ret; + + asm volatile ("LOCK xchgl %[new], %[ptr]" + : "=a" (ret) + : [new] "0" (new), + [ptr] "m" (*(ptr)) + : "memory" + ); + + return ret; + +} + diff --git a/config.m4 b/config.m4 index c36d0df..19a07e7 100644 --- a/config.m4 +++ b/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4,v 3.30 2007/12/26 22:31:20 rasmus Exp $ +dnl $Id: config.m4,v 3.30.2.2 2008/03/25 21:00:22 rasmus Exp $ dnl AC_MSG_CHECKING(whether apc needs to get compiler flags from apxs) @@ -107,8 +107,8 @@ AC_ARG_ENABLE(apc-pthreadmutex, [ --disable-apc-pthreadmutex Disable pthread mutex locking ], [ - PHP_APC_PTHREADMUTEX=no - AC_MSG_RESULT(no) + PHP_APC_PTHREADMUTEX=$enableval + AC_MSG_RESULT($enableval) ], [ PHP_APC_PTHREADMUTEX=yes @@ -210,6 +210,21 @@ if test "$PHP_APC" != "no"; then AC_DEFINE(HAVE_SEMUN, 0, [ ]) fi + AC_MSG_CHECKING(whether we should enable valgrind support) + AC_ARG_ENABLE(valgrind-checks, + [ --enable-valgrind-checks + Enable valgrind based memory checks], + [ + PHP_APC_VALGRIND=$enableval + AC_MSG_RESULT($enableval) + AC_CHECK_HEADER(valgrind/memcheck.h, + [AC_DEFINE([HAVE_VALGRIND_MEMCHECK_H],1, [enable valgrind memchecks])]) + ], [ + PHP_APC_VALGRIND=no + AC_MSG_RESULT(no) + ]) + + apc_sources="apc.c php_apc.c \ apc_cache.c \ apc_compile.c \ @@ -227,7 +242,8 @@ if test "$PHP_APC" != "no"; then apc_stack.c \ apc_zend.c \ apc_rfc1867.c \ - apc_signal.c " + apc_signal.c \ + apc_pool.c " PHP_CHECK_LIBRARY(rt, shm_open, [PHP_ADD_LIBRARY(rt,,APC_SHARED_LIBADD)]) PHP_NEW_EXTENSION(apc, $apc_sources, $ext_shared,, \\$(APC_CFLAGS)) diff --git a/debian/changelog b/debian/changelog index f2370fa..ad97268 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +php5-apc (3.0.18-1) stable; urgency=low + + * New PHP5 APC - version 3.0.18, using PHP5 5.2.0-8+etch10, + 20060613+lfs. + + -- Dragan Dosen Tue, 1 Apr 2008 21:00:13 +0200 + php5-apc (3.0.16-2) stable; urgency=low * Removed dependency on php5-common. diff --git a/debian/rules b/debian/rules index d4db67e..630d7d4 100755 --- a/debian/rules +++ b/debian/rules @@ -84,7 +84,7 @@ binary-arch: build install dh_testroot dh_install -X.svn dh_installchangelogs CHANGELOG - dh_installdocs TECHNOTES.txt TODO + dh_installdocs TECHNOTES.txt TODO INSTALL dh_installexamples dh_installdebconf dh_link diff --git a/pgsql_s_lock.c b/pgsql_s_lock.c new file mode 100644 index 0000000..4b8b40a --- /dev/null +++ b/pgsql_s_lock.c @@ -0,0 +1,481 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | The following code was ported from the PostgreSQL project, please | + | see appropriate copyright notices that follow. | + | Initial conversion by Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: pgsql_s_lock.c,v 3.2 2007/02/25 05:19:11 shire Exp $ */ + +/*------------------------------------------------------------------------- + * + * s_lock.c + * Hardware-dependent implementation of spinlocks. + * + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/storage/lmgr/s_lock.c,v 1.47 2006/10/04 00:29:58 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +/* #include "postgres.h" -- Removed for APC */ + +/* -- Added for APC -- */ +#include "apc.h" +#ifdef APC_SPIN_LOCKS + +#ifdef S_LOCK_TEST +#include +#endif +#ifndef WIN32 +#include +#endif +/* ---- */ + +#include +#include + +/* #include "storage/s_lock.h" -- Removed for APC */ +#include "pgsql_s_lock.h" + +static int spins_per_delay = DEFAULT_SPINS_PER_DELAY; + + +/* -- APC specific additions ------------------------------*/ +/* The following dependencies have been copied from + * other pgsql source files. The original locations + * have been noted. + */ + +/* -- from include/c.h -- */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* -- from include/pg_config_manual.h -- */ +#define MAX_RANDOM_VALUE (0x7FFFFFFF) + +/* + * Max + * Return the maximum of two numbers. + */ +#define Max(x, y) ((x) > (y) ? (x) : (y)) + +/* -- from include/c.h -- */ +/* + * Min + * Return the minimum of two numbers. + */ +#define Min(x, y) ((x) < (y) ? (x) : (y)) + + +/* -- from backend/port/win32/signal.c -- */ +/* + * pg_usleep --- delay the specified number of microseconds. + * + * NOTE: although the delay is specified in microseconds, the effective + * resolution is only 1/HZ, or 10 milliseconds, on most Unixen. Expect + * the requested delay to be rounded up to the next resolution boundary. + * + * On machines where "long" is 32 bits, the maximum delay is ~2000 seconds. + */ +void +pg_usleep(long microsec) +{ + if (microsec > 0) + { +#ifndef WIN32 + struct timeval delay; + + delay.tv_sec = microsec / 1000000L; + delay.tv_usec = microsec % 1000000L; + (void) select(0, NULL, NULL, NULL, &delay); +#else + SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE); +#endif + } +} + +/* -- End APC specific additions ------------------------------*/ + + +/* + * s_lock_stuck() - complain about a stuck spinlock + */ +static void +s_lock_stuck(volatile slock_t *lock, const char *file, int line) +{ +#if defined(S_LOCK_TEST) + fprintf(stderr, + "\nStuck spinlock (%p) detected at %s:%d.\n", + lock, file, line); + exit(1); +#else + /* -- Removed for APC + elog(PANIC, "stuck spinlock (%p) detected at %s:%d", + lock, file, line); + */ + apc_eprint("Stuck spinlock (%p) detected", lock); +#endif +} + + +/* + * s_lock(lock) - platform-independent portion of waiting for a spinlock. + */ +void +s_lock(volatile slock_t *lock, const char *file, int line) +{ + /* + * We loop tightly for awhile, then delay using pg_usleep() and try again. + * Preferably, "awhile" should be a small multiple of the maximum time we + * expect a spinlock to be held. 100 iterations seems about right as an + * initial guess. However, on a uniprocessor the loop is a waste of + * cycles, while in a multi-CPU scenario it's usually better to spin a bit + * longer than to call the kernel, so we try to adapt the spin loop count + * depending on whether we seem to be in a uniprocessor or multiprocessor. + * + * Note: you might think MIN_SPINS_PER_DELAY should be just 1, but you'd + * be wrong; there are platforms where that can result in a "stuck + * spinlock" failure. This has been seen particularly on Alphas; it seems + * that the first TAS after returning from kernel space will always fail + * on that hardware. + * + * Once we do decide to block, we use randomly increasing pg_usleep() + * delays. The first delay is 1 msec, then the delay randomly increases to + * about one second, after which we reset to 1 msec and start again. The + * idea here is that in the presence of heavy contention we need to + * increase the delay, else the spinlock holder may never get to run and + * release the lock. (Consider situation where spinlock holder has been + * nice'd down in priority by the scheduler --- it will not get scheduled + * until all would-be acquirers are sleeping, so if we always use a 1-msec + * sleep, there is a real possibility of starvation.) But we can't just + * clamp the delay to an upper bound, else it would take a long time to + * make a reasonable number of tries. + * + * We time out and declare error after NUM_DELAYS delays (thus, exactly + * that many tries). With the given settings, this will usually take 2 or + * so minutes. It seems better to fix the total number of tries (and thus + * the probability of unintended failure) than to fix the total time + * spent. + * + * The pg_usleep() delays are measured in milliseconds because 1 msec is a + * common resolution limit at the OS level for newer platforms. On older + * platforms the resolution limit is usually 10 msec, in which case the + * total delay before timeout will be a bit more. + */ +#define MIN_SPINS_PER_DELAY 10 +#define MAX_SPINS_PER_DELAY 1000 +#define NUM_DELAYS 1000 +#define MIN_DELAY_MSEC 1 +#define MAX_DELAY_MSEC 1000 + + int spins = 0; + int delays = 0; + int cur_delay = 0; + + while (TAS(lock)) + { + /* CPU-specific delay each time through the loop */ + SPIN_DELAY(); + + /* Block the process every spins_per_delay tries */ + if (++spins >= spins_per_delay) + { + if (++delays > NUM_DELAYS) + s_lock_stuck(lock, file, line); + + if (cur_delay == 0) /* first time to delay? */ + cur_delay = MIN_DELAY_MSEC; + + pg_usleep(cur_delay * 1000L); + +#if defined(S_LOCK_TEST) + fprintf(stdout, "*"); + fflush(stdout); +#endif + + /* increase delay by a random fraction between 1X and 2X */ + cur_delay += (int) (cur_delay * + ((double) random() / (double) MAX_RANDOM_VALUE) + 0.5); + /* wrap back to minimum delay when max is exceeded */ + if (cur_delay > MAX_DELAY_MSEC) + cur_delay = MIN_DELAY_MSEC; + + spins = 0; + } + } + + /* + * If we were able to acquire the lock without delaying, it's a good + * indication we are in a multiprocessor. If we had to delay, it's a sign + * (but not a sure thing) that we are in a uniprocessor. Hence, we + * decrement spins_per_delay slowly when we had to delay, and increase it + * rapidly when we didn't. It's expected that spins_per_delay will + * converge to the minimum value on a uniprocessor and to the maximum + * value on a multiprocessor. + * + * Note: spins_per_delay is local within our current process. We want to + * average these observations across multiple backends, since it's + * relatively rare for this function to even get entered, and so a single + * backend might not live long enough to converge on a good value. That + * is handled by the two routines below. + */ + if (cur_delay == 0) + { + /* we never had to delay */ + if (spins_per_delay < MAX_SPINS_PER_DELAY) + spins_per_delay = Min(spins_per_delay + 100, MAX_SPINS_PER_DELAY); + } + else + { + if (spins_per_delay > MIN_SPINS_PER_DELAY) + spins_per_delay = Max(spins_per_delay - 1, MIN_SPINS_PER_DELAY); + } +} + + +#if 0 /* -- APC doesn't use the set_spins_per_delay or update_spins_per_delay -- */ +/* + * Set local copy of spins_per_delay during backend startup. + * + * NB: this has to be pretty fast as it is called while holding a spinlock + */ +void +set_spins_per_delay(int shared_spins_per_delay) +{ + spins_per_delay = shared_spins_per_delay; +} + +/* + * Update shared estimate of spins_per_delay during backend exit. + * + * NB: this has to be pretty fast as it is called while holding a spinlock + */ +int +update_spins_per_delay(int shared_spins_per_delay) +{ + /* + * We use an exponential moving average with a relatively slow adaption + * rate, so that noise in any one backend's result won't affect the shared + * value too much. As long as both inputs are within the allowed range, + * the result must be too, so we need not worry about clamping the result. + * + * We deliberately truncate rather than rounding; this is so that single + * adjustments inside a backend can affect the shared estimate (see the + * asymmetric adjustment rules above). + */ + return (shared_spins_per_delay * 15 + spins_per_delay) / 16; +} +#endif + +/* + * Various TAS implementations that cannot live in s_lock.h as no inline + * definition exists (yet). + * In the future, get rid of tas.[cso] and fold it into this file. + * + * If you change something here, you will likely need to modify s_lock.h too, + * because the definitions for these are split between this file and s_lock.h. + */ + + +#ifdef HAVE_SPINLOCKS /* skip spinlocks if requested */ + + +#if defined(__GNUC__) + +/* + * All the gcc flavors that are not inlined + */ + + +/* + * Note: all the if-tests here probably ought to be testing gcc version + * rather than platform, but I don't have adequate info to know what to + * write. Ideally we'd flush all this in favor of the inline version. + */ +#if defined(__m68k__) && !defined(__linux__) +/* really means: extern int tas(slock_t* **lock); */ +static void +tas_dummy() +{ + __asm__ __volatile__( +#if defined(__NetBSD__) && defined(__ELF__) +/* no underscore for label and % for registers */ + "\ +.global tas \n\ +tas: \n\ + movel %sp@(0x4),%a0 \n\ + tas %a0@ \n\ + beq _success \n\ + moveq #-128,%d0 \n\ + rts \n\ +_success: \n\ + moveq #0,%d0 \n\ + rts \n" +#else + "\ +.global _tas \n\ +_tas: \n\ + movel sp@(0x4),a0 \n\ + tas a0@ \n\ + beq _success \n\ + moveq #-128,d0 \n\ + rts \n\ +_success: \n\ + moveq #0,d0 \n\ + rts \n" +#endif /* __NetBSD__ && __ELF__ */ + ); +} +#endif /* __m68k__ && !__linux__ */ +#else /* not __GNUC__ */ + +/* + * All non gcc + */ + + +#if defined(sun3) +static void +tas_dummy() /* really means: extern int tas(slock_t + * *lock); */ +{ + asm("LLA0:"); + asm(" .data"); + asm(" .text"); + asm("|#PROC# 04"); + asm(" .globl _tas"); + asm("_tas:"); + asm("|#PROLOGUE# 1"); + asm(" movel sp@(0x4),a0"); + asm(" tas a0@"); + asm(" beq LLA1"); + asm(" moveq #-128,d0"); + asm(" rts"); + asm("LLA1:"); + asm(" moveq #0,d0"); + asm(" rts"); + asm(" .data"); +} +#endif /* sun3 */ +#endif /* not __GNUC__ */ +#endif /* HAVE_SPINLOCKS */ + + + +/*****************************************************************************/ +#if defined(S_LOCK_TEST) + +/* + * test program for verifying a port's spinlock support. + */ + +struct test_lock_struct +{ + char pad1; + slock_t lock; + char pad2; +}; + +volatile struct test_lock_struct test_lock; + +int +main() +{ + srandom((unsigned int) time(NULL)); + + test_lock.pad1 = test_lock.pad2 = 0x44; + + S_INIT_LOCK(&test_lock.lock); + + if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44) + { + printf("S_LOCK_TEST: failed, declared datatype is wrong size\n"); + return 1; + } + + if (!S_LOCK_FREE(&test_lock.lock)) + { + printf("S_LOCK_TEST: failed, lock not initialized\n"); + return 1; + } + + S_LOCK(&test_lock.lock); + + if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44) + { + printf("S_LOCK_TEST: failed, declared datatype is wrong size\n"); + return 1; + } + + if (S_LOCK_FREE(&test_lock.lock)) + { + printf("S_LOCK_TEST: failed, lock not locked\n"); + return 1; + } + + S_UNLOCK(&test_lock.lock); + + if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44) + { + printf("S_LOCK_TEST: failed, declared datatype is wrong size\n"); + return 1; + } + + if (!S_LOCK_FREE(&test_lock.lock)) + { + printf("S_LOCK_TEST: failed, lock not unlocked\n"); + return 1; + } + + S_LOCK(&test_lock.lock); + + if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44) + { + printf("S_LOCK_TEST: failed, declared datatype is wrong size\n"); + return 1; + } + + if (S_LOCK_FREE(&test_lock.lock)) + { + printf("S_LOCK_TEST: failed, lock not re-locked\n"); + return 1; + } + + printf("S_LOCK_TEST: this will print %d stars and then\n", NUM_DELAYS); + printf(" exit with a 'stuck spinlock' message\n"); + printf(" if S_LOCK() and TAS() are working.\n"); + fflush(stdout); + + s_lock(&test_lock.lock, __FILE__, __LINE__); + + printf("S_LOCK_TEST: failed, lock not locked\n"); + return 1; +} + +#endif /* S_LOCK_TEST */ + +#endif /* APC_SPIN_LOCKS */ diff --git a/pgsql_s_lock.h b/pgsql_s_lock.h new file mode 100644 index 0000000..6ee13c9 --- /dev/null +++ b/pgsql_s_lock.h @@ -0,0 +1,928 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | The following code was ported from the PostgreSQL project, please | + | see appropriate copyright notices that follow. | + | Initial conversion by Brian Shire | + +----------------------------------------------------------------------+ + + */ + +/* $Id: pgsql_s_lock.h,v 3.3 2007/02/16 21:28:04 shire Exp $ */ + +/*------------------------------------------------------------------------- + * + * s_lock.h + * Hardware-dependent implementation of spinlocks. + * + * NOTE: none of the macros in this file are intended to be called directly. + * Call them through the hardware-independent macros in spin.h. + * + * The following hardware-dependent macros must be provided for each + * supported platform: + * + * void S_INIT_LOCK(slock_t *lock) + * Initialize a spinlock (to the unlocked state). + * + * void S_LOCK(slock_t *lock) + * Acquire a spinlock, waiting if necessary. + * Time out and abort() if unable to acquire the lock in a + * "reasonable" amount of time --- typically ~ 1 minute. + * + * void S_UNLOCK(slock_t *lock) + * Unlock a previously acquired lock. + * + * bool S_LOCK_FREE(slock_t *lock) + * Tests if the lock is free. Returns TRUE if free, FALSE if locked. + * This does *not* change the state of the lock. + * + * void SPIN_DELAY(void) + * Delay operation to occur inside spinlock wait loop. + * + * Note to implementors: there are default implementations for all these + * macros at the bottom of the file. Check if your platform can use + * these or needs to override them. + * + * Usually, S_LOCK() is implemented in terms of an even lower-level macro + * TAS(): + * + * int TAS(slock_t *lock) + * Atomic test-and-set instruction. Attempt to acquire the lock, + * but do *not* wait. Returns 0 if successful, nonzero if unable + * to acquire the lock. + * + * TAS() is NOT part of the API, and should never be called directly. + * + * CAUTION: on some platforms TAS() may sometimes report failure to acquire + * a lock even when the lock is not locked. For example, on Alpha TAS() + * will "fail" if interrupted. Therefore TAS() should always be invoked + * in a retry loop, even if you are certain the lock is free. + * + * ANOTHER CAUTION: be sure that TAS() and S_UNLOCK() represent sequence + * points, ie, loads and stores of other values must not be moved across + * a lock or unlock. In most cases it suffices to make the operation be + * done through a "volatile" pointer. + * + * On most supported platforms, TAS() uses a tas() function written + * in assembly language to execute a hardware atomic-test-and-set + * instruction. Equivalent OS-supplied mutex routines could be used too. + * + * If no system-specific TAS() is available (ie, HAVE_SPINLOCKS is not + * defined), then we fall back on an emulation that uses SysV semaphores + * (see spin.c). This emulation will be MUCH MUCH slower than a proper TAS() + * implementation, because of the cost of a kernel call per lock or unlock. + * An old report is that Postgres spends around 40% of its time in semop(2) + * when using the SysV semaphore code. + * + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL: pgsql/src/include/storage/s_lock.h,v 1.157 2006/06/07 22:24:45 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef S_LOCK_H +#define S_LOCK_H + +/** APC namespace protection ************************************************/ +/* hack to protect against any possible runtime namespace collisions...*/ +#define pg_usleep apc_spin_pg_usleep +#define s_lock apc_spin_s_lock +#define spins_per_delay apc_spin_spins_per_delay +/****************************************************************************/ + + +/* #include "storage/pg_sema.h" -- Removed for APC */ + +#define HAVE_SPINLOCKS 1 /* -- Added for APC */ + +#ifdef HAVE_SPINLOCKS /* skip spinlocks if requested */ + + +#if defined(__GNUC__) || defined(__ICC) +/************************************************************************* + * All the gcc inlines + * Gcc consistently defines the CPU as __cpu__. + * Other compilers use __cpu or __cpu__ so we test for both in those cases. + */ + +/*---------- + * Standard gcc asm format (assuming "volatile slock_t *lock"): + + __asm__ __volatile__( + " instruction \n" + " instruction \n" + " instruction \n" +: "=r"(_res), "+m"(*lock) // return register, in/out lock value +: "r"(lock) // lock pointer, in input register +: "memory", "cc"); // show clobbered registers here + + * The output-operands list (after first colon) should always include + * "+m"(*lock), whether or not the asm code actually refers to this + * operand directly. This ensures that gcc believes the value in the + * lock variable is used and set by the asm code. Also, the clobbers + * list (after third colon) should always include "memory"; this prevents + * gcc from thinking it can cache the values of shared-memory fields + * across the asm code. Add "cc" if your asm code changes the condition + * code register, and also list any temp registers the code uses. + *---------- + */ + + +#ifdef __i386__ /* 32-bit i386 */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + /* + * Use a non-locking test before asserting the bus lock. Note that the + * extra test appears to be a small loss on some x86 platforms and a small + * win on others; it's by no means clear that we should keep it. + */ + __asm__ __volatile__( + " cmpb $0,%1 \n" + " jne 1f \n" + " lock \n" + " xchgb %0,%1 \n" + "1: \n" +: "+q"(_res), "+m"(*lock) +: +: "memory", "cc"); + return (int) _res; +} + +#define SPIN_DELAY() spin_delay() + +static __inline__ void +spin_delay(void) +{ + /* + * This sequence is equivalent to the PAUSE instruction ("rep" is + * ignored by old IA32 processors if the following instruction is + * not a string operation); the IA-32 Architecture Software + * Developer's Manual, Vol. 3, Section 7.7.2 describes why using + * PAUSE in the inner loop of a spin lock is necessary for good + * performance: + * + * The PAUSE instruction improves the performance of IA-32 + * processors supporting Hyper-Threading Technology when + * executing spin-wait loops and other routines where one + * thread is accessing a shared lock or semaphore in a tight + * polling loop. When executing a spin-wait loop, the + * processor can suffer a severe performance penalty when + * exiting the loop because it detects a possible memory order + * violation and flushes the core processor's pipeline. The + * PAUSE instruction provides a hint to the processor that the + * code sequence is a spin-wait loop. The processor uses this + * hint to avoid the memory order violation and prevent the + * pipeline flush. In addition, the PAUSE instruction + * de-pipelines the spin-wait loop to prevent it from + * consuming execution resources excessively. + */ + __asm__ __volatile__( + " rep; nop \n"); +} + +#endif /* __i386__ */ + + +#ifdef __x86_64__ /* AMD Opteron, Intel EM64T */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + /* + * On Opteron, using a non-locking test before the locking instruction + * is a huge loss. On EM64T, it appears to be a wash or small loss, + * so we needn't bother to try to distinguish the sub-architectures. + */ + __asm__ __volatile__( + " lock \n" + " xchgb %0,%1 \n" +: "+q"(_res), "+m"(*lock) +: +: "memory", "cc"); + return (int) _res; +} + +#define SPIN_DELAY() spin_delay() + +static __inline__ void +spin_delay(void) +{ + /* + * Adding a PAUSE in the spin delay loop is demonstrably a no-op on + * Opteron, but it may be of some use on EM64T, so we keep it. + */ + __asm__ __volatile__( + " rep; nop \n"); +} + +#endif /* __x86_64__ */ + + +#if defined(__ia64__) || defined(__ia64) /* Intel Itanium */ +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) tas(lock) + +#ifndef __INTEL_COMPILER + +static __inline__ int +tas(volatile slock_t *lock) +{ + long int ret; + + __asm__ __volatile__( + " xchg4 %0=%1,%2 \n" +: "=r"(ret), "+m"(*lock) +: "r"(1) +: "memory"); + return (int) ret; +} + +#else /* __INTEL_COMPILER */ + +static __inline__ int +tas(volatile slock_t *lock) +{ + int ret; + + ret = _InterlockedExchange(lock,1); /* this is a xchg asm macro */ + + return ret; +} + +#endif /* __INTEL_COMPILER */ +#endif /* __ia64__ || __ia64 */ + + +#if defined(__arm__) || defined(__arm) +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + __asm__ __volatile__( + " swpb %0, %0, [%2] \n" +: "+r"(_res), "+m"(*lock) +: "r"(lock) +: "memory"); + return (int) _res; +} + +#endif /* __arm__ */ + + +/* S/390 and S/390x Linux (32- and 64-bit zSeries) */ +#if defined(__s390__) || defined(__s390x__) +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + int _res = 0; + + __asm__ __volatile__( + " cs %0,%3,0(%2) \n" +: "+d"(_res), "+m"(*lock) +: "a"(lock), "d"(1) +: "memory", "cc"); + return _res; +} + +#endif /* __s390__ || __s390x__ */ + + +#if defined(__sparc__) /* Sparc */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res; + + /* + * See comment in /pg/backend/port/tas/solaris_sparc.s for why this + * uses "ldstub", and that file uses "cas". gcc currently generates + * sparcv7-targeted binaries, so "cas" use isn't possible. + */ + __asm__ __volatile__( + " ldstub [%2], %0 \n" +: "=r"(_res), "+m"(*lock) +: "r"(lock) +: "memory"); + return (int) _res; +} + +#endif /* __sparc__ */ + + +/* PowerPC */ +#if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__) +#define HAS_TEST_AND_SET + +#if defined(__ppc64__) || defined(__powerpc64__) +typedef unsigned long slock_t; +#else +typedef unsigned int slock_t; +#endif + +#define TAS(lock) tas(lock) +/* + * NOTE: per the Enhanced PowerPC Architecture manual, v1.0 dated 7-May-2002, + * an isync is a sufficient synchronization barrier after a lwarx/stwcx loop. + */ +static __inline__ int +tas(volatile slock_t *lock) +{ + slock_t _t; + int _res; + + __asm__ __volatile__( +" lwarx %0,0,%3 \n" +" cmpwi %0,0 \n" +" bne 1f \n" +" addi %0,%0,1 \n" +" stwcx. %0,0,%3 \n" +" beq 2f \n" +"1: li %1,1 \n" +" b 3f \n" +"2: \n" +" isync \n" +" li %1,0 \n" +"3: \n" + +: "=&r"(_t), "=r"(_res), "+m"(*lock) +: "r"(lock) +: "memory", "cc"); + return _res; +} + +/* PowerPC S_UNLOCK is almost standard but requires a "sync" instruction */ +#define S_UNLOCK(lock) \ +do \ +{ \ + __asm__ __volatile__ (" sync \n"); \ + *((volatile slock_t *) (lock)) = 0; \ +} while (0) + +#endif /* powerpc */ + + +/* Linux Motorola 68k */ +#if (defined(__mc68000__) || defined(__m68k__)) && defined(__linux__) +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register int rv; + + __asm__ __volatile__( + " clrl %0 \n" + " tas %1 \n" + " sne %0 \n" +: "=d"(rv), "+m"(*lock) +: +: "memory", "cc"); + return rv; +} + +#endif /* (__mc68000__ || __m68k__) && __linux__ */ + + +/* + * VAXen -- even multiprocessor ones + * (thanks to Tom Ivar Helbekkmo) + */ +#if defined(__vax__) +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register int _res; + + __asm__ __volatile__( + " movl $1, %0 \n" + " bbssi $0, (%2), 1f \n" + " clrl %0 \n" + "1: \n" +: "=&r"(_res), "+m"(*lock) +: "r"(lock) +: "memory"); + return _res; +} + +#endif /* __vax__ */ + + +#if defined(__ns32k__) /* National Semiconductor 32K */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register int _res; + + __asm__ __volatile__( + " sbitb 0, %1 \n" + " sfsd %0 \n" +: "=r"(_res), "+m"(*lock) +: +: "memory"); + return _res; +} + +#endif /* __ns32k__ */ + + +#if defined(__alpha) || defined(__alpha__) /* Alpha */ +/* + * Correct multi-processor locking methods are explained in section 5.5.3 + * of the Alpha AXP Architecture Handbook, which at this writing can be + * found at ftp://ftp.netbsd.org/pub/NetBSD/misc/dec-docs/index.html. + * For gcc we implement the handbook's code directly with inline assembler. + */ +#define HAS_TEST_AND_SET + +typedef unsigned long slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res; + + __asm__ __volatile__( + " ldq $0, %1 \n" + " bne $0, 2f \n" + " ldq_l %0, %1 \n" + " bne %0, 2f \n" + " mov 1, $0 \n" + " stq_c $0, %1 \n" + " beq $0, 2f \n" + " mb \n" + " br 3f \n" + "2: mov 1, %0 \n" + "3: \n" +: "=&r"(_res), "+m"(*lock) +: +: "memory", "0"); + return (int) _res; +} + +#define S_UNLOCK(lock) \ +do \ +{\ + __asm__ __volatile__ (" mb \n"); \ + *((volatile slock_t *) (lock)) = 0; \ +} while (0) + +#endif /* __alpha || __alpha__ */ + + +#if defined(__mips__) && !defined(__sgi) /* non-SGI MIPS */ +/* Note: on SGI we use the OS' mutex ABI, see below */ +/* Note: R10000 processors require a separate SYNC */ +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register volatile slock_t *_l = lock; + register int _res; + register int _tmp; + + __asm__ __volatile__( + " .set push \n" + " .set mips2 \n" + " .set noreorder \n" + " .set nomacro \n" + " ll %0, %2 \n" + " or %1, %0, 1 \n" + " sc %1, %2 \n" + " xori %1, 1 \n" + " or %0, %0, %1 \n" + " sync \n" + " .set pop " +: "=&r" (_res), "=&r" (_tmp), "+R" (*_l) +: +: "memory"); + return _res; +} + +/* MIPS S_UNLOCK is almost standard but requires a "sync" instruction */ +#define S_UNLOCK(lock) \ +do \ +{ \ + __asm__ __volatile__( \ + " .set push \n" \ + " .set mips2 \n" \ + " .set noreorder \n" \ + " .set nomacro \n" \ + " sync \n" \ + " .set pop "); \ + *((volatile slock_t *) (lock)) = 0; \ +} while (0) + +#endif /* __mips__ && !__sgi */ + + +/* These live in s_lock.c, but only for gcc */ + + +#if defined(__m68k__) && !defined(__linux__) /* non-Linux Motorola 68k */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; +#endif + + +#endif /* __GNUC__ */ + + + +/* + * --------------------------------------------------------------------- + * Platforms that use non-gcc inline assembly: + * --------------------------------------------------------------------- + */ + +#if !defined(HAS_TEST_AND_SET) /* We didn't trigger above, let's try here */ + + +#if defined(USE_UNIVEL_CC) /* Unixware compiler */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; + +#define TAS(lock) tas(lock) + +asm int +tas(volatile slock_t *s_lock) +{ +/* UNIVEL wants %mem in column 1, so we don't pg_indent this file */ +%mem s_lock + pushl %ebx + movl s_lock, %ebx + movl $255, %eax + lock + xchgb %al, (%ebx) + popl %ebx +} + +#endif /* defined(USE_UNIVEL_CC) */ + + +#if defined(__alpha) || defined(__alpha__) /* Tru64 Unix Alpha compiler */ +/* + * The Tru64 compiler doesn't support gcc-style inline asm, but it does + * have some builtin functions that accomplish much the same results. + * For simplicity, slock_t is defined as long (ie, quadword) on Alpha + * regardless of the compiler in use. LOCK_LONG and UNLOCK_LONG only + * operate on an int (ie, longword), but that's OK as long as we define + * S_INIT_LOCK to zero out the whole quadword. + */ +#define HAS_TEST_AND_SET + +typedef unsigned long slock_t; + +#include +#define S_INIT_LOCK(lock) (*(lock) = 0) +#define TAS(lock) (__LOCK_LONG_RETRY((lock), 1) == 0) +#define S_UNLOCK(lock) __UNLOCK_LONG(lock) + +#endif /* __alpha || __alpha__ */ + + +#if defined(__hppa) || defined(__hppa__) /* HP PA-RISC, GCC and HP compilers */ +/* + * HP's PA-RISC + * + * See src/backend/port/hpux/tas.c.template for details about LDCWX. Because + * LDCWX requires a 16-byte-aligned address, we declare slock_t as a 16-byte + * struct. The active word in the struct is whichever has the aligned address; + * the other three words just sit at -1. + * + * When using gcc, we can inline the required assembly code. + */ +#define HAS_TEST_AND_SET + +typedef struct +{ + int sema[4]; +} slock_t; + +#define TAS_ACTIVE_WORD(lock) ((volatile int *) (((long) (lock) + 15) & ~15)) + +#if defined(__GNUC__) + +static __inline__ int +tas(volatile slock_t *lock) +{ + volatile int *lockword = TAS_ACTIVE_WORD(lock); + register int lockval; + + __asm__ __volatile__( + " ldcwx 0(0,%2),%0 \n" +: "=r"(lockval), "+m"(*lockword) +: "r"(lockword) +: "memory"); + return (lockval == 0); +} + +#endif /* __GNUC__ */ + +#define S_UNLOCK(lock) (*TAS_ACTIVE_WORD(lock) = -1) + +#define S_INIT_LOCK(lock) \ + do { \ + volatile slock_t *lock_ = (lock); \ + lock_->sema[0] = -1; \ + lock_->sema[1] = -1; \ + lock_->sema[2] = -1; \ + lock_->sema[3] = -1; \ + } while (0) + +#define S_LOCK_FREE(lock) (*TAS_ACTIVE_WORD(lock) != 0) + +#endif /* __hppa || __hppa__ */ + + +#if defined(__hpux) && defined(__ia64) && !defined(__GNUC__) + +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#include +#define TAS(lock) _Asm_xchg(_SZ_W, lock, 1, _LDHINT_NONE) + +#endif /* HPUX on IA64, non gcc */ + + +#if defined(__sgi) /* SGI compiler */ +/* + * SGI IRIX 5 + * slock_t is defined as a unsigned long. We use the standard SGI + * mutex API. + * + * The following comment is left for historical reasons, but is probably + * not a good idea since the mutex ABI is supported. + * + * This stuff may be supplemented in the future with Masato Kataoka's MIPS-II + * assembly from his NECEWS SVR4 port, but we probably ought to retain this + * for the R3000 chips out there. + */ +#define HAS_TEST_AND_SET + +typedef unsigned long slock_t; + +#include "mutex.h" +#define TAS(lock) (test_and_set(lock,1)) +#define S_UNLOCK(lock) (test_then_and(lock,0)) +#define S_INIT_LOCK(lock) (test_then_and(lock,0)) +#define S_LOCK_FREE(lock) (test_then_add(lock,0) == 0) +#endif /* __sgi */ + + +#if defined(sinix) /* Sinix */ +/* + * SINIX / Reliant UNIX + * slock_t is defined as a struct abilock_t, which has a single unsigned long + * member. (Basically same as SGI) + */ +#define HAS_TEST_AND_SET + +#include "abi_mutex.h" +typedef abilock_t slock_t; + +#define TAS(lock) (!acquire_lock(lock)) +#define S_UNLOCK(lock) release_lock(lock) +#define S_INIT_LOCK(lock) init_lock(lock) +#define S_LOCK_FREE(lock) (stat_lock(lock) == UNLOCKED) +#endif /* sinix */ + + +#if defined(_AIX) /* AIX */ +/* + * AIX (POWER) + */ +#define HAS_TEST_AND_SET + +typedef unsigned int slock_t; + +#define TAS(lock) _check_lock(lock, 0, 1) +#define S_UNLOCK(lock) _clear_lock(lock, 0) +#endif /* _AIX */ + + +#if defined (nextstep) /* Nextstep */ +#define HAS_TEST_AND_SET + +typedef struct mutex slock_t; + +#define APC_SLOCK_NONBLOCKING_LOCK_AVAILABLE 0 /* -- APC: non-blocking lock not available in this case -- */ + +#define S_LOCK(lock) mutex_lock(lock) +#define S_UNLOCK(lock) mutex_unlock(lock) +#define S_INIT_LOCK(lock) mutex_init(lock) +/* For Mach, we have to delve inside the entrails of `struct mutex'. Ick! */ +#define S_LOCK_FREE(alock) ((alock)->lock == 0) +#endif /* nextstep */ + + +/* These are in s_lock.c */ + + +#if defined(sun3) /* Sun3 */ +#define HAS_TEST_AND_SET + +typedef unsigned char slock_t; +#endif + + +#if defined(__sun) && (defined(__i386) || defined(__x86_64__) || defined(__sparc__) || defined(__sparc)) +#define HAS_TEST_AND_SET + +#if defined(__i386) || defined(__x86_64__) || defined(__sparcv9) || defined(__sparcv8plus) +typedef unsigned int slock_t; +#else +typedef unsigned char slock_t; +#endif + +extern slock_t pg_atomic_cas(volatile slock_t *lock, slock_t with, + slock_t cmp); + +#define TAS(a) (pg_atomic_cas((a), 1, 0) != 0) +#endif + + +#ifdef WIN32_ONLY_COMPILER +typedef LONG slock_t; + +#define HAS_TEST_AND_SET +#define TAS(lock) (InterlockedCompareExchange(lock, 1, 0)) + +#define SPIN_DELAY() spin_delay() + +static __forceinline void +spin_delay(void) +{ + /* See comment for gcc code. Same code, MASM syntax */ + __asm rep nop; +} + +#endif + + +#endif /* !defined(HAS_TEST_AND_SET) */ + + +/* Blow up if we didn't have any way to do spinlocks */ +#ifndef HAS_TEST_AND_SET +/* -- APC: We have better options in APC than this, that should be specified explicitly so just fail out and notify the user -- */ +#error Spin locking is not available on your platform, please select another locking method (see ./configure --help). +/* #error PostgreSQL does not have native spinlock support on this platform. To continue the compilation, rerun configure using --disable-spinlocks. However, performance will be poor. Please report this to pgsql-bugs@postgresql.org. */ +#endif + + +#else /* !HAVE_SPINLOCKS */ + + +/* + * Fake spinlock implementation using semaphores --- slow and prone + * to fall foul of kernel limits on number of semaphores, so don't use this + * unless you must! The subroutines appear in spin.c. + */ + +/* -- Removed for APC +typedef PGSemaphoreData slock_t; + +extern bool s_lock_free_sema(volatile slock_t *lock); +extern void s_unlock_sema(volatile slock_t *lock); +extern void s_init_lock_sema(volatile slock_t *lock); +extern int tas_sema(volatile slock_t *lock); + +#define S_LOCK_FREE(lock) s_lock_free_sema(lock) +#define S_UNLOCK(lock) s_unlock_sema(lock) +#define S_INIT_LOCK(lock) s_init_lock_sema(lock) +#define TAS(lock) tas_sema(lock) +*/ + +#endif /* HAVE_SPINLOCKS */ + + +/* + * Default Definitions - override these above as needed. + */ + +#define APC_SLOCK_NONBLOCKING_LOCK_AVAILABLE 1 /* -- APC: Non-blocking lock available for this case -- */ + +#if !defined(S_LOCK) +#define S_LOCK(lock) \ + do { \ + if (TAS(lock)) \ + s_lock((lock), __FILE__, __LINE__); \ + } while (0) +#endif /* S_LOCK */ + +#if !defined(S_LOCK_FREE) +#define S_LOCK_FREE(lock) (*(lock) == 0) +#endif /* S_LOCK_FREE */ + +#if !defined(S_UNLOCK) +#define S_UNLOCK(lock) (*((volatile slock_t *) (lock)) = 0) +#endif /* S_UNLOCK */ + +#if !defined(S_INIT_LOCK) +#define S_INIT_LOCK(lock) S_UNLOCK(lock) +#endif /* S_INIT_LOCK */ + +#if !defined(SPIN_DELAY) +#define SPIN_DELAY() ((void) 0) +#endif /* SPIN_DELAY */ + +#if !defined(TAS) +extern int tas(volatile slock_t *lock); /* in port/.../tas.s, or + * s_lock.c */ + +#define TAS(lock) tas(lock) +#endif /* TAS */ + + +/* + * Platform-independent out-of-line support routines + */ +extern void s_lock(volatile slock_t *lock, const char *file, int line); + +/* Support for dynamic adjustment of spins_per_delay */ +#define DEFAULT_SPINS_PER_DELAY 100 + +#if 0 /* -- Removed from APC use -- */ +extern void set_spins_per_delay(int shared_spins_per_delay); +extern int update_spins_per_delay(int shared_spins_per_delay); +#endif + +#endif /* S_LOCK_H */ diff --git a/php_apc.c b/php_apc.c index f7b7a7b..ab20091 100644 --- a/php_apc.c +++ b/php_apc.c @@ -26,7 +26,7 @@ */ -/* $Id: php_apc.c,v 3.154 2007/12/26 22:31:20 rasmus Exp $ */ +/* $Id: php_apc.c,v 3.154.2.2 2008/03/28 18:35:41 gopalv Exp $ */ #include "apc_zend.h" #include "apc_cache.h" @@ -93,9 +93,6 @@ static void php_apc_init_globals(zend_apc_globals* apc_globals TSRMLS_DC) #ifdef ZEND_ENGINE_2 apc_globals->reserved_offset = -1; #endif - apc_globals->localcache = 0; - apc_globals->localcache_size = 0; - apc_globals->lcache = NULL; apc_globals->force_file_update = 0; apc_globals->coredump_unmap = 0; } @@ -201,8 +198,6 @@ STD_PHP_INI_ENTRY("apc.rfc1867_prefix", "upload_", PHP_INI_SYSTEM, OnUpdateStrin STD_PHP_INI_ENTRY("apc.rfc1867_name", "APC_UPLOAD_PROGRESS", PHP_INI_SYSTEM, OnUpdateStringUnempty, rfc1867_name, zend_apc_globals, apc_globals) STD_PHP_INI_ENTRY("apc.rfc1867_freq", "0", PHP_INI_SYSTEM, OnUpdateRfc1867Freq, rfc1867_freq, zend_apc_globals, apc_globals) #endif -STD_PHP_INI_BOOLEAN("apc.localcache", "0", PHP_INI_SYSTEM, OnUpdateBool, localcache, zend_apc_globals, apc_globals) -STD_PHP_INI_ENTRY("apc.localcache.size", "512", PHP_INI_SYSTEM, OnUpdateInt, localcache_size, zend_apc_globals, apc_globals) STD_PHP_INI_BOOLEAN("apc.coredump_unmap", "0", PHP_INI_SYSTEM, OnUpdateBool, coredump_unmap, zend_apc_globals, apc_globals) PHP_INI_END() @@ -231,7 +226,7 @@ static PHP_MINFO_FUNCTION(apc) #else php_info_print_table_row(2, "Locking type", "File Locks"); #endif - php_info_print_table_row(2, "Revision", "$Revision: 3.154 $"); + php_info_print_table_row(2, "Revision", "$Revision: 3.154.2.2 $"); php_info_print_table_row(2, "Build Date", __DATE__ " " __TIME__); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); @@ -284,6 +279,9 @@ static PHP_MSHUTDOWN_FUNCTION(apc) #ifndef ZTS php_apc_shutdown_globals(&apc_globals); #endif +#if HAVE_SIGACTION + apc_shutdown_signals(); +#endif } #ifdef ZTS ts_free_id(apc_globals_id); @@ -300,7 +298,7 @@ static PHP_RINIT_FUNCTION(apc) apc_request_init(TSRMLS_C); #if HAVE_SIGACTION - apc_set_signals(); + apc_set_signals(TSRMLS_C); #endif } return SUCCESS; @@ -451,7 +449,7 @@ PHP_FUNCTION(apc_cache_info) } /* }}} */ -/* {{{ proto void apc_clear_cache() */ +/* {{{ proto void apc_clear_cache([string cache]) */ PHP_FUNCTION(apc_clear_cache) { char *cache_type; @@ -561,33 +559,32 @@ int _apc_store(char *strkey, int strkey_len, const zval *val, const unsigned int HANDLE_BLOCK_INTERRUPTIONS(); APCG(mem_size_ptr) = &mem_size; + APCG(current_cache) = apc_user_cache; if (!(entry = apc_cache_make_user_entry(strkey, strkey_len + 1, val, ttl))) { APCG(mem_size_ptr) = NULL; - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); + APCG(current_cache) = NULL; HANDLE_UNBLOCK_INTERRUPTIONS(); return 0; } if (!apc_cache_make_user_key(&key, strkey, strkey_len + 1, t)) { APCG(mem_size_ptr) = NULL; + APCG(current_cache) = NULL; apc_cache_free_entry(entry); - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); HANDLE_UNBLOCK_INTERRUPTIONS(); return 0; } if (!apc_cache_user_insert(apc_user_cache, key, entry, t, exclusive TSRMLS_CC)) { - APCG(mem_size_ptr) = NULL; apc_cache_free_entry(entry); - apc_cache_expunge(apc_cache,t); - apc_cache_expunge(apc_user_cache,t); + APCG(mem_size_ptr) = NULL; + APCG(current_cache) = NULL; HANDLE_UNBLOCK_INTERRUPTIONS(); return 0; } APCG(mem_size_ptr) = NULL; + APCG(current_cache) = NULL; HANDLE_UNBLOCK_INTERRUPTIONS(); @@ -595,7 +592,7 @@ int _apc_store(char *strkey, int strkey_len, const zval *val, const unsigned int } /* }}} */ -/* {{{ proto int apc_store(string key, zval var [, ttl ]) +/* {{{ proto int apc_store(string key, mixed var [, long ttl ]) */ PHP_FUNCTION(apc_store) { zval *val; @@ -614,7 +611,7 @@ PHP_FUNCTION(apc_store) { } /* }}} */ -/* {{{ proto int apc_add(string key, zval var [, ttl ]) +/* {{{ proto int apc_add(string key, mixed var [, long ttl ]) */ PHP_FUNCTION(apc_add) { zval *val; @@ -636,7 +633,6 @@ PHP_FUNCTION(apc_add) { void *apc_erealloc_wrapper(void *ptr, size_t size) { return _erealloc(ptr, size, 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); } - /* {{{ RETURN_ZVAL for php4 */ #if !defined(ZEND_ENGINE_2) && !defined(RETURN_ZVAL) #define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } @@ -661,10 +657,11 @@ void *apc_erealloc_wrapper(void *ptr, size_t size) { #endif /* }}} */ -/* {{{ proto mixed apc_fetch(mixed key) +/* {{{ proto mixed apc_fetch(mixed key[, bool &success]) */ PHP_FUNCTION(apc_fetch) { zval *key; + zval *success = NULL; HashTable *hash; HashPosition hpos; zval **hentry; @@ -677,7 +674,7 @@ PHP_FUNCTION(apc_fetch) { if(!APCG(enabled)) RETURN_FALSE; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &key, &success) == FAILURE) { return; } @@ -691,6 +688,10 @@ PHP_FUNCTION(apc_fetch) { t = sapi_get_request_time(TSRMLS_C); #endif + if (success) { + ZVAL_BOOL(success, 0); + } + if(Z_TYPE_P(key) != IS_STRING && Z_TYPE_P(key) != IS_ARRAY) { convert_to_string(key); } @@ -727,12 +728,16 @@ PHP_FUNCTION(apc_fetch) { } /* don't set values we didn't find */ zend_hash_move_forward_ex(hash, &hpos); } - RETURN_ZVAL(result, 0, 1); + RETVAL_ZVAL(result, 0, 1); } else { apc_wprint("apc_fetch() expects a string or array of strings."); RETURN_FALSE; } + if (success) { + ZVAL_BOOL(success, 1); + } + return; } /* }}} */ @@ -945,13 +950,26 @@ PHP_FUNCTION(apc_compile_file) { } /* }}} */ +#ifdef ZEND_ENGINE_2 +/* {{{ arginfo */ +static +ZEND_BEGIN_ARG_INFO(php_apc_fetch_arginfo, 0) + ZEND_ARG_INFO(0, "key") + ZEND_ARG_INFO(1, "success") +ZEND_END_ARG_INFO() +/* }}} */ +#else +#define php_apc_fetch_arginfo NULL +#endif + + /* {{{ apc_functions[] */ function_entry apc_functions[] = { PHP_FE(apc_cache_info, NULL) PHP_FE(apc_clear_cache, NULL) PHP_FE(apc_sma_info, NULL) PHP_FE(apc_store, NULL) - PHP_FE(apc_fetch, NULL) + PHP_FE(apc_fetch, php_apc_fetch_arginfo) PHP_FE(apc_delete, NULL) PHP_FE(apc_define_constants, NULL) PHP_FE(apc_load_constants, NULL) diff --git a/php_apc.h b/php_apc.h new file mode 100644 index 0000000..780e46e --- /dev/null +++ b/php_apc.h @@ -0,0 +1,52 @@ +/* + +----------------------------------------------------------------------+ + | APC | + +----------------------------------------------------------------------+ + | Copyright (c) 2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Cowgill | + | George Schlossnagle | + | Rasmus Lerdorf | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Community Connect Inc. in 2002 + and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. + Future revisions and derivatives of this source code must acknowledge + Community Connect Inc. as the original contributor of this module by + leaving this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. + + */ + +/* $Id: php_apc.h,v 3.14 2006/03/12 00:31:45 rasmus Exp $ */ + +#ifndef PHP_APC_H +#define PHP_APC_H + +#include "apc_php.h" +#include "apc_globals.h" + +extern zend_module_entry apc_module_entry; +#define apc_module_ptr &apc_module_entry + +#define phpext_apc_ptr apc_module_ptr + +#endif /* PHP_APC_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 sts=4 + */ diff --git a/tests/apc_001.phpt b/tests/apc_001.phpt new file mode 100644 index 0000000..c1592de --- /dev/null +++ b/tests/apc_001.phpt @@ -0,0 +1,27 @@ +--TEST-- +APC: apc_store/fetch with strings +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- + +===DONE=== + +--EXPECTF-- +string(11) "hello world" +string(11) "hello world" +string(4) "nice" +===DONE=== diff --git a/tests/apc_002.phpt b/tests/apc_002.phpt new file mode 100644 index 0000000..8f22162 --- /dev/null +++ b/tests/apc_002.phpt @@ -0,0 +1,34 @@ +--TEST-- +APC: apc_store/fetch with objects +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +a = true; +var_dump($bar); + +?> +===DONE=== + +--EXPECTF-- +object(foo)#%d (0) { +} +object(foo)#%d (0) { +} +object(foo)#%d (1) { + ["a"]=> + bool(true) +} +===DONE=== diff --git a/tests/apc_003.phpt b/tests/apc_003.phpt new file mode 100644 index 0000000..4f5b5ff --- /dev/null +++ b/tests/apc_003.phpt @@ -0,0 +1,112 @@ +--TEST-- +APC: apc_store/fetch with objects +--SKIPIF-- + +--INI-- +apc.enabled=1 +apc.enable_cli=1 +apc.file_update_protection=0 +--FILE-- +a = true; +var_dump($bar); + +class bar extends foo +{ + public $pub = 'bar'; + protected $pro = 'bar'; + private $pri = 'bar'; // we don't see this, we'd need php 5.1 new serialization + + function __construct() + { + $this->bar = true; + } + + function change() + { + $this->pri = 'mod'; + } +} + +class baz extends bar +{ + private $pri = 'baz'; + + function __construct() + { + parent::__construct(); + $this->baz = true; + } +} + +$baz = new baz; +var_dump($baz); +$baz->change(); +var_dump($baz); +apc_store('baz', $baz); +unset($baz); +var_dump(apc_fetch('baz')); + +?> +===DONE=== + +--EXPECTF-- +object(foo)#%d (0) { +} +object(foo)#%d (0) { +} +object(foo)#%d (1) { + ["a"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri:private"]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro:protected"]=> + string(3) "bar" + ["pri:private"]=> + string(3) "bar" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri:private"]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro:protected"]=> + string(3) "bar" + ["pri:private"]=> + string(3) "mod" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +object(baz)#%d (6) { + ["pri:private"]=> + string(3) "baz" + ["pub"]=> + string(3) "bar" + ["pro:protected"]=> + string(3) "bar" + ["pri:private"]=> + string(3) "mod" + ["bar"]=> + bool(true) + ["baz"]=> + bool(true) +} +===DONE=== diff --git a/tests/skipif.inc b/tests/skipif.inc new file mode 100644 index 0000000..18c75f2 --- /dev/null +++ b/tests/skipif.inc @@ -0,0 +1,6 @@ + -- 1.7.10.4