int apc_sem_create(int proj, int initval TSRMLS_DC) { int semid; int perms = 0777; union semun arg; key_t key = IPC_PRIVATE; if ((semid = semget(key, 1, IPC_CREAT | IPC_EXCL | perms)) >= 0) { /* sempahore created for the first time, initialize now */ arg.val = initval; if (semctl(semid, 0, SETVAL, arg) < 0) { apc_error("apc_sem_create: semctl(%d,...) failed:" TSRMLS_CC, semid); } } else if (errno == EEXIST) { /* sempahore already exists, don't initialize */ if ((semid = semget(key, 1, perms)) < 0) { apc_error("apc_sem_create: semget(%u,...) failed:" TSRMLS_CC, key); } /* insert <sleazy way to avoid race condition> here */ } else { apc_error("apc_sem_create: semget(%u,...) failed:" TSRMLS_CC, key); } return semid; }
void apc_shm_detach(apc_segment_t* segment) { if (shmdt(segment->shmaddr) < 0) { apc_error("apc_shm_detach: shmdt failed:"); } #ifdef APC_MEMPROTECT if (segment->roaddr && shmdt(segment->roaddr) < 0) { apc_error("apc_shm_detach: shmdt failed:"); } #endif }
apc_segment_t apc_shm_attach(int shmid, size_t size) { apc_segment_t segment; /* shm segment */ if ((zend_long)(segment.shmaddr = shmat(shmid, 0, 0)) == -1) { apc_error("apc_shm_attach: shmat failed:"); } #ifdef APC_MEMPROTECT if ((zend_long)(segment.roaddr = shmat(shmid, 0, SHM_RDONLY)) == -1) { segment.roaddr = NULL; } #endif segment.size = size; /* * We set the shmid for removal immediately after attaching to it. The * segment won't disappear until all processes have detached from it. */ apc_shm_destroy(shmid); return segment; }
/* {{{ apc_cache_preload shall load the prepared data files in path into the specified cache */ PHP_APCU_API zend_bool apc_cache_preload(apc_cache_t* cache, const char *path) { #ifndef ZTS zend_bool result = 0; char file[MAXPATHLEN]={0,}; int ndir, i; char *p = NULL; struct dirent **namelist = NULL; if ((ndir = php_scandir(path, &namelist, 0, php_alphasort)) > 0) { for (i = 0; i < ndir; i++) { /* check for extension */ if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".data"))) { free(namelist[i]); continue; } snprintf(file, MAXPATHLEN, "%s%c%s", path, DEFAULT_SLASH, namelist[i]->d_name); if(apc_load_data(cache, file)) { result = 1; } free(namelist[i]); } free(namelist); } return result; #else apc_error("Cannot load data from apc.preload_path=%s in thread-safe mode", path); return 0; #endif } /* }}} */
void* apc_erealloc(void* p, size_t n TSRMLS_DC) { p = realloc(p, n); if (p == NULL) { apc_error("apc_erealloc: realloc failed to allocate %u bytes:" TSRMLS_CC, n); } return p; }
void* apc_emalloc(size_t n TSRMLS_DC) { void* p = malloc(n); if (p == NULL) { apc_error("apc_emalloc: malloc failed to allocate %u bytes:" TSRMLS_CC, n); } return p; }
int apc_sem_get_value(int semid TSRMLS_DC) { union semun arg; unsigned short val[1]; arg.array = val; if (semctl(semid, 0, GETALL, arg) < 0) { apc_error("apc_sem_getvalue: semctl(%d,...) failed:" TSRMLS_CC, semid); } return val[0]; }
/* {{{ apc_cache_create */ PHP_APCU_API apc_cache_t* apc_cache_create(apc_sma_t* sma, apc_serializer_t* serializer, zend_long size_hint, zend_long gc_ttl, zend_long ttl, zend_long smart, zend_bool defend) { apc_cache_t* cache; zend_long cache_size; zend_long nslots; /* calculate number of slots */ nslots = make_prime(size_hint > 0 ? size_hint : 2000); /* allocate pointer by normal means */ cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t)); /* calculate cache size for shm allocation */ cache_size = sizeof(apc_cache_header_t) + nslots*sizeof(apc_cache_slot_t*); /* allocate shm */ cache->shmaddr = sma->smalloc(cache_size); if(!cache->shmaddr) { apc_error("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). "); return NULL; } /* zero shm */ memset(cache->shmaddr, 0, cache_size); /* set default header */ cache->header = (apc_cache_header_t*) cache->shmaddr; cache->header->nhits = 0; cache->header->nmisses = 0; cache->header->nentries = 0; cache->header->nexpunges = 0; cache->header->gc = NULL; cache->header->stime = time(NULL); cache->header->state |= APC_CACHE_ST_NONE; /* set cache options */ cache->slots = (apc_cache_slot_t**) (((char*) cache->shmaddr) + sizeof(apc_cache_header_t)); cache->sma = sma; cache->serializer = serializer; cache->nslots = nslots; cache->gc_ttl = gc_ttl; cache->ttl = ttl; cache->smart = smart; cache->defend = defend; /* header lock */ CREATE_LOCK(&cache->header->lock); /* zero slots */ memset(cache->slots, 0, sizeof(apc_cache_slot_t*)*nslots); return cache; } /* }}} */
/* {{{ uninstall_class */ static int uninstall_class(apc_class_t cl TSRMLS_DC) { int status; status = zend_hash_del(EG(class_table), cl.name, cl.name_len+1); if (status == FAILURE) { apc_error("Cannot delete class %s" TSRMLS_CC, cl.name); } return status; }
int apc_shm_create(int proj, size_t size) { int shmid; /* shared memory id */ int oflag; /* permissions on shm */ key_t key = IPC_PRIVATE; /* shm key */ oflag = IPC_CREAT | SHM_R | SHM_A; if ((shmid = shmget(key, size, oflag)) < 0) { apc_error("apc_shm_create: shmget(%d, %d, %d) failed: %s. It is possible that the chosen SHM segment size is higher than the operation system allows. Linux has usually a default limit of 32MB per segment.", key, size, oflag, strerror(errno)); } return shmid; }
void apc_sem_wait_for_zero(int semid TSRMLS_DC) { struct sembuf op; op.sem_num = 0; op.sem_op = 0; op.sem_flg = UNDO; if (semop(semid, &op, 1) < 0) { if (errno != EINTR) { apc_error("apc_sem_waitforzero: semop(%d) failed:" TSRMLS_CC, semid); } } }
void apc_sem_unlock(int semid TSRMLS_DC) { struct sembuf op; op.sem_num = 0; op.sem_op = 1; op.sem_flg = UNDO; if (semop(semid, &op, 1) < 0) { if (errno != EINTR) { apc_error("apc_sem_unlock: semop(%d) failed:" TSRMLS_CC, semid); } } }
/* * s_lock_stuck() - complain about a stuck spinlock */ static void s_lock_stuck(volatile slock_t *lock, const char *file, int line TSRMLS_DC) { #if defined(S_LOCK_TEST) fprintf(stderr, "\nStuck spinlock (%p) detected at %s:%d.\n", lock, file, line); exit(1); #else /* -- Removed for APC elog(PANIC, "stuck spinlock (%p) detected at %s:%d", lock, file, line); */ apc_error("Stuck spinlock (%p) detected" TSRMLS_CC, lock); #endif }
int apc_sem_nonblocking_lock(int semid TSRMLS_DC) { struct sembuf op; op.sem_num = 0; op.sem_op = -1; op.sem_flg = UNDO | IPC_NOWAIT; if (semop(semid, &op, 1) < 0) { if (errno == EAGAIN) { return 0; /* Lock is already held */ } else if (errno != EINTR) { apc_error("apc_sem_lock: semop(%d) failed:" TSRMLS_CC, semid); } } return 1; /* Lock obtained */ }
/* {{{ apc_iterator_clone */ static zend_object* apc_iterator_clone(zval *zobject) { apc_error(APC_ITERATOR_NAME " object cannot be cloned"); return NULL; }
/* {{{ install_class */ static int install_class(apc_class_t cl, apc_context_t* ctxt, int lazy TSRMLS_DC) { zend_class_entry* class_entry = cl.class_entry; zend_class_entry* parent = NULL; int status; /* 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; } } if(lazy && cl.name_len != 0 && cl.name[0] != '\0') { status = zend_hash_add(APCG(lazy_class_table), cl.name, cl.name_len+1, &cl, sizeof(apc_class_t), NULL); if(status == FAILURE) { zend_error(E_ERROR, "Cannot redeclare class %s", cl.name); } return status; } class_entry = apc_copy_class_entry_for_execution(cl.class_entry, ctxt TSRMLS_CC); if (class_entry == NULL) return FAILURE; /* restore parent class pointer for compile-time inheritance */ if (cl.parent_name != NULL) { zend_class_entry** parent_ptr = NULL; /* * __autoload brings in the old issues with mixed inheritance. * When a statically inherited class triggers autoload, it runs * afoul of a potential require_once "parent.php" in the previous * line, which when executed provides the parent class, but right * now goes and hits __autoload which could fail. * * missing parent == re-compile. * * whether __autoload is enabled or not, because __autoload errors * cause php to die. * * Aside: Do NOT pass *strlen(cl.parent_name)+1* because * zend_lookup_class_ex does it internally anyway! */ status = zend_lookup_class_ex(cl.parent_name, strlen(cl.parent_name), #ifdef ZEND_ENGINE_2_4 NULL, #endif 0, &parent_ptr TSRMLS_CC); if (status == FAILURE) { if(APCG(report_autofilter)) { apc_warning("Dynamic inheritance detected for class %s" TSRMLS_CC, cl.name); } class_entry->parent = NULL; return status; } else { parent = *parent_ptr; class_entry->parent = parent; zend_do_inheritance(class_entry, parent TSRMLS_CC); } } status = zend_hash_add(EG(class_table), cl.name, cl.name_len+1, &class_entry, sizeof(zend_class_entry*), NULL); if (status == FAILURE) { apc_error("Cannot redeclare class %s" TSRMLS_CC, cl.name); } return status; }
apc_segment_t apc_mmap(char *file_mask, size_t size TSRMLS_DC) { apc_segment_t segment; int fd = -1; int flags = MAP_SHARED | MAP_NOSYNC; #ifdef APC_MEMPROTECT int remap = 1; #endif /* If no filename was provided, do an anonymous mmap */ if(!file_mask || (file_mask && !strlen(file_mask))) { #if !defined(MAP_ANON) apc_error("Anonymous mmap does not apear to be available on this system (MAP_ANON/MAP_ANONYMOUS). Please see the apc.mmap_file_mask INI option." TSRMLS_CC); #else fd = -1; flags = MAP_SHARED | MAP_ANON; #ifdef APC_MEMPROTECT remap = 0; #endif #endif } else if(!strcmp(file_mask,"/dev/zero")) { fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR); if(fd == -1) { apc_error("apc_mmap: open on /dev/zero failed:" TSRMLS_CC); goto error; } #ifdef APC_MEMPROTECT remap = 0; /* cannot remap */ #endif } else if(strstr(file_mask,".shm")) { /* * 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(!mktemp(file_mask)) { apc_error("apc_mmap: mktemp on %s failed:" TSRMLS_CC, file_mask); goto error; } fd = shm_open(file_mask, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if(fd == -1) { apc_error("apc_mmap: shm_open on %s failed:" TSRMLS_CC, file_mask); goto error; } if (ftruncate(fd, size) < 0) { close(fd); shm_unlink(file_mask); apc_error("apc_mmap: ftruncate failed:" TSRMLS_CC); goto error; } shm_unlink(file_mask); } else { /* * Otherwise we do a normal filesystem mmap */ fd = mkstemp(file_mask); if(fd == -1) { apc_error("apc_mmap: mkstemp on %s failed:" TSRMLS_CC, file_mask); goto error; } if (ftruncate(fd, size) < 0) { close(fd); unlink(file_mask); apc_error("apc_mmap: ftruncate failed:" TSRMLS_CC); goto error; } unlink(file_mask); } segment.shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0); segment.size = size; #ifdef APC_MEMPROTECT if(remap) { segment.roaddr = (void *)mmap(NULL, size, PROT_READ, flags, fd, 0); } else { segment.roaddr = NULL; } #endif if((long)segment.shmaddr == -1) { apc_error("apc_mmap: mmap failed:" TSRMLS_CC); } if(fd != -1) close(fd); return segment; error: segment.shmaddr = (void*)-1; segment.size = 0; #ifdef APC_MEMPROTECT segment.roaddr = NULL; #endif return segment; }