mirror of
https://github.com/php-win-ext/pecl-system-sync.git
synced 2026-03-24 00:52:14 +01:00
1598 lines
40 KiB
C
1598 lines
40 KiB
C
/*
|
|
Direct port of the cross platform 'sync' library: https://github.com/cubiclesoft/cross-platform-cpp
|
|
This source file is under the MIT, LGPL, or version 3.01 of the PHP license, your choice.
|
|
(C) 2014 CubicleSoft. All rights reserved.
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "php.h"
|
|
#include "php_ini.h"
|
|
#include "zend_exceptions.h"
|
|
#include "ext/standard/info.h"
|
|
#include <stdint.h>
|
|
#include "php_sync.h"
|
|
|
|
/* {{{ sync_module_entry
|
|
*/
|
|
zend_module_entry sync_module_entry = {
|
|
#if ZEND_MODULE_API_NO >= 20010901
|
|
STANDARD_MODULE_HEADER,
|
|
#endif
|
|
"sync",
|
|
NULL,
|
|
PHP_MINIT(sync),
|
|
PHP_MSHUTDOWN(sync),
|
|
NULL,
|
|
NULL,
|
|
PHP_MINFO(sync),
|
|
#if ZEND_MODULE_API_NO >= 20010901
|
|
PHP_SYNC_VERSION,
|
|
#endif
|
|
STANDARD_MODULE_PROPERTIES
|
|
};
|
|
/* }}} */
|
|
|
|
#ifdef COMPILE_DL_SYNC
|
|
ZEND_GET_MODULE(sync)
|
|
#endif
|
|
|
|
#ifndef INFINITE
|
|
# define INFINITE 0xFFFFFFFF
|
|
#endif
|
|
|
|
|
|
/* Define some generic functions used several places. */
|
|
#if defined(PHP_WIN32)
|
|
|
|
/* Windows. */
|
|
sync_ThreadIDType sync_GetCurrentThreadID()
|
|
{
|
|
return GetCurrentThreadId();
|
|
}
|
|
|
|
uint64_t sync_GetUnixMicrosecondTime()
|
|
{
|
|
FILETIME TempTime;
|
|
ULARGE_INTEGER TempTime2;
|
|
uint64_t Result;
|
|
|
|
GetSystemTimeAsFileTime(&TempTime);
|
|
TempTime2.HighPart = TempTime.dwHighDateTime;
|
|
TempTime2.LowPart = TempTime.dwLowDateTime;
|
|
Result = TempTime2.QuadPart;
|
|
|
|
Result = (Result / 10) - (uint64_t)11644473600000000ULL;
|
|
|
|
return Result;
|
|
}
|
|
|
|
#else
|
|
|
|
/* POSIX pthreads. */
|
|
sync_ThreadIDType sync_GetCurrentThreadID()
|
|
{
|
|
return pthread_self();
|
|
}
|
|
|
|
uint64_t sync_GetUnixMicrosecondTime()
|
|
{
|
|
struct timeval TempTime;
|
|
|
|
if (gettimeofday(&TempTime, NULL)) return 0;
|
|
|
|
return (uint64_t)((uint64_t)TempTime.tv_sec * (uint64_t)1000000 + (uint64_t)TempTime.tv_usec);
|
|
}
|
|
|
|
/* Simplifies timer-based mutex/semaphore acquisition. */
|
|
int sync_WaitForSemaphore(sem_t *SemPtr, uint32_t Wait)
|
|
{
|
|
if (SemPtr == SEM_FAILED) return 0;
|
|
|
|
if (Wait == INFINITE)
|
|
{
|
|
while (sem_wait(SemPtr) < 0)
|
|
{
|
|
if (errno != EINTR) return 0;
|
|
}
|
|
}
|
|
else if (Wait == 0)
|
|
{
|
|
while (sem_trywait(SemPtr) < 0)
|
|
{
|
|
if (errno != EINTR) return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct timespec TempTime;
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, &TempTime) == -1) return 0;
|
|
TempTime.tv_sec += Wait / 1000;
|
|
TempTime.tv_nsec += (Wait % 1000) * 1000000;
|
|
TempTime.tv_sec += TempTime.tv_nsec / 1000000000;
|
|
TempTime.tv_nsec = TempTime.tv_nsec % 1000000000;
|
|
|
|
while (sem_timedwait(SemPtr, &TempTime) < 0)
|
|
{
|
|
if (errno != EINTR) return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Mutex */
|
|
PHPAPI zend_class_entry *sync_Mutex_ce;
|
|
|
|
void sync_Mutex_free_object(void *object TSRMLS_DC);
|
|
|
|
/* {{{ Initialize internal Mutex structure. */
|
|
zend_object_value sync_Mutex_create_object(zend_class_entry *ce TSRMLS_DC)
|
|
{
|
|
zend_object_value retval;
|
|
sync_Mutex_object *obj;
|
|
#if PHP_VERSION_ID < 50399
|
|
zval *tmp;
|
|
#endif
|
|
|
|
/* Create the object. */
|
|
obj = (sync_Mutex_object *)ecalloc(1, sizeof(sync_Mutex_object));
|
|
|
|
/* Initialize Zend. */
|
|
zend_object_std_init(&obj->std, ce TSRMLS_CC);
|
|
#if PHP_VERSION_ID < 50399
|
|
zend_hash_copy(obj->std.properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *));
|
|
#else
|
|
object_properties_init(&obj->std, ce);
|
|
#endif
|
|
|
|
/* Initialize destructor callback. */
|
|
retval.handle = zend_objects_store_put((void *)obj, (zend_objects_store_dtor_t)zend_objects_destroy_object, sync_Mutex_free_object, NULL TSRMLS_CC);
|
|
retval.handlers = zend_get_std_object_handlers();
|
|
|
|
/* Initialize Mutex information. */
|
|
#if defined(PHP_WIN32)
|
|
obj->MxWinMutex = NULL;
|
|
InitializeCriticalSection(&obj->MxWinCritSection);
|
|
#else
|
|
obj->MxSemMutex = SEM_FAILED;
|
|
obj->MxAllocated = 0;
|
|
pthread_mutex_init(&obj->MxPthreadCritSection, NULL);
|
|
#endif
|
|
obj->MxOwnerID = 0;
|
|
obj->MxCount = 0;
|
|
|
|
return retval;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Unlocks a mutex. */
|
|
int sync_Mutex_unlock_internal(sync_Mutex_object *obj, int all)
|
|
{
|
|
#if defined(PHP_WIN32)
|
|
|
|
EnterCriticalSection(&obj->MxWinCritSection);
|
|
|
|
/* Make sure the mutex exists and make sure it is owned by the calling thread. */
|
|
if (obj->MxWinMutex == NULL || obj->MxOwnerID != sync_GetCurrentThreadID())
|
|
{
|
|
LeaveCriticalSection(&obj->MxWinCritSection);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (all) obj->MxCount = 1;
|
|
obj->MxCount--;
|
|
if (!obj->MxCount)
|
|
{
|
|
obj->MxOwnerID = 0;
|
|
|
|
/* Release the mutex. */
|
|
ReleaseMutex(obj->MxWinMutex);
|
|
}
|
|
|
|
LeaveCriticalSection(&obj->MxWinCritSection);
|
|
|
|
#else
|
|
|
|
if (pthread_mutex_lock(&obj->MxPthreadCritSection) != 0) return 0;
|
|
|
|
/* Make sure the mutex exists and make sure it is owned by the calling thread. */
|
|
if (obj->MxSemMutex == SEM_FAILED || obj->MxOwnerID != sync_GetCurrentThreadID())
|
|
{
|
|
pthread_mutex_unlock(&obj->MxPthreadCritSection);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (all) obj->MxCount = 1;
|
|
obj->MxCount--;
|
|
if (!obj->MxCount)
|
|
{
|
|
obj->MxOwnerID = 0;
|
|
|
|
/* Release the mutex. */
|
|
sem_post(obj->MxSemMutex);
|
|
}
|
|
|
|
pthread_mutex_unlock(&obj->MxPthreadCritSection);
|
|
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Free internal Mutex structure. */
|
|
void sync_Mutex_free_object(void *object TSRMLS_DC)
|
|
{
|
|
sync_Mutex_object *obj = (sync_Mutex_object *)object;
|
|
|
|
sync_Mutex_unlock_internal(obj, 1);
|
|
|
|
#if defined(PHP_WIN32)
|
|
if (obj->MxWinMutex != NULL) CloseHandle(obj->MxWinMutex);
|
|
DeleteCriticalSection(&obj->MxWinCritSection);
|
|
#else
|
|
if (obj->MxSemMutex != SEM_FAILED)
|
|
{
|
|
if (obj->MxAllocated) efree(obj->MxSemMutex);
|
|
else sem_close(obj->MxSemMutex);
|
|
}
|
|
|
|
pthread_mutex_destroy(&obj->MxPthreadCritSection);
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Sync_Mutex::__construct([string $name = null])
|
|
Constructs a named or unnamed mutex object. */
|
|
PHP_METHOD(sync_Mutex, __construct)
|
|
{
|
|
char *name = NULL;
|
|
int name_len;
|
|
sync_Mutex_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
SECURITY_ATTRIBUTES SecAttr;
|
|
#else
|
|
char *name2;
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len) == FAILURE) return;
|
|
|
|
obj = (sync_Mutex_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
SecAttr.nLength = sizeof(SecAttr);
|
|
SecAttr.lpSecurityDescriptor = NULL;
|
|
SecAttr.bInheritHandle = TRUE;
|
|
|
|
obj->MxWinMutex = CreateMutexA(&SecAttr, FALSE, name);
|
|
if (obj->MxWinMutex == NULL)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Mutex could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#else
|
|
|
|
if (name != NULL)
|
|
{
|
|
name2 = emalloc(name_len + 20);
|
|
|
|
sprintf(name2, "/Sync_Mutex_%s_0", name);
|
|
obj->MxSemMutex = sem_open(name2, O_CREAT, 0666, 1);
|
|
|
|
efree(name2);
|
|
}
|
|
else
|
|
{
|
|
obj->MxAllocated = 1;
|
|
|
|
obj->MxSemMutex = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemMutex, 0, 1);
|
|
}
|
|
|
|
if (obj->MxSemMutex == SEM_FAILED)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Mutex could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_Mutex::lock([int $wait = -1])
|
|
Locks a mutex object. */
|
|
PHP_METHOD(sync_Mutex, lock)
|
|
{
|
|
long wait = -1;
|
|
sync_Mutex_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
DWORD Result;
|
|
#else
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &wait) == FAILURE) return;
|
|
|
|
obj = (sync_Mutex_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
EnterCriticalSection(&obj->MxWinCritSection);
|
|
|
|
/* Check to see if this is already owned by the calling thread. */
|
|
if (obj->MxOwnerID == sync_GetCurrentThreadID())
|
|
{
|
|
obj->MxCount++;
|
|
LeaveCriticalSection(&obj->MxWinCritSection);
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
LeaveCriticalSection(&obj->MxWinCritSection);
|
|
|
|
/* Acquire the mutex. */
|
|
Result = WaitForSingleObject(obj->MxWinMutex, (DWORD)(wait > -1 ? wait : INFINITE));
|
|
if (Result != WAIT_OBJECT_0) RETURN_FALSE;
|
|
|
|
EnterCriticalSection(&obj->MxWinCritSection);
|
|
obj->MxOwnerID = sync_GetCurrentThreadID();
|
|
obj->MxCount = 1;
|
|
LeaveCriticalSection(&obj->MxWinCritSection);
|
|
|
|
#else
|
|
|
|
if (pthread_mutex_lock(&obj->MxPthreadCritSection) != 0)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Unable to acquire mutex critical section", 0 TSRMLS_CC);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Check to see if this mutex is already owned by the calling thread. */
|
|
if (obj->MxOwnerID == sync_GetCurrentThreadID())
|
|
{
|
|
obj->MxCount++;
|
|
pthread_mutex_unlock(&obj->MxPthreadCritSection);
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
pthread_mutex_unlock(&obj->MxPthreadCritSection);
|
|
|
|
if (!sync_WaitForSemaphore(obj->MxSemMutex, (uint32_t)(wait > -1 ? wait : INFINITE))) RETURN_FALSE;
|
|
|
|
pthread_mutex_lock(&obj->MxPthreadCritSection);
|
|
obj->MxOwnerID = sync_GetCurrentThreadID();
|
|
obj->MxCount = 1;
|
|
pthread_mutex_unlock(&obj->MxPthreadCritSection);
|
|
|
|
#endif
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_Mutex::unlock([bool $all = false])
|
|
Unlocks a mutex object. */
|
|
PHP_METHOD(sync_Mutex, unlock)
|
|
{
|
|
long all = 0;
|
|
sync_Mutex_object *obj;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &all) == FAILURE) return;
|
|
|
|
obj = (sync_Mutex_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
if (!sync_Mutex_unlock_internal(obj, all)) RETURN_FALSE;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_mutex___construct, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, name)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_mutex_lock, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, wait)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_mutex_unlock, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, all)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
static const zend_function_entry sync_Mutex_methods[] = {
|
|
PHP_ME(sync_Mutex, __construct, arginfo_sync_mutex___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
|
|
PHP_ME(sync_Mutex, lock, arginfo_sync_mutex_lock, ZEND_ACC_PUBLIC)
|
|
PHP_ME(sync_Mutex, unlock, arginfo_sync_mutex_unlock, ZEND_ACC_PUBLIC)
|
|
PHP_FE_END
|
|
};
|
|
|
|
|
|
|
|
/* Semaphore */
|
|
PHPAPI zend_class_entry *sync_Semaphore_ce;
|
|
|
|
void sync_Semaphore_free_object(void *object TSRMLS_DC);
|
|
|
|
/* {{{ Initialize internal Semaphore structure. */
|
|
zend_object_value sync_Semaphore_create_object(zend_class_entry *ce TSRMLS_DC)
|
|
{
|
|
zend_object_value retval;
|
|
sync_Semaphore_object *obj;
|
|
#if PHP_VERSION_ID < 50399
|
|
zval *tmp;
|
|
#endif
|
|
|
|
/* Create the object. */
|
|
obj = (sync_Semaphore_object *)ecalloc(1, sizeof(sync_Semaphore_object));
|
|
|
|
/* Initialize Zend. */
|
|
zend_object_std_init(&obj->std, ce TSRMLS_CC);
|
|
#if PHP_VERSION_ID < 50399
|
|
zend_hash_copy(obj->std.properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *));
|
|
#else
|
|
object_properties_init(&obj->std, ce);
|
|
#endif
|
|
|
|
/* Initialize destructor callback. */
|
|
retval.handle = zend_objects_store_put((void *)obj, (zend_objects_store_dtor_t)zend_objects_destroy_object, sync_Semaphore_free_object, NULL TSRMLS_CC);
|
|
retval.handlers = zend_get_std_object_handlers();
|
|
|
|
/* Initialize Semaphore information. */
|
|
#if defined(PHP_WIN32)
|
|
obj->MxWinSemaphore = NULL;
|
|
#else
|
|
obj->MxSemSemaphore = SEM_FAILED;
|
|
obj->MxAllocated = 0;
|
|
#endif
|
|
obj->MxAutoUnlock = 0;
|
|
obj->MxCount = 0;
|
|
|
|
return retval;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Free internal Semaphore structure. */
|
|
void sync_Semaphore_free_object(void *object TSRMLS_DC)
|
|
{
|
|
sync_Semaphore_object *obj = (sync_Semaphore_object *)object;
|
|
|
|
if (obj->MxAutoUnlock)
|
|
{
|
|
while (obj->MxCount)
|
|
{
|
|
#if defined(PHP_WIN32)
|
|
ReleaseSemaphore(obj->MxWinSemaphore, 1, NULL);
|
|
#else
|
|
sem_post(obj->MxSemSemaphore);
|
|
#endif
|
|
|
|
obj->MxCount--;
|
|
}
|
|
}
|
|
|
|
#if defined(PHP_WIN32)
|
|
if (obj->MxWinSemaphore != NULL) CloseHandle(obj->MxWinSemaphore);
|
|
#else
|
|
if (obj->MxSemSemaphore != SEM_FAILED)
|
|
{
|
|
if (obj->MxAllocated) efree(obj->MxSemSemaphore);
|
|
else sem_close(obj->MxSemSemaphore);
|
|
}
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Sync_Semaphore::__construct([string $name = null, [int $initialval = 1, [bool $autounlock = true]]])
|
|
Constructs a named or unnamed semaphore object. Don't set $autounlock to false unless you really know what you are doing. */
|
|
PHP_METHOD(sync_Semaphore, __construct)
|
|
{
|
|
char *name = NULL;
|
|
int name_len;
|
|
long initialval = 1;
|
|
long autounlock = 1;
|
|
sync_Semaphore_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
SECURITY_ATTRIBUTES SecAttr;
|
|
#else
|
|
char *name2;
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sll", &name, &name_len, &initialval, &autounlock) == FAILURE) return;
|
|
|
|
obj = (sync_Semaphore_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
obj->MxAutoUnlock = (autounlock ? 1 : 0);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
SecAttr.nLength = sizeof(SecAttr);
|
|
SecAttr.lpSecurityDescriptor = NULL;
|
|
SecAttr.bInheritHandle = TRUE;
|
|
|
|
obj->MxWinSemaphore = CreateSemaphoreA(&SecAttr, (LONG)initialval, (LONG)initialval, name);
|
|
if (obj->MxWinSemaphore == NULL)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Semaphore could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#else
|
|
|
|
if (name != NULL)
|
|
{
|
|
name2 = emalloc(name_len + 20);
|
|
|
|
sprintf(name2, "/Sync_Semaphore_%s_0", name);
|
|
obj->MxSemSemaphore = sem_open(name2, O_CREAT, 0666, (unsigned int)initialval);
|
|
|
|
efree(name2);
|
|
}
|
|
else
|
|
{
|
|
obj->MxAllocated = 1;
|
|
|
|
obj->MxSemSemaphore = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemSemaphore, 0, (unsigned int)initialval);
|
|
}
|
|
|
|
if (obj->MxSemSemaphore == SEM_FAILED)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Semaphore could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_Semaphore::lock([int $wait = -1])
|
|
Locks a semaphore object. */
|
|
PHP_METHOD(sync_Semaphore, lock)
|
|
{
|
|
long wait = -1;
|
|
sync_Semaphore_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
DWORD Result;
|
|
#else
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &wait) == FAILURE) return;
|
|
|
|
obj = (sync_Semaphore_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
Result = WaitForSingleObject(obj->MxWinSemaphore, (DWORD)(wait > -1 ? wait : INFINITE));
|
|
if (Result != WAIT_OBJECT_0) RETURN_FALSE;
|
|
|
|
#else
|
|
|
|
if (!sync_WaitForSemaphore(obj->MxSemSemaphore, (uint32_t)(wait > -1 ? wait : INFINITE))) RETURN_FALSE;
|
|
|
|
#endif
|
|
|
|
if (obj->MxAutoUnlock) obj->MxCount++;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_Semaphore::unlock([int &$prevcount])
|
|
Unlocks a semaphore object. */
|
|
PHP_METHOD(sync_Semaphore, unlock)
|
|
{
|
|
zval **zprevcount = NULL;
|
|
sync_Semaphore_object *obj;
|
|
int count;
|
|
int argc = ZEND_NUM_ARGS();
|
|
#if defined(PHP_WIN32)
|
|
LONG PrevCount;
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|Z", &zprevcount) == FAILURE) return;
|
|
|
|
obj = (sync_Semaphore_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
if (!ReleaseSemaphore(obj->MxWinSemaphore, 1, &PrevCount)) RETURN_FALSE;
|
|
count = (int)PrevCount;
|
|
|
|
#else
|
|
|
|
/* Get the current value first. */
|
|
sem_getvalue(obj->MxSemSemaphore, &count);
|
|
|
|
/* Release the semaphore. */
|
|
sem_post(obj->MxSemSemaphore);
|
|
|
|
#endif
|
|
|
|
if (argc > 0)
|
|
{
|
|
zval_dtor(*zprevcount);
|
|
ZVAL_LONG(*zprevcount, count);
|
|
}
|
|
|
|
if (obj->MxAutoUnlock) obj->MxCount--;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_semaphore___construct, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, name)
|
|
ZEND_ARG_INFO(0, initialval)
|
|
ZEND_ARG_INFO(0, autounlock)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_semaphore_lock, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, wait)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_semaphore_unlock, 0, 0, 0)
|
|
ZEND_ARG_INFO(1, prevcount)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
static const zend_function_entry sync_Semaphore_methods[] = {
|
|
PHP_ME(sync_Semaphore, __construct, arginfo_sync_semaphore___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
|
|
PHP_ME(sync_Semaphore, lock, arginfo_sync_semaphore_lock, ZEND_ACC_PUBLIC)
|
|
PHP_ME(sync_Semaphore, unlock, arginfo_sync_semaphore_unlock, ZEND_ACC_PUBLIC)
|
|
PHP_FE_END
|
|
};
|
|
|
|
|
|
|
|
/* Event */
|
|
PHPAPI zend_class_entry *sync_Event_ce;
|
|
|
|
void sync_Event_free_object(void *object TSRMLS_DC);
|
|
|
|
/* {{{ Initialize internal Event structure. */
|
|
zend_object_value sync_Event_create_object(zend_class_entry *ce TSRMLS_DC)
|
|
{
|
|
zend_object_value retval;
|
|
sync_Event_object *obj;
|
|
#if PHP_VERSION_ID < 50399
|
|
zval *tmp;
|
|
#endif
|
|
|
|
/* Create the object. */
|
|
obj = (sync_Event_object *)ecalloc(1, sizeof(sync_Event_object));
|
|
|
|
/* Initialize Zend. */
|
|
zend_object_std_init(&obj->std, ce TSRMLS_CC);
|
|
#if PHP_VERSION_ID < 50399
|
|
zend_hash_copy(obj->std.properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *));
|
|
#else
|
|
object_properties_init(&obj->std, ce);
|
|
#endif
|
|
|
|
/* Initialize destructor callback. */
|
|
retval.handle = zend_objects_store_put((void *)obj, (zend_objects_store_dtor_t)zend_objects_destroy_object, sync_Event_free_object, NULL TSRMLS_CC);
|
|
retval.handlers = zend_get_std_object_handlers();
|
|
|
|
/* Initialize Event information. */
|
|
#if defined(PHP_WIN32)
|
|
obj->MxWinWaitEvent = NULL;
|
|
#else
|
|
obj->MxSemWaitMutex = SEM_FAILED;
|
|
obj->MxSemWaitEvent = SEM_FAILED;
|
|
obj->MxSemWaitCount = SEM_FAILED;
|
|
obj->MxSemWaitStatus = SEM_FAILED;
|
|
obj->MxAllocated = 0;
|
|
obj->MxManual = 0;
|
|
#endif
|
|
|
|
return retval;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Free internal Event structure. */
|
|
void sync_Event_free_object(void *object TSRMLS_DC)
|
|
{
|
|
sync_Event_object *obj = (sync_Event_object *)object;
|
|
|
|
#if defined(PHP_WIN32)
|
|
if (obj->MxWinWaitEvent != NULL) CloseHandle(obj->MxWinWaitEvent);
|
|
#else
|
|
if (obj->MxAllocated)
|
|
{
|
|
if (obj->MxSemWaitStatus != SEM_FAILED) efree(obj->MxSemWaitStatus);
|
|
if (obj->MxSemWaitCount != SEM_FAILED) efree(obj->MxSemWaitCount);
|
|
if (obj->MxSemWaitEvent != SEM_FAILED) efree(obj->MxSemWaitEvent);
|
|
if (obj->MxSemWaitMutex != SEM_FAILED) efree(obj->MxSemWaitMutex);
|
|
}
|
|
else
|
|
{
|
|
if (obj->MxSemWaitStatus != SEM_FAILED) sem_close(obj->MxSemWaitStatus);
|
|
if (obj->MxSemWaitCount != SEM_FAILED) sem_close(obj->MxSemWaitCount);
|
|
if (obj->MxSemWaitEvent != SEM_FAILED) sem_close(obj->MxSemWaitEvent);
|
|
if (obj->MxSemWaitMutex != SEM_FAILED) sem_close(obj->MxSemWaitMutex);
|
|
}
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Sync_Event::__construct([string $name = null, [bool $manual = false]])
|
|
Constructs a named or unnamed event object. */
|
|
PHP_METHOD(sync_Event, __construct)
|
|
{
|
|
char *name = NULL;
|
|
int name_len;
|
|
long manual = 0;
|
|
sync_Event_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
SECURITY_ATTRIBUTES SecAttr;
|
|
#else
|
|
char *name2;
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &name, &name_len, &manual) == FAILURE) return;
|
|
|
|
obj = (sync_Event_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
SecAttr.nLength = sizeof(SecAttr);
|
|
SecAttr.lpSecurityDescriptor = NULL;
|
|
SecAttr.bInheritHandle = TRUE;
|
|
|
|
obj->MxWinWaitEvent = CreateEventA(&SecAttr, (BOOL)manual, FALSE, name);
|
|
if (obj->MxWinWaitEvent == NULL)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Event object could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#else
|
|
|
|
obj->MxManual = (manual ? 1 : 0);
|
|
|
|
if (name != NULL)
|
|
{
|
|
name2 = emalloc(name_len + 20);
|
|
|
|
sprintf(name2, "/Sync_Event_%s_0", name);
|
|
obj->MxSemWaitMutex = sem_open(name2, O_CREAT, 0666, 1);
|
|
sprintf(name2, "/Sync_Event_%s_1", name);
|
|
obj->MxSemWaitEvent = sem_open(name2, O_CREAT, 0666, 0);
|
|
|
|
if (manual)
|
|
{
|
|
sprintf(name2, "/Sync_Event_%s_2", name);
|
|
obj->MxSemWaitCount = sem_open(name2, O_CREAT, 0666, 0);
|
|
|
|
sprintf(name2, "/Sync_Event_%s_3", name);
|
|
obj->MxSemWaitStatus = sem_open(name2, O_CREAT, 0666, 0);
|
|
}
|
|
|
|
efree(name2);
|
|
}
|
|
else
|
|
{
|
|
obj->MxAllocated = 1;
|
|
|
|
obj->MxSemWaitMutex = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemWaitMutex, 0, 1);
|
|
|
|
obj->MxSemWaitEvent = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemWaitEvent, 0, 0);
|
|
|
|
if (manual)
|
|
{
|
|
obj->MxSemWaitCount = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemWaitCount, 0, 0);
|
|
|
|
obj->MxSemWaitStatus = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemWaitStatus, 0, 0);
|
|
}
|
|
}
|
|
|
|
if (obj->MxSemWaitMutex == SEM_FAILED || obj->MxSemWaitEvent == SEM_FAILED || (manual && (obj->MxSemWaitCount == SEM_FAILED || obj->MxSemWaitStatus == SEM_FAILED)))
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Event object could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_Event::wait([int $wait = -1])
|
|
Waits for an event object to fire. */
|
|
PHP_METHOD(sync_Event, wait)
|
|
{
|
|
long wait = -1;
|
|
sync_Event_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
DWORD Result;
|
|
#else
|
|
uint32_t WaitAmt;
|
|
uint64_t StartTime, CurrTime;
|
|
int Val, Result;
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &wait) == FAILURE) return;
|
|
|
|
obj = (sync_Event_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
Result = WaitForSingleObject(obj->MxWinWaitEvent, (DWORD)(wait > -1 ? wait : INFINITE));
|
|
if (Result != WAIT_OBJECT_0) RETURN_FALSE;
|
|
|
|
#else
|
|
|
|
WaitAmt = (uint32_t)(wait > -1 ? wait : INFINITE);
|
|
|
|
/* Get current time in milliseconds. */
|
|
StartTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
|
|
if (!obj->MxManual) CurrTime = StartTime;
|
|
else
|
|
{
|
|
/* Lock the mutex. */
|
|
if (!sync_WaitForSemaphore(obj->MxSemWaitMutex, WaitAmt)) RETURN_FALSE;
|
|
|
|
/* Get the status. If it is 1, then the event has been fired. */
|
|
sem_getvalue(obj->MxSemWaitStatus, &Val);
|
|
if (Val == 1)
|
|
{
|
|
sem_post(obj->MxSemWaitMutex);
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
/* Increment the wait count. */
|
|
CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
if (WaitAmt < CurrTime - StartTime)
|
|
{
|
|
sem_post(obj->MxSemWaitMutex);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
sem_post(obj->MxSemWaitCount);
|
|
|
|
/* Release the mutex. */
|
|
sem_post(obj->MxSemWaitMutex);
|
|
}
|
|
|
|
/* Wait for the semaphore. */
|
|
Result = sync_WaitForSemaphore(obj->MxSemWaitEvent, WaitAmt - (CurrTime - StartTime));
|
|
|
|
if (obj->MxManual)
|
|
{
|
|
/* Lock the mutex. */
|
|
sync_WaitForSemaphore(obj->MxSemWaitMutex, INFINITE);
|
|
|
|
/* Decrease the wait count. */
|
|
sync_WaitForSemaphore(obj->MxSemWaitCount, INFINITE);
|
|
|
|
/* Release the mutex. */
|
|
sem_post(obj->MxSemWaitMutex);
|
|
}
|
|
|
|
if (!Result) RETURN_FALSE;
|
|
|
|
#endif
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_Event::fire()
|
|
Lets a thread through that is waiting. Lets multiple threads through that are waiting if the event object is 'manual'. */
|
|
PHP_METHOD(sync_Event, fire)
|
|
{
|
|
sync_Event_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
#else
|
|
int x, Val;
|
|
#endif
|
|
|
|
obj = (sync_Event_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
if (!SetEvent(obj->MxWinWaitEvent)) RETURN_FALSE;
|
|
|
|
#else
|
|
|
|
if (obj->MxManual)
|
|
{
|
|
/* Lock the mutex. */
|
|
if (!sync_WaitForSemaphore(obj->MxSemWaitMutex, INFINITE)) RETURN_FALSE;
|
|
|
|
/* Update the status. No wait. */
|
|
sem_getvalue(obj->MxSemWaitStatus, &Val);
|
|
if (Val == 0) sem_post(obj->MxSemWaitStatus);
|
|
|
|
/* Release the mutex. */
|
|
sem_post(obj->MxSemWaitMutex);
|
|
|
|
/* Release all waiting threads. Might do too many sem_post() calls. */
|
|
sem_getvalue(obj->MxSemWaitCount, &Val);
|
|
for (x = 0; x < Val; x++) sem_post(obj->MxSemWaitEvent);
|
|
}
|
|
else
|
|
{
|
|
/* Release one thread. */
|
|
sem_getvalue(obj->MxSemWaitEvent, &Val);
|
|
if (Val == 0) sem_post(obj->MxSemWaitEvent);
|
|
}
|
|
|
|
#endif
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_Event::reset()
|
|
Resets the event object state. Only use when the event object is 'manual'. */
|
|
PHP_METHOD(sync_Event, reset)
|
|
{
|
|
sync_Event_object *obj;
|
|
#if defined(PHP_WIN32)
|
|
#else
|
|
int Val;
|
|
#endif
|
|
|
|
obj = (sync_Event_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
if (!ResetEvent(obj->MxWinWaitEvent)) RETURN_FALSE;
|
|
|
|
#else
|
|
|
|
if (!obj->MxManual) RETURN_FALSE;
|
|
|
|
/* Lock the mutex. */
|
|
if (!sync_WaitForSemaphore(obj->MxSemWaitMutex, INFINITE)) RETURN_FALSE;
|
|
|
|
/* Restrict the semaphore. Fixes the too many sem_post() calls in Fire(). */
|
|
while (sync_WaitForSemaphore(obj->MxSemWaitEvent, 0)) {}
|
|
|
|
/* Update the status. Start waiting. */
|
|
sem_getvalue(obj->MxSemWaitStatus, &Val);
|
|
if (Val == 1) sync_WaitForSemaphore(obj->MxSemWaitStatus, INFINITE);
|
|
|
|
/* Release the mutex. */
|
|
sem_post(obj->MxSemWaitMutex);
|
|
|
|
#endif
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_event___construct, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, name)
|
|
ZEND_ARG_INFO(0, manual)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_event_wait, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, wait)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_event_fire, 0, 0, 0)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_event_reset, 0, 0, 0)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
static const zend_function_entry sync_Event_methods[] = {
|
|
PHP_ME(sync_Event, __construct, arginfo_sync_event___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
|
|
PHP_ME(sync_Event, wait, arginfo_sync_event_wait, ZEND_ACC_PUBLIC)
|
|
PHP_ME(sync_Event, fire, arginfo_sync_event_fire, ZEND_ACC_PUBLIC)
|
|
PHP_ME(sync_Event, reset, arginfo_sync_event_reset, ZEND_ACC_PUBLIC)
|
|
PHP_FE_END
|
|
};
|
|
|
|
|
|
|
|
/* Reader-Writer */
|
|
PHPAPI zend_class_entry *sync_ReaderWriter_ce;
|
|
|
|
void sync_ReaderWriter_free_object(void *object TSRMLS_DC);
|
|
|
|
/* {{{ Initialize internal Reader-Writer structure. */
|
|
zend_object_value sync_ReaderWriter_create_object(zend_class_entry *ce TSRMLS_DC)
|
|
{
|
|
zend_object_value retval;
|
|
sync_ReaderWriter_object *obj;
|
|
#if PHP_VERSION_ID < 50399
|
|
zval *tmp;
|
|
#endif
|
|
|
|
/* Create the object. */
|
|
obj = (sync_ReaderWriter_object *)ecalloc(1, sizeof(sync_ReaderWriter_object));
|
|
|
|
/* Initialize Zend. */
|
|
zend_object_std_init(&obj->std, ce TSRMLS_CC);
|
|
#if PHP_VERSION_ID < 50399
|
|
zend_hash_copy(obj->std.properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *));
|
|
#else
|
|
object_properties_init(&obj->std, ce);
|
|
#endif
|
|
|
|
/* Initialize destructor callback. */
|
|
retval.handle = zend_objects_store_put((void *)obj, (zend_objects_store_dtor_t)zend_objects_destroy_object, sync_ReaderWriter_free_object, NULL TSRMLS_CC);
|
|
retval.handlers = zend_get_std_object_handlers();
|
|
|
|
/* Initialize Reader-Writer information. */
|
|
#if defined(PHP_WIN32)
|
|
obj->MxWinRSemMutex = NULL;
|
|
obj->MxWinRSemaphore = NULL;
|
|
obj->MxWinRWaitEvent = NULL;
|
|
obj->MxWinWWaitMutex = NULL;
|
|
#else
|
|
obj->MxSemRSemMutex = SEM_FAILED;
|
|
obj->MxSemRSemaphore = SEM_FAILED;
|
|
obj->MxSemRWaitEvent = SEM_FAILED;
|
|
obj->MxSemWWaitMutex = SEM_FAILED;
|
|
obj->MxAllocated = 0;
|
|
#endif
|
|
|
|
obj->MxAutoUnlock = 1;
|
|
obj->MxReadLocks = 0;
|
|
obj->MxWriteLock = 0;
|
|
|
|
return retval;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Unlocks a read lock. */
|
|
int sync_ReaderWriter_readunlock_internal(sync_ReaderWriter_object *obj)
|
|
{
|
|
#if defined(PHP_WIN32)
|
|
|
|
DWORD Result;
|
|
LONG Val;
|
|
|
|
if (obj->MxWinRSemMutex == NULL || obj->MxWinRSemaphore == NULL || obj->MxWinRWaitEvent == NULL) return 0;
|
|
|
|
/* Acquire the semaphore mutex. */
|
|
Result = WaitForSingleObject(obj->MxWinRSemMutex, INFINITE);
|
|
if (Result != WAIT_OBJECT_0) return 0;
|
|
|
|
/* Release the semaphore. */
|
|
if (!ReleaseSemaphore(obj->MxWinRSemaphore, 1, &Val))
|
|
{
|
|
ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Update the event state. */
|
|
if (Val == LONG_MAX - 1)
|
|
{
|
|
if (!SetEvent(obj->MxWinRWaitEvent))
|
|
{
|
|
ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (obj->MxReadLocks) obj->MxReadLocks--;
|
|
|
|
/* Release the semaphore mutex. */
|
|
ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL);
|
|
|
|
return 1;
|
|
|
|
#else
|
|
|
|
int Result, Val;
|
|
|
|
if (obj->MxSemRSemMutex == NULL || obj->MxSemRSemaphore == NULL || obj->MxSemRWaitEvent == NULL) return 0;
|
|
|
|
/* Acquire the semaphore mutex. */
|
|
if (!sync_WaitForSemaphore(obj->MxSemRSemMutex, INFINITE)) return 0;
|
|
|
|
/* Release the semaphore. */
|
|
Result = sem_post(obj->MxSemRSemaphore);
|
|
|
|
/* Update the event state. */
|
|
sem_getvalue(obj->MxSemRSemaphore, &Val);
|
|
if (Val == SEM_VALUE_MAX) sem_post(obj->MxSemRWaitEvent);
|
|
|
|
if (obj->MxReadLocks) obj->MxReadLocks--;
|
|
|
|
/* Release the semaphore mutex. */
|
|
sem_post(obj->MxSemRSemMutex);
|
|
|
|
return (Result == 0);
|
|
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Unlocks a write lock. */
|
|
int sync_ReaderWriter_writeunlock_internal(sync_ReaderWriter_object *obj)
|
|
{
|
|
#if defined(PHP_WIN32)
|
|
|
|
if (obj->MxWinWWaitMutex == NULL) return 0;
|
|
|
|
obj->MxWriteLock = 0;
|
|
|
|
/* Release the write lock. */
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
#else
|
|
|
|
if (obj->MxSemWWaitMutex == NULL) return 0;
|
|
|
|
obj->MxWriteLock = 0;
|
|
|
|
/* Release the write lock. */
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Free internal Reader-Writer structure. */
|
|
void sync_ReaderWriter_free_object(void *object TSRMLS_DC)
|
|
{
|
|
sync_ReaderWriter_object *obj = (sync_ReaderWriter_object *)object;
|
|
|
|
if (obj->MxAutoUnlock)
|
|
{
|
|
while (obj->MxReadLocks) sync_ReaderWriter_readunlock_internal(obj);
|
|
|
|
if (obj->MxWriteLock) sync_ReaderWriter_writeunlock_internal(obj);
|
|
}
|
|
|
|
#if defined(PHP_WIN32)
|
|
if (obj->MxWinWWaitMutex != NULL) CloseHandle(obj->MxWinWWaitMutex);
|
|
if (obj->MxWinRWaitEvent != NULL) CloseHandle(obj->MxWinRWaitEvent);
|
|
if (obj->MxWinRSemaphore != NULL) CloseHandle(obj->MxWinRSemaphore);
|
|
if (obj->MxWinRSemMutex != NULL) CloseHandle(obj->MxWinRSemMutex);
|
|
#else
|
|
if (obj->MxAllocated)
|
|
{
|
|
if (obj->MxSemWWaitMutex != SEM_FAILED) efree(obj->MxSemWWaitMutex);
|
|
if (obj->MxSemRWaitEvent != SEM_FAILED) efree(obj->MxSemRWaitEvent);
|
|
if (obj->MxSemRSemaphore != SEM_FAILED) efree(obj->MxSemRSemaphore);
|
|
if (obj->MxSemRSemMutex != SEM_FAILED) efree(obj->MxSemRSemMutex);
|
|
}
|
|
else
|
|
{
|
|
if (obj->MxSemWWaitMutex != SEM_FAILED) sem_close(obj->MxSemWWaitMutex);
|
|
if (obj->MxSemRWaitEvent != SEM_FAILED) sem_close(obj->MxSemRWaitEvent);
|
|
if (obj->MxSemRSemaphore != SEM_FAILED) sem_close(obj->MxSemRSemaphore);
|
|
if (obj->MxSemRSemMutex != SEM_FAILED) sem_close(obj->MxSemRSemMutex);
|
|
}
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void Sync_ReaderWriter::__construct([string $name = null, [bool $autounlock = true]])
|
|
Constructs a named or unnamed reader-writer object. Don't set $autounlock to false unless you really know what you are doing. */
|
|
PHP_METHOD(sync_ReaderWriter, __construct)
|
|
{
|
|
char *name = NULL;
|
|
int name_len;
|
|
long autounlock = 1;
|
|
sync_ReaderWriter_object *obj;
|
|
char *name2;
|
|
#if defined(PHP_WIN32)
|
|
SECURITY_ATTRIBUTES SecAttr;
|
|
#else
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &name, &name_len, &autounlock) == FAILURE) return;
|
|
|
|
obj = (sync_ReaderWriter_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
obj->MxAutoUnlock = (autounlock ? 1 : 0);
|
|
|
|
if (name == NULL) name2 = NULL;
|
|
else name2 = emalloc(name_len + 20);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
SecAttr.nLength = sizeof(SecAttr);
|
|
SecAttr.lpSecurityDescriptor = NULL;
|
|
SecAttr.bInheritHandle = TRUE;
|
|
|
|
/* Create the mutexes, semaphore, and event objects. */
|
|
if (name2 != NULL) sprintf(name2, "Sync_ReadWrite|%s|0", name);
|
|
obj->MxWinRSemMutex = CreateSemaphoreA(&SecAttr, 1, 1, name2);
|
|
if (name2 != NULL) sprintf(name2, "Sync_ReadWrite|%s|1", name);
|
|
obj->MxWinRSemaphore = CreateSemaphoreA(&SecAttr, LONG_MAX, LONG_MAX, name2);
|
|
if (name2 != NULL) sprintf(name2, "Sync_ReadWrite|%s|2", name);
|
|
obj->MxWinRWaitEvent = CreateEventA(&SecAttr, TRUE, TRUE, name2);
|
|
if (name2 != NULL) sprintf(name2, "Sync_ReadWrite|%s|3", name);
|
|
obj->MxWinWWaitMutex = CreateSemaphoreA(&SecAttr, 1, 1, name2);
|
|
|
|
#else
|
|
|
|
if (name2 != NULL)
|
|
{
|
|
sprintf(name2, "/Sync_ReadWrite_%s_0", name);
|
|
obj->MxSemRSemMutex = sem_open(name2, O_CREAT, 0666, 1);
|
|
sprintf(name2, "/Sync_ReadWrite_%s_1", name);
|
|
obj->MxSemRSemaphore = sem_open(name2, O_CREAT, 0666, SEM_VALUE_MAX);
|
|
sprintf(name2, "/Sync_ReadWrite_%s_2", name);
|
|
obj->MxSemRWaitEvent = sem_open(name2, O_CREAT, 0666, 1);
|
|
sprintf(name2, "/Sync_ReadWrite_%s_3", name);
|
|
obj->MxSemWWaitMutex = sem_open(name2, O_CREAT, 0666, 1);
|
|
}
|
|
else
|
|
{
|
|
obj->MxAllocated = 1;
|
|
|
|
obj->MxSemRSemMutex = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemRSemMutex, 0, 1);
|
|
|
|
obj->MxSemRSemaphore = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemRSemaphore, 0, SEM_VALUE_MAX);
|
|
|
|
obj->MxSemRWaitEvent = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemRWaitEvent, 0, 1);
|
|
|
|
obj->MxSemWWaitMutex = (sem_t *)ecalloc(1, sizeof(sem_t));
|
|
sem_init(obj->MxSemWWaitMutex, 0, 1);
|
|
}
|
|
|
|
#endif
|
|
|
|
if (name2 != NULL) efree(name2);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
if (obj->MxWinRSemMutex == NULL || obj->MxWinRSemaphore == NULL || obj->MxWinRWaitEvent == NULL || obj->MxWinWWaitMutex == NULL)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Reader-Writer object could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#else
|
|
|
|
if (obj->MxSemRSemMutex == SEM_FAILED || obj->MxSemRSemaphore == SEM_FAILED || obj->MxSemRWaitEvent == SEM_FAILED || obj->MxSemWWaitMutex == SEM_FAILED)
|
|
{
|
|
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Reader-Writer object could not be created", 0 TSRMLS_CC);
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_ReaderWriter::readlock([int $wait = -1])
|
|
Read locks a reader-writer object. */
|
|
PHP_METHOD(sync_ReaderWriter, readlock)
|
|
{
|
|
long wait = -1;
|
|
sync_ReaderWriter_object *obj;
|
|
uint32_t WaitAmt;
|
|
uint64_t StartTime, CurrTime;
|
|
#if defined(PHP_WIN32)
|
|
DWORD Result;
|
|
#else
|
|
int Val;
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &wait) == FAILURE) return;
|
|
|
|
obj = (sync_ReaderWriter_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
WaitAmt = (uint32_t)(wait > -1 ? wait : INFINITE);
|
|
|
|
/* Get current time in milliseconds. */
|
|
StartTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
/* Acquire the write lock mutex. Guarantees that readers can't starve the writer. */
|
|
Result = WaitForSingleObject(obj->MxWinWWaitMutex, WaitAmt);
|
|
if (Result != WAIT_OBJECT_0) RETURN_FALSE;
|
|
|
|
/* Acquire the semaphore mutex. */
|
|
CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
if (WaitAmt < CurrTime - StartTime)
|
|
{
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
Result = WaitForSingleObject(obj->MxWinRSemMutex, WaitAmt - (DWORD)(CurrTime - StartTime));
|
|
if (Result != WAIT_OBJECT_0)
|
|
{
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Acquire the semaphore. */
|
|
CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
if (WaitAmt < CurrTime - StartTime)
|
|
{
|
|
ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL);
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
Result = WaitForSingleObject(obj->MxWinRSemaphore, WaitAmt - (DWORD)(CurrTime - StartTime));
|
|
if (Result != WAIT_OBJECT_0)
|
|
{
|
|
ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL);
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Update the event state. */
|
|
if (!ResetEvent(obj->MxWinRWaitEvent))
|
|
{
|
|
ReleaseSemaphore(obj->MxWinRSemaphore, 1, NULL);
|
|
ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL);
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Release the mutexes. */
|
|
ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL);
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
#else
|
|
|
|
/* Acquire the write lock mutex. Guarantees that readers can't starve the writer. */
|
|
if (!sync_WaitForSemaphore(obj->MxSemWWaitMutex, WaitAmt)) RETURN_FALSE;
|
|
|
|
/* Acquire the semaphore mutex. */
|
|
CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
if (WaitAmt < CurrTime - StartTime)
|
|
{
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
if (!sync_WaitForSemaphore(obj->MxSemRSemMutex, WaitAmt - (CurrTime - StartTime)))
|
|
{
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Acquire the semaphore. */
|
|
CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
if (WaitAmt < CurrTime - StartTime)
|
|
{
|
|
sem_post(obj->MxSemRSemMutex);
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
if (!sync_WaitForSemaphore(obj->MxSemRSemaphore, WaitAmt - (CurrTime - StartTime)))
|
|
{
|
|
sem_post(obj->MxSemRSemMutex);
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* Update the event state. */
|
|
sem_getvalue(obj->MxSemRSemaphore, &Val);
|
|
if (Val == SEM_VALUE_MAX - 1)
|
|
{
|
|
if (!sync_WaitForSemaphore(obj->MxSemRWaitEvent, INFINITE))
|
|
{
|
|
sem_post(obj->MxSemRSemaphore);
|
|
sem_post(obj->MxSemRSemMutex);
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
/* Release the mutexes. */
|
|
sem_post(obj->MxSemRSemMutex);
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
#endif
|
|
|
|
obj->MxReadLocks++;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_ReaderWriter::writelock([int $wait = -1])
|
|
Write locks a reader-writer object. */
|
|
PHP_METHOD(sync_ReaderWriter, writelock)
|
|
{
|
|
long wait = -1;
|
|
sync_ReaderWriter_object *obj;
|
|
uint32_t WaitAmt;
|
|
uint64_t StartTime, CurrTime;
|
|
#if defined(PHP_WIN32)
|
|
DWORD Result;
|
|
#else
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &wait) == FAILURE) return;
|
|
|
|
obj = (sync_ReaderWriter_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
WaitAmt = (uint32_t)(wait > -1 ? wait : INFINITE);
|
|
|
|
/* Get current time in milliseconds. */
|
|
StartTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
|
|
#if defined(PHP_WIN32)
|
|
|
|
/* Acquire the write lock mutex. */
|
|
Result = WaitForSingleObject(obj->MxWinWWaitMutex, WaitAmt);
|
|
if (Result != WAIT_OBJECT_0) RETURN_FALSE;
|
|
|
|
/* Wait for readers to reach zero. */
|
|
CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
Result = WaitForSingleObject(obj->MxWinRWaitEvent, WaitAmt - (DWORD)(CurrTime - StartTime));
|
|
if (Result != WAIT_OBJECT_0)
|
|
{
|
|
ReleaseSemaphore(obj->MxWinWWaitMutex, 1, NULL);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
#else
|
|
|
|
/* Acquire the write lock mutex. */
|
|
if (!sync_WaitForSemaphore(obj->MxSemWWaitMutex, WaitAmt)) RETURN_FALSE;
|
|
|
|
// Wait for readers to reach zero.
|
|
CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000);
|
|
if (!sync_WaitForSemaphore(obj->MxSemRWaitEvent, WaitAmt - (CurrTime - StartTime)))
|
|
{
|
|
sem_post(obj->MxSemWWaitMutex);
|
|
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
obj->MxWriteLock = 1;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_ReaderWriter::readunlock()
|
|
Read unlocks a reader-writer object. */
|
|
PHP_METHOD(sync_ReaderWriter, readunlock)
|
|
{
|
|
sync_ReaderWriter_object *obj;
|
|
|
|
obj = (sync_ReaderWriter_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
if (!sync_ReaderWriter_readunlock_internal(obj)) RETURN_FALSE;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool Sync_ReaderWriter::writeunlock()
|
|
Write unlocks a reader-writer object. */
|
|
PHP_METHOD(sync_ReaderWriter, writeunlock)
|
|
{
|
|
sync_ReaderWriter_object *obj;
|
|
|
|
obj = (sync_ReaderWriter_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
|
|
|
|
if (!sync_ReaderWriter_writeunlock_internal(obj)) RETURN_FALSE;
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_readerwriter___construct, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, name)
|
|
ZEND_ARG_INFO(0, autounlock)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_readerwriter_readlock, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, wait)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_readerwriter_writelock, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, wait)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_readerwriter_readunlock, 0, 0, 0)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_readerwriter_writeunlock, 0, 0, 0)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
static const zend_function_entry sync_ReaderWriter_methods[] = {
|
|
PHP_ME(sync_ReaderWriter, __construct, arginfo_sync_readerwriter___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
|
|
PHP_ME(sync_ReaderWriter, readlock, arginfo_sync_readerwriter_readlock, ZEND_ACC_PUBLIC)
|
|
PHP_ME(sync_ReaderWriter, writelock, arginfo_sync_readerwriter_writelock, ZEND_ACC_PUBLIC)
|
|
PHP_ME(sync_ReaderWriter, readunlock, arginfo_sync_readerwriter_readunlock, ZEND_ACC_PUBLIC)
|
|
PHP_ME(sync_ReaderWriter, writeunlock, arginfo_sync_readerwriter_writeunlock, ZEND_ACC_PUBLIC)
|
|
PHP_FE_END
|
|
};
|
|
|
|
|
|
|
|
/* {{{ PHP_MINIT_FUNCTION(sync)
|
|
*/
|
|
PHP_MINIT_FUNCTION(sync)
|
|
{
|
|
zend_class_entry ce;
|
|
|
|
INIT_CLASS_ENTRY(ce, "SyncMutex", sync_Mutex_methods);
|
|
ce.create_object = sync_Mutex_create_object;
|
|
sync_Mutex_ce = zend_register_internal_class(&ce TSRMLS_CC);
|
|
|
|
INIT_CLASS_ENTRY(ce, "SyncSemaphore", sync_Semaphore_methods);
|
|
ce.create_object = sync_Semaphore_create_object;
|
|
sync_Semaphore_ce = zend_register_internal_class(&ce TSRMLS_CC);
|
|
|
|
INIT_CLASS_ENTRY(ce, "SyncEvent", sync_Event_methods);
|
|
ce.create_object = sync_Event_create_object;
|
|
sync_Event_ce = zend_register_internal_class(&ce TSRMLS_CC);
|
|
|
|
INIT_CLASS_ENTRY(ce, "SyncReaderWriter", sync_ReaderWriter_methods);
|
|
ce.create_object = sync_ReaderWriter_create_object;
|
|
sync_ReaderWriter_ce = zend_register_internal_class(&ce TSRMLS_CC);
|
|
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MSHUTDOWN_FUNCTION
|
|
*/
|
|
PHP_MSHUTDOWN_FUNCTION(sync)
|
|
{
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MINFO_FUNCTION
|
|
*/
|
|
PHP_MINFO_FUNCTION(sync)
|
|
{
|
|
php_info_print_table_start();
|
|
php_info_print_table_header(2, "sync support", "enabled");
|
|
php_info_print_table_end();
|
|
}
|
|
/* }}} */
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim600: noet sw=4 ts=4 fdm=marker
|
|
* vim<600: noet sw=4 ts=4
|
|
*/
|