5 Commits
6 changed files with 299 additions and 40 deletions
+10 -11
View File
@@ -1,14 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Copyright 2019 Denis "ftc" Denisov
Version 2, December 2004 Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> http://www.apache.org/licenses/LICENSE-2.0
Everyone is permitted to copy and distribute verbatim or modified copies of Unless required by applicable law or agreed to in writing, software
this license document, and changing it is allowed as long as the name is changed. distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE See the License for the specific language governing permissions and
limitations under the License.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
Regular → Executable
+134 -2
View File
@@ -1,3 +1,135 @@
# php_semaphores # posix_semaphores - POSIX semaphores extension for PHP
POSIX semaphores extension for PHP ## Introduction
This a thin wrapper around [POSIX semaphores API](http://man7.org/linux/man-pages/man7/sem_overview.7.html).
There is built-in PHP extension for [semaphores and other IPC](https://www.php.net/manual/en/book.sem.php),
but it supports only System V semaphores, when POSIX version has much more convenient API.
Also System V semaphore support in PHP does not match exactly with C API (in fact it constructs three semaphores
in set under the hood and uses them for initialization and other bookkeeping tasks). That's very convenient
when one uses semaphores solely from PHP code, but prevents some mixed use cases when one wants to sync PHP code
and other code with semaphores.
For example it's not possible to create locked semaphore in C++ application, wait for it in PHP (with locking
of course) and then unlock in C++ application (this case could be used for shared memory IPC syncronization, for instance).
So the goal here is to make C API wrapper as thin as possible to allow simultaneous usage of semaphores in
applications written in PHP or other programming languages.
## Installation
Make sure you have `php-devel` package installed in your system. You need to be able to build PHP extensions.
After that run commands:
```
phpize
./configure --enable-posix_semaphores
make
```
If everything goes well, you will have file `./modules/posix_semaphores.so`. The last step is to include this file
into your `php.ini`. Add line `extension=/<PATH>/posix_semaphores.so` (replace `<PATH>` with actual path to this
extension) and you're done. Don't forget to restart php-fpm or apache if you're using PHP from there.
## Reference
Extension consists of several functions which are clearly mapped to POSIX semaphores API.
### posix_sem_open($name, $flags, $mode = 0666, $value = 0)
This is equivalent of [sem_open](http://man7.org/linux/man-pages/man3/sem_open.3.html).
Parameters:
* $name - name of semaphore to open,
* $flags - opening flags (such as O_CREAT or O_EXCL),
* $mode - opening mode (default value is 0666),
* $value - initial value for semaphore (default is 0).
Function returns semaphore resource on success or `null` on failure.
### posix_sem_close($semaphore)
Function closes semaphore resource (in fact it's just a proxy to [sem_close](http://man7.org/linux/man-pages/man3/sem_close.3.html)).
Parameters:
* $semaphore - semaphore resource acquired via `posix_sem_open`.
Returns `true` on success, `false` otherwise.
### posix_sem_unlink($name)
Unlinks named semaphore from system. The semaphore name is removed immediately. The semaphore itself is removed when
all processed having the semaphore opened, close it.
Parameters:
* $name - name of semaphore to remove.
Returns `true` on success, `false` otherwise.
### posix_sem_post($semaphore)
Unlocks semaphore. If the semaphore's value consequently becomes greater than zero, then
another process or thread blocked in a sem_wait call will be woken
up and proceed to lock the semaphore.
Parameters:
* $semaphore - semaphore resource to unlock (acquired via `posix_sem_open`).
Returns `true` on success, `false` otherwise.
### posix_sem_wait($semaphore)
Locks a semaphore. If
the semaphore's value is greater than zero, then the decrement
proceeds, and the function returns, immediately. If the semaphore
currently has the value zero, then the call blocks until either it
becomes possible to perform the decrement (i.e., the semaphore value
rises above zero), or a signal handler interrupts the call.
Parameters:
* $semaphore - semaphore resource to unlock (acquired via `posix_sem_open`).
Returns `true` on success, `false` otherwise.
### posix_sem_trywait($semaphore)
The same as `posix_sem_wait`, except that if the decrement
cannot be immediately performed, then call returns an error (last error
set to EAGAIN) instead of blocking.
### posix_sem_timedwait($semaphore, $timeout_sec, $timeout_nsec = 0)
Tries to lock a semaphore (as `posix_sem_wait`), but if lock could not
be performed in a specified timeout, returns and sets last error to ETIMEDOUT.
Parameters:
* $semaphore - semaphore resource to unlock (acquired via `posix_sem_open`).
* $timeout_sec - part of the timeout in seconds.
* $timeout_nsec - part of the timeout in nanoseconds.
Total timeout is computed as `$timeout_sec * 1000000000 + $timeout_nsec` and is
measured in nanoseconds.
### posix_sem_getvalue($semaphore)
Gets value of specified semaphore.
Parameters:
* $semaphore - semaphore resource which value to get (acquired via `posix_sem_open`).
Returns semaphore value on success and `null` on error.
Please note that underlying function `sem_getvalue` can behave differently according to
POSIX specification, so please read your OS documentation on that.
### posix_sem_error()
Returns value of last error occured during call of another semaphore-related function.
## Notes for PHP users
All semaphore resources are closed automatically when PHP exits. But underlying semaphores are not unlinked by default. You
should use `register_shutdown_function` to unlink all semaphores which you don't need after program exit.
Alternative way for that is to manage semaphore creation and deletion outside of PHP.
+104 -26
View File
@@ -11,12 +11,15 @@
#include <fcntl.h> #include <fcntl.h>
static zend_function_entry posix_semaphores_functions[] = { static zend_function_entry posix_semaphores_functions[] = {
PHP_FE(posix_sem_open, NULL) PHP_FE(posix_sem_open, arginfo_posix_sem_open)
PHP_FE(posix_sem_close, NULL) PHP_FE(posix_sem_close, arginfo_posix_sem_close)
PHP_FE(posix_sem_unlink, NULL) PHP_FE(posix_sem_unlink, arginfo_posix_sem_unlink)
PHP_FE(posix_sem_getvalue, NULL) PHP_FE(posix_sem_getvalue, arginfo_posix_sem_getvalue)
PHP_FE(posix_sem_post, NULL) PHP_FE(posix_sem_post, arginfo_posix_sem_post)
PHP_FE(posix_sem_wait, NULL) PHP_FE(posix_sem_wait, arginfo_posix_sem_wait)
PHP_FE(posix_sem_trywait, arginfo_posix_sem_trywait)
PHP_FE(posix_sem_timedwait, arginfo_posix_sem_timedwait)
PHP_FE(posix_sem_error, arginfo_posix_sem_error)
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };
@@ -48,7 +51,7 @@ typedef struct _php_posix_semaphore {
} php_posix_semaphore; } php_posix_semaphore;
static void php_posix_semaphore_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) static void php_posix_semaphore_dtor(zend_resource *rsrc)
{ {
php_posix_semaphore *s = (php_posix_semaphore*)rsrc->ptr; php_posix_semaphore *s = (php_posix_semaphore*)rsrc->ptr;
if (s) if (s)
@@ -58,6 +61,7 @@ static void php_posix_semaphore_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
sem_close(s->sem); sem_close(s->sem);
s->sem = NULL; s->sem = NULL;
} }
efree(s);
} }
} }
@@ -66,9 +70,13 @@ static void php_posix_semaphore_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
// list entry identifier // list entry identifier
int le_posix_semaphore; int le_posix_semaphore;
// error which occured during last function call
int last_errno;
PHP_MINIT_FUNCTION(posix_semaphores) PHP_MINIT_FUNCTION(posix_semaphores)
{ {
le_posix_semaphore = zend_register_list_destructors_ex(php_posix_semaphore_dtor, NULL, PHP_POSIX_SEMAPHORES_RES_NAME, module_number); le_posix_semaphore = zend_register_list_destructors_ex(php_posix_semaphore_dtor, NULL, PHP_POSIX_SEMAPHORES_RES_NAME, module_number);
last_errno = 0;
return SUCCESS; return SUCCESS;
} }
@@ -84,39 +92,39 @@ PHP_MSHUTDOWN_FUNCTION(posix_semaphores)
PHP_FUNCTION(posix_sem_open) PHP_FUNCTION(posix_sem_open)
{ {
char *name; char *name;
int name_length; size_t name_length;
long flags; zend_long flags;
long mode = 0666; zend_long mode = 0666;
long value = 0; zend_long value = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|ll", &name, &name_length, &flags, &mode, &value) == FAILURE) if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|ll", &name, &name_length, &flags, &mode, &value) == FAILURE)
{ {
RETURN_NULL(); RETURN_NULL();
} }
sem_t* sem = sem_open(name, flags, mode, value); sem_t* sem = sem_open(name, flags, mode, value);
if (sem == SEM_FAILED) if (sem == SEM_FAILED)
{ {
printf("sem_open failed, errno = %d flags = %d O_CREAT = %d", errno, flags, O_CREAT); last_errno = errno;
RETURN_NULL(); RETURN_NULL();
} }
php_posix_semaphore *semaphore = emalloc(sizeof(php_posix_semaphore)); php_posix_semaphore *semaphore = emalloc(sizeof(php_posix_semaphore));
semaphore->sem = sem; semaphore->sem = sem;
ZEND_REGISTER_RESOURCE(return_value, semaphore, le_posix_semaphore); ZVAL_RES(return_value, zend_register_resource(semaphore, le_posix_semaphore));
} }
PHP_FUNCTION(posix_sem_close) PHP_FUNCTION(posix_sem_close)
{ {
php_posix_semaphore *semaphore; php_posix_semaphore *semaphore;
zval *zsemaphore; zval *zsemaphore;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsemaphore) == FAILURE) if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsemaphore) == FAILURE)
{ {
RETURN_FALSE; RETURN_FALSE;
} }
semaphore = (php_posix_semaphore*)zend_fetch_resource(Z_RES_P(zsemaphore), PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore);
ZEND_FETCH_RESOURCE(semaphore, php_posix_semaphore*, &zsemaphore, -1, PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore);
if (semaphore->sem) if (semaphore->sem)
{ {
if (sem_close(semaphore->sem) == -1) if (sem_close(semaphore->sem) == -1)
{ {
last_errno = errno;
RETURN_FALSE; RETURN_FALSE;
} }
semaphore->sem = NULL; semaphore->sem = NULL;
@@ -128,14 +136,15 @@ PHP_FUNCTION(posix_sem_close)
PHP_FUNCTION(posix_sem_unlink) PHP_FUNCTION(posix_sem_unlink)
{ {
char *name; char *name;
int name_length; size_t name_length;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_length) == FAILURE) if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_length) == FAILURE)
{ {
RETURN_NULL(); RETURN_NULL();
} }
if (sem_unlink(name) == -1) if (sem_unlink(name) == -1)
{ {
RETURN_FALSE; last_errno = errno;
RETURN_FALSE;
} }
RETURN_TRUE; RETURN_TRUE;
} }
@@ -144,17 +153,18 @@ PHP_FUNCTION(posix_sem_getvalue)
{ {
php_posix_semaphore *semaphore; php_posix_semaphore *semaphore;
zval *zsemaphore; zval *zsemaphore;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsemaphore) == FAILURE) if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsemaphore) == FAILURE)
{ {
RETURN_NULL(); RETURN_NULL();
} }
ZEND_FETCH_RESOURCE(semaphore, php_posix_semaphore*, &zsemaphore, -1, PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore); semaphore = (php_posix_semaphore*)zend_fetch_resource(Z_RES_P(zsemaphore), PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore);
if (semaphore->sem) if (semaphore->sem)
{ {
int value; int value;
if (sem_getvalue(semaphore->sem, &value) == -1) if (sem_getvalue(semaphore->sem, &value) == -1)
{ {
last_errno = errno;
RETURN_NULL(); RETURN_NULL();
} }
else else
@@ -172,16 +182,17 @@ PHP_FUNCTION(posix_sem_post)
{ {
php_posix_semaphore *semaphore; php_posix_semaphore *semaphore;
zval *zsemaphore; zval *zsemaphore;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsemaphore) == FAILURE) if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsemaphore) == FAILURE)
{ {
RETURN_FALSE; RETURN_FALSE;
} }
ZEND_FETCH_RESOURCE(semaphore, php_posix_semaphore*, &zsemaphore, -1, PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore); semaphore = (php_posix_semaphore*)zend_fetch_resource(Z_RES_P(zsemaphore), PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore);
if (semaphore->sem) if (semaphore->sem)
{ {
if (sem_post(semaphore->sem) == -1) if (sem_post(semaphore->sem) == -1)
{ {
last_errno = errno;
RETURN_FALSE; RETURN_FALSE;
} }
else else
@@ -199,16 +210,17 @@ PHP_FUNCTION(posix_sem_wait)
{ {
php_posix_semaphore *semaphore; php_posix_semaphore *semaphore;
zval *zsemaphore; zval *zsemaphore;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsemaphore) == FAILURE) if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsemaphore) == FAILURE)
{ {
RETURN_FALSE; RETURN_FALSE;
} }
ZEND_FETCH_RESOURCE(semaphore, php_posix_semaphore*, &zsemaphore, -1, PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore); semaphore = (php_posix_semaphore*)zend_fetch_resource(Z_RES_P(zsemaphore), PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore);
if (semaphore->sem) if (semaphore->sem)
{ {
if (sem_wait(semaphore->sem) == -1) if (sem_wait(semaphore->sem) == -1)
{ {
last_errno = errno;
RETURN_FALSE; RETURN_FALSE;
} }
else else
@@ -222,3 +234,69 @@ PHP_FUNCTION(posix_sem_wait)
} }
} }
PHP_FUNCTION(posix_sem_trywait)
{
php_posix_semaphore *semaphore;
zval *zsemaphore;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsemaphore) == FAILURE)
{
RETURN_FALSE;
}
semaphore = (php_posix_semaphore*)zend_fetch_resource(Z_RES_P(zsemaphore), PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore);
if (semaphore->sem)
{
if (sem_trywait(semaphore->sem) == -1)
{
last_errno = errno;
RETURN_FALSE;
}
else
{
RETURN_TRUE;
}
}
else
{
RETURN_FALSE;
}
}
PHP_FUNCTION(posix_sem_timedwait)
{
php_posix_semaphore *semaphore;
zval *zsemaphore;
zend_long timeout_sec;
zend_long timeout_nsec = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|l", &zsemaphore, &timeout_sec, &timeout_nsec) == FAILURE)
{
RETURN_FALSE;
}
semaphore = (php_posix_semaphore*)zend_fetch_resource(Z_RES_P(zsemaphore), PHP_POSIX_SEMAPHORES_RES_NAME, le_posix_semaphore);
if (semaphore->sem)
{
struct timespec tm;
tm.tv_sec = (int)timeout_sec;
tm.tv_nsec = (int)timeout_nsec;
if (sem_timedwait(semaphore->sem, &tm) == -1)
{
last_errno = errno;
RETURN_FALSE;
}
else
{
RETURN_TRUE;
}
}
else
{
RETURN_FALSE;
}
}
PHP_FUNCTION(posix_sem_error)
{
RETURN_LONG(last_errno);
}
+6 -1
View File
@@ -1,9 +1,11 @@
#ifndef POSIX_SEMAPHORES_H #ifndef POSIX_SEMAPHORES_H
#define POSIX_SEMAPHORES_H 1 #define POSIX_SEMAPHORES_H 1
#define PHP_POSIX_SEMAPHORES_VERSION "1.0" #define PHP_POSIX_SEMAPHORES_VERSION "1.1"
#define PHP_POSIX_SEMAPHORES_EXTNAME "posix_semaphores" #define PHP_POSIX_SEMAPHORES_EXTNAME "posix_semaphores"
#include "posix_semaphores_arginfo.h"
PHP_MINIT_FUNCTION(posix_semaphores); PHP_MINIT_FUNCTION(posix_semaphores);
PHP_MSHUTDOWN_FUNCTION(posix_semaphores); PHP_MSHUTDOWN_FUNCTION(posix_semaphores);
@@ -13,6 +15,9 @@ PHP_FUNCTION(posix_sem_unlink);
PHP_FUNCTION(posix_sem_getvalue); PHP_FUNCTION(posix_sem_getvalue);
PHP_FUNCTION(posix_sem_post); PHP_FUNCTION(posix_sem_post);
PHP_FUNCTION(posix_sem_wait); PHP_FUNCTION(posix_sem_wait);
PHP_FUNCTION(posix_sem_trywait);
PHP_FUNCTION(posix_sem_timedwait);
PHP_FUNCTION(posix_sem_error);
extern zend_module_entry posix_semaphores_module_entry; extern zend_module_entry posix_semaphores_module_entry;
#define phpext_posix_semaphores_ptr &posix_semaphores_module_entry; #define phpext_posix_semaphores_ptr &posix_semaphores_module_entry;
+11
View File
@@ -0,0 +1,11 @@
<?php
function posix_sem_open(string $name, int $flags, int $mode, int $value) : resource|null {}
function posix_sem_close(resource $semaphore) : boolean {}
function posix_sem_unlink(resource $semaphore) : boolean {}
function posix_sem_post(resource $semaphore) : boolean {}
function posix_sem_wait(resource $semaphore) : boolean {}
function posix_sem_trywait(resource $semaphore) : boolean {}
function posix_sem_timedwait(resource $semaphore, int $timeout_sec, int $timeout_nsec = 0) : boolean {}
function posix_sem_getvalue(resource $semaphore) : int|null {}
function posix_sem_error() : int {}
+34
View File
@@ -0,0 +1,34 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 9e3bcd8b1562df022b0491acc13c50410cec2648 */
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_posix_sem_open, 0, 4, resource, 1)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, mode, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_posix_sem_close, 0, 1, boolean, 0)
ZEND_ARG_OBJ_INFO(0, semaphore, resource, 0)
ZEND_END_ARG_INFO()
#define arginfo_posix_sem_unlink arginfo_posix_sem_close
#define arginfo_posix_sem_post arginfo_posix_sem_close
#define arginfo_posix_sem_wait arginfo_posix_sem_close
#define arginfo_posix_sem_trywait arginfo_posix_sem_close
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_posix_sem_timedwait, 0, 2, boolean, 0)
ZEND_ARG_OBJ_INFO(0, semaphore, resource, 0)
ZEND_ARG_TYPE_INFO(0, timeout_sec, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout_nsec, IS_LONG, 0, "0")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_posix_sem_getvalue, 0, 1, IS_LONG, 1)
ZEND_ARG_OBJ_INFO(0, semaphore, resource, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_posix_sem_error, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()