From: Dragan Dosen Date: Fri, 4 Jan 2008 00:33:37 +0000 (+0000) Subject: r5: X-Git-Tag: 3.0.18~3 X-Git-Url: http://ftp.carnet.hr/pub/carnet-debian/scm?a=commitdiff_plain;h=27531cfcd6e8fb46402c6771ef4c2d97233917af;p=php5-apc.git r5: php5-apc (3.0.16-1) stable; urgency=low * New PHP5 APC - version 3.0.16, using PHP5 5.2.0-8+etch9, 20060613+lfs. --- diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..c9de553 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,211 @@ +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 +- Added optional shm unmap on a fatal signal feature (Lucas Nealan) +- Added PTHREAD_MUTEX_ADAPTIVE_NP option pthread locks (Paul Saab) +- Minor cleanups (Lucas Nealan) +- Added configure option to enable apc_cache_info('filehits') (Shire) + +3.0.15: 2007-10-18 +- Eliminate a per-request time() syscall (Rasmus) +- Added rfc1867 prefix, name, and freq ini options (Shire) +- Allow deletion of individual user cache entries via apc.php (Sara) +- Fix overzealous cleanup during RSHUTDOWN (Gopal) +- Fix memory alignment and locking issues (Gopal) +- Make apc_compile insert/replace entries (Shire) +- Make mixed inheritance recompile & cache afresh (Gopal) +- Make nostat mode search include_path for canonicalization (Gopal) +- ZTS & other compile fixes (Gopal, Edin, Shire) + +3.0.14: 2007-03-21 +- Build fix (Shire) +- Don't hook the upload hook if APC is disabled (Rasmus) +- Local shadow cache support (Gopal) +- Avoid uneccessary loops over op_arrays for "known" auto-globals (Gopal) +- Fix apc_add() to overwrite timed out user entries (Rasmus) +- Fix double inclusion of files with conditional classes in php4 (Gopal) +- Allocator fixes to reduce fragmentation (Gopal) + +3.0.13: 2007-02-24 +- File upload progress (Rasmus) +- Pthread mutex and spin locks (Shire) +- Recursive zval support for apc_fetch/_store (Shire, Gopal) +- apc.stat_ctime flag for ctime checks (Rasmus) +- Multiple key fetches with apc_fetch (Shire) +- Canary checks for shm memory deallocation (Gopal) +- Add hooks for external optimizer (Shire) +- Obsolete and remove apc optimizer (Gopal) +- APC info changes - cache insert rate, hit and miss rates (Shire) +- Fix apc_load_constants (Gopal) +- Rewrite dump opcode code to use vld (Gopal) +- Use apc_[ewn]print functions for error reporting (Shire) +- Auto global fixes and refactoring (Gopal, Shire) +- Fix memory leaks in object serialization (Ilia) +- Memory cleanup code for destructor order (Gopal) +- Win32 build fixes (Ilia, Wez) +- ZTS and Php 4 build fixes (Bjori) +- Add apc_add() function (Rasmus) +- Add optional limited flag to apc_sma_info() (Rasmus) + +3.0.12p2: 2006-09-05 +- Package version up + +3.0,12p1: 2006-09-05 +- PHP4 build fixes + +3.0.12: 2006-09-05 +- PHP 5.2 compatibility (Gopal) +- TSRM fixes (Gopal) +- Add extra flags to op_array->reserved to improve op array + processing code (Gopal) +- Fix crashes in optimizer and cli mode (Ilia) +- Optimizer fixes for PHP5 (Ilia, Gopal) +- Allow multiple inclusions of a file with a dynamic class (Gopal) +- Php 4 function table and properties fixes (Gopal) +- Fix memory leaks in apc_cache_info (Gopal) + +3.0.11: 2006-08-16 +- Made --enable-apc-mmap the default compile option (for real this time) +- Add an optional flag to apc_cache_info() and some apc.php tweaks to make it + only fetch header information to make it useful when you have tens of + thousands of entries. (Brian Shire) +- 64-bit fixes (George) +- Don't mix Full Path and Inode keys (George) +- Override ZEND_INCLUDE_OR_EVAL opcode (when possible) to speed up use of + require_once() and include_once() statements. (Sara) +- Add a non-blocking write_lock for cache inserts. This is a better approach + to prevent cache slams and deprecates the slam_defense setting. (Rasmus) +- A bit of work on the optimizer. (Sara) +- Various memory issues resolved. (Gopal) + +3.0.10: 2006-03-11 +- Add apc.stat ini flag which defaults to 1. If set to 0, the main script and any fullpath + includes will not be stat'ed for any changes. You will have to restart the server if you + change anything. This mode increases performance quite a bit, especially if you have a + lot of includes. + +- Get rid of the lock safety net hack I added in 3.0.9. It seems to cause more problems + than it solves. I'll need to revisit locking and signal handling at some point soon. + +3.0.9: 2006-03-04 +- Eliminate rand() call when slam_defense is not set (Rasmus) +- Fix for __isset problem (Gopal) +- Rewrite allocator from a "best fit" to a "next fit" algorithm (Rasmus) +- Added a Cache Full counter so we have an idea how many times the segment has filled up causing an expunge (Rasmus) +- Report back the correct number of available bytes in the segment instead of the allocated bytes. (Rasmus) +- Add cache busy flag which is set when an expunge is underway (Rasmus) +- Add automatic serialization of objects in apc_store() (Marcus) +- 64-bit .ini flag fix (Rasmus) +- Static members fix (Gopal) +- sma_cleanup() mem leak fix (Rasmus) +- Fix for http://pecl.php.net/bugs/5311 (Rasmus) +- Fix autoglobals JIT bug (Gopal) +- Fix instance bug (Gopal) +- Add a lock cleanup safety net to request shutdown (Rasmus) +- Fix apc.slam_defense edge-case bug (Rasmus) +- User entry memory usage tracking support (Ilia) +- Allow keys used in apc_store/apc_fetch/apc_delete to be binary safe and prevent conflicts between keys that are found at the start of other keys. (Ilia) + +3.0.8: 2005-08-24 +Fix invalid free in globals destructor introduced in 3.0.7 (Rasmus) +Cache corruption fix in cache-full cleanup code (Gopal) + +3.0.7: 2005-08-16 +- Fix to apc.php to show final segment in frag chart. (Ilia) +- A couple of win32 fixes. (Frank) +- Add apc.enable_cli ini directive. (Rasmus) +- Add test cases. (Marcus) +- Fix apc_define_constants() bug - http://pecl.php.net/bugs/5084 (Rasmus) +- Simplify user cache handling by removing the user_cache_stack (Rasmus) +- Fix apc_fetch() memory corruption (Andrei,Rasmus) +- Added apc.max_file_size INI setting that allows exclusion of large files from being cached. Default file size limit, 1 megabyte. (Ilia) + +3.0.6: 2005-07-30 +- Added apc.php to package.xml file. +- Track per-entry memory usage. (Val) +- Various apc.php fixes and enhancements. (Ralf, Ilia, Rasmus) +- fcntl locking robustness fixes. (Rasmus) +- Shared read-locks where possible. (Rasmus) +- Added file_update_protection configuration parameter. (Rasmus) +- Windows ZTS fixes (Frank) + +3.0.5: 2005-07-27 +- Make it easier for sapis that only populate file_handle->filename to use APC. (Rasmus) +- Support extensions such as bcompiler that need to hook into compile_file. (Val) +- Ralf Becker's apcgui code has now become the default apc.php status page. (Ralf, Rasmus, Ilia) +- Segfault in cache cleanup code (Ilia, Rasmus) + +3.0.4: 2005-07-18 +- Add win32 support (Edin ) +- Add --with-apxs switch to work around problem when loading APC into Apache binary compiled with LFS switches (Rasmus) +- A couple of other minor fixes + +3.0.3: 2005-07-05 +- Fix compile problem against PHP 5.0.x + +3.0.2: 2005-07-05 +- Better shm error message + +3.0.1: 2005-07-05 +- PHP4 build fix + +3.0: 2005-06-23 +- PHP 5.1 support (Arun, Gopal, Rasmus) +- Major Inheritance bug fix (Arun, Gopal) + +2.0: 2003-02-10 +- ground-up rewrite sharing none of the original source code (djc) + +1.0.10: +- merge mmap / shm code to be in one file, module supports both modes now [mpb 2001-05-15] +- added apc.mode config parameter [mpb 2001-05-15] NOTE: You'll have to add + this parameter to your php.ini file to activate apc shm or mmap caching +- generic source cleanup (missing includes, PATH_MAX usage etc) [mpb + 2001-05-15] +- fixed: realpath return result checking in generate_key [mpb 2001-05-15] +- updated: gui updated (extras/apc_gui-1.0.2.tar.gz) +- experminental 'fast' cache-retrieval [djc 2001-05-20] +- fixed regex support [gws 2001-05-16] +- enhanced reader-writer lock support [rg 2001-05-07] + +1.0.9: +- fixed (?) memory alignment bug on 64 bit archiecures +- added many cache visibiliy functions +- added opional fcntl locks under shm version +- numerous bug fixes + +1.0.8: +- added ability to detect and decompile compiled files placed as 'source' + [gws,dw 2001-01-30] +- fixed apc_rstat bug [gws 2001-01-29] +- added hack to support included urls [gws 2001-01-30] +- fixed apc_cache_index [mb 2001-01-31] +- added multiple regex support [gs 2001-02-03] +- added apc_cache_info [mb,gs 2001-02-03] + +1.0.7: +- partially fixed for Solaris [gws 2001-01-29] +- fixed mtime support for relative includes [gws 2001-01-29] +- code cleanup [yg,ta,gws 2001-01-29] + +1.0.6: +- support for mtime in mmap [yg,gws 2001-01-27] +- fixed indexed-array initialization bug [djc,gws 2001-01-27] + +1.0.5: +- support for relative include paths [djc,gws 2001-01-19] +- class member array support fixed [djc 2001-01-18] +- added apc_cache_index [gws 2001-01-18] + +1.0.4: +- support for class hierarchies greater than two levels deep [djc 2001-01-17] + +1.0.3: +- fixed support for class inheritance [djc 2001-01-16] + +1.0.2: +- support for inherited classes [gws 2001-01-15] +- support for intialization of class variables and objects [gws 2001-01-13] + +1.0.1: +- added optional file modification time check [djc 2001-01-12] diff --git a/apc.c b/apc.c new file mode 100644 index 0000000..ad770f8 --- /dev/null +++ b/apc.c @@ -0,0 +1,554 @@ +/* + +----------------------------------------------------------------------+ + | 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.c,v 3.18 2007/11/29 22:15:53 shire Exp $ */ + +#include "apc.h" +#include /* for POSIX regular expressions */ +#include "php.h" + +#define NELEMS(a) (sizeof(a)/sizeof((a)[0])) + +/* {{{ memory allocation wrappers */ + +void* apc_emalloc(size_t n) +{ + void* p = malloc(n); + if (p == NULL) { + apc_eprint("apc_emalloc: malloc failed to allocate %u bytes:", n); + } + return p; +} + +void* apc_erealloc(void* p, size_t n) +{ + p = realloc(p, n); + if (p == NULL) { + apc_eprint("apc_erealloc: realloc failed to allocate %u bytes:", n); + } + return p; +} + +void apc_efree(void* p) +{ + if (p == NULL) { + apc_eprint("apc_efree: attempt to free null pointer"); + } + free(p); +} + +char* apc_estrdup(const char* s) +{ + int len; + char* dup; + + if (s == NULL) { + return NULL; + } + len = strlen(s); + dup = (char*) malloc(len+1); + if (dup == NULL) { + apc_eprint("apc_estrdup: malloc failed to allocate %u bytes:", len+1); + } + memcpy(dup, s, len); + dup[len] = '\0'; + return dup; +} + +void* apc_xstrdup(const char* s, apc_malloc_t f) +{ + return s != NULL ? apc_xmemcpy(s, strlen(s)+1, f) : NULL; +} + +void* apc_xmemcpy(const void* p, size_t n, apc_malloc_t f) +{ + void* q; + + if (p != NULL && (q = f(n)) != NULL) { + memcpy(q, p, n); + return q; + } + return NULL; +} + +/* }}} */ + +/* {{{ console display functions */ + +static void my_log(int level, const char* fmt, va_list args) +{ + static const char* level_strings[] = { + "apc-debug", + "apc-notice", + "apc-warning", + "apc-error" + }; + static const int num_levels = NELEMS(level_strings); + + time_t now; + char* buf; /* for ctime */ + + fflush(stdout); + + if (level < 0) + level = 0; + else if (level >= num_levels) + level = num_levels-1; + + now = time(0); + buf = ctime(&now); /* TODO: replace with reentrant impl */ + buf[24] = '\0'; + + fprintf(stderr, "[%s] [%s] ", buf, level_strings[level]); + vfprintf(stderr, fmt, args); + + if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { + fprintf(stderr, " %s", strerror(errno)); + } + fprintf(stderr, "\n"); + + if (level == APC_ERROR) { + exit(2); + } +} + +void apc_log(int level, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + my_log(level, fmt, args); + va_end(args); +} + +void apc_eprint(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + my_log(APC_ERROR, fmt, args); + va_end(args); +} + +void apc_wprint(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + my_log(APC_WARNING, fmt, args); + va_end(args); +} + +void apc_nprint(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + my_log(APC_NOTICE, fmt, args); + va_end(args); +} + +void apc_dprint(const char* fmt, ...) +{ +#ifdef APC_DEBUG + va_list args; + va_start(args, fmt); + my_log(APC_DBG, fmt, args); + va_end(args); +#endif +} + +/* }}} */ + +/* {{{ string and text manipulation */ + +char* apc_append(const char* s, const char* t) +{ + int slen; + int tlen; + char* p; + + slen = strlen(s); + tlen = strlen(t); + + p = (char*) apc_emalloc((slen + tlen + 1) * sizeof(char)); + memcpy(p, s, slen); + memcpy(p + slen, t, tlen + 1); + + return p; +} + +char* apc_substr(const char* s, int start, int length) +{ + char* substr; + int src_len = strlen(s); + + /* bring start into range */ + if (start < 0) { + start = 0; + } + else if (start >= src_len) { + start = src_len - 1; + } + + /* bring length into range */ + if (length < 0 || src_len - start < length) { + length = src_len - start; + } + + /* create the substring */ + substr = apc_xmemcpy(s + start, length + 1, apc_emalloc); + substr[length] = '\0'; + return substr; +} + +char** apc_tokenize(const char* s, char delim) +{ + char** tokens; /* array of tokens, NULL terminated */ + int size; /* size of tokens array */ + int n; /* index of next token in tokens array */ + int cur; /* current position in input string */ + int end; /* final legal position in input string */ + int next; /* position of next delimiter in input */ + + if (!s) { + return NULL; + } + + size = 2; + n = 0; + cur = 0; + end = strlen(s) - 1; + + tokens = (char**) apc_emalloc(size * sizeof(char*)); + tokens[n] = NULL; + + while (cur <= end) { + /* search for the next delimiter */ + char* p = strchr(s + cur, delim); + next = p ? p-s : end+1; + + /* resize token array if necessary */ + if (n == size-1) { + size *= 2; + tokens = (char**) apc_erealloc(tokens, size * sizeof(char*)); + } + + /* save the current token */ + tokens[n] = apc_substr(s, cur, next-cur); + + tokens[++n] = NULL; + cur = next + 1; + } + + return tokens; +} + +/* }}} */ + +/* {{{ 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; +} +#endif + +int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo) +{ + char** paths; + char *exec_fname; + int exec_fname_length; + int found = 0; + int i; + TSRMLS_FETCH(); + + assert(filename && fileinfo); + + if (IS_ABSOLUTE_PATH(filename, strlen(filename)) && apc_stat(filename, &fileinfo->st_buf) == 0) { + strncpy(fileinfo->fullpath, filename, MAXPATHLEN); + return 0; + } + + paths = apc_tokenize(path, DEFAULT_DIR_SEPARATOR); + if (!paths) + return -1; + + /* 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) { + found = 1; + break; + } + } + + /* check in path of the calling scripts' current working directory */ + /* modified from main/streams/plain_wrapper.c */ + if(!found && zend_is_executing(TSRMLS_C)) { + exec_fname = zend_get_executed_filename(TSRMLS_C); + exec_fname_length = strlen(exec_fname); + while((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if((exec_fname && exec_fname[0] != '[') && exec_fname_length > 0) { + /* 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) { + found = 1; + } + } + } + + /* free the value returned by apc_tokenize */ + for (i = 0; paths[i]; i++) { + apc_efree(paths[i]); + } + apc_efree(paths); + + return found ? 0 : -1; +} + +/* }}} */ + +/* {{{ regular expression wrapper functions */ + +typedef struct { + regex_t *reg; + unsigned char type; +} apc_regex; + +void* apc_regex_compile_array(char* patterns[]) +{ + apc_regex** regs; + int npat; + int i; + + if (!patterns) + return NULL; + + /* count the number of patterns in patterns */ + for (npat = 0; patterns[npat] != NULL; npat++) {} + + if (npat == 0) + return NULL; + + /* allocate the array of compiled expressions */ + regs = (apc_regex**) apc_emalloc(sizeof(apc_regex*) * (npat + 1)); + for (i = 0; i <= npat; i++) { + regs[i] = (apc_regex *) apc_emalloc(sizeof(apc_regex)); + regs[i]->reg = NULL; + regs[i]->type = APC_NEGATIVE_MATCH; + } + + /* compile the expressions */ + for (i = 0; i < npat; i++) { + char *pattern = patterns[i]; + if(pattern[0]=='+') { regs[i]->type = APC_POSITIVE_MATCH; pattern = patterns[i]+sizeof(char); } + else if(pattern[0]=='-') { regs[i]->type = APC_NEGATIVE_MATCH; pattern = patterns[i]+sizeof(char); } + + regs[i]->reg = (regex_t*) apc_emalloc(sizeof(regex_t)); + + if (regcomp(regs[i]->reg, pattern, REG_EXTENDED | REG_NOSUB) != 0) { + apc_wprint("apc_regex_compile_array: invalid expression '%s'", + pattern); + + apc_regex_destroy_array(regs); + + return NULL; + } + } + + return (void*) regs; +} + +void apc_regex_destroy_array(void* p) +{ + if (p != NULL) { + apc_regex** regs = (apc_regex**) p; + int i; + + for (i = 0; regs[i]->reg != NULL; i++) { + regfree(regs[i]->reg); + apc_efree(regs[i]->reg); + apc_efree(regs[i]); + } + apc_efree(regs); + } +} + +int apc_regex_match_array(void* p, const char* input) +{ + apc_regex** regs; + int i; + + if (!p) + return 0; + + regs = (apc_regex**) p; + for (i = 0; regs[i]->reg != NULL; i++) + if (regexec(regs[i]->reg, input, 0, NULL, 0) == 0) + return (int)(regs[i]->type); + + return 0; +} + +/* }}} */ + +/* {{{ crc32 implementation */ + +/* this table was generated by crc32gen() */ +static unsigned int crc32tab[] = { + /* 0 */ 0x00000000, 0x3b83984b, 0x77073096, 0x4c84a8dd, + /* 4 */ 0xee0e612c, 0xd58df967, 0x990951ba, 0xa28ac9f1, + /* 8 */ 0x076dc419, 0x3cee5c52, 0x706af48f, 0x4be96cc4, + /* 12 */ 0xe963a535, 0xd2e03d7e, 0x9e6495a3, 0xa5e70de8, + /* 16 */ 0x0edb8832, 0x35581079, 0x79dcb8a4, 0x425f20ef, + /* 20 */ 0xe0d5e91e, 0xdb567155, 0x97d2d988, 0xac5141c3, + /* 24 */ 0x09b64c2b, 0x3235d460, 0x7eb17cbd, 0x4532e4f6, + /* 28 */ 0xe7b82d07, 0xdc3bb54c, 0x90bf1d91, 0xab3c85da, + /* 32 */ 0x1db71064, 0x2634882f, 0x6ab020f2, 0x5133b8b9, + /* 36 */ 0xf3b97148, 0xc83ae903, 0x84be41de, 0xbf3dd995, + /* 40 */ 0x1adad47d, 0x21594c36, 0x6ddde4eb, 0x565e7ca0, + /* 44 */ 0xf4d4b551, 0xcf572d1a, 0x83d385c7, 0xb8501d8c, + /* 48 */ 0x136c9856, 0x28ef001d, 0x646ba8c0, 0x5fe8308b, + /* 52 */ 0xfd62f97a, 0xc6e16131, 0x8a65c9ec, 0xb1e651a7, + /* 56 */ 0x14015c4f, 0x2f82c404, 0x63066cd9, 0x5885f492, + /* 60 */ 0xfa0f3d63, 0xc18ca528, 0x8d080df5, 0xb68b95be, + /* 64 */ 0x3b6e20c8, 0x00edb883, 0x4c69105e, 0x77ea8815, + /* 68 */ 0xd56041e4, 0xeee3d9af, 0xa2677172, 0x99e4e939, + /* 72 */ 0x3c03e4d1, 0x07807c9a, 0x4b04d447, 0x70874c0c, + /* 76 */ 0xd20d85fd, 0xe98e1db6, 0xa50ab56b, 0x9e892d20, + /* 80 */ 0x35b5a8fa, 0x0e3630b1, 0x42b2986c, 0x79310027, + /* 84 */ 0xdbbbc9d6, 0xe038519d, 0xacbcf940, 0x973f610b, + /* 88 */ 0x32d86ce3, 0x095bf4a8, 0x45df5c75, 0x7e5cc43e, + /* 92 */ 0xdcd60dcf, 0xe7559584, 0xabd13d59, 0x9052a512, + /* 96 */ 0x26d930ac, 0x1d5aa8e7, 0x51de003a, 0x6a5d9871, + /* 100 */ 0xc8d75180, 0xf354c9cb, 0xbfd06116, 0x8453f95d, + /* 104 */ 0x21b4f4b5, 0x1a376cfe, 0x56b3c423, 0x6d305c68, + /* 108 */ 0xcfba9599, 0xf4390dd2, 0xb8bda50f, 0x833e3d44, + /* 112 */ 0x2802b89e, 0x138120d5, 0x5f058808, 0x64861043, + /* 116 */ 0xc60cd9b2, 0xfd8f41f9, 0xb10be924, 0x8a88716f, + /* 120 */ 0x2f6f7c87, 0x14ece4cc, 0x58684c11, 0x63ebd45a, + /* 124 */ 0xc1611dab, 0xfae285e0, 0xb6662d3d, 0x8de5b576, + /* 128 */ 0x76dc4190, 0x4d5fd9db, 0x01db7106, 0x3a58e94d, + /* 132 */ 0x98d220bc, 0xa351b8f7, 0xefd5102a, 0xd4568861, + /* 136 */ 0x71b18589, 0x4a321dc2, 0x06b6b51f, 0x3d352d54, + /* 140 */ 0x9fbfe4a5, 0xa43c7cee, 0xe8b8d433, 0xd33b4c78, + /* 144 */ 0x7807c9a2, 0x438451e9, 0x0f00f934, 0x3483617f, + /* 148 */ 0x9609a88e, 0xad8a30c5, 0xe10e9818, 0xda8d0053, + /* 152 */ 0x7f6a0dbb, 0x44e995f0, 0x086d3d2d, 0x33eea566, + /* 156 */ 0x91646c97, 0xaae7f4dc, 0xe6635c01, 0xdde0c44a, + /* 160 */ 0x6b6b51f4, 0x50e8c9bf, 0x1c6c6162, 0x27eff929, + /* 164 */ 0x856530d8, 0xbee6a893, 0xf262004e, 0xc9e19805, + /* 168 */ 0x6c0695ed, 0x57850da6, 0x1b01a57b, 0x20823d30, + /* 172 */ 0x8208f4c1, 0xb98b6c8a, 0xf50fc457, 0xce8c5c1c, + /* 176 */ 0x65b0d9c6, 0x5e33418d, 0x12b7e950, 0x2934711b, + /* 180 */ 0x8bbeb8ea, 0xb03d20a1, 0xfcb9887c, 0xc73a1037, + /* 184 */ 0x62dd1ddf, 0x595e8594, 0x15da2d49, 0x2e59b502, + /* 188 */ 0x8cd37cf3, 0xb750e4b8, 0xfbd44c65, 0xc057d42e, + /* 192 */ 0x4db26158, 0x7631f913, 0x3ab551ce, 0x0136c985, + /* 196 */ 0xa3bc0074, 0x983f983f, 0xd4bb30e2, 0xef38a8a9, + /* 200 */ 0x4adfa541, 0x715c3d0a, 0x3dd895d7, 0x065b0d9c, + /* 204 */ 0xa4d1c46d, 0x9f525c26, 0xd3d6f4fb, 0xe8556cb0, + /* 208 */ 0x4369e96a, 0x78ea7121, 0x346ed9fc, 0x0fed41b7, + /* 212 */ 0xad678846, 0x96e4100d, 0xda60b8d0, 0xe1e3209b, + /* 216 */ 0x44042d73, 0x7f87b538, 0x33031de5, 0x088085ae, + /* 220 */ 0xaa0a4c5f, 0x9189d414, 0xdd0d7cc9, 0xe68ee482, + /* 224 */ 0x5005713c, 0x6b86e977, 0x270241aa, 0x1c81d9e1, + /* 228 */ 0xbe0b1010, 0x8588885b, 0xc90c2086, 0xf28fb8cd, + /* 232 */ 0x5768b525, 0x6ceb2d6e, 0x206f85b3, 0x1bec1df8, + /* 236 */ 0xb966d409, 0x82e54c42, 0xce61e49f, 0xf5e27cd4, + /* 240 */ 0x5edef90e, 0x655d6145, 0x29d9c998, 0x125a51d3, + /* 244 */ 0xb0d09822, 0x8b530069, 0xc7d7a8b4, 0xfc5430ff, + /* 248 */ 0x59b33d17, 0x6230a55c, 0x2eb40d81, 0x153795ca, + /* 252 */ 0xb7bd5c3b, 0x8c3ec470, 0xc0ba6cad, 0xfb39f4e6, +}; + +unsigned int apc_crc32(const char* buf, int len) +{ + int i; + int k; + unsigned int crc; + + /* preconditioning */ + crc = 0xFFFFFFFF; + + for (i = 0; i < len; i++) { + k = (crc ^ buf[i]) & 0x000000FF; + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[k]; + } + + /* postconditioning */ + return ~crc; +} + +/* crc32gen: generate the nth (0..255) crc32 table value */ +#if 0 +static unsigned long crc32gen(int n) +{ + int i; + unsigned long crc; + + crc = n; + for (i = 8; i >= 0; i--) { + if (crc & 1) { + crc = (crc >> 1) ^ 0xEDB88320; + } + else { + crc >>= 1; + } + } + return crc; +} +#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 new file mode 100644 index 0000000..c2b2ce7 --- /dev/null +++ b/apc_globals.h @@ -0,0 +1,119 @@ +/* + +----------------------------------------------------------------------+ + | 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_globals.h,v 3.70 2007/12/26 22:46:33 rasmus Exp $ */ + +#ifndef APC_GLOBALS_H +#define APC_GLOBALS_H + +#define APC_VERSION "3.0.16" + +#include "apc_cache.h" +#include "apc_stack.h" +#include "apc_php.h" + +ZEND_BEGIN_MODULE_GLOBALS(apc) + /* configuration parameters */ + zend_bool enabled; /* if true, apc is enabled (defaults to true) */ + long shm_segments; /* number of shared memory segments to use */ + long shm_size; /* size of each shared memory segment (in MB) */ + long num_files_hint; /* parameter to apc_cache_create */ + long user_entries_hint; + long gc_ttl; /* parameter to apc_cache_create */ + long ttl; /* parameter to apc_cache_create */ + long user_ttl; +#if APC_MMAP + char *mmap_file_mask; /* mktemp-style file-mask to pass to mmap */ +#endif + char** filters; /* array of regex filters that prevent caching */ + + /* module variables */ + zend_bool initialized; /* true if module was initialized */ + apc_stack_t* cache_stack; /* the stack of cached executable code */ + zend_bool cache_by_default; /* true if files should be cached unless filtered out */ + /* false if files should only be cached if filtered in */ + long slam_defense; /* Probability of a process not caching an uncached file */ + size_t* mem_size_ptr; /* size of blocks allocated to file being cached (NULL outside my_compile_file) */ + long file_update_protection; /* Age in seconds before a file is eligible to be cached - 0 to disable */ + zend_bool enable_cli; /* Flag to override turning APC off for CLI */ + long max_file_size; /* Maximum size of file, in bytes that APC will be allowed to cache */ + long slam_rand; /* A place to store the slam rand value for the request */ + zend_bool fpstat; /* true if fullpath includes should be stat'ed */ + zend_bool stat_ctime; /* true if ctime in addition to mtime should be checked */ + zend_bool write_lock; /* true for a global write lock */ + zend_bool report_autofilter; /* true for auto-filter warnings */ + zend_bool include_once; /* Override the ZEND_INCLUDE_OR_EVAL opcode handler to avoid pointless fopen()s [still experimental] */ + apc_optimize_function_t apc_optimize_function; /* optimizer function callback */ +#ifdef MULTIPART_EVENT_FORMDATA + zend_bool rfc1867; /* Flag to enable rfc1867 handler */ + char* rfc1867_prefix; /* Key prefix */ + char* rfc1867_name; /* Name of hidden field to activate upload progress/key suffix */ + double rfc1867_freq; /* Update frequency as percentage or bytes */ +#endif + HashTable *copied_zvals; /* my_copy recursion detection list */ +#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 */ +ZEND_END_MODULE_GLOBALS(apc) + +/* (the following declaration is defined in php_apc.c) */ +ZEND_EXTERN_MODULE_GLOBALS(apc) + +#ifdef ZTS +# define APCG(v) TSRMG(apc_globals_id, zend_apc_globals *, v) +#else +# define APCG(v) (apc_globals.v) +#endif + +/* True globals */ +extern apc_cache_t* apc_cache; /* the global compiler cache */ +extern apc_cache_t* apc_user_cache; /* the global user content cache */ +extern void* apc_compiled_filters; /* compiled filters */ + +#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_main.c b/apc_main.c new file mode 100644 index 0000000..50b673d --- /dev/null +++ b/apc_main.c @@ -0,0 +1,701 @@ +/* + +----------------------------------------------------------------------+ + | 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_main.c,v 3.103 2007/11/14 19:46:46 shire Exp $ */ + +#include "apc_php.h" +#include "apc_main.h" +#include "apc.h" +#include "apc_lock.h" +#include "apc_cache.h" +#include "apc_compile.h" +#include "apc_globals.h" +#include "apc_sma.h" +#include "apc_stack.h" +#include "apc_zend.h" +#include "SAPI.h" +#if PHP_API_VERSION <= 20020918 +#if HAVE_APACHE +#ifdef APC_PHP4_STAT +#undef XtOffsetOf +#include "httpd.h" +#endif +#endif +#endif + +/* {{{ module variables */ + +/* pointer to the original Zend engine compile_file function */ +typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC); +static zend_compile_t *old_compile_file; + +/* }}} */ + +/* {{{ get/set old_compile_file (to interact with other extensions that need the compile hook) */ +static zend_compile_t* set_compile_hook(zend_compile_t *ptr) +{ + zend_compile_t *retval = old_compile_file; + + if (ptr != NULL) old_compile_file = ptr; + return retval; +} +/* }}} */ + +/* {{{ install_function */ +static int install_function(apc_function_t fn TSRMLS_DC) +{ + int status = + zend_hash_add(EG(function_table), + fn.name, + fn.name_len+1, + apc_copy_function_for_execution(fn.function), + sizeof(fn.function[0]), + NULL); + + if (status == FAILURE) { + /* apc_eprint("Cannot redeclare %s()", fn.name); */ + } + + return status; +} +/* }}} */ + +/* {{{ install_class */ +static int install_class(apc_class_t cl TSRMLS_DC) +{ + zend_class_entry* class_entry = cl.class_entry; + zend_class_entry* parent = NULL; + int status; +#ifdef ZEND_ENGINE_2 + zend_class_entry** allocated_ce = NULL; +#endif + + + /* Special case for mangled names. Mangled names are unique to a file. + * There is no way two classes with the same mangled name will occur, + * unless a file is included twice. And if in case, a file is included + * twice, all mangled name conflicts can be ignored and the class redeclaration + * error may be deferred till runtime of the corresponding DECLARE_CLASS + * calls. + */ + + if(cl.name_len != 0 && cl.name[0] == '\0') { + if(zend_hash_exists(CG(class_table), cl.name, cl.name_len+1)) { + return SUCCESS; + } + } + +#ifdef ZEND_ENGINE_2 + /* + * XXX: We need to free this somewhere... + */ + allocated_ce = apc_php_malloc(sizeof(zend_class_entry*)); + + if(!allocated_ce) { + return FAILURE; + } + + *allocated_ce = +#endif + class_entry = + apc_copy_class_entry_for_execution(cl.class_entry, + cl.is_derived); + + + /* restore parent class pointer for compile-time inheritance */ + if (cl.parent_name != NULL) { +#ifdef ZEND_ENGINE_2 + zend_class_entry** parent_ptr = NULL; + /* + * zend_lookup_class has to be due to presence of __autoload, + * just looking up the EG(class_table) is not enough in php5! + * Even more dangerously, thanks to __autoload and people using + * class names as filepaths for inclusion, this has to be case + * sensitive. zend_lookup_class automatically does a case_fold + * internally, but passes the case preserved version to __autoload. + * Aside: Do NOT pass *strlen(cl.parent_name)+1* because + * zend_lookup_class does it internally anyway! + */ + status = zend_lookup_class(cl.parent_name, + strlen(cl.parent_name), + &parent_ptr TSRMLS_CC); +#else + status = zend_hash_find(EG(class_table), + cl.parent_name, + strlen(cl.parent_name)+1, + (void**) &parent); +#endif + if (status == FAILURE) { + if(APCG(report_autofilter)) { + apc_wprint("Dynamic inheritance detected for class %s", cl.name); + } + class_entry->parent = NULL; + return status; + } + else { +#ifdef ZEND_ENGINE_2 + parent = *parent_ptr; +#endif + class_entry->parent = parent; +#ifdef ZEND_ENGINE_2 + zend_do_inheritance(class_entry, parent TSRMLS_CC); +#else + zend_do_inheritance(class_entry, parent); +#endif + } + + + } + +#ifdef ZEND_ENGINE_2 + status = zend_hash_add(EG(class_table), + cl.name, + cl.name_len+1, + allocated_ce, + sizeof(zend_class_entry*), + NULL); +#else + status = zend_hash_add(EG(class_table), + cl.name, + cl.name_len+1, + class_entry, + sizeof(zend_class_entry), + NULL); +#endif + + if (status == FAILURE) { + apc_eprint("Cannot redeclare class %s", cl.name); + } + return status; +} +/* }}} */ + +/* {{{ uninstall_class */ +static int uninstall_class(apc_class_t cl TSRMLS_DC) +{ + int status; + +#ifdef ZEND_ENGINE_2 + status = zend_hash_del(EG(class_table), + cl.name, + cl.name_len+1); +#else + status = zend_hash_del(EG(class_table), + cl.name, + cl.name_len+1); +#endif + if (status == FAILURE) { + apc_eprint("Cannot delete class %s", cl.name); + } + return status; +} +/* }}} */ + +/* {{{ compare_file_handles */ +static int compare_file_handles(void* a, void* b) +{ + zend_file_handle* fh1 = (zend_file_handle*)a; + zend_file_handle* fh2 = (zend_file_handle*)b; + return (fh1->type == fh2->type && + fh1->filename == fh2->filename && + fh1->opened_path == fh2->opened_path); +} +/* }}} */ + +/* {{{ cached_compile */ +static zend_op_array* cached_compile(zend_file_handle* h, + int type TSRMLS_DC) +{ + apc_cache_entry_t* cache_entry; + int i, ii; + + cache_entry = (apc_cache_entry_t*) apc_stack_top(APCG(cache_stack)); + assert(cache_entry != NULL); + + if (cache_entry->data.file.classes) { + for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) { + if(install_class(cache_entry->data.file.classes[i] TSRMLS_CC) == FAILURE) { + goto default_compile; + } + } + } + + if (cache_entry->data.file.functions) { + for (i = 0; cache_entry->data.file.functions[i].function != NULL; i++) { + install_function(cache_entry->data.file.functions[i] TSRMLS_CC); + } + } + + + return apc_copy_op_array_for_execution(NULL, cache_entry->data.file.op_array TSRMLS_CC); + +default_compile: + + if(APCG(report_autofilter)) { + apc_wprint("Autofiltering %s", h->opened_path); + } + + if(cache_entry->data.file.classes) { + for(ii = 0; ii < i ; ii++) { + uninstall_class(cache_entry->data.file.classes[ii] TSRMLS_CC); + } + } + + apc_stack_pop(APCG(cache_stack)); /* pop out cache_entry */ + + apc_cache_release(apc_cache, cache_entry); + + /* 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; +} +/* }}} */ + +/* {{{ my_compile_file + Overrides zend_compile_file */ +static zend_op_array* my_compile_file(zend_file_handle* h, + int type TSRMLS_DC) +{ + apc_cache_key_t key; + apc_cache_entry_t* cache_entry; + zend_op_array* op_array; + int num_functions, num_classes, ret; + zend_op_array* alloc_op_array; + apc_function_t* alloc_functions; + apc_class_t* alloc_classes; + time_t t; + char *path; + size_t mem_size; + + if (!APCG(enabled) || (apc_cache_busy(apc_cache) && !APCG(localcache))) { + return old_compile_file(h, type TSRMLS_CC); + } + + /* check our regular expression filters */ + if (APCG(filters) && apc_compiled_filters) { + int ret = apc_regex_match_array(apc_compiled_filters, h->filename); + if(ret == APC_NEGATIVE_MATCH || (ret != APC_POSITIVE_MATCH && !APCG(cache_by_default))) { + return old_compile_file(h, type TSRMLS_CC); + } + } else if(!APCG(cache_by_default)) { + return old_compile_file(h, type TSRMLS_CC); + } + +#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 + +#ifdef __DEBUG_APC__ + fprintf(stderr,"1. h->opened_path=[%s] h->filename=[%s]\n", h->opened_path?h->opened_path:"null",h->filename); +#endif + + /* try to create a cache key; if we fail, give up on caching */ + if (!apc_cache_make_file_key(&key, h->filename, PG(include_path), t TSRMLS_CC)) { + return old_compile_file(h, type TSRMLS_CC); + } + + + 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); + } + } else { + cache_entry = NULL; + } + + if (cache_entry != NULL) { + int dummy = 1; + if (h->opened_path == NULL) { + h->opened_path = estrdup(cache_entry->data.file.filename); + } + 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) { +#ifdef APC_FILEHITS + /* If the file comes from the cache, add it to the global request file list */ + add_next_index_string(APCG(filehits), h->filename, 1); +#endif + return op_array; + } + if(APCG(report_autofilter)) { + apc_wprint("Recompiling %s", h->opened_path); + } + /* 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)); + + /* compile the file using the default compile function */ + op_array = old_compile_file(h, type TSRMLS_CC); + if (op_array == NULL) { + return NULL; + } + /* + * Basically this will cause a file only to be cached on a percentage + * of the attempts. This is to avoid cache slams when starting up a + * very busy server or when modifying files on a very busy live server. + * There is no point having many processes all trying to cache the same + * file at the same time. By introducing a chance of being cached + * we theoretically cut the cache slam problem by the given percentage. + * For example if apc.slam_defense is set to 66 then 2/3 of the attempts + * to cache an uncached file will be ignored. + */ + if(APCG(slam_defense)) { + if(APCG(slam_rand)==-1) { + APCG(slam_rand) = (int)(100.0*rand()/(RAND_MAX+1.0)); + } + if(APCG(slam_rand) < APCG(slam_defense)) { + return op_array; + } + } + + /* Make sure the mtime reflects the files last known mtime in the case of fpstat==0 */ + if(key.type == APC_CACHE_KEY_FPFILE) { + apc_fileinfo_t fileinfo; + struct stat *tmp_buf = NULL; + if(!strcmp(SG(request_info).path_translated, h->filename)) { + tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */ + } + if(tmp_buf) { + fileinfo.st_buf = *tmp_buf; + } else { + if (apc_search_paths(h->filename, PG(include_path), &fileinfo) != 0) { +#ifdef __DEBUG_APC__ + fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated); +#endif + return op_array; + } + } + key.mtime = fileinfo.st_buf.st_mtime; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + if(!apc_cache_write_lock(apc_cache)) { + HANDLE_UNBLOCK_INTERRUPTIONS(); + return op_array; + } + } +#endif + + mem_size = 0; + APCG(mem_size_ptr) = &mem_size; + 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; +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + return op_array; + } + + 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; +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + return op_array; + } + 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; +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + return op_array; + } + + path = h->opened_path; + if(!path) path=h->filename; + +#ifdef __DEBUG_APC__ + fprintf(stderr,"2. h->opened_path=[%s] h->filename=[%s]\n", h->opened_path?h->opened_path:"null",h->filename); +#endif + + if(!(cache_entry = apc_cache_make_file_entry(path, alloc_op_array, alloc_functions, alloc_classes))) { + 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; +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + return op_array; + } + APCG(mem_size_ptr) = NULL; + cache_entry->mem_size = mem_size; + + 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); + } + } + +#if NONBLOCKING_LOCK_AVAILABLE + if(APCG(write_lock)) { + apc_cache_write_unlock(apc_cache); + } +#endif + HANDLE_UNBLOCK_INTERRUPTIONS(); + + return op_array; +} +/* }}} */ + +/* {{{ module init and shutdown */ + +int apc_module_init(int module_number TSRMLS_DC) +{ + /* apc initialization */ +#if APC_MMAP + apc_sma_init(APCG(shm_segments), APCG(shm_size)*1024*1024, APCG(mmap_file_mask)); +#else + apc_sma_init(APCG(shm_segments), APCG(shm_size)*1024*1024, NULL); +#endif + apc_cache = apc_cache_create(APCG(num_files_hint), APCG(gc_ttl), APCG(ttl)); + apc_user_cache = apc_cache_create(APCG(user_entries_hint), APCG(gc_ttl), APCG(user_ttl)); + + apc_compiled_filters = apc_regex_compile_array(APCG(filters)); + + /* override compilation */ + old_compile_file = zend_compile_file; + zend_compile_file = my_compile_file; + REGISTER_LONG_CONSTANT("\000apc_magic", (long)&set_compile_hook, CONST_PERSISTENT | CONST_CS); + + APCG(initialized) = 1; + return 0; +} + +int apc_module_shutdown(TSRMLS_D) +{ + if (!APCG(initialized)) + return 0; + + /* restore compilation */ + zend_compile_file = old_compile_file; + + /* + * In case we got interrupted by a SIGTERM or something else during execution + * we may have cache entries left on the stack that we need to check to make + * sure that any functions or classes these may have added to the global function + * and class tables are removed before we blow away the memory that hold them. + * + * This is merely to remove memory leak warnings - as the process is terminated + * immediately after shutdown. The following while loop can be removed without + * affecting anything else. + */ + while (apc_stack_size(APCG(cache_stack)) > 0) { + int i; + apc_cache_entry_t* cache_entry = (apc_cache_entry_t*) apc_stack_pop(APCG(cache_stack)); + if (cache_entry->data.file.functions) { + for (i = 0; cache_entry->data.file.functions[i].function != NULL; i++) { + zend_hash_del(EG(function_table), + cache_entry->data.file.functions[i].name, + cache_entry->data.file.functions[i].name_len+1); + } + } + if (cache_entry->data.file.classes) { + for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) { + zend_hash_del(EG(class_table), + cache_entry->data.file.classes[i].name, + cache_entry->data.file.classes[i].name_len+1); + } + } + apc_cache_release(apc_cache, cache_entry); + } + + apc_cache_destroy(apc_cache); + apc_cache_destroy(apc_user_cache); + apc_sma_cleanup(); + + APCG(initialized) = 0; + return 0; +} + +/* }}} */ + +/* {{{ 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) +{ + /* The execution stack was unwound, which prevented us from decrementing + * the reference counts on active cache entries in `my_execute`. + */ + while (apc_stack_size(APCG(cache_stack)) > 0) { + int i; + zend_class_entry* zce = NULL; + void ** centry = (void*)(&zce); +#ifdef ZEND_ENGINE_2 + zend_class_entry** pzce = NULL; +#endif + + apc_cache_entry_t* cache_entry = + (apc_cache_entry_t*) apc_stack_pop(APCG(cache_stack)); + + if (cache_entry->data.file.classes) { + for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) { +#ifdef ZEND_ENGINE_2 + centry = (void**)&pzce; /* a triple indirection to get zend_class_entry*** */ +#endif + if(zend_hash_find(EG(class_table), + cache_entry->data.file.classes[i].name, + cache_entry->data.file.classes[i].name_len+1, + (void**)centry) == FAILURE) + { + /* double inclusion of conditional classes ends up failing + * this lookup the second time around. + */ + continue; + } + +#ifdef ZEND_ENGINE_2 + zce = *pzce; +#endif + zend_hash_del(EG(class_table), + cache_entry->data.file.classes[i].name, + cache_entry->data.file.classes[i].name_len+1); + + apc_free_class_entry_after_execution(zce); + } + } + apc_cache_release(apc_cache, cache_entry); + } + if(APCG(localcache)) { + apc_local_cache_cleanup(APCG(lcache)); + } +} +/* }}} */ + +/* + * 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 new file mode 100644 index 0000000..8b9cccb --- /dev/null +++ b/apc_mmap.c @@ -0,0 +1,139 @@ +/* + +----------------------------------------------------------------------+ + | 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_mmap.c,v 3.7 2007/12/20 23:00:51 shire Exp $ */ + +#include "apc.h" + +#if APC_MMAP + +#include +#include +#include + +/* + * Some operating systems (like FreeBSD) have a MAP_NOSYNC flag that + * tells whatever update daemons might be running to not flush dirty + * vm pages to disk unless absolutely necessary. My guess is that + * most systems that don't have this probably default to only synching + * to disk when absolutely necessary. + */ +#ifndef MAP_NOSYNC +#define MAP_NOSYNC 0 +#endif + +void *apc_mmap(char *file_mask, size_t size) +{ + void* shmaddr; /* the shared memory address */ + + /* If no filename was provided, do an anonymous mmap */ + if(!file_mask || (file_mask && !strlen(file_mask))) { + shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + } else { + int fd; + + /* + * If the filemask contains .shm we try to do a POSIX-compliant shared memory + * backed mmap which should avoid synchs on some platforms. At least on + * FreeBSD this implies MAP_NOSYNC and on Linux it is equivalent of mmap'ing + * a file in a mounted shmfs. For this to work on Linux you need to make sure + * you actually have shmfs mounted. Also on Linux, make sure the file_mask you + * pass in has a leading / and no other /'s. eg. /apc.shm.XXXXXX + * On FreeBSD these are mapped onto the regular filesystem so you can put whatever + * path you want here. + */ + if(strstr(file_mask,".shm")) { + mktemp(file_mask); + fd = shm_open(file_mask, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); + if(fd == -1) { + apc_eprint("apc_mmap: shm_open on %s failed:", file_mask); + return (void *)-1; + } + if (ftruncate(fd, size) < 0) { + close(fd); + shm_unlink(file_mask); + apc_eprint("apc_mmap: ftruncate failed:"); + return (void *)-1; + } + shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + shm_unlink(file_mask); + close(fd); + } + /* + * Support anonymous mmap through the /dev/zero interface as well + */ + else if(!strcmp(file_mask,"/dev/zero")) { + fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR); + if(fd == -1) { + apc_eprint("apc_mmap: open on /dev/zero failed:"); + return (void *)-1; + } + shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + close(fd); + } + /* + * Otherwise we do a normal filesystem mmap + */ + else { + fd = mkstemp(file_mask); + if(fd == -1) { + apc_eprint("apc_mmap: mkstemp on %s failed:", file_mask); + return (void *)-1; + } + if (ftruncate(fd, size) < 0) { + close(fd); + unlink(file_mask); + apc_eprint("apc_mmap: ftruncate failed:"); + } + shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOSYNC, fd, 0); + close(fd); + unlink(file_mask); + } + } + if((int)shmaddr == -1) { + apc_eprint("apc_mmap: mmap failed:"); + } + return shmaddr; +} + +void apc_unmap(void* shmaddr, size_t size) +{ + if (munmap(shmaddr, size) < 0) { + apc_wprint("apc_unmap: munmap 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_pthreadmutex.c b/apc_pthreadmutex.c new file mode 100644 index 0000000..e3b2c26 --- /dev/null +++ b/apc_pthreadmutex.c @@ -0,0 +1,111 @@ +/* + +----------------------------------------------------------------------+ + | 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.c,v 3.3 2007/12/21 01:36:51 shire Exp $ */ + +#include "apc_pthreadmutex.h" + +#ifdef APC_PTHREADMUTEX_LOCKS + +pthread_mutex_t *apc_pthreadmutex_create(pthread_mutex_t *lock) +{ + int result; + pthread_mutexattr_t* attr; + attr = malloc(sizeof(pthread_mutexattr_t)); + + result = pthread_mutexattr_init(attr); + if(result == ENOMEM) { + apc_eprint("pthread mutex error: Insufficient memory exists to create the mutex attribute object."); + } else if(result == EINVAL) { + apc_eprint("pthread mutex error: attr does not point to writeable memory."); + } else if(result == EFAULT) { + apc_eprint("pthread mutex error: attr is an invalid pointer."); + } + +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + result = pthread_mutexattr_settype(attr, PTHREAD_MUTEX_ADAPTIVE_NP); + if (result == EINVAL) { + apc_eprint("pthread_mutexattr_settype: unable to set adaptive mutexes"); + } +#endif + + /* pthread_mutexattr_settype(attr, PTHREAD_MUTEX_ERRORCHECK); */ + result = pthread_mutexattr_setpshared(attr, PTHREAD_PROCESS_SHARED); + if(result == EINVAL) { + apc_eprint("pthread mutex error: attr is not an initialized mutex attribute object, or pshared is not a valid process-shared state setting."); + } else if(result == EFAULT) { + apc_eprint("pthread mutex error: attr is an invalid pointer."); + } else if(result == ENOTSUP) { + apc_eprint("pthread mutex error: pshared was set to PTHREAD_PROCESS_SHARED."); + } + + if(pthread_mutex_init(lock, attr)) { + apc_eprint("unable to initialize pthread lock"); + } + return lock; +} + +void apc_pthreadmutex_destroy(pthread_mutex_t *lock) +{ + return; /* we don't actually destroy the mutex, as it would destroy it for all processes */ +} + +void apc_pthreadmutex_lock(pthread_mutex_t *lock) +{ + int result; + result = pthread_mutex_lock(lock); + if(result == EINVAL) { + apc_eprint("unable to obtain pthread lock (EINVAL)"); + } else if(result == EDEADLK) { + apc_eprint("unable to obtain pthread lock (EDEADLK)"); + } +} + +void apc_pthreadmutex_unlock(pthread_mutex_t *lock) +{ + if(pthread_mutex_unlock(lock)) { + apc_eprint("unable to unlock pthread lock"); + } +} + +zend_bool apc_pthreadmutex_nonblocking_lock(pthread_mutex_t *lock) +{ + int rval; + rval = pthread_mutex_trylock(lock); + if(rval == EBUSY) { /* Lock is already held */ + return 0; + } else if(rval == 0) { /* Obtained lock */ + return 1; + } else { /* Other error */ + apc_eprint("unable to obtain pthread trylock"); + return 0; + } +} + + +#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_sma.c b/apc_sma.c new file mode 100644 index 0000000..20b8e45 --- /dev/null +++ b/apc_sma.c @@ -0,0 +1,628 @@ +/* + +----------------------------------------------------------------------+ + | 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_sma.c,v 1.69 2007/12/26 21:35:39 gopalv Exp $ */ + +#include "apc_sma.h" +#include "apc.h" +#include "apc_globals.h" +#include "apc_lock.h" +#include "apc_shm.h" +#include +#if APC_MMAP +void *apc_mmap(char *file_mask, size_t size); +void apc_unmap(void* shmaddr, size_t size); +#endif + +/* {{{ locking macros */ +#define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c); } +#define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c); } +#define UNLOCK(c) { apc_lck_unlock(c); HANDLE_UNBLOCK_INTERRUPTIONS(); } +/* }}} */ + +enum { DEFAULT_NUMSEG=1, DEFAULT_SEGSIZE=30*1024*1024 }; + +static int sma_initialized = 0; /* true if the sma has been initialized */ +static unsigned int sma_numseg; /* number of shm segments to allow */ +static size_t sma_segsize; /* size of each shm segment */ +static size_t* sma_segments; /* array of shm segment ids */ +static void** sma_shmaddrs; /* array of shm segment addresses */ +static int sma_lastseg = 0; /* index of MRU segment */ + +typedef struct header_t header_t; +struct header_t { + apc_lck_t sma_lock; /* segment lock, MUST BE ALIGNED for futex locks */ + size_t segsize; /* size of entire segment */ + size_t avail; /* bytes available (not necessarily contiguous) */ + size_t nfoffset; /* start next fit search from this offset */ +#if ALLOC_DISTRIBUTION + size_t adist[30]; +#endif +}; + + +/* do not enable for threaded http servers */ +/* #define __APC_SMA_DEBUG__ 1 */ + +#ifdef __APC_SMA_DEBUG__ +/* global counter for identifying blocks + * Technically it is possible to do the same + * using offsets, but double allocations of the + * same offset can happen. */ +static volatile size_t block_id = 0; +#endif + +#define APC_SMA_CANARIES 1 + +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 */ +#ifdef APC_SMA_CANARIES + size_t canary; /* canary to check for memory overwrites */ +#endif +#ifdef __APC_SMA_DEBUG__ + size_t id; /* identifier for the memory block */ +#endif +}; + +/* The macros BLOCKAT and OFFSET are used for convenience throughout this + * module. Both assume the presence of a variable shmaddr that points to the + * beginning of the shared memory segment in question. */ + +#define BLOCKAT(offset) ((block_t*)((char *)shmaddr + offset)) +#define OFFSET(block) ((size_t)(((char*)block) - (char*)shmaddr)) + +/* Canary macros for setting, checking and resetting memory canaries */ +#ifdef APC_SMA_CANARIES + #define SET_CANARY(v) (v)->canary = 0x42424242 + #define CHECK_CANARY(v) assert((v)->canary == 0x42424242) + #define RESET_CANARY(v) (v)->canary = -42 +#else + #define SET_CANARY(v) + #define CHECK_CANARY(v) + #define RESET_CANARY(v) +#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)))) +#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) +{ + header_t* header; /* header of shared memory segment */ + block_t* prv; /* block prior to working block */ + block_t* cur; /* working block in list */ + block_t* prvnextfit; /* block before next fit */ + size_t realsize; /* actual size of block needed, including header */ + size_t last_offset; /* save the last search offset */ + int wrapped=0; + const size_t block_size = ALIGNWORD(sizeof(struct block_t)); + + realsize = ALIGNWORD(size + block_size); + + /* + * First, insure that the segment contains at least realsize free bytes, + * even if they are not contiguous. + */ + header = (header_t*) shmaddr; + if (header->avail < realsize) { + return -1; + } + + prvnextfit = 0; /* initially null (no fit) */ + last_offset = 0; + + /* If we have a next fit offset, start searching from there */ + if(header->nfoffset) { + prv = BLOCKAT(header->nfoffset); + /* if prv is the last block, jump to the beginning */ + if(prv->next == 0) { + prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + wrapped = 1; + } + } else { + prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + } + + CHECK_CANARY(prv); + + while (prv->next != header->nfoffset) { + cur = BLOCKAT(prv->next); +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(cur); +#endif + /* If it can fit realiszie bytes in cur block, stop searching */ + if (cur->size >= realsize) { + prvnextfit = prv; + break; + } + last_offset = prv->next; + prv = cur; + if(wrapped && (prv->next >= header->nfoffset)) break; + + /* Check to see if we need to wrap around and search from the top */ + if(header->nfoffset && prv->next == 0) { + prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(prv); +#endif + last_offset = 0; + wrapped = 1; + } + } + + if (prvnextfit == 0) { + header->nfoffset = 0; + return -1; + } + + prv = prvnextfit; + cur = BLOCKAT(prv->next); + + CHECK_CANARY(prv); + CHECK_CANARY(cur); + + if (cur->size == realsize || (cur->size > realsize && cur->size < (realsize + (MINBLOCKSIZE * 2)))) { + /* cur is big enough for realsize, but too small to split - unlink it */ + prv->next = cur->next; + } + else { + block_t* nxt; /* the new block (chopped part of cur) */ + size_t nxtoffset; /* offset of the block currently after cur */ + size_t oldsize; /* size of cur before split */ + + /* nextfit is too big; split it into two smaller blocks */ + nxtoffset = cur->next; + oldsize = cur->size; + prv->next += realsize; /* skip over newly allocated block */ + cur->size = realsize; /* Set the size of this new block */ + nxt = BLOCKAT(prv->next); + nxt->next = nxtoffset; /* Re-link the shortened block */ + nxt->size = oldsize - realsize; /* and fix the size */ + SET_CANARY(nxt); +#ifdef __APC_SMA_DEBUG__ + nxt->id = -1; +#endif + } + + /* update the block header */ + header->avail -= cur->size; +#if ALLOC_DISTRIBUTION + header->adist[(int)(log(size)/log(2))]++; +#endif + + header->nfoffset = last_offset; + + SET_CANARY(cur); +#ifdef __APC_SMA_DEBUG__ + cur->id = ++block_id; + fprintf(stderr, "allocate(realsize=%d,size=%d,id=%d)\n", (int)(size), (int)(cur->size), cur->id); +#endif + + return OFFSET(cur) + block_size; +} +/* }}} */ + +/* {{{ sma_deallocate: deallocates the block at the given offset */ +static int sma_deallocate(void* shmaddr, int offset) +{ + header_t* header; /* header of shared memory segment */ + block_t* cur; /* the new block to insert */ + block_t* prv; /* the block before cur */ + block_t* nxt; /* the block after cur */ + size_t size; /* size of deallocated block */ + + offset -= ALIGNWORD(sizeof(struct block_t)); + assert(offset >= 0); + + /* find position of new block in free list */ + cur = BLOCKAT(offset); + prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + + CHECK_CANARY(cur); + +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(prv); + fprintf(stderr, "free(%p, size=%d,id=%d)\n", cur, (int)(cur->size), cur->id); +#endif + while (prv->next != 0 && prv->next < offset) { + prv = BLOCKAT(prv->next); +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(prv); +#endif + } + + CHECK_CANARY(prv); + + /* insert new block after prv */ + cur->next = prv->next; + prv->next = offset; + +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(cur); + cur->id = -1; +#endif + + /* update the block header */ + header = (header_t*) shmaddr; + header->avail += cur->size; + size = cur->size; + + if (((char *)prv) + prv->size == (char *) cur) { + /* cur and prv share an edge, combine them */ + prv->size += cur->size; + prv->next = cur->next; + RESET_CANARY(cur); + cur = prv; + } + + nxt = BLOCKAT(cur->next); + + if (((char *)cur) + cur->size == (char *) nxt) { + /* cur and nxt shared an edge, combine them */ + cur->size += nxt->size; + cur->next = nxt->next; +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(nxt); + nxt->id = -1; /* assert this or set it ? */ +#endif + RESET_CANARY(nxt); + } + header->nfoffset = 0; /* Reset the next fit search marker */ + + return size; +} +/* }}} */ + +/* {{{ apc_sma_init */ + +void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask) +{ + int i; + + if (sma_initialized) { + return; + } + sma_initialized = 1; + +#if APC_MMAP + /* + * I don't think multiple anonymous mmaps makes any sense + * so force sma_numseg to 1 in this case + */ + if(!mmap_file_mask || + (mmap_file_mask && !strlen(mmap_file_mask)) || + (mmap_file_mask && !strcmp(mmap_file_mask, "/dev/zero"))) { + sma_numseg = 1; + } else { + sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG; + } +#else + sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG; +#endif + + sma_segsize = segsize > 0 ? segsize : DEFAULT_SEGSIZE; + + sma_segments = (size_t*) apc_emalloc(sma_numseg*sizeof(size_t)); + sma_shmaddrs = (void**) apc_emalloc(sma_numseg*sizeof(void*)); + + for (i = 0; i < sma_numseg; i++) { + header_t* header; + block_t* block; + void* shmaddr; + +#if APC_MMAP + sma_segments[i] = sma_segsize; + sma_shmaddrs[i] = apc_mmap(mmap_file_mask, sma_segsize); + if(sma_numseg != 1) memcpy(&mmap_file_mask[strlen(mmap_file_mask)-6], "XXXXXX", 6); +#else + sma_segments[i] = apc_shm_create(NULL, i, sma_segsize); + sma_shmaddrs[i] = apc_shm_attach(sma_segments[i]); +#endif + shmaddr = sma_shmaddrs[i]; + + header = (header_t*) shmaddr; + apc_lck_create(NULL, 0, 1, header->sma_lock); + header->segsize = sma_segsize; + header->avail = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t)); + header->nfoffset = 0; +#if ALLOC_DISTRIBUTION + { + int j; + for(j=0; j<30; j++) header->adist[j] = 0; + } +#endif + block = BLOCKAT(ALIGNWORD(sizeof(header_t))); + block->size = 0; + block->next = ALIGNWORD(sizeof(header_t)) + ALIGNWORD(sizeof(block_t)); + SET_CANARY(block); +#ifdef __APC_SMA_DEBUG__ + block->id = -1; +#endif + block = BLOCKAT(block->next); + block->size = header->avail; + block->next = 0; + SET_CANARY(block); +#ifdef __APC_SMA_DEBUG__ + block->id = -1; +#endif + } +} +/* }}} */ + +/* {{{ apc_sma_cleanup */ +void apc_sma_cleanup() +{ + int i; + + assert(sma_initialized); + + for (i = 0; i < sma_numseg; i++) { + apc_lck_destroy(((header_t*)sma_shmaddrs[i])->sma_lock); +#if APC_MMAP + apc_unmap(sma_shmaddrs[i], sma_segments[i]); +#else + apc_shm_detach(sma_shmaddrs[i]); +#endif + } + sma_initialized = 0; + apc_efree(sma_segments); + apc_efree(sma_shmaddrs); +} +/* }}} */ + +/* {{{ apc_sma_malloc */ +void* apc_sma_malloc(size_t n) +{ + int off; + int i; + + TSRMLS_FETCH(); + assert(sma_initialized); + LOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); + + off = sma_allocate(sma_shmaddrs[sma_lastseg], n); + if (off != -1) { + 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); + return p; + } + UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock); + + for (i = 0; i < sma_numseg; i++) { + if (i == sma_lastseg) { + continue; + } + LOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + off = sma_allocate(sma_shmaddrs[i], n); + if (off != -1) { + void* p = (void *)(((char *)(sma_shmaddrs[i])) + off); + if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; } + UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + sma_lastseg = i; + return p; + } + UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + } + + return NULL; +} +/* }}} */ + +/* {{{ apc_sma_realloc */ +void* apc_sma_realloc(void *p, size_t n) +{ + apc_sma_free(p); + return apc_sma_malloc(n); +} +/* }}} */ + +/* {{{ apc_sma_strdup */ +char* apc_sma_strdup(const char* s) +{ + void* q; + int len; + + if(!s) return NULL; + + len = strlen(s)+1; + q = apc_sma_malloc(len); + if(!q) return NULL; + memcpy(q, s, len); + return q; +} +/* }}} */ + +/* {{{ apc_sma_free */ +void apc_sma_free(void* p) +{ + int i; + size_t offset; + size_t d_size; + TSRMLS_FETCH(); + + if (p == NULL) { + return; + } + + assert(sma_initialized); + + for (i = 0; i < sma_numseg; i++) { + LOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + offset = (size_t)((char *)p - (char *)(sma_shmaddrs[i])); + if (p >= sma_shmaddrs[i] && offset < sma_segsize) { + 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); + return; + } + UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + } + + apc_eprint("apc_sma_free: could not locate address %p", p); +} +/* }}} */ + +/* {{{ apc_sma_info */ +apc_sma_info_t* apc_sma_info(zend_bool limited) +{ + apc_sma_info_t* info; + apc_sma_link_t** link; + int i; + char* shmaddr; + block_t* prv; + + if (!sma_initialized) { + return NULL; + } + + info = (apc_sma_info_t*) apc_emalloc(sizeof(apc_sma_info_t)); + info->num_seg = sma_numseg; + info->seg_size = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t)); + + info->list = apc_emalloc(info->num_seg * sizeof(apc_sma_link_t*)); + for (i = 0; i < sma_numseg; i++) { + info->list[i] = NULL; + } + + if(limited) return info; + + /* For each segment */ + for (i = 0; i < sma_numseg; i++) { + RDLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + shmaddr = sma_shmaddrs[i]; + prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + + link = &info->list[i]; + + /* For each block in this segment */ + while (prv->next != 0) { + block_t* cur = BLOCKAT(prv->next); +#ifdef __APC_SMA_DEBUG__ + CHECK_CANARY(cur); +#endif + + *link = apc_emalloc(sizeof(apc_sma_link_t)); + (*link)->size = cur->size; + (*link)->offset = prv->next; + (*link)->next = NULL; + link = &(*link)->next; + + prv = cur; + } + UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock); + } + + return info; +} +/* }}} */ + +/* {{{ apc_sma_free_info */ +void apc_sma_free_info(apc_sma_info_t* info) +{ + int i; + + for (i = 0; i < info->num_seg; i++) { + apc_sma_link_t* p = info->list[i]; + while (p) { + apc_sma_link_t* q = p; + p = p->next; + apc_efree(q); + } + } + apc_efree(info->list); + apc_efree(info); +} +/* }}} */ + +/* {{{ apc_sma_get_avail_mem */ +size_t apc_sma_get_avail_mem() +{ + size_t avail_mem = 0; + int i; + + for (i = 0; i < sma_numseg; i++) { + header_t* header = (header_t*) sma_shmaddrs[i]; + avail_mem += header->avail; + } + return avail_mem; +} +/* }}} */ + +#if ALLOC_DISTRIBUTION +size_t *apc_sma_get_alloc_distribution(void) { + header_t* header = (header_t*) sma_shmaddrs[0]; + return header->adist; +} +#endif + +#if 0 +/* {{{ apc_sma_check_integrity */ +void apc_sma_check_integrity() +{ + int i; + + /* For each segment */ + for (i = 0; i < sma_numseg; i++) { + char* shmaddr = sma_shmaddrs[i]; + header_t* header = (header_t*) shmaddr; + block_t* prv = BLOCKAT(ALIGNWORD(sizeof(header_t))); + int avail = 0; + + /* For each block in this segment */ + while (prv->next != 0) { + block_t* cur = BLOCKAT(prv->next); + avail += cur->size; + prv = cur; + } + + assert(avail == header->avail); + } +} +/* }}} */ +#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/config.m4 b/config.m4 new file mode 100644 index 0000000..c36d0df --- /dev/null +++ b/config.m4 @@ -0,0 +1,238 @@ +dnl +dnl $Id: config.m4,v 3.30 2007/12/26 22:31:20 rasmus Exp $ +dnl + +AC_MSG_CHECKING(whether apc needs to get compiler flags from apxs) +AC_ARG_WITH(apxs, +[ --with-apxs[=FILE] Get compiler flags from apxs -q. Provide the + pathname to the Apache apxs tool; defaults to "apxs".],[ + if test "$withval" != "no"; then + if test "$withval" = "yes"; then + APXS=apxs + $APXS -q CFLAGS >/dev/null 2>&1 + if test "$?" != "0" && test -x /usr/sbin/apxs; then #SUSE 6.x + APXS=/usr/sbin/apxs + elif test -x /usr/bin/apxs2; then + APXS=/usr/bin/apxs2 + elif test -x /usr/sbin/apxs2; then + APXS=/usr/sbin/apxs2 + fi + else + PHP_EXPAND_PATH($withval, APXS) + fi + + $APXS -q CFLAGS >/dev/null 2>&1 + if test "$?" != "0"; then + AC_MSG_RESULT() + AC_MSG_RESULT() + AC_MSG_RESULT([Sorry, I was not able to successfully run APXS. Possible reasons:]) + AC_MSG_RESULT() + AC_MSG_RESULT([1. Perl is not installed;]) + AC_MSG_RESULT([2. Apache was not compiled with DSO support (--enable-module=so);]) + AC_MSG_RESULT([3. 'apxs' is not in your path. Try to use --with-apxs=/path/to/apxs]) + AC_MSG_RESULT([The output of $APXS follows]) + $APXS -q CFLAGS + AC_MSG_ERROR([Aborting]) + fi + + APC_CFLAGS=`$APXS -q CFLAGS` + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +],[ + AC_MSG_RESULT(no) +]) + +PHP_ARG_ENABLE(apc, whether to enable APC support, +[ --enable-apc Enable APC support]) + +AC_MSG_CHECKING(Checking whether we should enable cache request file info) +AC_ARG_ENABLE(apc-filehits, +[ --enable-apc-filehits Enable per request file info about files used from the APC cache (ie: apc_cache_info('filehits')) ], +[ + PHP_APC_FILEHITS=$enableval + AC_MSG_RESULT($enableval) +], +[ + PHP_APC_FILEHITS=no + AC_MSG_RESULT(no) +]) + + + +AC_MSG_CHECKING(Checking whether we should use mmap) +AC_ARG_ENABLE(apc-mmap, +[ --disable-apc-mmap + Disable mmap support and use IPC shm instead], +[ + PHP_APC_MMAP=$enableval + AC_MSG_RESULT($enableval) +], [ + PHP_APC_MMAP=yes + AC_MSG_RESULT(yes) +]) + +AC_MSG_CHECKING(Checking whether we should use semaphore locking instead of fcntl) +AC_ARG_ENABLE(apc-sem, +[ --enable-apc-sem + Enable semaphore locks instead of fcntl], +[ + PHP_APC_SEM=$enableval + AC_MSG_RESULT($enableval) +], [ + PHP_APC_SEM=no + AC_MSG_RESULT(no) +]) + +AC_MSG_CHECKING(Checking whether we should use futex locking) +AC_ARG_ENABLE(apc-futex, +[ --enable-apc-futex + Enable linux futex based locks EXPERIMENTAL ], +[ + PHP_APC_FUTEX=$enableval + AC_MSG_RESULT($enableval) +], +[ + PHP_APC_FUTEX=no + AC_MSG_RESULT(no) +]) + +if test "$PHP_APC_FUTEX" != "no"; then + AC_CHECK_HEADER(linux/futex.h, , [ AC_MSG_ERROR([futex.h not found. Please verify you that are running a 2.5 or older linux kernel and that futex support is enabled.]); ] ) +fi + +AC_MSG_CHECKING(Checking whether we should use pthread mutex locking) +AC_ARG_ENABLE(apc-pthreadmutex, +[ --disable-apc-pthreadmutex + Disable pthread mutex locking ], +[ + PHP_APC_PTHREADMUTEX=no + AC_MSG_RESULT(no) +], +[ + PHP_APC_PTHREADMUTEX=yes + AC_MSG_RESULT(yes) +]) +if test "$PHP_APC_PTHREADMUTEX" != "no"; then + orig_LIBS="$LIBS" + LIBS="$LIBS -lpthread" + AC_TRY_RUN( + [ + #include + #include + main() { + pthread_mutex_t mutex; + pthread_mutexattr_t attr; + + if(pthread_mutexattr_init(&attr)) { + puts("Unable to initialize pthread attributes (pthread_mutexattr_init)."); + return -1; + } + if(pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED)) { + puts("Unable to set PTHREAD_PROCESS_SHARED (pthread_mutexattr_setpshared), your system may not support shared mutex's."); + return -1; + } + if(pthread_mutex_init(&mutex, &attr)) { + puts("Unable to initialize the mutex (pthread_mutex_init)."); + return -1; + } + if(pthread_mutexattr_destroy(&attr)) { + puts("Unable to destroy mutex attributes (pthread_mutexattr_destroy)."); + return -1; + } + if(pthread_mutex_destroy(&mutex)) { + puts("Unable to destroy mutex (pthread_mutex_destroy)."); + return -1; + } + + puts("pthread mutex's are supported!"); + return 0; + } + ], + [ dnl -Success- + PHP_ADD_LIBRARY(pthread) + ], + [ dnl -Failure- + AC_MSG_WARN([It doesn't appear that pthread mutex's are supported on your system]) + PHP_APC_PTHREADMUTEX=no + ], + [ + PHP_ADD_LIBRARY(pthread) + ] + ) + LIBS="$orig_LIBS" +fi + +AC_MSG_CHECKING(Checking whether we should use spin locks) +AC_ARG_ENABLE(apc-spinlocks, +[ --enable-apc-spinlocks + Enable spin locks EXPERIMENTAL ], +[ + PHP_APC_SPINLOCKS=$enableval + AC_MSG_RESULT($enableval) +], +[ + PHP_APC_SPINLOCKS=no + AC_MSG_RESULT(no) +]) + +if test "$PHP_APC" != "no"; then + test "$PHP_APC_MMAP" != "no" && AC_DEFINE(APC_MMAP, 1, [ ]) + test "$PHP_APC_FILEHITS" != "no" && AC_DEFINE(APC_FILEHITS, 1, [ ]) + + if test "$PHP_APC_SEM" != "no"; then + AC_DEFINE(APC_SEM_LOCKS, 1, [ ]) + elif test "$PHP_APC_FUTEX" != "no"; then + AC_DEFINE(APC_FUTEX_LOCKS, 1, [ ]) + elif test "$PHP_APC_SPINLOCKS" != "no"; then + AC_DEFINE(APC_SPIN_LOCKS, 1, [ ]) + elif test "$PHP_APC_PTHREADMUTEX" != "no"; then + AC_DEFINE(APC_PTHREADMUTEX_LOCKS, 1, [ ]) + fi + + AC_CHECK_FUNCS(sigaction) + AC_CACHE_CHECK(for union semun, php_cv_semun, + [ + AC_TRY_COMPILE([ +#include +#include +#include + ], [union semun x;], [ + php_cv_semun=yes + ],[ + php_cv_semun=no + ]) + ]) + if test "$php_cv_semun" = "yes"; then + AC_DEFINE(HAVE_SEMUN, 1, [ ]) + else + AC_DEFINE(HAVE_SEMUN, 0, [ ]) + fi + + apc_sources="apc.c php_apc.c \ + apc_cache.c \ + apc_compile.c \ + apc_debug.c \ + apc_fcntl.c \ + apc_main.c \ + apc_mmap.c \ + apc_sem.c \ + apc_shm.c \ + apc_futex.c \ + apc_pthreadmutex.c \ + apc_spin.c \ + pgsql_s_lock.c \ + apc_sma.c \ + apc_stack.c \ + apc_zend.c \ + apc_rfc1867.c \ + apc_signal.c " + + PHP_CHECK_LIBRARY(rt, shm_open, [PHP_ADD_LIBRARY(rt,,APC_SHARED_LIBADD)]) + PHP_NEW_EXTENSION(apc, $apc_sources, $ext_shared,, \\$(APC_CFLAGS)) + PHP_SUBST(APC_SHARED_LIBADD) + PHP_SUBST(APC_CFLAGS) + AC_DEFINE(HAVE_APC, 1, [ ]) +fi + diff --git a/debian/changelog b/debian/changelog index a77c7b2..4277ece 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +php5-apc (3.0.16-1) stable; urgency=low + + * New PHP5 APC - version 3.0.16, using PHP5 5.2.0-8+etch9, + 20060613+lfs. + + -- Dragan Dosen Fri, 4 Dec 2008 00:44:23 +0100 + php5-apc (3.0.15-1) stable; urgency=low * Initial release for Debian etch distribution. diff --git a/debian/control b/debian/control index 12dbc85..9219ebf 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,7 @@ Standards-Version: 3.7.2 Package: php5-apc Section: web Architecture: i386 -Depends: ${shlibs:Depends}, ${misc:Depends}, ${php:Depends}, php5-common (= 5.2.0-8+etch7) +Depends: ${shlibs:Depends}, ${misc:Depends}, ${php:Depends}, php5-common (= 5.2.0-8+etch9) Conflicts: php4-apc Description: APC module for PHP5 This package provides a module for APC functions. diff --git a/php_apc.c b/php_apc.c new file mode 100644 index 0000000..f7b7a7b --- /dev/null +++ b/php_apc.c @@ -0,0 +1,991 @@ +/* + +----------------------------------------------------------------------+ + | 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: php_apc.c,v 3.154 2007/12/26 22:31:20 rasmus Exp $ */ + +#include "apc_zend.h" +#include "apc_cache.h" +#include "apc_main.h" +#include "apc_sma.h" +#include "apc_lock.h" +#include "php_globals.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "SAPI.h" +#include "rfc1867.h" +#include "php_apc.h" +#if PHP_API_VERSION <= 20020918 +#if HAVE_APACHE +#ifdef APC_PHP4_STAT +#undef XtOffsetOf +#include "httpd.h" +#endif +#endif +#endif + +#if HAVE_SIGACTION +#include "apc_signal.h" +#endif + +/* {{{ PHP_FUNCTION declarations */ +PHP_FUNCTION(apc_cache_info); +PHP_FUNCTION(apc_clear_cache); +PHP_FUNCTION(apc_sma_info); +PHP_FUNCTION(apc_store); +PHP_FUNCTION(apc_fetch); +PHP_FUNCTION(apc_delete); +PHP_FUNCTION(apc_compile_file); +PHP_FUNCTION(apc_define_constants); +PHP_FUNCTION(apc_load_constants); +PHP_FUNCTION(apc_add); +/* }}} */ + +/* {{{ ZEND_DECLARE_MODULE_GLOBALS(apc) */ +ZEND_DECLARE_MODULE_GLOBALS(apc) + +/* True globals */ +apc_cache_t* apc_cache = NULL; +apc_cache_t* apc_user_cache = NULL; +void* apc_compiled_filters = NULL; + +static void php_apc_init_globals(zend_apc_globals* apc_globals TSRMLS_DC) +{ + apc_globals->filters = NULL; + apc_globals->initialized = 0; + apc_globals->cache_stack = apc_stack_create(0); + apc_globals->cache_by_default = 1; + apc_globals->slam_defense = 0; + apc_globals->mem_size_ptr = NULL; + apc_globals->fpstat = 1; + apc_globals->stat_ctime = 0; + apc_globals->write_lock = 1; + apc_globals->report_autofilter = 0; + apc_globals->apc_optimize_function = NULL; +#ifdef MULTIPART_EVENT_FORMDATA + apc_globals->rfc1867 = 0; +#endif + apc_globals->copied_zvals = NULL; +#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; +} + +static void php_apc_shutdown_globals(zend_apc_globals* apc_globals TSRMLS_DC) +{ + /* deallocate the ignore patterns */ + if (apc_globals->filters != NULL) { + int i; + for (i=0; apc_globals->filters[i] != NULL; i++) { + apc_efree(apc_globals->filters[i]); + } + apc_efree(apc_globals->filters); + } + + /* the stack should be empty */ + assert(apc_stack_size(apc_globals->cache_stack) == 0); + + /* apc cleanup */ + apc_stack_destroy(apc_globals->cache_stack); + + /* the rest of the globals are cleaned up in apc_module_shutdown() */ +} + +/* }}} */ + +/* {{{ PHP_INI */ + +static PHP_INI_MH(OnUpdate_filters) /* {{{ */ +{ + APCG(filters) = apc_tokenize(new_value, ','); + return SUCCESS; +} +/* }}} */ + +static PHP_INI_MH(OnUpdateShmSegments) /* {{{ */ +{ +#if APC_MMAP + if(atoi(new_value)!=1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "apc.shm_segments setting ignored in MMAP mode"); + } + APCG(shm_segments) = 1; +#else + APCG(shm_segments) = atoi(new_value); +#endif + return SUCCESS; +} +/* }}} */ + +#ifdef MULTIPART_EVENT_FORMDATA +static PHP_INI_MH(OnUpdateRfc1867Freq) /* {{{ */ +{ + int tmp; + tmp = zend_atoi(new_value, new_value_length); + if(tmp < 0) { + apc_eprint("rfc1867_freq must be greater than or equal to zero."); + return FAILURE; + } + if(new_value[new_value_length-1] == '%') { + if(tmp > 100) { + apc_eprint("rfc1867_freq cannot be over 100%%"); + return FAILURE; + } + APCG(rfc1867_freq) = tmp / 100.0; + } else { + APCG(rfc1867_freq) = tmp; + } + return SUCCESS; +} +/* }}} */ +#endif + +#ifdef ZEND_ENGINE_2 +#define OnUpdateInt OnUpdateLong +#endif + +PHP_INI_BEGIN() +STD_PHP_INI_BOOLEAN("apc.enabled", "1", PHP_INI_SYSTEM, OnUpdateBool, enabled, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.shm_segments", "1", PHP_INI_SYSTEM, OnUpdateShmSegments, shm_segments, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.shm_size", "30", PHP_INI_SYSTEM, OnUpdateInt, shm_size, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.include_once_override", "0", PHP_INI_SYSTEM, OnUpdateBool, include_once, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.num_files_hint", "1000", PHP_INI_SYSTEM, OnUpdateInt, num_files_hint, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.user_entries_hint", "4096", PHP_INI_SYSTEM, OnUpdateInt, user_entries_hint, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.gc_ttl", "3600", PHP_INI_SYSTEM, OnUpdateInt, gc_ttl, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.ttl", "0", PHP_INI_SYSTEM, OnUpdateInt, ttl, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.user_ttl", "0", PHP_INI_SYSTEM, OnUpdateInt, user_ttl, zend_apc_globals, apc_globals) +#if APC_MMAP +STD_PHP_INI_ENTRY("apc.mmap_file_mask", NULL, PHP_INI_SYSTEM, OnUpdateString, mmap_file_mask, zend_apc_globals, apc_globals) +#endif +PHP_INI_ENTRY("apc.filters", NULL, PHP_INI_SYSTEM, OnUpdate_filters) +STD_PHP_INI_BOOLEAN("apc.cache_by_default", "1", PHP_INI_ALL, OnUpdateBool, cache_by_default, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.slam_defense", "0", PHP_INI_SYSTEM, OnUpdateInt, slam_defense, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.file_update_protection", "2", PHP_INI_SYSTEM, OnUpdateInt,file_update_protection, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.enable_cli", "0", PHP_INI_SYSTEM, OnUpdateBool, enable_cli, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.max_file_size", "1M", PHP_INI_SYSTEM, OnUpdateInt, max_file_size, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.stat", "1", PHP_INI_SYSTEM, OnUpdateBool, fpstat, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.stat_ctime", "0", PHP_INI_SYSTEM, OnUpdateBool, stat_ctime, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.write_lock", "1", PHP_INI_SYSTEM, OnUpdateBool, write_lock, zend_apc_globals, apc_globals) +STD_PHP_INI_BOOLEAN("apc.report_autofilter", "0", PHP_INI_SYSTEM, OnUpdateBool, report_autofilter,zend_apc_globals, apc_globals) +#ifdef MULTIPART_EVENT_FORMDATA +STD_PHP_INI_BOOLEAN("apc.rfc1867", "0", PHP_INI_SYSTEM, OnUpdateBool, rfc1867, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.rfc1867_prefix", "upload_", PHP_INI_SYSTEM, OnUpdateStringUnempty, rfc1867_prefix, zend_apc_globals, apc_globals) +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() + +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION(apc) */ +static PHP_MINFO_FUNCTION(apc) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "APC Support", APCG(enabled) ? "enabled" : "disabled"); + php_info_print_table_row(2, "Version", APC_VERSION); +#if APC_MMAP + php_info_print_table_row(2, "MMAP Support", "Enabled"); + php_info_print_table_row(2, "MMAP File Mask", APCG(mmap_file_mask)); +#else + php_info_print_table_row(2, "MMAP Support", "Disabled"); +#endif +#if APC_SEM_LOCKS + php_info_print_table_row(2, "Locking type", "IPC Semaphore"); +#elif APC_FUTEX_LOCKS + php_info_print_table_row(2, "Locking type", "Linux Futex Locks"); +#elif APC_PTHREADMUTEX_LOCKS + php_info_print_table_row(2, "Locking type", "pthread mutex Locks"); +#elif APC_SPIN_LOCKS + php_info_print_table_row(2, "Locking type", "spin Locks"); +#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, "Build Date", __DATE__ " " __TIME__); + php_info_print_table_end(); + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +#ifdef MULTIPART_EVENT_FORMDATA +extern int apc_rfc1867_progress(unsigned int event, void *event_data, void **extra TSRMLS_DC); +#endif + +/* {{{ PHP_MINIT_FUNCTION(apc) */ +static PHP_MINIT_FUNCTION(apc) +{ + ZEND_INIT_MODULE_GLOBALS(apc, php_apc_init_globals, php_apc_shutdown_globals); + + REGISTER_INI_ENTRIES(); + + /* Disable APC in cli mode unless overridden by apc.enable_cli */ + if(!APCG(enable_cli) && !strcmp(sapi_module.name, "cli")) { + APCG(enabled) = 0; + } + + if (APCG(enabled)) { + if(APCG(initialized)) { + apc_process_init(module_number TSRMLS_CC); + } else { + apc_module_init(module_number TSRMLS_CC); + apc_zend_init(TSRMLS_C); + apc_process_init(module_number TSRMLS_CC); +#ifdef MULTIPART_EVENT_FORMDATA + /* File upload progress tracking */ + if(APCG(rfc1867)) { + php_rfc1867_callback = apc_rfc1867_progress; + } +#endif + } + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION(apc) */ +static PHP_MSHUTDOWN_FUNCTION(apc) +{ + if(APCG(enabled)) { + apc_process_shutdown(TSRMLS_C); + apc_zend_shutdown(TSRMLS_C); + apc_module_shutdown(TSRMLS_C); +#ifndef ZTS + php_apc_shutdown_globals(&apc_globals); +#endif + } +#ifdef ZTS + ts_free_id(apc_globals_id); +#endif + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION(apc) */ +static PHP_RINIT_FUNCTION(apc) +{ + if(APCG(enabled)) { + apc_request_init(TSRMLS_C); + +#if HAVE_SIGACTION + apc_set_signals(); +#endif + } + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION(apc) */ +static PHP_RSHUTDOWN_FUNCTION(apc) +{ + if(APCG(enabled)) { + apc_request_shutdown(TSRMLS_C); + } + return SUCCESS; +} +/* }}} */ + +/* {{{ proto array apc_cache_info([string type] [, bool limited]) */ +PHP_FUNCTION(apc_cache_info) +{ + apc_cache_info_t* info; + apc_cache_link_t* p; + zval* list; + char *cache_type; + int ct_len; + zend_bool limited=0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sb", &cache_type, &ct_len, &limited) == FAILURE) { + return; + } + + if(ZEND_NUM_ARGS()) { + if(!strcasecmp(cache_type,"user")) { + info = apc_cache_info(apc_user_cache, limited); + } else if(!strcasecmp(cache_type,"filehits")) { +#ifdef APC_FILEHITS + RETVAL_ZVAL(APCG(filehits), 1, 0); + return; +#else + RETURN_FALSE; +#endif + } else { + info = apc_cache_info(apc_cache, limited); + } + } else info = apc_cache_info(apc_cache, limited); + + if(!info) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No APC info available. Perhaps APC is not enabled? Check apc.enabled in your ini file"); + RETURN_FALSE; + } + + array_init(return_value); + add_assoc_long(return_value, "num_slots", info->num_slots); + add_assoc_long(return_value, "ttl", info->ttl); + add_assoc_long(return_value, "num_hits", info->num_hits); + add_assoc_long(return_value, "num_misses", info->num_misses); + add_assoc_long(return_value, "start_time", info->start_time); + add_assoc_long(return_value, "expunges", info->expunges); + add_assoc_long(return_value, "mem_size", info->mem_size); + add_assoc_long(return_value, "num_entries", info->num_entries); + add_assoc_long(return_value, "num_inserts", info->num_inserts); +#ifdef MULTIPART_EVENT_FORMDATA + add_assoc_long(return_value, "file_upload_progress", 1); +#else + add_assoc_long(return_value, "file_upload_progress", 0); +#endif +#if APC_MMAP + add_assoc_stringl(return_value, "memory_type", "mmap", sizeof("mmap")-1, 1); +#else + add_assoc_stringl(return_value, "memory_type", "IPC shared", sizeof("IPC shared")-1, 1); +#endif +#if APC_SEM_LOCKS + add_assoc_stringl(return_value, "locking_type", "IPC semaphore", sizeof("IPC semaphore")-1, 1); +#elif APC_FUTEX_LOCKS + add_assoc_stringl(return_value, "locking_type", "Linux Futex", sizeof("Linux Futex")-1, 1); +#elif APC_PTHREADMUTEX_LOCKS + add_assoc_stringl(return_value, "locking_type", "pthread mutex", sizeof("pthread mutex")-1, 1); +#elif APC_SPIN_LOCKS + add_assoc_stringl(return_value, "locking_type", "spin", sizeof("spin")-1, 1); +#else + add_assoc_stringl(return_value, "locking_type", "file", sizeof("file")-1, 1); +#endif + if(limited) { + apc_cache_free_info(info); + return; + } + + ALLOC_INIT_ZVAL(list); + array_init(list); + + for (p = info->list; p != NULL; p = p->next) { + zval* link; + + ALLOC_INIT_ZVAL(link); + array_init(link); + + if(p->type == APC_CACHE_ENTRY_FILE) { + add_assoc_string(link, "filename", p->data.file.filename, 1); + add_assoc_long(link, "device", p->data.file.device); + add_assoc_long(link, "inode", p->data.file.inode); + add_assoc_string(link, "type", "file", 1); + } else if(p->type == APC_CACHE_ENTRY_USER) { + add_assoc_string(link, "info", p->data.user.info, 1); + add_assoc_long(link, "ttl", (long)p->data.user.ttl); + add_assoc_string(link, "type", "user", 1); + } + add_assoc_long(link, "num_hits", p->num_hits); + add_assoc_long(link, "mtime", p->mtime); + add_assoc_long(link, "creation_time", p->creation_time); + add_assoc_long(link, "deletion_time", p->deletion_time); + add_assoc_long(link, "access_time", p->access_time); + add_assoc_long(link, "ref_count", p->ref_count); + add_assoc_long(link, "mem_size", p->mem_size); + add_next_index_zval(list, link); + } + add_assoc_zval(return_value, "cache_list", list); + + ALLOC_INIT_ZVAL(list); + array_init(list); + + for (p = info->deleted_list; p != NULL; p = p->next) { + zval* link; + + ALLOC_INIT_ZVAL(link); + array_init(link); + + if(p->type == APC_CACHE_ENTRY_FILE) { + add_assoc_string(link, "filename", p->data.file.filename, 1); + add_assoc_long(link, "device", p->data.file.device); + add_assoc_long(link, "inode", p->data.file.inode); + add_assoc_string(link, "type", "file", 1); + } else if(p->type == APC_CACHE_ENTRY_USER) { + add_assoc_string(link, "info", p->data.user.info, 1); + add_assoc_long(link, "ttl", (long)p->data.user.ttl); + add_assoc_string(link, "type", "user", 1); + } + add_assoc_long(link, "num_hits", p->num_hits); + add_assoc_long(link, "mtime", p->mtime); + add_assoc_long(link, "creation_time", p->creation_time); + add_assoc_long(link, "deletion_time", p->deletion_time); + add_assoc_long(link, "access_time", p->access_time); + add_assoc_long(link, "ref_count", p->ref_count); + add_assoc_long(link, "mem_size", p->mem_size); + add_next_index_zval(list, link); + } + add_assoc_zval(return_value, "deleted_list", list); + + apc_cache_free_info(info); +} +/* }}} */ + +/* {{{ proto void apc_clear_cache() */ +PHP_FUNCTION(apc_clear_cache) +{ + char *cache_type; + int ct_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cache_type, &ct_len) == FAILURE) { + return; + } + + if(ZEND_NUM_ARGS()) { + if(!strcasecmp(cache_type,"user")) { + apc_cache_clear(apc_user_cache); + RETURN_TRUE; + } + } + apc_cache_clear(apc_cache); +} +/* }}} */ + +/* {{{ proto array apc_sma_info([bool limited]) */ +PHP_FUNCTION(apc_sma_info) +{ + apc_sma_info_t* info; + zval* block_lists; + int i; + zend_bool limited = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &limited) == FAILURE) { + return; + } + + info = apc_sma_info(limited); + + if(!info) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No APC SMA info available. Perhaps APC is disabled via apc.enabled?"); + RETURN_FALSE; + } + + array_init(return_value); + add_assoc_long(return_value, "num_seg", info->num_seg); + add_assoc_long(return_value, "seg_size", info->seg_size); + add_assoc_long(return_value, "avail_mem", apc_sma_get_avail_mem()); + + if(limited) { + apc_sma_free_info(info); + return; + } + +#if ALLOC_DISTRIBUTION + { + size_t *adist = apc_sma_get_alloc_distribution(); + zval* list; + ALLOC_INIT_ZVAL(list); + array_init(list); + for(i=0; i<30; i++) { + add_next_index_long(list, adist[i]); + } + add_assoc_zval(return_value, "adist", list); + } +#endif + ALLOC_INIT_ZVAL(block_lists); + array_init(block_lists); + + for (i = 0; i < info->num_seg; i++) { + apc_sma_link_t* p; + zval* list; + + ALLOC_INIT_ZVAL(list); + array_init(list); + + for (p = info->list[i]; p != NULL; p = p->next) { + zval* link; + + ALLOC_INIT_ZVAL(link); + array_init(link); + + add_assoc_long(link, "size", p->size); + add_assoc_long(link, "offset", p->offset); + add_next_index_zval(list, link); + } + add_next_index_zval(block_lists, list); + } + add_assoc_zval(return_value, "block_lists", block_lists); + apc_sma_free_info(info); +} +/* }}} */ + +/* {{{ _apc_store */ +int _apc_store(char *strkey, int strkey_len, const zval *val, const unsigned int ttl, const int exclusive TSRMLS_DC) { + apc_cache_entry_t *entry; + apc_cache_key_t key; + time_t t; + size_t mem_size = 0; + +#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(!APCG(enabled)) return 0; + + HANDLE_BLOCK_INTERRUPTIONS(); + + APCG(mem_size_ptr) = &mem_size; + 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); + HANDLE_UNBLOCK_INTERRUPTIONS(); + return 0; + } + + if (!apc_cache_make_user_key(&key, strkey, strkey_len + 1, t)) { + APCG(mem_size_ptr) = 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); + HANDLE_UNBLOCK_INTERRUPTIONS(); + return 0; + } + + APCG(mem_size_ptr) = NULL; + + HANDLE_UNBLOCK_INTERRUPTIONS(); + + return 1; +} +/* }}} */ + +/* {{{ proto int apc_store(string key, zval var [, ttl ]) + */ +PHP_FUNCTION(apc_store) { + zval *val; + char *strkey; + int strkey_len; + long ttl = 0L; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &strkey, &strkey_len, &val, &ttl) == FAILURE) { + return; + } + + if(!strkey_len) RETURN_FALSE; + + if(_apc_store(strkey, strkey_len, val, (unsigned int)ttl, 0 TSRMLS_CC)) RETURN_TRUE; + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto int apc_add(string key, zval var [, ttl ]) + */ +PHP_FUNCTION(apc_add) { + zval *val; + char *strkey; + int strkey_len; + long ttl = 0L; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &strkey, &strkey_len, &val, &ttl) == FAILURE) { + return; + } + + if(!strkey_len) RETURN_FALSE; + + if(_apc_store(strkey, strkey_len, val, (unsigned int)ttl, 1 TSRMLS_CC)) RETURN_TRUE; + RETURN_FALSE; +} +/* }}} */ + +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; } +#define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor) +#define ZVAL_ZVAL(z, zv, copy, dtor) { \ + int is_ref, refcount; \ + is_ref = (z)->is_ref; \ + refcount = (z)->refcount; \ + *(z) = *(zv); \ + if (copy) { \ + zval_copy_ctor(z); \ + } \ + if (dtor) { \ + if (!copy) { \ + ZVAL_NULL(zv); \ + } \ + zval_ptr_dtor(&zv); \ + } \ + (z)->is_ref = is_ref; \ + (z)->refcount = refcount; \ + } +#endif +/* }}} */ + +/* {{{ proto mixed apc_fetch(mixed key) + */ +PHP_FUNCTION(apc_fetch) { + zval *key; + HashTable *hash; + HashPosition hpos; + zval **hentry; + zval *result; + zval *result_entry; + char *strkey; + int strkey_len; + apc_cache_entry_t* entry; + time_t t; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) == FAILURE) { + return; + } + +#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(Z_TYPE_P(key) != IS_STRING && Z_TYPE_P(key) != IS_ARRAY) { + convert_to_string(key); + } + + if(Z_TYPE_P(key) == IS_STRING) { + strkey = Z_STRVAL_P(key); + strkey_len = Z_STRLEN_P(key); + if(!strkey_len) RETURN_FALSE; + entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t); + if(entry) { + /* deep-copy returned shm zval to emalloc'ed return_value */ + apc_cache_fetch_zval(return_value, entry->data.user.val, apc_php_malloc, apc_php_free); + apc_cache_release(apc_user_cache, entry); + } else { + RETURN_FALSE; + } + } else if(Z_TYPE_P(key) == IS_ARRAY) { + hash = Z_ARRVAL_P(key); + MAKE_STD_ZVAL(result); + array_init(result); + zend_hash_internal_pointer_reset_ex(hash, &hpos); + while(zend_hash_get_current_data_ex(hash, (void**)&hentry, &hpos) == SUCCESS) { + if(Z_TYPE_PP(hentry) != IS_STRING) { + apc_wprint("apc_fetch() expects a string or array of strings."); + RETURN_FALSE; + } + entry = apc_cache_user_find(apc_user_cache, Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) + 1, t); + if(entry) { + /* deep-copy returned shm zval to emalloc'ed return_value */ + MAKE_STD_ZVAL(result_entry); + apc_cache_fetch_zval(result_entry, entry->data.user.val, apc_php_malloc, apc_php_free); + apc_cache_release(apc_user_cache, entry); + zend_hash_add(Z_ARRVAL_P(result), Z_STRVAL_PP(hentry), Z_STRLEN_PP(hentry) +1, &result_entry, sizeof(zval*), NULL); + } /* don't set values we didn't find */ + zend_hash_move_forward_ex(hash, &hpos); + } + RETURN_ZVAL(result, 0, 1); + } else { + apc_wprint("apc_fetch() expects a string or array of strings."); + RETURN_FALSE; + } + + return; +} +/* }}} */ + +/* {{{ proto mixed apc_delete(string key) + */ +PHP_FUNCTION(apc_delete) { + char *strkey; + int strkey_len; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &strkey, &strkey_len) == FAILURE) { + return; + } + + if(!strkey_len) RETURN_FALSE; + + if(apc_cache_user_delete(apc_user_cache, strkey, strkey_len + 1)) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +static void _apc_define_constants(zval *constants, zend_bool case_sensitive TSRMLS_DC) { + char *const_key; + unsigned int const_key_len; + zval **entry; + HashPosition pos; + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(constants), &pos); + while (zend_hash_get_current_data_ex(Z_ARRVAL_P(constants), (void**)&entry, &pos) == SUCCESS) { + zend_constant c; + int key_type; + ulong num_key; + + key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(constants), &const_key, &const_key_len, &num_key, 0, &pos); + if(key_type != HASH_KEY_IS_STRING) { + zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos); + continue; + } + switch(Z_TYPE_PP(entry)) { + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_BOOL: + case IS_RESOURCE: + case IS_NULL: + break; + default: + zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos); + continue; + } + c.value = **entry; + zval_copy_ctor(&c.value); + c.flags = case_sensitive; + c.name = zend_strndup(const_key, const_key_len); + c.name_len = const_key_len; +#ifdef ZEND_ENGINE_2 + c.module_number = PHP_USER_CONSTANT; +#endif + zend_register_constant(&c TSRMLS_CC); + + zend_hash_move_forward_ex(Z_ARRVAL_P(constants), &pos); + } +} + +/* {{{ proto mixed apc_define_constants(string key, array constants [,bool case-sensitive]) + */ +PHP_FUNCTION(apc_define_constants) { + char *strkey; + int strkey_len; + zval *constants = NULL; + zend_bool case_sensitive = 1; + int argc = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(argc TSRMLS_CC, "sa|b", &strkey, &strkey_len, &constants, &case_sensitive) == FAILURE) { + return; + } + + if(!strkey_len) RETURN_FALSE; + + _apc_define_constants(constants, case_sensitive TSRMLS_CC); + if(_apc_store(strkey, strkey_len, constants, 0, 0 TSRMLS_CC)) RETURN_TRUE; + RETURN_FALSE; +} /* }}} */ + +/* {{{ proto mixed apc_load_constants(string key [, bool case-sensitive]) + */ +PHP_FUNCTION(apc_load_constants) { + char *strkey; + int strkey_len; + apc_cache_entry_t* entry; + time_t t; + zend_bool case_sensitive = 1; + + if(!APCG(enabled)) RETURN_FALSE; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &strkey, &strkey_len, &case_sensitive) == FAILURE) { + return; + } + + if(!strkey_len) RETURN_FALSE; + +#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 + + entry = apc_cache_user_find(apc_user_cache, strkey, strkey_len + 1, t); + + if(entry) { + _apc_define_constants(entry->data.user.val, case_sensitive TSRMLS_CC); + apc_cache_release(apc_user_cache, entry); + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto boolean apc_compile_file(string filename) + */ +PHP_FUNCTION(apc_compile_file) { + char *filename; + int filename_len; + zend_file_handle file_handle; + zend_op_array *op_array; + long slam_defense = 0; + char** filters = NULL; + zend_bool cache_by_default = 1; + HashTable cg_function_table, cg_class_table, eg_function_table, eg_class_table; + HashTable *cg_orig_function_table, *cg_orig_class_table, *eg_orig_function_table, *eg_orig_class_table; + + if(!APCG(enabled)) RETURN_FALSE; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { + return; + } + + if(!filename) RETURN_FALSE; + + /* reset slam defense, filters, and cache_by_default */ + slam_defense = APCG(slam_defense); + APCG(slam_defense) = 0; + + filters = APCG(filters); + APCG(filters) = NULL; + + cache_by_default = APCG(cache_by_default); + APCG(cache_by_default) = 1; + + /* Replace function/class tables to avoid namespace conflicts */ + zend_hash_init_ex(&cg_function_table, 100, NULL, ZEND_FUNCTION_DTOR, 1, 0); + cg_orig_function_table = CG(function_table); + CG(function_table) = &cg_function_table; + zend_hash_init_ex(&cg_class_table, 10, NULL, ZEND_CLASS_DTOR, 1, 0); + cg_orig_class_table = CG(class_table); + CG(class_table) = &cg_class_table; + eg_orig_function_table = EG(function_table); + EG(function_table) = CG(function_table); + eg_orig_class_table = EG(class_table); + EG(class_table) = CG(class_table); + APCG(force_file_update) = 1; + + /* Compile the file, loading it into the cache */ + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = filename; + file_handle.free_filename = 0; + file_handle.opened_path = NULL; + zend_try { + op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC); + } zend_catch { + apc_wprint("Error compiling %s in apc_compile_file.", filename); + op_array = NULL; + } zend_end_try(); + + /* Return class/function tables to previous states, destroy temp tables */ + APCG(force_file_update) = 0; + CG(function_table) = cg_orig_function_table; + zend_hash_destroy(&cg_function_table); + CG(class_table) = cg_orig_class_table; + zend_hash_destroy(&cg_class_table); + EG(function_table) = eg_orig_function_table; + EG(class_table) = eg_orig_class_table; + + /* Restore global settings */ + APCG(slam_defense) = slam_defense; + APCG(filters) = filters; + APCG(cache_by_default) = cache_by_default; + + if(op_array == NULL) { RETURN_FALSE; } + + /* Free up everything */ + zend_destroy_file_handle(&file_handle TSRMLS_CC); +#ifdef ZEND_ENGINE_2 + destroy_op_array(op_array TSRMLS_CC); +#else + destroy_op_array(op_array); +#endif + efree(op_array); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ 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_delete, NULL) + PHP_FE(apc_define_constants, NULL) + PHP_FE(apc_load_constants, NULL) + PHP_FE(apc_compile_file, NULL) + PHP_FE(apc_add, NULL) + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ module definition structure */ + +zend_module_entry apc_module_entry = { + STANDARD_MODULE_HEADER, + "apc", + apc_functions, + PHP_MINIT(apc), + PHP_MSHUTDOWN(apc), + PHP_RINIT(apc), + PHP_RSHUTDOWN(apc), + PHP_MINFO(apc), + APC_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_APC +ZEND_GET_MODULE(apc) +#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 + */