From: Dragan Dosen Date: Tue, 1 Apr 2008 19:40:51 +0000 (+0200) Subject: New PHP5 APC - version 3.0.18, using PHP5 5.2.0-8+etch10, X-Git-Tag: 3.0.18 X-Git-Url: http://ftp.carnet.hr/carnet-debian/scm?p=php5-apc.git;a=commitdiff_plain;h=3682e0a7a26931aabca2b6e54eb08efd7dc0430b;hp=575ce08215526bb71a967d69d601e77e1afbcd11 New PHP5 APC - version 3.0.18, using PHP5 5.2.0-8+etch10, 20060613+lfs. --- 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 @@ +