php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
sysvsem.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Tom May <tom@go2net.com> |
14 | Gavin Sherry <gavin@linuxworld.com.au> |
15 +----------------------------------------------------------------------+
16 */
17
18#ifdef HAVE_CONFIG_H
19#include <config.h>
20#endif
21
22#include "php.h"
23
24#ifdef HAVE_SYSVSEM
25
26#include <sys/types.h>
27#include <sys/ipc.h>
28#include <sys/sem.h>
29#include <errno.h>
30
31#include "sysvsem_arginfo.h"
32#include "php_sysvsem.h"
33#include "ext/standard/info.h"
34
35#ifndef HAVE_UNION_SEMUN
36union semun {
37 int val; /* value for SETVAL */
38 struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
39 unsigned short int *array; /* array for GETALL, SETALL */
40 struct seminfo *__buf; /* buffer for IPC_INFO */
41};
42#endif
43
44/* {{{ sysvsem_module_entry */
45zend_module_entry sysvsem_module_entry = {
47 "sysvsem",
48 ext_functions,
49 PHP_MINIT(sysvsem),
50 NULL,
51 NULL,
52 NULL,
53 PHP_MINFO(sysvsem),
54 PHP_SYSVSEM_VERSION,
56};
57/* }}} */
58
59#ifdef COMPILE_DL_SYSVSEM
60ZEND_GET_MODULE(sysvsem)
61#endif
62
63/* Semaphore functions using System V semaphores. Each semaphore
64 * actually consists of three semaphores allocated as a unit under the
65 * same key. Semaphore 0 (SYSVSEM_SEM) is the actual semaphore, it is
66 * initialized to max_acquire and decremented as processes acquire it.
67 * The value of semaphore 1 (SYSVSEM_USAGE) is a count of the number
68 * of processes using the semaphore. After calling semget(), if a
69 * process finds that the usage count is 1, it will set the value of
70 * SYSVSEM_SEM to max_acquire. This allows max_acquire to be set and
71 * track the PHP code without having a global init routine or external
72 * semaphore init code. Except see the bug regarding a race condition
73 * php_sysvsem_get(). Semaphore 2 (SYSVSEM_SETVAL) serializes the
74 * calls to GETVAL SYSVSEM_USAGE and SETVAL SYSVSEM_SEM. It can be
75 * acquired only when it is zero.
76 */
77
78#define SYSVSEM_SEM 0
79#define SYSVSEM_USAGE 1
80#define SYSVSEM_SETVAL 2
81
82/* SysvSemaphore class */
83
84zend_class_entry *sysvsem_ce;
85static zend_object_handlers sysvsem_object_handlers;
86
87static inline sysvsem_sem *sysvsem_from_obj(zend_object *obj) {
88 return (sysvsem_sem *)((char *)(obj) - XtOffsetOf(sysvsem_sem, std));
89}
90
91#define Z_SYSVSEM_P(zv) sysvsem_from_obj(Z_OBJ_P(zv))
92
93static zend_object *sysvsem_create_object(zend_class_entry *class_type) {
94 sysvsem_sem *intern = zend_object_alloc(sizeof(sysvsem_sem), class_type);
95
96 zend_object_std_init(&intern->std, class_type);
97 object_properties_init(&intern->std, class_type);
98
99 return &intern->std;
100}
101
102static zend_function *sysvsem_get_constructor(zend_object *object) {
103 zend_throw_error(NULL, "Cannot directly construct SysvSemaphore, use sem_get() instead");
104 return NULL;
105}
106
107static void sysvsem_free_obj(zend_object *object)
108{
109 sysvsem_sem *sem_ptr = sysvsem_from_obj(object);
110 struct sembuf sop[2];
111 int opcount = 1;
112
113 /*
114 * if count == -1, semaphore has been removed
115 * Need better way to handle this
116 */
117 if (sem_ptr->count == -1 || !sem_ptr->auto_release) {
118 zend_object_std_dtor(&sem_ptr->std);
119 return;
120 }
121 /* Decrement the usage count. */
122
123 sop[0].sem_num = SYSVSEM_USAGE;
124 sop[0].sem_op = -1;
125 sop[0].sem_flg = SEM_UNDO;
126
127 /* Release the semaphore if it has been acquired but not released. */
128
129 if (sem_ptr->count) {
130
131 sop[1].sem_num = SYSVSEM_SEM;
132 sop[1].sem_op = sem_ptr->count;
133 sop[1].sem_flg = SEM_UNDO;
134
135 opcount++;
136 }
137
138 semop(sem_ptr->semid, sop, opcount);
139
140 zend_object_std_dtor(&sem_ptr->std);
141}
142/* }}} */
143
144/* {{{ PHP_MINIT_FUNCTION */
145PHP_MINIT_FUNCTION(sysvsem)
146{
147 sysvsem_ce = register_class_SysvSemaphore();
148 sysvsem_ce->create_object = sysvsem_create_object;
149 sysvsem_ce->default_object_handlers = &sysvsem_object_handlers;
150
151 memcpy(&sysvsem_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
152 sysvsem_object_handlers.offset = XtOffsetOf(sysvsem_sem, std);
153 sysvsem_object_handlers.free_obj = sysvsem_free_obj;
154 sysvsem_object_handlers.get_constructor = sysvsem_get_constructor;
155 sysvsem_object_handlers.clone_obj = NULL;
156 sysvsem_object_handlers.compare = zend_objects_not_comparable;
157
158 return SUCCESS;
159}
160/* }}} */
161
162/* {{{ PHP_MINFO_FUNCTION */
163PHP_MINFO_FUNCTION(sysvsem)
164{
166 php_info_print_table_row(2, "sysvsem support", "enabled");
168}
169/* }}} */
170
171/* {{{ Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously */
173{
174 zend_long key, max_acquire = 1, perm = 0666;
175 bool auto_release = 1;
176 int semid;
177 struct sembuf sop[3];
178 int count;
179 sysvsem_sem *sem_ptr;
180
181 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|llb", &key, &max_acquire, &perm, &auto_release)) {
183 }
184
185 /* Get/create the semaphore. Note that we rely on the semaphores
186 * being zeroed when they are created. Despite the fact that
187 * the(?) Linux semget() man page says they are not initialized,
188 * the kernel versions 2.0.x and 2.1.z do in fact zero them.
189 */
190
191 semid = semget(key, 3, perm|IPC_CREAT);
192 if (semid == -1) {
193 php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
195 }
196
197 /* Find out how many processes are using this semaphore. Note
198 * that on Linux (at least) there is a race condition here because
199 * semaphore undo on process exit is not atomic, so we could
200 * acquire SYSVSEM_SETVAL before a crashed process has decremented
201 * SYSVSEM_USAGE in which case count will be greater than it
202 * should be and we won't set max_acquire. Fortunately this
203 * doesn't actually matter in practice.
204 */
205
206 /* Wait for sem 1 to be zero . . . */
207
208 sop[0].sem_num = SYSVSEM_SETVAL;
209 sop[0].sem_op = 0;
210 sop[0].sem_flg = 0;
211
212 /* . . . and increment it so it becomes non-zero . . . */
213
214 sop[1].sem_num = SYSVSEM_SETVAL;
215 sop[1].sem_op = 1;
216 sop[1].sem_flg = SEM_UNDO;
217
218 /* . . . and increment the usage count. */
219
220 sop[2].sem_num = SYSVSEM_USAGE;
221 sop[2].sem_op = 1;
222 sop[2].sem_flg = SEM_UNDO;
223 while (semop(semid, sop, 3) == -1) {
224 if (errno != EINTR) {
225 php_error_docref(NULL, E_WARNING, "Failed acquiring SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
226 break;
227 }
228 }
229
230 /* Get the usage count. */
231 count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL);
232 if (count == -1) {
233 php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
234 }
235
236 /* If we are the only user, then take this opportunity to set the max. */
237
238 if (count == 1) {
239 union semun semarg;
240 semarg.val = max_acquire;
241 if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) {
242 php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
243 }
244 }
245
246 /* Set semaphore 1 back to zero. */
247
248 sop[0].sem_num = SYSVSEM_SETVAL;
249 sop[0].sem_op = -1;
250 sop[0].sem_flg = SEM_UNDO;
251 while (semop(semid, sop, 1) == -1) {
252 if (errno != EINTR) {
253 php_error_docref(NULL, E_WARNING, "Failed releasing SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
254 break;
255 }
256 }
257
258 object_init_ex(return_value, sysvsem_ce);
259
260 sem_ptr = Z_SYSVSEM_P(return_value);
261 sem_ptr->key = key;
262 sem_ptr->semid = semid;
263 sem_ptr->count = 0;
264 sem_ptr->auto_release = (int) auto_release;
265}
266/* }}} */
267
268/* {{{ php_sysvsem_semop */
269static void php_sysvsem_semop(INTERNAL_FUNCTION_PARAMETERS, int acquire)
270{
271 zval *arg_id;
272 bool nowait = 0;
273 sysvsem_sem *sem_ptr;
274 struct sembuf sop;
275
276 if (acquire) {
277 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &arg_id, sysvsem_ce, &nowait) == FAILURE) {
279 }
280 } else {
281 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &arg_id, sysvsem_ce) == FAILURE) {
283 }
284 }
285
286 sem_ptr = Z_SYSVSEM_P(arg_id);
287
288 if (!acquire && sem_ptr->count == 0) {
289 php_error_docref(NULL, E_WARNING, "SysV semaphore for key 0x%x is not currently acquired", sem_ptr->key);
291 }
292
293 sop.sem_num = SYSVSEM_SEM;
294 sop.sem_op = acquire ? -1 : 1;
295 sop.sem_flg = SEM_UNDO | (nowait ? IPC_NOWAIT : 0);
296
297 while (semop(sem_ptr->semid, &sop, 1) == -1) {
298 if (errno != EINTR) {
299 if (errno != EAGAIN) {
300 php_error_docref(NULL, E_WARNING, "Failed to %s key 0x%x: %s", acquire ? "acquire" : "release", sem_ptr->key, strerror(errno));
301 }
303 }
304 }
305
306 sem_ptr->count -= acquire ? -1 : 1;
308}
309/* }}} */
310
311/* {{{ Acquires the semaphore with the given id, blocking if necessary */
313{
314 php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
315}
316/* }}} */
317
318/* {{{ Releases the semaphore with the given id */
320{
321 php_sysvsem_semop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
322}
323/* }}} */
324
325/* {{{ Removes semaphore from Unix systems */
326
327/*
328 * contributed by Gavin Sherry gavin@linuxworld.com.au
329 * Fri Mar 16 00:50:13 EST 2001
330 */
331
333{
334 zval *arg_id;
335 sysvsem_sem *sem_ptr;
336 union semun un;
337 struct semid_ds buf;
338
339 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &arg_id, sysvsem_ce) == FAILURE) {
341 }
342
343 sem_ptr = Z_SYSVSEM_P(arg_id);
344
345 un.buf = &buf;
346 if (semctl(sem_ptr->semid, 0, IPC_STAT, un) < 0) {
347 php_error_docref(NULL, E_WARNING, "SysV semaphore for key 0x%x does not (any longer) exist", sem_ptr->key);
349 }
350
351 if (semctl(sem_ptr->semid, 0, IPC_RMID, un) < 0) {
352 php_error_docref(NULL, E_WARNING, "Failed for SysV semaphore for key 0x%x: %s", sem_ptr->key, strerror(errno));
354 }
355
356 /* let release_sysvsem_sem know we have removed
357 * the semaphore to avoid issues with releasing.
358 */
359
360 sem_ptr->count = -1;
362}
363
364/* }}} */
365
366#endif /* HAVE_SYSVSEM */
count(Countable|array $value, int $mode=COUNT_NORMAL)
memcpy(ptr1, ptr2, size)
zval * val
Definition ffi.c:4262
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
php_info_print_table_start()
Definition info.c:1064
php_info_print_table_row(2, "PDO Driver for Firebird", "enabled")
php_info_print_table_end()
Definition info.c:1074
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MINFO
Definition php.h:396
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define PHP_MINFO_FUNCTION
Definition php.h:404
#define PHP_MINIT
Definition php.h:392
unsigned char key[REFLECTION_KEY_LEN]
zend_object_compare_t compare
zend_object_free_obj_t free_obj
zend_object_get_constructor_t get_constructor
zend_object_clone_obj_t clone_obj
sem_release(SysvSemaphore $semaphore)
sem_get(int $key, int $max_acquire=1, int $permissions=0666, bool $auto_release=true)
sem_remove(SysvSemaphore $semaphore)
sem_acquire(SysvSemaphore $semaphore, bool $non_blocking=false)
#define errno
#define EAGAIN
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
Definition zend.c:1772
#define INTERNAL_FUNCTION_PARAMETERS
Definition zend.h:49
#define INTERNAL_FUNCTION_PARAM_PASSTHRU
Definition zend.h:50
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type)
Definition zend_API.c:1849
ZEND_API zend_result zend_parse_parameters(uint32_t num_args, const char *type_spec,...)
Definition zend_API.c:1300
ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type)
Definition zend_API.c:1688
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
#define RETURN_FALSE
Definition zend_API.h:1058
#define ZEND_GET_MODULE(name)
Definition zend_API.h:241
#define RETURN_THROWS()
Definition zend_API.h:1060
#define RETURN_TRUE
Definition zend_API.h:1059
struct _zval_struct zval
#define E_WARNING
Definition zend_errors.h:24
union _zend_function zend_function
int32_t zend_long
Definition zend_long.h:42
#define ZEND_XLONG_FMT
Definition zend_long.h:89
#define STANDARD_MODULE_HEADER
struct _zend_module_entry zend_module_entry
#define STANDARD_MODULE_PROPERTIES
ZEND_API int zend_objects_not_comparable(zval *o1, zval *o2)
ZEND_API const zend_object_handlers std_object_handlers
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
ZEND_API void zend_object_std_dtor(zend_object *object)
#define XtOffsetOf(s_type, field)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
@ FAILURE
Definition zend_types.h:61
struct _zend_object_handlers zend_object_handlers
Definition zend_types.h:88
zval * return_value