Annotation of embedaddon/php/ext/sysvsem/sysvsem.c, revision 1.1.1.4
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.4 ! misho 5: | Copyright (c) 1997-2014 The PHP Group |
1.1 misho 6: +----------------------------------------------------------------------+
7: | This source file is subject to version 3.01 of the PHP license, |
8: | that is bundled with this package in the file LICENSE, and is |
9: | available through the world-wide-web at the following url: |
10: | http://www.php.net/license/3_01.txt |
11: | If you did not receive a copy of the PHP license and are unable to |
12: | obtain it through the world-wide-web, please send a note to |
13: | license@php.net so we can mail you a copy immediately. |
14: +----------------------------------------------------------------------+
15: | Authors: Tom May <tom@go2net.com> |
16: | Gavin Sherry <gavin@linuxworld.com.au> |
17: +----------------------------------------------------------------------+
18: */
19:
1.1.1.2 misho 20: /* $Id$ */
1.1 misho 21:
22: /* Latest update build anc tested on Linux 2.2.14
23: *
24: * This has been built and tested on Solaris 2.6 and Linux 2.1.122.
25: * It may not compile or execute correctly on other systems.
26: *
27: * sas: Works for me on Linux 2.0.36 and FreeBSD 3.0-current
28: */
29:
30: #ifdef HAVE_CONFIG_H
31: #include "config.h"
32: #endif
33:
34: #include "php.h"
35:
36: #if HAVE_SYSVSEM
37:
38: #include <sys/types.h>
39: #include <sys/ipc.h>
40: #include <sys/sem.h>
41: #include <errno.h>
42:
43: #include "php_sysvsem.h"
44:
45: #if !HAVE_SEMUN
46:
47: union semun {
48: int val; /* value for SETVAL */
49: struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
50: unsigned short int *array; /* array for GETALL, SETALL */
51: struct seminfo *__buf; /* buffer for IPC_INFO */
52: };
53:
54: #undef HAVE_SEMUN
55: #define HAVE_SEMUN 1
56:
57: #endif
58:
59: /* {{{ arginfo */
60: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_get, 0, 0, 1)
61: ZEND_ARG_INFO(0, key)
62: ZEND_ARG_INFO(0, max_acquire)
63: ZEND_ARG_INFO(0, perm)
64: ZEND_ARG_INFO(0, auto_release)
65: ZEND_END_ARG_INFO()
66:
67: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_acquire, 0, 0, 1)
68: ZEND_ARG_INFO(0, sem_identifier)
69: ZEND_END_ARG_INFO()
70:
71: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_release, 0, 0, 1)
72: ZEND_ARG_INFO(0, sem_identifier)
73: ZEND_END_ARG_INFO()
74:
75: ZEND_BEGIN_ARG_INFO_EX(arginfo_sem_remove, 0, 0, 1)
76: ZEND_ARG_INFO(0, sem_identifier)
77: ZEND_END_ARG_INFO()
78: /* }}} */
79:
80: /* {{{ sysvsem_functions[]
81: */
82: const zend_function_entry sysvsem_functions[] = {
83: PHP_FE(sem_get, arginfo_sem_get)
84: PHP_FE(sem_acquire, arginfo_sem_acquire)
85: PHP_FE(sem_release, arginfo_sem_release)
86: PHP_FE(sem_remove, arginfo_sem_remove)
87: PHP_FE_END
88: };
89: /* }}} */
90:
91: /* {{{ sysvsem_module_entry
92: */
93: zend_module_entry sysvsem_module_entry = {
94: STANDARD_MODULE_HEADER,
95: "sysvsem",
96: sysvsem_functions,
97: PHP_MINIT(sysvsem),
98: NULL,
99: NULL,
100: NULL,
101: NULL,
102: NO_VERSION_YET,
103: STANDARD_MODULE_PROPERTIES
104: };
105: /* }}} */
106:
107: #ifdef COMPILE_DL_SYSVSEM
108: ZEND_GET_MODULE(sysvsem)
109: #endif
110:
111:
112: THREAD_LS sysvsem_module php_sysvsem_module;
113:
114: /* Semaphore functions using System V semaphores. Each semaphore
115: * actually consists of three semaphores allocated as a unit under the
116: * same key. Semaphore 0 (SYSVSEM_SEM) is the actual semaphore, it is
117: * initialized to max_acquire and decremented as processes acquire it.
118: * The value of semaphore 1 (SYSVSEM_USAGE) is a count of the number
119: * of processes using the semaphore. After calling semget(), if a
120: * process finds that the usage count is 1, it will set the value of
121: * SYSVSEM_SEM to max_acquire. This allows max_acquire to be set and
122: * track the PHP code without having a global init routine or external
123: * semaphore init code. Except see the bug regarding a race condition
124: * php_sysvsem_get(). Semaphore 2 (SYSVSEM_SETVAL) serializes the
125: * calls to GETVAL SYSVSEM_USAGE and SETVAL SYSVSEM_SEM. It can be
126: * acquired only when it is zero.
127: */
128:
129: #define SYSVSEM_SEM 0
130: #define SYSVSEM_USAGE 1
131: #define SYSVSEM_SETVAL 2
132:
133: /* {{{ release_sysvsem_sem
134: */
135: static void release_sysvsem_sem(zend_rsrc_list_entry *rsrc TSRMLS_DC)
136: {
137: sysvsem_sem *sem_ptr = (sysvsem_sem *)rsrc->ptr;
138: struct sembuf sop[2];
139: int opcount = 1;
140: /*
141: * if count == -1, semaphore has been removed
142: * Need better way to handle this
143: */
144:
145: if (sem_ptr->count == -1 || !sem_ptr->auto_release) {
146: efree(sem_ptr);
147: return;
148: }
149: /* Decrement the usage count. */
150:
151: sop[0].sem_num = SYSVSEM_USAGE;
152: sop[0].sem_op = -1;
153: sop[0].sem_flg = SEM_UNDO;
154:
155: /* Release the semaphore if it has been acquired but not released. */
156:
157: if (sem_ptr->count) {
158:
159: sop[1].sem_num = SYSVSEM_SEM;
160: sop[1].sem_op = sem_ptr->count;
161: sop[1].sem_flg = SEM_UNDO;
162:
163: opcount++;
164: }
165:
166: semop(sem_ptr->semid, sop, opcount);
167: efree(sem_ptr);
168: }
169: /* }}} */
170:
171: /* {{{ PHP_MINIT_FUNCTION
172: */
173: PHP_MINIT_FUNCTION(sysvsem)
174: {
175: php_sysvsem_module.le_sem = zend_register_list_destructors_ex(release_sysvsem_sem, NULL, "sysvsem", module_number);
176: return SUCCESS;
177: }
178: /* }}} */
179:
180: #define SETVAL_WANTS_PTR
181:
182: #if defined(_AIX)
183: #undef SETVAL_WANTS_PTR
184: #endif
185:
186: /* {{{ proto resource sem_get(int key [, int max_acquire [, int perm [, int auto_release]])
187: Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously */
188: PHP_FUNCTION(sem_get)
189: {
190: long key, max_acquire = 1, perm = 0666, auto_release = 1;
191: int semid;
192: struct sembuf sop[3];
193: int count;
194: sysvsem_sem *sem_ptr;
195:
196: if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|lll", &key, &max_acquire, &perm, &auto_release)) {
197: RETURN_FALSE;
198: }
199:
200: /* Get/create the semaphore. Note that we rely on the semaphores
201: * being zeroed when they are created. Despite the fact that
202: * the(?) Linux semget() man page says they are not initialized,
203: * the kernel versions 2.0.x and 2.1.z do in fact zero them.
204: */
205:
206: semid = semget(key, 3, perm|IPC_CREAT);
207: if (semid == -1) {
208: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
209: RETURN_FALSE;
210: }
211:
212: /* Find out how many processes are using this semaphore. Note
213: * that on Linux (at least) there is a race condition here because
214: * semaphore undo on process exit is not atomic, so we could
215: * acquire SYSVSEM_SETVAL before a crashed process has decremented
216: * SYSVSEM_USAGE in which case count will be greater than it
217: * should be and we won't set max_acquire. Fortunately this
218: * doesn't actually matter in practice.
219: */
220:
221: /* Wait for sem 1 to be zero . . . */
222:
223: sop[0].sem_num = SYSVSEM_SETVAL;
224: sop[0].sem_op = 0;
225: sop[0].sem_flg = 0;
226:
227: /* . . . and increment it so it becomes non-zero . . . */
228:
229: sop[1].sem_num = SYSVSEM_SETVAL;
230: sop[1].sem_op = 1;
231: sop[1].sem_flg = SEM_UNDO;
232:
233: /* . . . and increment the usage count. */
234:
235: sop[2].sem_num = SYSVSEM_USAGE;
236: sop[2].sem_op = 1;
237: sop[2].sem_flg = SEM_UNDO;
238: while (semop(semid, sop, 3) == -1) {
239: if (errno != EINTR) {
240: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed acquiring SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno));
241: break;
242: }
243: }
244:
245: /* Get the usage count. */
246: count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL);
247: if (count == -1) {
248: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
249: }
250:
251: /* If we are the only user, then take this opportunity to set the max. */
252:
253: if (count == 1) {
254: #if HAVE_SEMUN
255: /* This is correct for Linux which has union semun. */
256: union semun semarg;
257: semarg.val = max_acquire;
258: if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) {
259: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
260: }
261: #elif defined(SETVAL_WANTS_PTR)
262: /* This is correct for Solaris 2.6 which does not have union semun. */
263: if (semctl(semid, SYSVSEM_SEM, SETVAL, &max_acquire) == -1) {
264: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
265: }
266: #else
267: /* This works for i.e. AIX */
268: if (semctl(semid, SYSVSEM_SEM, SETVAL, max_acquire) == -1) {
269: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for key 0x%lx: %s", key, strerror(errno));
270: }
271: #endif
272: }
273:
274: /* Set semaphore 1 back to zero. */
275:
276: sop[0].sem_num = SYSVSEM_SETVAL;
277: sop[0].sem_op = -1;
278: sop[0].sem_flg = SEM_UNDO;
279: while (semop(semid, sop, 1) == -1) {
280: if (errno != EINTR) {
281: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed releasing SYSVSEM_SETVAL for key 0x%lx: %s", key, strerror(errno));
282: break;
283: }
284: }
285:
286: sem_ptr = (sysvsem_sem *) emalloc(sizeof(sysvsem_sem));
287: sem_ptr->key = key;
288: sem_ptr->semid = semid;
289: sem_ptr->count = 0;
290: sem_ptr->auto_release = auto_release;
291:
292: sem_ptr->id = ZEND_REGISTER_RESOURCE(return_value, sem_ptr, php_sysvsem_module.le_sem);
293: }
294: /* }}} */
295:
296: /* {{{ php_sysvsem_semop
297: */
298: static void php_sysvsem_semop(INTERNAL_FUNCTION_PARAMETERS, int acquire)
299: {
300: zval *arg_id;
301: sysvsem_sem *sem_ptr;
302: struct sembuf sop;
303:
304: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg_id) == FAILURE) {
305: return;
306: }
307:
308: ZEND_FETCH_RESOURCE(sem_ptr, sysvsem_sem *, &arg_id, -1, "SysV semaphore", php_sysvsem_module.le_sem);
309:
310: if (!acquire && sem_ptr->count == 0) {
311: 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);
312: RETURN_FALSE;
313: }
314:
315: sop.sem_num = SYSVSEM_SEM;
316: sop.sem_op = acquire ? -1 : 1;
317: sop.sem_flg = SEM_UNDO;
318:
319: while (semop(sem_ptr->semid, &sop, 1) == -1) {
320: if (errno != EINTR) {
321: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to %s key 0x%x: %s", acquire ? "acquire" : "release", sem_ptr->key, strerror(errno));
322: RETURN_FALSE;
323: }
324: }
325:
326: sem_ptr->count -= acquire ? -1 : 1;
327: RETURN_TRUE;
328: }
329: /* }}} */
330:
331: /* {{{ proto bool sem_acquire(resource id)
332: Acquires the semaphore with the given id, blocking if necessary */
333: PHP_FUNCTION(sem_acquire)
334: {
335: php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
336: }
337: /* }}} */
338:
339: /* {{{ proto bool sem_release(resource id)
340: Releases the semaphore with the given id */
341: PHP_FUNCTION(sem_release)
342: {
343: php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
344: }
345: /* }}} */
346:
347: /* {{{ proto bool sem_remove(resource id)
348: Removes semaphore from Unix systems */
349:
350: /*
351: * contributed by Gavin Sherry gavin@linuxworld.com.au
352: * Fri Mar 16 00:50:13 EST 2001
353: */
354:
355: PHP_FUNCTION(sem_remove)
356: {
357: zval *arg_id;
358: sysvsem_sem *sem_ptr;
359: #if HAVE_SEMUN
360: union semun un;
361: struct semid_ds buf;
362: #endif
363:
364: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg_id) == FAILURE) {
365: return;
366: }
367:
368: ZEND_FETCH_RESOURCE(sem_ptr, sysvsem_sem *, &arg_id, -1, "SysV semaphore", php_sysvsem_module.le_sem);
369:
370: #if HAVE_SEMUN
371: un.buf = &buf;
372: if (semctl(sem_ptr->semid, 0, IPC_STAT, un) < 0) {
373: #else
374: if (semctl(sem_ptr->semid, 0, IPC_STAT, NULL) < 0) {
375: #endif
376: php_error_docref(NULL TSRMLS_CC, E_WARNING, "SysV semaphore %ld does not (any longer) exist", Z_LVAL_P(arg_id));
377: RETURN_FALSE;
378: }
379:
380: #if HAVE_SEMUN
381: if (semctl(sem_ptr->semid, 0, IPC_RMID, un) < 0) {
382: #else
383: if (semctl(sem_ptr->semid, 0, IPC_RMID, NULL) < 0) {
384: #endif
385: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed for SysV sempphore %ld: %s", Z_LVAL_P(arg_id), strerror(errno));
386: RETURN_FALSE;
387: }
388:
389: /* let release_sysvsem_sem know we have removed
390: * the semaphore to avoid issues with releasing.
391: */
392:
393: sem_ptr->count = -1;
394: RETURN_TRUE;
395: }
396:
397: /* }}} */
398:
399: #endif /* HAVE_SYSVSEM */
400:
401: /*
402: * Local variables:
403: * tab-width: 4
404: * c-basic-offset: 4
405: * End:
406: * vim600: sw=4 ts=4 fdm=marker
407: * vim<600: sw=4 ts=4
408: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>