Back to index

php5  5.3.10
sysvsem.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Tom May <tom@go2net.com>                                    |
00016    |          Gavin Sherry <gavin@linuxworld.com.au>                      |
00017    +----------------------------------------------------------------------+
00018  */
00019  
00020 /* $Id: sysvsem.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 /* Latest update build anc tested on Linux 2.2.14
00023  *
00024  * This has been built and tested on Solaris 2.6 and Linux 2.1.122.
00025  * It may not compile or execute correctly on other systems.
00026  *
00027  * sas: Works for me on Linux 2.0.36 and FreeBSD 3.0-current
00028  */
00029 
00030 #ifdef HAVE_CONFIG_H
00031 #include "config.h"
00032 #endif
00033 
00034 #include "php.h"
00035 
00036 #if HAVE_SYSVSEM
00037 
00038 #include <sys/types.h>
00039 #include <sys/ipc.h>
00040 #include <sys/sem.h>
00041 #include <errno.h>
00042 
00043 #include "php_sysvsem.h"
00044 
00045 #if !HAVE_SEMUN
00046 
00047 union semun {
00048        int val;                    /* value for SETVAL */
00049        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
00050        unsigned short int *array;  /* array for GETALL, SETALL */
00051        struct seminfo *__buf;      /* buffer for IPC_INFO */
00052 };
00053 
00054 #undef HAVE_SEMUN
00055 #define HAVE_SEMUN 1
00056 
00057 #endif
00058 
00059 /* {{{ arginfo */
00060 ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_get, 0, 0, 1)
00061        ZEND_ARG_INFO(0, key)
00062        ZEND_ARG_INFO(0, max_acquire)
00063        ZEND_ARG_INFO(0, perm)
00064        ZEND_ARG_INFO(0, auto_release)
00065 ZEND_END_ARG_INFO()
00066 
00067 ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_acquire, 0, 0, 1)
00068        ZEND_ARG_INFO(0, sem_identifier)
00069 ZEND_END_ARG_INFO()
00070 
00071 ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_release, 0, 0, 1)
00072        ZEND_ARG_INFO(0, sem_identifier)
00073 ZEND_END_ARG_INFO()
00074 
00075 ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_remove, 0, 0, 1)
00076        ZEND_ARG_INFO(0, sem_identifier)
00077 ZEND_END_ARG_INFO()
00078 /* }}} */
00079 
00080 /* {{{ sysvsem_functions[]
00081  */
00082 const zend_function_entry sysvsem_functions[] = {
00083        PHP_FE(sem_get,                    arginfo_sem_get)
00084        PHP_FE(sem_acquire,         arginfo_sem_acquire)
00085        PHP_FE(sem_release,         arginfo_sem_release)
00086        PHP_FE(sem_remove,          arginfo_sem_remove)
00087        PHP_FE_END
00088 };
00089 /* }}} */
00090 
00091 /* {{{ sysvsem_module_entry
00092  */
00093 zend_module_entry sysvsem_module_entry = {
00094        STANDARD_MODULE_HEADER,
00095        "sysvsem",
00096        sysvsem_functions,
00097        PHP_MINIT(sysvsem),
00098        NULL,
00099        NULL,
00100        NULL,
00101        NULL,
00102        NO_VERSION_YET,
00103        STANDARD_MODULE_PROPERTIES
00104 };
00105 /* }}} */
00106 
00107 #ifdef COMPILE_DL_SYSVSEM
00108 ZEND_GET_MODULE(sysvsem)
00109 #endif
00110 
00111 
00112 THREAD_LS sysvsem_module php_sysvsem_module;
00113 
00114 /* Semaphore functions using System V semaphores.  Each semaphore
00115  * actually consists of three semaphores allocated as a unit under the
00116  * same key.  Semaphore 0 (SYSVSEM_SEM) is the actual semaphore, it is
00117  * initialized to max_acquire and decremented as processes acquire it.
00118  * The value of semaphore 1 (SYSVSEM_USAGE) is a count of the number
00119  * of processes using the semaphore.  After calling semget(), if a
00120  * process finds that the usage count is 1, it will set the value of
00121  * SYSVSEM_SEM to max_acquire.  This allows max_acquire to be set and
00122  * track the PHP code without having a global init routine or external
00123  * semaphore init code.  Except see the bug regarding a race condition
00124  * php_sysvsem_get().  Semaphore 2 (SYSVSEM_SETVAL) serializes the
00125  * calls to GETVAL SYSVSEM_USAGE and SETVAL SYSVSEM_SEM.  It can be
00126  * acquired only when it is zero.
00127  */
00128 
00129 #define SYSVSEM_SEM         0
00130 #define SYSVSEM_USAGE       1
00131 #define SYSVSEM_SETVAL      2
00132 
00133 /* {{{ release_sysvsem_sem
00134  */
00135 static void release_sysvsem_sem(zend_rsrc_list_entry *rsrc TSRMLS_DC)
00136 {
00137        sysvsem_sem *sem_ptr = (sysvsem_sem *)rsrc->ptr;
00138        struct sembuf sop[2];
00139        int opcount = 1;
00140 /*
00141  * if count == -1, semaphore has been removed
00142  * Need better way to handle this
00143  */
00144 
00145        if (sem_ptr->count == -1 || !sem_ptr->auto_release) {
00146               efree(sem_ptr);
00147               return;
00148        }
00149        /* Decrement the usage count. */
00150 
00151        sop[0].sem_num = SYSVSEM_USAGE;
00152        sop[0].sem_op  = -1;
00153        sop[0].sem_flg = SEM_UNDO;
00154 
00155        /* Release the semaphore if it has been acquired but not released. */
00156 
00157        if (sem_ptr->count) {
00158 
00159               sop[1].sem_num = SYSVSEM_SEM;
00160               sop[1].sem_op  = sem_ptr->count;
00161               sop[1].sem_flg = SEM_UNDO;
00162 
00163               opcount++;
00164        }
00165 
00166        semop(sem_ptr->semid, sop, opcount);
00167        efree(sem_ptr);
00168 }
00169 /* }}} */
00170 
00171 /* {{{ PHP_MINIT_FUNCTION
00172  */
00173 PHP_MINIT_FUNCTION(sysvsem)
00174 {
00175        php_sysvsem_module.le_sem = zend_register_list_destructors_ex(release_sysvsem_sem, NULL, "sysvsem", module_number);
00176        return SUCCESS;
00177 }
00178 /* }}} */
00179 
00180 #define SETVAL_WANTS_PTR
00181 
00182 #if defined(_AIX)
00183 #undef SETVAL_WANTS_PTR
00184 #endif
00185 
00186 /* {{{ proto resource sem_get(int key [, int max_acquire [, int perm [, int auto_release]])
00187    Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously */
00188 PHP_FUNCTION(sem_get)
00189 {
00190        long key, max_acquire = 1, perm = 0666, auto_release = 1;
00191        int semid;
00192        struct sembuf sop[3];
00193        int count;
00194        sysvsem_sem *sem_ptr;
00195 
00196        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|lll", &key, &max_acquire, &perm, &auto_release)) {
00197               RETURN_FALSE;
00198        }
00199 
00200        /* Get/create the semaphore.  Note that we rely on the semaphores
00201         * being zeroed when they are created.  Despite the fact that
00202         * the(?)  Linux semget() man page says they are not initialized,
00203         * the kernel versions 2.0.x and 2.1.z do in fact zero them.
00204         */
00205 
00206        semid = semget(key, 3, perm|IPC_CREAT);
00207        if (semid == -1) {
00208               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
00209               RETURN_FALSE;
00210        }
00211 
00212        /* Find out how many processes are using this semaphore.  Note
00213         * that on Linux (at least) there is a race condition here because
00214         * semaphore undo on process exit is not atomic, so we could
00215         * acquire SYSVSEM_SETVAL before a crashed process has decremented
00216         * SYSVSEM_USAGE in which case count will be greater than it
00217         * should be and we won't set max_acquire.  Fortunately this
00218         * doesn't actually matter in practice.
00219         */
00220 
00221        /* Wait for sem 1 to be zero . . . */
00222 
00223        sop[0].sem_num = SYSVSEM_SETVAL;
00224        sop[0].sem_op  = 0;
00225        sop[0].sem_flg = 0;
00226 
00227        /* . . . and increment it so it becomes non-zero . . . */
00228 
00229        sop[1].sem_num = SYSVSEM_SETVAL;
00230        sop[1].sem_op  = 1;
00231        sop[1].sem_flg = SEM_UNDO;
00232 
00233        /* . . . and increment the usage count. */
00234 
00235        sop[2].sem_num = SYSVSEM_USAGE;
00236        sop[2].sem_op  = 1;
00237        sop[2].sem_flg = SEM_UNDO;
00238        while (semop(semid, sop, 3) == -1) {
00239               if (errno != EINTR) {
00240                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed acquiring SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno));
00241                      break;
00242               }
00243        }
00244 
00245        /* Get the usage count. */
00246        count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL);
00247        if (count == -1) {
00248               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
00249        }
00250 
00251        /* If we are the only user, then take this opportunity to set the max. */
00252 
00253        if (count == 1) {
00254 #if HAVE_SEMUN
00255               /* This is correct for Linux which has union semun. */
00256               union semun semarg;
00257               semarg.val = max_acquire;
00258               if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) {
00259                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
00260               }
00261 #elif defined(SETVAL_WANTS_PTR)
00262               /* This is correct for Solaris 2.6 which does not have union semun. */
00263               if (semctl(semid, SYSVSEM_SEM, SETVAL, &max_acquire) == -1) {
00264                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
00265               }
00266 #else
00267               /* This works for i.e. AIX */
00268               if (semctl(semid, SYSVSEM_SEM, SETVAL, max_acquire) == -1) {
00269                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
00270               }
00271 #endif
00272        }
00273 
00274        /* Set semaphore 1 back to zero. */
00275 
00276        sop[0].sem_num = SYSVSEM_SETVAL;
00277        sop[0].sem_op  = -1;
00278        sop[0].sem_flg = SEM_UNDO;
00279        while (semop(semid, sop, 1) == -1) {
00280               if (errno != EINTR) {
00281                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed releasing SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno));
00282                      break;
00283               }
00284        }
00285 
00286        sem_ptr = (sysvsem_sem *) emalloc(sizeof(sysvsem_sem));
00287        sem_ptr->key   = key;
00288        sem_ptr->semid = semid;
00289        sem_ptr->count = 0;
00290        sem_ptr->auto_release = auto_release;
00291 
00292        sem_ptr->id = ZEND_REGISTER_RESOURCE(return_value, sem_ptr, php_sysvsem_module.le_sem);
00293 }
00294 /* }}} */
00295 
00296 /* {{{ php_sysvsem_semop
00297  */
00298 static void php_sysvsem_semop(INTERNAL_FUNCTION_PARAMETERS, int acquire)
00299 {
00300        zval *arg_id;
00301        sysvsem_sem *sem_ptr;
00302        struct sembuf sop;
00303 
00304        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg_id) == FAILURE) {
00305               return;
00306        }
00307 
00308        ZEND_FETCH_RESOURCE(sem_ptr, sysvsem_sem *, &arg_id, -1, "SysV semaphore", php_sysvsem_module.le_sem);
00309 
00310        if (!acquire && sem_ptr->count == 0) {
00311               php_error_docref(NULL TSRMLS_CC, E_WARNING, "SysV semaphore %ld (key 0x%x) is not currently acquired", Z_LVAL_P(arg_id), sem_ptr->key);
00312               RETURN_FALSE;
00313        }
00314 
00315        sop.sem_num = SYSVSEM_SEM;
00316        sop.sem_op  = acquire ? -1 : 1;
00317        sop.sem_flg = SEM_UNDO;
00318 
00319        while (semop(sem_ptr->semid, &sop, 1) == -1) {
00320               if (errno != EINTR) {
00321                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to %s key 0x%x: %s", acquire ? "acquire" : "release", sem_ptr->key, strerror(errno));
00322                      RETURN_FALSE;
00323               }
00324        }
00325 
00326        sem_ptr->count -= acquire ? -1 : 1;
00327        RETURN_TRUE;
00328 }
00329 /* }}} */
00330 
00331 /* {{{ proto bool sem_acquire(resource id)
00332    Acquires the semaphore with the given id, blocking if necessary */
00333 PHP_FUNCTION(sem_acquire)
00334 {
00335        php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
00336 }
00337 /* }}} */
00338 
00339 /* {{{ proto bool sem_release(resource id)
00340    Releases the semaphore with the given id */
00341 PHP_FUNCTION(sem_release)
00342 {
00343        php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
00344 }
00345 /* }}} */
00346 
00347 /* {{{ proto bool sem_remove(resource id)
00348    Removes semaphore from Unix systems */
00349 
00350 /*
00351  * contributed by Gavin Sherry gavin@linuxworld.com.au
00352  * Fri Mar 16 00:50:13 EST 2001
00353  */
00354 
00355 PHP_FUNCTION(sem_remove)
00356 {
00357        zval *arg_id;
00358        sysvsem_sem *sem_ptr;
00359 #if HAVE_SEMUN
00360        union semun un;
00361        struct semid_ds buf;
00362 #endif
00363 
00364        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg_id) == FAILURE) {
00365               return;
00366        }
00367 
00368        ZEND_FETCH_RESOURCE(sem_ptr, sysvsem_sem *, &arg_id, -1, "SysV semaphore", php_sysvsem_module.le_sem);
00369 
00370 #if HAVE_SEMUN
00371        un.buf = &buf;
00372        if (semctl(sem_ptr->semid, 0, IPC_STAT, un) < 0) {
00373 #else
00374        if (semctl(sem_ptr->semid, 0, IPC_STAT, NULL) < 0) {
00375 #endif
00376               php_error_docref(NULL TSRMLS_CC, E_WARNING, "SysV semaphore %ld does not (any longer) exist", Z_LVAL_P(arg_id));
00377               RETURN_FALSE;
00378        }
00379 
00380 #if HAVE_SEMUN
00381        if (semctl(sem_ptr->semid, 0, IPC_RMID, un) < 0) {
00382 #else
00383        if (semctl(sem_ptr->semid, 0, IPC_RMID, NULL) < 0) {
00384 #endif
00385               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for SysV sempphore %ld: %s", Z_LVAL_P(arg_id), strerror(errno));
00386               RETURN_FALSE;
00387        }
00388        
00389        /* let release_sysvsem_sem know we have removed
00390         * the semaphore to avoid issues with releasing.
00391         */ 
00392 
00393        sem_ptr->count = -1;
00394        RETURN_TRUE;
00395 }
00396 
00397 /* }}} */
00398 
00399 #endif /* HAVE_SYSVSEM */
00400 
00401 /*
00402  * Local variables:
00403  * tab-width: 4
00404  * c-basic-offset: 4
00405  * End:
00406  * vim600: sw=4 ts=4 fdm=marker
00407  * vim<600: sw=4 ts=4
00408  */