diff --git a/README.md b/README.md index 7c8b5ed..c0624dc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ CubicleSoft PHP Extension: Synchronization Objects (sync) ========================================================== -The 'sync' extension introduces synchronization objects into PHP. Named and unnamed Mutex, Semaphore, Event, and Reader-Writer objects provide OS-level synchronization on both *NIX (POSIX semaphores required) and Windows platforms. The extension comes with a test suite that integrates cleanly into 'make test'. +The 'sync' extension introduces synchronization objects into PHP. Named and unnamed Mutex, Semaphore, Event, Reader-Writer, and named Shared Memory objects provide OS-level synchronization mechanisms on both *NIX (POSIX shared memory and pthread shared memory synchronization required) and Windows platforms. The extension comes with a test suite that integrates cleanly into 'make test'. + +The 'sync' extension is a direct port of and compatible with the cross platform 'sync' library: https://github.com/cubiclesoft/cross-platform-cpp This extension uses the liberal MIT open source license. And, of course, it sits on GitHub for all of that pull request and issue tracker goodness to easily submit changes and ideas respectively. @@ -14,7 +16,7 @@ All synchronization objects are attempted to be unlocked cleanly within PHP itse NOTE: When using "named" objects, the initialization must be identical for a given name and have a specific purpose. Reusing named objects for other purposes is not a good idea and will probably result in breaking both applications. However, different object types can share the same name (e.g. a Mutex and an Event object can have the same name). -``` +```` void SyncMutex::__construct([string $name = null]) Constructs a named or unnamed mutex object. @@ -35,7 +37,7 @@ bool SyncSemaphore::unlock([int &$prevcount]) Unlocks a semaphore object. -void SyncEvent::__construct([string $name = null, [bool $manual = false]]) +void SyncEvent::__construct([string $name = null, [bool $manual = false], [bool $prefire = false]]) Constructs a named or unnamed event object. bool SyncEvent::wait([int $wait = -1]) @@ -62,7 +64,23 @@ bool SyncReaderWriter::readunlock() bool SyncReaderWriter::writeunlock() Write unlocks a reader-writer object. -``` + + +void SyncSharedMemory::__construct(string $name, int $size) + Constructs a named shared memory object. + +bool SyncSharedMemory::first() + Returns whether or not this shared memory segment is the first time accessed (i.e. not initialized). + +int SyncSharedMemory::size() + Returns the shared memory size. + +int SyncSharedMemory::write(string $string, [int $start = 0]) + Copies data to shared memory. + +string SyncSharedMemory::read([int $start = 0, [int $length = null]]) + Copies data from shared memory. +```` Usage Examples -------------- @@ -94,7 +112,7 @@ $mutex2->unlock(); Example Semaphore usage: ```php -$semaphore = new SyncSemaphore("LimitedResource_2clients", 2); +$semaphore = new SyncSemaphore("LimitedResource_2_clients", 2); if (!$semaphore->lock(3000)) { @@ -132,3 +150,17 @@ $readwrite->writelock(); ... $readwrite->writeunlock(); ``` + +Example Shared Memory usage: + +```php +// You will probably need to protect shared memory with other synchronization objects. +// Shared memory goes away when the last reference to it disappears. +$mem = new SyncSharedMemory("AppReportName", 1024); +if ($mem->first()) +{ + // Do first time initialization work here. +} + +$result = $mem->write(json_encode(array("name" => "my_report.txt"))); +``` \ No newline at end of file diff --git a/config.m4 b/config.m4 index 877bb61..ed69e91 100644 --- a/config.m4 +++ b/config.m4 @@ -5,22 +5,22 @@ PHP_ARG_ENABLE(sync, whether to enable synchronization object support (--enable- [ --enable-sync Enable synchronization object support]) if test "$PHP_SYNC" != "no"; then - dnl # Check for sem_open() support. - AC_MSG_CHECKING([for sem_open in -pthread -lrt]) + dnl # Check for shm_open() support. + AC_MSG_CHECKING([for shm_open in -pthread -lrt]) SAVED_LIBS="$LIBS" LIBS="$LIBS -pthread -lrt" AC_TRY_LINK([ #include - #include + #include ], [ - sem_t *MxSemMutex = sem_open("", O_CREAT, 0666, 1); + int fp = shm_open("", O_RDWR | O_CREAT | O_EXCL, 0666); ], [ - have_sem_open=yes + have_shm_open=yes AC_MSG_RESULT([yes]) ], [ - AC_MSG_ERROR([sem_open() is not available on this platform]) + AC_MSG_ERROR([shm_open() is not available on this platform]) ]) PHP_ADD_LIBRARY(rt,,SYNC_SHARED_LIBADD) diff --git a/package.xml b/package.xml index 3b2a380..34bec90 100644 --- a/package.xml +++ b/package.xml @@ -3,18 +3,18 @@ sync pecl.php.net Named and unnamed synchronization objects - The 'sync' extension introduces synchronization objects into PHP. Named and unnamed Mutex, Semaphore, Event, and Reader-Writer objects provide OS-level synchronization on both *NIX (POSIX semaphores required) and Windows platforms. + The 'sync' extension introduces synchronization objects into PHP. Named and unnamed Mutex, Semaphore, Event, Reader-Writer, and named Shared Memory objects provide OS-level synchronization mechanisms on both *NIX (POSIX shared memory and pthread shared memory synchronization required) and Windows platforms. This extension is a direct port of and compatible with the cross platform 'sync' library: https://github.com/cubiclesoft/cross-platform-cpp Thomas Hruska cubic cubic@php.net yes - 2014-07-25 - + 2016-11-26 + - 1.0.1 - 1.0.0 + 1.1.0 + 1.1.0 stable @@ -22,8 +22,12 @@ MIT License -- Fixed Reader-Writer objects. -- Removed a lead from package maintainers list. +- Added cross-platform named shared memory objects. +- Rewrote *NIX objects to be much lighter on shared resources. +- Fixed null name issue. +- Fixed object memory leak issue. +- Now works on Mac OSX. +- Now works on PHP 7. The same code base also works on PHP 5. @@ -41,6 +45,8 @@ + + diff --git a/php_sync.h b/php_sync.h index bebb335..71dd14f 100644 --- a/php_sync.h +++ b/php_sync.h @@ -1,7 +1,7 @@ /* - 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. + Direct port of and compatible with the cross platform 'sync' library: https://github.com/cubiclesoft/cross-platform-cpp + This source file is under the MIT license. + (C) 2016 CubicleSoft. All rights reserved. */ /* $Id$ */ @@ -12,7 +12,7 @@ extern zend_module_entry sync_module_entry; #define phpext_sync_ptr &sync_module_entry -#define PHP_SYNC_VERSION "1.0.1" +#define PHP_SYNC_VERSION "1.1.0" #ifdef PHP_WIN32 # define PHP_SYNC_API __declspec(dllexport) @@ -29,6 +29,7 @@ extern zend_module_entry sync_module_entry; #ifdef PHP_WIN32 #include #else +#include #include #include #include @@ -36,8 +37,20 @@ extern zend_module_entry sync_module_entry; #include #include #include -#include #include + +#ifdef __APPLE__ +#include +#include +#ifndef SHM_NAME_MAX +# define SHM_NAME_MAX 31 +#endif +#else +#ifndef SHM_NAME_MAX +# define SHM_NAME_MAX 255 +#endif +#endif + #endif PHP_MINIT_FUNCTION(sync); @@ -50,9 +63,45 @@ typedef DWORD sync_ThreadIDType; typedef pthread_t sync_ThreadIDType; #endif +#if PHP_MAJOR_VERSION >= 7 +#define PHP_SYNC_PHP_5_zend_object_std +#define PHP_SYNC_PHP_7_zend_object_std zend_object std; +#else +#define PHP_SYNC_PHP_5_zend_object_std zend_object std; +#define PHP_SYNC_PHP_7_zend_object_std +#endif + +/* Generic structures */ +#if defined(PHP_WIN32) + +/* Nothing for Windows at the moment. */ + +#else + +/* Some platforms are broken even for unnamed semaphores (e.g. Mac OSX). */ +/* This allows for implementing all semaphores directly, bypassing POSIX semaphores. */ +typedef struct _sync_UnixSemaphoreWrapper { + pthread_mutex_t *MxMutex; + volatile uint32_t *MxCount; + volatile uint32_t *MxMax; + pthread_cond_t *MxCond; +} sync_UnixSemaphoreWrapper; + +/* Implements a more efficient (and portable) event object interface than trying to use semaphores. */ +typedef struct _sync_UnixEventWrapper { + pthread_mutex_t *MxMutex; + volatile char *MxManual; + volatile char *MxSignaled; + volatile uint32_t *MxWaiting; + pthread_cond_t *MxCond; +} sync_UnixEventWrapper; + +#endif + + /* Mutex */ typedef struct _sync_Mutex_object { - zend_object std; + PHP_SYNC_PHP_5_zend_object_std #if defined(PHP_WIN32) CRITICAL_SECTION MxWinCritSection; @@ -61,61 +110,94 @@ typedef struct _sync_Mutex_object { #else pthread_mutex_t MxPthreadCritSection; - sem_t *MxSemMutex; - int MxAllocated; + int MxNamed; + char *MxMem; + sync_UnixSemaphoreWrapper MxPthreadMutex; + #endif volatile sync_ThreadIDType MxOwnerID; volatile unsigned int MxCount; + + PHP_SYNC_PHP_7_zend_object_std } sync_Mutex_object; /* Semaphore */ typedef struct _sync_Semaphore_object { - zend_object std; + PHP_SYNC_PHP_5_zend_object_std #if defined(PHP_WIN32) HANDLE MxWinSemaphore; #else - sem_t *MxSemSemaphore; - int MxAllocated; + int MxNamed; + char *MxMem; + sync_UnixSemaphoreWrapper MxPthreadSemaphore; #endif int MxAutoUnlock; volatile unsigned int MxCount; + + PHP_SYNC_PHP_7_zend_object_std } sync_Semaphore_object; /* Event */ typedef struct _sync_Event_object { - zend_object std; + PHP_SYNC_PHP_5_zend_object_std #if defined(PHP_WIN32) HANDLE MxWinWaitEvent; #else - sem_t *MxSemWaitMutex, *MxSemWaitEvent, *MxSemWaitCount, *MxSemWaitStatus; - int MxAllocated; - int MxManual; + int MxNamed; + char *MxMem; + sync_UnixEventWrapper MxPthreadEvent; #endif + + PHP_SYNC_PHP_7_zend_object_std } sync_Event_object; /* Reader-Writer */ typedef struct _sync_ReaderWriter_object { - zend_object std; + PHP_SYNC_PHP_5_zend_object_std #if defined(PHP_WIN32) HANDLE MxWinRSemMutex, MxWinRSemaphore, MxWinRWaitEvent, MxWinWWaitMutex; #else - sem_t *MxSemRSemMutex, *MxSemRSemaphore, *MxSemRWaitEvent, *MxSemWWaitMutex; - int MxAllocated; + int MxNamed; + char *MxMem; + sync_UnixSemaphoreWrapper MxPthreadRCountMutex; + volatile uint32_t *MxRCount; + sync_UnixEventWrapper MxPthreadRWaitEvent; + sync_UnixSemaphoreWrapper MxPthreadWWaitMutex; #endif int MxAutoUnlock; volatile unsigned int MxReadLocks, MxWriteLock; + + PHP_SYNC_PHP_7_zend_object_std } sync_ReaderWriter_object; +/* Named shared memory */ +typedef struct _sync_SharedMemory_object { + PHP_SYNC_PHP_5_zend_object_std + + int MxFirst; + size_t MxSize; + char *MxMem; + +#if defined(PHP_WIN32) + HANDLE MxFile; +#else + char *MxMemInternal; +#endif + + PHP_SYNC_PHP_7_zend_object_std +} sync_SharedMemory_object; + + #endif /* PHP_SYNC_H */ diff --git a/sync.c b/sync.c index fd614d2..a8b6421 100644 --- a/sync.c +++ b/sync.c @@ -1,7 +1,7 @@ /* - 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. + Direct port of and compatible with the cross platform 'sync' library: https://github.com/cubiclesoft/cross-platform-cpp + This source file is under the MIT license. + (C) 2016 CubicleSoft. All rights reserved. */ /* $Id$ */ @@ -19,9 +19,16 @@ #else #include #endif -#include "php_sync.h" -/* PHP_FE_END not define in php < 5.3.7 */ +/* Macros. Oh joy. */ +#if PHP_MAJOR_VERSION >= 7 +#include "zend_portability.h" +#endif + +#include "php_sync.h" +#define PORTABLE_default_zend_object_name std + +/* PHP_FE_END not defined in PHP < 5.3.7 */ #ifndef PHP_FE_END #define PHP_FE_END {NULL, NULL, NULL} #endif @@ -97,90 +104,668 @@ uint64_t sync_GetUnixMicrosecondTime() 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) +/* Dear Apple: You hire plenty of developers, so please fix your OS. */ +int sync_CSGX__ClockGetTimeRealtime(struct timespec *ts) { - if (SemPtr == SEM_FAILED) return 0; +#ifdef __APPLE__ + clock_serv_t cclock; + mach_timespec_t mts; - if (Wait == INFINITE) + if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) != KERN_SUCCESS) return -1; + if (clock_get_time(cclock, &mts) != KERN_SUCCESS) return -1; + if (mach_port_deallocate(mach_task_self(), cclock) != KERN_SUCCESS) return -1; + + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; + + return 0; +#else + return clock_gettime(CLOCK_REALTIME, ts); +#endif +} + +size_t sync_GetUnixSystemAlignmentSize() +{ + struct { + int MxInt; + } x; + + struct { + int MxInt; + char MxChar; + } y; + + return sizeof(y) - sizeof(x); +} + +size_t sync_AlignUnixSize(size_t Size) +{ + size_t AlignSize = sync_GetUnixSystemAlignmentSize(); + + if (Size % AlignSize) Size += AlignSize - (Size % AlignSize); + + return Size; +} + +int sync_InitUnixNamedMem(char **ResultMem, size_t *StartPos, const char *Prefix, const char *Name, size_t Size) +{ + int Result = -1; + *ResultMem = NULL; + *StartPos = (Name != NULL ? sync_AlignUnixSize(1) + sync_AlignUnixSize(sizeof(pthread_mutex_t)) + sync_AlignUnixSize(sizeof(uint32_t)) : 0); + + /* First byte indicates initialization status (0 = completely uninitialized, 1 = first mutex initialized, 2 = ready). */ + /* Next few bytes are a shared mutex object. */ + /* Size bytes follow for whatever. */ + Size += *StartPos; + Size = sync_AlignUnixSize(Size); + + if (Name == NULL) { - while (sem_wait(SemPtr) < 0) + *ResultMem = (char *)ecalloc(1, Size); + + Result = 0; + } + else + { + /* Deal with really small name limits with a pseudo-hash. */ + char Name2[SHM_NAME_MAX], Nums[50]; + size_t x, x2 = 0, y = strlen(Prefix), z = 0; + + memset(Name2, 0, sizeof(Name2)); + + for (x = 0; x < y; x++) { - if (errno != EINTR) return 0; + Name2[x2] = (char)(((unsigned int)(unsigned char)Name2[x2]) * 37 + ((unsigned int)(unsigned char)Prefix[x])); + x2++; + + if (x2 == sizeof(Name2) - 1) + { + x2 = 1; + z++; + } + } + + sprintf(Nums, "-%u-%u-", (unsigned int)sync_GetUnixSystemAlignmentSize(), (unsigned int)Size); + + y = strlen(Nums); + for (x = 0; x < y; x++) + { + Name2[x2] = (char)(((unsigned int)(unsigned char)Name2[x2]) * 37 + ((unsigned int)(unsigned char)Nums[x])); + x2++; + + if (x2 == sizeof(Name2) - 1) + { + x2 = 1; + z++; + } + } + + y = strlen(Name); + for (x = 0; x < y; x++) + { + Name2[x2] = (char)(((unsigned int)(unsigned char)Name2[x2]) * 37 + ((unsigned int)(unsigned char)Name[x])); + x2++; + + if (x2 == sizeof(Name2) - 1) + { + x2 = 1; + z++; + } + } + + /* Normalize the alphabet if it looped. */ + if (z) + { + unsigned char TempChr; + y = (z > 1 ? sizeof(Name2) - 1 : x2); + for (x = 1; x < y; x++) + { + TempChr = ((unsigned char)Name2[x]) & 0x3F; + + if (TempChr < 10) TempChr += '0'; + else if (TempChr < 36) TempChr = TempChr - 10 + 'A'; + else if (TempChr < 62) TempChr = TempChr - 36 + 'a'; + else if (TempChr == 62) TempChr = '_'; + else TempChr = '-'; + + Name2[x] = (char)TempChr; + } + } + + for (x = 1; x < sizeof(Name2) && Name2[x]; x++) + { + if (Name2[x] == '\\' || Name2[x] == '/') Name2[x] = '_'; + } + + pthread_mutex_t *MutexPtr; + uint32_t *RefCountPtr; + + /* Attempt to create the named shared memory object. */ + mode_t PrevMask = umask(0); + int fp = shm_open(Name2, O_RDWR | O_CREAT | O_EXCL, 0666); + if (fp > -1) + { + /* Ignore platform errors (for now). */ + while (ftruncate(fp, Size) < 0 && errno == EINTR) + { + } + + *ResultMem = (char *)mmap(NULL, Size, PROT_READ | PROT_WRITE, MAP_SHARED, fp, 0); + if ((*ResultMem) == MAP_FAILED) *ResultMem = NULL; + else + { + pthread_mutexattr_t MutexAttr; + + pthread_mutexattr_init(&MutexAttr); + pthread_mutexattr_setpshared(&MutexAttr, PTHREAD_PROCESS_SHARED); + + MutexPtr = (pthread_mutex_t *)((*ResultMem) + sync_AlignUnixSize(1)); + RefCountPtr = (uint32_t *)((*ResultMem) + sync_AlignUnixSize(1) + sync_AlignUnixSize(sizeof(pthread_mutex_t))); + + pthread_mutex_init(MutexPtr, &MutexAttr); + pthread_mutex_lock(MutexPtr); + + (*ResultMem)[0] = '\x01'; + RefCountPtr[0] = 1; + + Result = 0; + } + + close(fp); + } + else + { + /* Attempt to open the named shared memory object. */ + fp = shm_open(Name2, O_RDWR, 0666); + if (fp > -1) + { + /* Ignore platform errors (for now). */ + while (ftruncate(fp, Size) < 0 && errno == EINTR) + { + } + + *ResultMem = (char *)mmap(NULL, Size, PROT_READ | PROT_WRITE, MAP_SHARED, fp, 0); + if (*ResultMem == MAP_FAILED) ResultMem = NULL; + else + { + /* Wait until the space is fully initialized. */ + if ((*ResultMem)[0] == '\x00') + { + while ((*ResultMem)[0] == '\x00') + { + usleep(2000); + } + } + + char *MemPtr = (*ResultMem) + sync_AlignUnixSize(1); + MutexPtr = (pthread_mutex_t *)(MemPtr); + MemPtr += sync_AlignUnixSize(sizeof(pthread_mutex_t)); + + RefCountPtr = (uint32_t *)(MemPtr); + MemPtr += sync_AlignUnixSize(sizeof(uint32_t)); + + pthread_mutex_lock(MutexPtr); + + if (RefCountPtr[0]) Result = 1; + else + { + /* If this is the first reference, reset the RAM to 0's for platform consistency to force a rebuild of the object. */ + memset(MemPtr, 0, Size); + + Result = 0; + } + + RefCountPtr[0]++; + + pthread_mutex_unlock(MutexPtr); + } + + close(fp); + } + } + + umask(PrevMask); + } + + return Result; +} + +void sync_UnixNamedMemReady(char *MemPtr) +{ + pthread_mutex_unlock((pthread_mutex_t *)(MemPtr + sync_AlignUnixSize(1))); +} + +void sync_UnmapUnixNamedMem(char *MemPtr, size_t Size) +{ + pthread_mutex_t *MutexPtr; + uint32_t *RefCountPtr; + + char *MemPtr2 = MemPtr + sync_AlignUnixSize(1); + MutexPtr = (pthread_mutex_t *)(MemPtr2); + MemPtr2 += sync_AlignUnixSize(sizeof(pthread_mutex_t)); + + RefCountPtr = (uint32_t *)(MemPtr2); + + pthread_mutex_lock(MutexPtr); + if (RefCountPtr[0]) RefCountPtr[0]--; + pthread_mutex_unlock(MutexPtr); + + munmap(MemPtr, sync_AlignUnixSize(1) + sync_AlignUnixSize(sizeof(pthread_mutex_t)) + sync_AlignUnixSize(sizeof(uint32_t)) + Size); +} + +/* Basic *NIX Semaphore functions. */ +size_t sync_GetUnixSemaphoreSize() +{ + return sync_AlignUnixSize(sizeof(pthread_mutex_t)) + sync_AlignUnixSize(sizeof(uint32_t)) + sync_AlignUnixSize(sizeof(uint32_t)) + sync_AlignUnixSize(sizeof(pthread_cond_t)); +} + +void sync_GetUnixSemaphore(sync_UnixSemaphoreWrapper *Result, char *Mem) +{ + Result->MxMutex = (pthread_mutex_t *)(Mem); + Mem += sync_AlignUnixSize(sizeof(pthread_mutex_t)); + + Result->MxCount = (uint32_t *)(Mem); + Mem += sync_AlignUnixSize(sizeof(uint32_t)); + + Result->MxMax = (uint32_t *)(Mem); + Mem += sync_AlignUnixSize(sizeof(uint32_t)); + + Result->MxCond = (pthread_cond_t *)(Mem); +} + +void sync_InitUnixSemaphore(sync_UnixSemaphoreWrapper *UnixSemaphore, int Shared, uint32_t Start, uint32_t Max) +{ + pthread_mutexattr_t MutexAttr; + pthread_condattr_t CondAttr; + + pthread_mutexattr_init(&MutexAttr); + pthread_condattr_init(&CondAttr); + + if (Shared) + { + pthread_mutexattr_setpshared(&MutexAttr, PTHREAD_PROCESS_SHARED); + pthread_condattr_setpshared(&CondAttr, PTHREAD_PROCESS_SHARED); + } + + pthread_mutex_init(UnixSemaphore->MxMutex, &MutexAttr); + if (Start > Max) Start = Max; + UnixSemaphore->MxCount[0] = Start; + UnixSemaphore->MxMax[0] = Max; + pthread_cond_init(UnixSemaphore->MxCond, &CondAttr); + + pthread_condattr_destroy(&CondAttr); + pthread_mutexattr_destroy(&MutexAttr); +} + +int sync_WaitForUnixSemaphore(sync_UnixSemaphoreWrapper *UnixSemaphore, uint32_t Wait) +{ + if (Wait == 0) + { + /* Avoid the scenario of deadlock on the semaphore itself for 0 wait. */ + if (pthread_mutex_trylock(UnixSemaphore->MxMutex) != 0) return 0; + } + else + { + if (pthread_mutex_lock(UnixSemaphore->MxMutex) != 0) return 0; + } + + int Result = 0; + + if (UnixSemaphore->MxCount[0]) + { + UnixSemaphore->MxCount[0]--; + + Result = 1; + } + else if (Wait == INFINITE) + { + int Result2; + do + { + Result2 = pthread_cond_wait(UnixSemaphore->MxCond, UnixSemaphore->MxMutex); + if (Result2 != 0) break; + } while (!UnixSemaphore->MxCount[0]); + + if (Result2 == 0) + { + UnixSemaphore->MxCount[0]--; + + Result = 1; } } else if (Wait == 0) { - while (sem_trywait(SemPtr) < 0) - { - if (errno != EINTR) return 0; - } + /* Failed to obtain lock. Nothing to do. */ } else { struct timespec TempTime; - if (clock_gettime(CLOCK_REALTIME, &TempTime) == -1) return 0; + if (sync_CSGX__ClockGetTimeRealtime(&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) + int Result2; + do { - if (errno != EINTR) return 0; + /* Some platforms have pthread_cond_timedwait() but not pthread_mutex_timedlock() or sem_timedwait() (e.g. Mac OSX). */ + Result2 = pthread_cond_timedwait(UnixSemaphore->MxCond, UnixSemaphore->MxMutex, &TempTime); + if (Result2 != 0) break; + } while (!UnixSemaphore->MxCount[0]); + + if (Result2 == 0) + { + UnixSemaphore->MxCount[0]--; + + Result = 1; } } + pthread_mutex_unlock(UnixSemaphore->MxMutex); + + return Result; +} + +int sync_ReleaseUnixSemaphore(sync_UnixSemaphoreWrapper *UnixSemaphore, uint32_t *PrevVal) +{ + if (pthread_mutex_lock(UnixSemaphore->MxMutex) != 0) return 0; + + if (PrevVal != NULL) *PrevVal = UnixSemaphore->MxCount[0]; + UnixSemaphore->MxCount[0]++; + if (UnixSemaphore->MxCount[0] > UnixSemaphore->MxMax[0]) UnixSemaphore->MxCount[0] = UnixSemaphore->MxMax[0]; + + /* Let a waiting thread have at it. */ + pthread_cond_signal(UnixSemaphore->MxCond); + + pthread_mutex_unlock(UnixSemaphore->MxMutex); + return 1; } +void sync_FreeUnixSemaphore(sync_UnixSemaphoreWrapper *UnixSemaphore) +{ + pthread_mutex_destroy(UnixSemaphore->MxMutex); + pthread_cond_destroy(UnixSemaphore->MxCond); +} + +/* Basic *NIX Event functions. */ +size_t sync_GetUnixEventSize() +{ + return sync_AlignUnixSize(sizeof(pthread_mutex_t)) + sync_AlignUnixSize(2) + sync_AlignUnixSize(sizeof(uint32_t)) + sync_AlignUnixSize(sizeof(pthread_cond_t)); +} + +void sync_GetUnixEvent(sync_UnixEventWrapper *Result, char *Mem) +{ + Result->MxMutex = (pthread_mutex_t *)(Mem); + Mem += sync_AlignUnixSize(sizeof(pthread_mutex_t)); + + Result->MxManual = Mem; + Result->MxSignaled = Mem + 1; + Mem += sync_AlignUnixSize(2); + + Result->MxWaiting = (uint32_t *)(Mem); + Mem += sync_AlignUnixSize(sizeof(uint32_t)); + + Result->MxCond = (pthread_cond_t *)(Mem); +} + +void sync_InitUnixEvent(sync_UnixEventWrapper *UnixEvent, int Shared, int Manual, int Signaled) +{ + pthread_mutexattr_t MutexAttr; + pthread_condattr_t CondAttr; + + pthread_mutexattr_init(&MutexAttr); + pthread_condattr_init(&CondAttr); + + if (Shared) + { + pthread_mutexattr_setpshared(&MutexAttr, PTHREAD_PROCESS_SHARED); + pthread_condattr_setpshared(&CondAttr, PTHREAD_PROCESS_SHARED); + } + + pthread_mutex_init(UnixEvent->MxMutex, &MutexAttr); + UnixEvent->MxManual[0] = (Manual ? '\x01' : '\x00'); + UnixEvent->MxSignaled[0] = (Signaled ? '\x01' : '\x00'); + UnixEvent->MxWaiting[0] = 0; + pthread_cond_init(UnixEvent->MxCond, &CondAttr); + + pthread_condattr_destroy(&CondAttr); + pthread_mutexattr_destroy(&MutexAttr); +} + +int sync_WaitForUnixEvent(sync_UnixEventWrapper *UnixEvent, uint32_t Wait) +{ + if (Wait == 0) + { + /* Avoid the scenario of deadlock on the semaphore itself for 0 wait. */ + if (pthread_mutex_trylock(UnixEvent->MxMutex) != 0) return 0; + } + else + { + if (pthread_mutex_lock(UnixEvent->MxMutex) != 0) return 0; + } + + int Result = 0; + + /* Avoid a potential starvation issue by only allowing signaled manual events OR if there are no other waiting threads. */ + if (UnixEvent->MxSignaled[0] != '\x00' && (UnixEvent->MxManual[0] == '\x00' || !UnixEvent->MxWaiting[0])) + { + /* Reset auto events. */ + if (UnixEvent->MxManual[0] == '\x00') UnixEvent->MxSignaled[0] = '\x00'; + + Result = 1; + } + else if (Wait == INFINITE) + { + UnixEvent->MxWaiting[0]++; + + int Result2; + do + { + Result2 = pthread_cond_wait(UnixEvent->MxCond, UnixEvent->MxMutex); + if (Result2 != 0) break; + } while (UnixEvent->MxSignaled[0] == '\x00'); + + if (Result2 == 0) + { + UnixEvent->MxWaiting[0]--; + + /* Reset auto events. */ + if (UnixEvent->MxManual[0] == '\x00') UnixEvent->MxSignaled[0] = '\x00'; + + Result = 1; + } + } + else if (Wait == 0) + { + /* Failed to obtain lock. Nothing to do. */ + } + else + { + UnixEvent->MxWaiting[0]++; + + struct timespec TempTime; + + if (sync_CSGX__ClockGetTimeRealtime(&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; + + int Result2; + do + { + /* Some platforms have pthread_cond_timedwait() but not pthread_mutex_timedlock() or sem_timedwait() (e.g. Mac OSX). */ + Result2 = pthread_cond_timedwait(UnixEvent->MxCond, UnixEvent->MxMutex, &TempTime); + if (Result2 != 0) break; + } while (UnixEvent->MxSignaled[0] == '\x00'); + + if (Result2 == 0) + { + UnixEvent->MxWaiting[0]--; + + /* Reset auto events. */ + if (UnixEvent->MxManual[0] == '\x00') UnixEvent->MxSignaled[0] = '\x00'; + + Result = 1; + } + } + + pthread_mutex_unlock(UnixEvent->MxMutex); + + return Result; +} + +int sync_FireUnixEvent(sync_UnixEventWrapper *UnixEvent) +{ + if (pthread_mutex_lock(UnixEvent->MxMutex) != 0) return 0; + + UnixEvent->MxSignaled[0] = '\x01'; + + /* Let all waiting threads through for manual events, otherwise just one waiting thread (if any). */ + if (UnixEvent->MxManual[0] == '\x00') pthread_cond_broadcast(UnixEvent->MxCond); + else pthread_cond_signal(UnixEvent->MxCond); + + pthread_mutex_unlock(UnixEvent->MxMutex); + + return 1; +} + +/* Only call for manual events. */ +int sync_ResetUnixEvent(sync_UnixEventWrapper *UnixEvent) +{ + if (UnixEvent->MxManual[0] == '\x00') return 0; + if (pthread_mutex_lock(UnixEvent->MxMutex) != 0) return 0; + + UnixEvent->MxSignaled[0] = '\x00'; + + pthread_mutex_unlock(UnixEvent->MxMutex); + + return 1; +} + +void sync_FreeUnixEvent(sync_UnixEventWrapper *UnixEvent) +{ + pthread_mutex_destroy(UnixEvent->MxMutex); + pthread_cond_destroy(UnixEvent->MxCond); +} + #endif +/* {{{ PHP 5 and 7 crossover compatibility defines and functions */ +#if PHP_MAJOR_VERSION >= 7 -/* Mutex */ -PHP_SYNC_API zend_class_entry *sync_Mutex_ce; +#define PORTABLE_new_zend_object_func(funcname) zend_object *funcname(zend_class_entry *ce) +#define PORTABLE_new_zend_object_return_var +#define PORTABLE_new_zend_object_return_var_ref NULL +#define PORTABLE_allocate_zend_object(size, ce) ecalloc(1, (size) + zend_object_properties_size(ce)); +#define PORTABLE_new_zend_object_return(std) return (std) -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) +void PORTABLE_InitZendObject(void *obj, zend_object *std, void *unused1, void *FreeFunc, zend_object_handlers *Handlers, zend_class_entry *ce TSRMLS_DC) +{ + /* Initialize Zend. */ + zend_object_std_init(std, ce TSRMLS_CC); + object_properties_init(std, ce); + + /* Initialize destructor callback. */ + std->handlers = Handlers; +} + +typedef zend_long PORTABLE_ZPP_ARG_long; +typedef size_t PORTABLE_ZPP_ARG_size; +typedef zval * PORTABLE_ZPP_ARG_zval_ref; +#define PORTABLE_ZPP_ARG_zval_ref_deref(var) var + +static inline void *PHP_7_zend_object_to_object(zend_object *std) +{ + return (void *)((char *)std - std->handlers->offset); +} + +#define PORTABLE_zend_object_store_get_object() PHP_7_zend_object_to_object(Z_OBJ_P(getThis())) + +#define PORTABLE_free_zend_object_func(funcname) void funcname(zend_object *object) +#define PORTABLE_free_zend_object_get_object(object) PHP_7_zend_object_to_object(object) +#define PORTABLE_free_zend_object_free_object(obj) zend_object_std_dtor(&obj->std); + +#define PORTABLE_RETURN_STRINGL(str, len) RETURN_STRINGL(str, len) + +#else + +#define PORTABLE_new_zend_object_func(funcname) zend_object_value funcname(zend_class_entry *ce TSRMLS_DC) +#define PORTABLE_new_zend_object_return_var zend_object_value retval +#define PORTABLE_new_zend_object_return_var_ref &retval +#define PORTABLE_new_zend_object_return(std) return retval +#define PORTABLE_allocate_zend_object(size, ce) ecalloc(1, (size)); + +void PORTABLE_InitZendObject(void *obj, zend_object *std, zend_object_value *retval, zend_objects_free_object_storage_t FreeFunc, zend_object_handlers *Handlers, 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); + zend_object_std_init(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 *)); + zend_hash_copy(std->properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); #else - object_properties_init(&obj->std, ce); + object_properties_init(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(); + retval->handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object, FreeFunc, NULL TSRMLS_CC); + retval->handlers = Handlers; +} + +typedef long PORTABLE_ZPP_ARG_long; +typedef int PORTABLE_ZPP_ARG_size; +typedef zval ** PORTABLE_ZPP_ARG_zval_ref; +#define PORTABLE_ZPP_ARG_zval_ref_deref(var) *var + +#define PORTABLE_zend_object_store_get_object() zend_object_store_get_object(getThis() TSRMLS_CC) + +#define PORTABLE_free_zend_object_func(funcname) void funcname(void *object TSRMLS_DC) +#define PORTABLE_free_zend_object_get_object(object) object; +#define PORTABLE_free_zend_object_free_object(obj) zend_object_std_dtor(&obj->std TSRMLS_CC); efree(obj); + +#define PORTABLE_RETURN_STRINGL(str, len) RETURN_STRINGL(str, len, 1) + +#endif +/* }}} */ + + +/* Mutex */ +PHP_SYNC_API zend_class_entry *sync_Mutex_ce; +static zend_object_handlers sync_Mutex_object_handlers; + +PORTABLE_free_zend_object_func(sync_Mutex_free_object); + +/* {{{ Initialize internal Mutex structure. */ +PORTABLE_new_zend_object_func(sync_Mutex_create_object) +{ + PORTABLE_new_zend_object_return_var; + sync_Mutex_object *obj; + + /* Create the object. */ + obj = (sync_Mutex_object *)PORTABLE_allocate_zend_object(sizeof(sync_Mutex_object), ce); + + PORTABLE_InitZendObject(obj, &obj->std, PORTABLE_new_zend_object_return_var_ref, sync_Mutex_free_object, &sync_Mutex_object_handlers, ce TSRMLS_CC); /* Initialize Mutex information. */ #if defined(PHP_WIN32) obj->MxWinMutex = NULL; InitializeCriticalSection(&obj->MxWinCritSection); #else - obj->MxSemMutex = SEM_FAILED; - obj->MxAllocated = 0; + obj->MxNamed = 0; + obj->MxMem = NULL; pthread_mutex_init(&obj->MxPthreadCritSection, NULL); #endif obj->MxOwnerID = 0; obj->MxCount = 0; - return retval; + PORTABLE_new_zend_object_return(&obj->std); } /* }}} */ @@ -200,6 +785,7 @@ int sync_Mutex_unlock_internal(sync_Mutex_object *obj, int all) } if (all) obj->MxCount = 1; + obj->MxCount--; if (!obj->MxCount) { @@ -216,7 +802,7 @@ int sync_Mutex_unlock_internal(sync_Mutex_object *obj, int all) 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()) + if (obj->MxMem == NULL || obj->MxOwnerID != sync_GetCurrentThreadID()) { pthread_mutex_unlock(&obj->MxPthreadCritSection); @@ -224,13 +810,14 @@ int sync_Mutex_unlock_internal(sync_Mutex_object *obj, int all) } if (all) obj->MxCount = 1; + obj->MxCount--; if (!obj->MxCount) { obj->MxOwnerID = 0; /* Release the mutex. */ - sem_post(obj->MxSemMutex); + sync_ReleaseUnixSemaphore(&obj->MxPthreadMutex, NULL); } pthread_mutex_unlock(&obj->MxPthreadCritSection); @@ -242,9 +829,9 @@ int sync_Mutex_unlock_internal(sync_Mutex_object *obj, int all) /* }}} */ /* {{{ Free internal Mutex structure. */ -void sync_Mutex_free_object(void *object TSRMLS_DC) +PORTABLE_free_zend_object_func(sync_Mutex_free_object) { - sync_Mutex_object *obj = (sync_Mutex_object *)object; + sync_Mutex_object *obj = (sync_Mutex_object *)PORTABLE_free_zend_object_get_object(object); sync_Mutex_unlock_internal(obj, 1); @@ -252,14 +839,21 @@ void sync_Mutex_free_object(void *object TSRMLS_DC) if (obj->MxWinMutex != NULL) CloseHandle(obj->MxWinMutex); DeleteCriticalSection(&obj->MxWinCritSection); #else - if (obj->MxSemMutex != SEM_FAILED) + if (obj->MxMem != NULL) { - if (obj->MxAllocated) efree(obj->MxSemMutex); - else sem_close(obj->MxSemMutex); + if (obj->MxNamed) sync_UnmapUnixNamedMem(obj->MxMem, sync_GetUnixSemaphoreSize()); + else + { + sync_FreeUnixSemaphore(&obj->MxPthreadMutex); + + efree(obj->MxMem); + } } pthread_mutex_destroy(&obj->MxPthreadCritSection); #endif + + PORTABLE_free_zend_object_free_object(obj); } /* }}} */ @@ -268,17 +862,19 @@ void sync_Mutex_free_object(void *object TSRMLS_DC) PHP_METHOD(sync_Mutex, __construct) { char *name = NULL; - int name_len; + PORTABLE_ZPP_ARG_size name_len; sync_Mutex_object *obj; #if defined(PHP_WIN32) SECURITY_ATTRIBUTES SecAttr; #else - char *name2; + size_t Pos, TempSize; #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); + obj = (sync_Mutex_object *)PORTABLE_zend_object_store_get_object(); + + if (name_len < 1) name = NULL; #if defined(PHP_WIN32) @@ -295,29 +891,27 @@ PHP_METHOD(sync_Mutex, __construct) #else - if (name != NULL) - { - name2 = emalloc(name_len + 20); + TempSize = sync_GetUnixSemaphoreSize(); + obj->MxNamed = (name != NULL ? 1 : 0); + int Result = sync_InitUnixNamedMem(&obj->MxMem, &Pos, "/Sync_Mutex", name, TempSize); - 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) + if (Result < 0) { zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Mutex could not be created", 0 TSRMLS_CC); + return; } + sync_GetUnixSemaphore(&obj->MxPthreadMutex, obj->MxMem + Pos); + + /* Handle the first time this mutex has been opened. */ + if (Result == 0) + { + sync_InitUnixSemaphore(&obj->MxPthreadMutex, obj->MxNamed, 1, 1); + + if (obj->MxNamed) sync_UnixNamedMemReady(obj->MxMem); + } + #endif } /* }}} */ @@ -326,7 +920,7 @@ PHP_METHOD(sync_Mutex, __construct) Locks a mutex object. */ PHP_METHOD(sync_Mutex, lock) { - long wait = -1; + PORTABLE_ZPP_ARG_long wait = -1; sync_Mutex_object *obj; #if defined(PHP_WIN32) DWORD Result; @@ -335,7 +929,7 @@ PHP_METHOD(sync_Mutex, lock) 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); + obj = (sync_Mutex_object *)PORTABLE_zend_object_store_get_object(); #if defined(PHP_WIN32) @@ -381,7 +975,7 @@ PHP_METHOD(sync_Mutex, lock) pthread_mutex_unlock(&obj->MxPthreadCritSection); - if (!sync_WaitForSemaphore(obj->MxSemMutex, (uint32_t)(wait > -1 ? wait : INFINITE))) RETURN_FALSE; + if (!sync_WaitForUnixSemaphore(&obj->MxPthreadMutex, (uint32_t)(wait > -1 ? wait : INFINITE))) RETURN_FALSE; pthread_mutex_lock(&obj->MxPthreadCritSection); obj->MxOwnerID = sync_GetCurrentThreadID(); @@ -398,12 +992,12 @@ PHP_METHOD(sync_Mutex, lock) Unlocks a mutex object. */ PHP_METHOD(sync_Mutex, unlock) { - long all = 0; + PORTABLE_ZPP_ARG_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); + obj = (sync_Mutex_object *)PORTABLE_zend_object_store_get_object(); if (!sync_Mutex_unlock_internal(obj, all)) RETURN_FALSE; @@ -435,51 +1029,39 @@ static const zend_function_entry sync_Mutex_methods[] = { /* Semaphore */ PHP_SYNC_API zend_class_entry *sync_Semaphore_ce; +static zend_object_handlers sync_Semaphore_object_handlers; -void sync_Semaphore_free_object(void *object TSRMLS_DC); +PORTABLE_free_zend_object_func(sync_Semaphore_free_object); /* {{{ Initialize internal Semaphore structure. */ -zend_object_value sync_Semaphore_create_object(zend_class_entry *ce TSRMLS_DC) +PORTABLE_new_zend_object_func(sync_Semaphore_create_object) { - zend_object_value retval; + PORTABLE_new_zend_object_return_var; 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)); + obj = (sync_Semaphore_object *)PORTABLE_allocate_zend_object(sizeof(sync_Semaphore_object), ce); - /* 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(); + PORTABLE_InitZendObject(obj, &obj->std, PORTABLE_new_zend_object_return_var_ref, sync_Semaphore_free_object, &sync_Semaphore_object_handlers, ce TSRMLS_CC); /* Initialize Semaphore information. */ #if defined(PHP_WIN32) obj->MxWinSemaphore = NULL; #else - obj->MxSemSemaphore = SEM_FAILED; - obj->MxAllocated = 0; + obj->MxNamed = 0; + obj->MxMem = NULL; #endif obj->MxAutoUnlock = 0; obj->MxCount = 0; - return retval; + PORTABLE_new_zend_object_return(&obj->std); } /* }}} */ /* {{{ Free internal Semaphore structure. */ -void sync_Semaphore_free_object(void *object TSRMLS_DC) +PORTABLE_free_zend_object_func(sync_Semaphore_free_object) { - sync_Semaphore_object *obj = (sync_Semaphore_object *)object; + sync_Semaphore_object *obj = (sync_Semaphore_object *)PORTABLE_free_zend_object_get_object(object); if (obj->MxAutoUnlock) { @@ -488,7 +1070,7 @@ void sync_Semaphore_free_object(void *object TSRMLS_DC) #if defined(PHP_WIN32) ReleaseSemaphore(obj->MxWinSemaphore, 1, NULL); #else - sem_post(obj->MxSemSemaphore); + sync_ReleaseUnixSemaphore(&obj->MxPthreadSemaphore, NULL); #endif obj->MxCount--; @@ -498,12 +1080,19 @@ void sync_Semaphore_free_object(void *object TSRMLS_DC) #if defined(PHP_WIN32) if (obj->MxWinSemaphore != NULL) CloseHandle(obj->MxWinSemaphore); #else - if (obj->MxSemSemaphore != SEM_FAILED) + if (obj->MxMem != NULL) { - if (obj->MxAllocated) efree(obj->MxSemSemaphore); - else sem_close(obj->MxSemSemaphore); + if (obj->MxNamed) sync_UnmapUnixNamedMem(obj->MxMem, sync_GetUnixSemaphoreSize()); + else + { + sync_FreeUnixSemaphore(&obj->MxPthreadSemaphore); + + efree(obj->MxMem); + } } #endif + + PORTABLE_free_zend_object_free_object(obj); } /* }}} */ @@ -512,19 +1101,21 @@ void sync_Semaphore_free_object(void *object TSRMLS_DC) PHP_METHOD(sync_Semaphore, __construct) { char *name = NULL; - int name_len; - long initialval = 1; - long autounlock = 1; + PORTABLE_ZPP_ARG_size name_len; + PORTABLE_ZPP_ARG_long initialval = 1; + PORTABLE_ZPP_ARG_long autounlock = 1; sync_Semaphore_object *obj; #if defined(PHP_WIN32) SECURITY_ATTRIBUTES SecAttr; #else - char *name2; + size_t Pos, TempSize; #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 = (sync_Semaphore_object *)PORTABLE_zend_object_store_get_object(); + + if (name_len < 1) name = NULL; obj->MxAutoUnlock = (autounlock ? 1 : 0); @@ -543,29 +1134,27 @@ PHP_METHOD(sync_Semaphore, __construct) #else - if (name != NULL) - { - name2 = emalloc(name_len + 20); + TempSize = sync_GetUnixSemaphoreSize(); + obj->MxNamed = (name != NULL ? 1 : 0); + int Result = sync_InitUnixNamedMem(&obj->MxMem, &Pos, "/Sync_Semaphore", name, TempSize); - 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) + if (Result < 0) { zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Semaphore could not be created", 0 TSRMLS_CC); + return; } + sync_GetUnixSemaphore(&obj->MxPthreadSemaphore, obj->MxMem + Pos); + + /* Handle the first time this semaphore has been opened. */ + if (Result == 0) + { + sync_InitUnixSemaphore(&obj->MxPthreadSemaphore, obj->MxNamed, (uint32_t)initialval, (uint32_t)initialval); + + if (obj->MxNamed) sync_UnixNamedMemReady(obj->MxMem); + } + #endif } /* }}} */ @@ -574,7 +1163,7 @@ PHP_METHOD(sync_Semaphore, __construct) Locks a semaphore object. */ PHP_METHOD(sync_Semaphore, lock) { - long wait = -1; + PORTABLE_ZPP_ARG_long wait = -1; sync_Semaphore_object *obj; #if defined(PHP_WIN32) DWORD Result; @@ -583,7 +1172,7 @@ PHP_METHOD(sync_Semaphore, lock) 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); + obj = (sync_Semaphore_object *)PORTABLE_zend_object_store_get_object(); #if defined(PHP_WIN32) @@ -592,7 +1181,7 @@ PHP_METHOD(sync_Semaphore, lock) #else - if (!sync_WaitForSemaphore(obj->MxSemSemaphore, (uint32_t)(wait > -1 ? wait : INFINITE))) RETURN_FALSE; + if (!sync_WaitForUnixSemaphore(&obj->MxPthreadSemaphore, (uint32_t)(wait > -1 ? wait : INFINITE))) RETURN_FALSE; #endif @@ -606,37 +1195,39 @@ PHP_METHOD(sync_Semaphore, lock) Unlocks a semaphore object. */ PHP_METHOD(sync_Semaphore, unlock) { - zval **zprevcount = NULL; + PORTABLE_ZPP_ARG_zval_ref zprevcount = NULL; sync_Semaphore_object *obj; - int count; - int argc = ZEND_NUM_ARGS(); + PORTABLE_ZPP_ARG_long count; #if defined(PHP_WIN32) LONG PrevCount; +#else + uint32_t PrevCount; #endif +#if PHP_MAJOR_VERSION >= 7 + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/", &zprevcount) == FAILURE) return; +#else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|Z", &zprevcount) == FAILURE) return; +#endif - obj = (sync_Semaphore_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + obj = (sync_Semaphore_object *)PORTABLE_zend_object_store_get_object(); #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); + sync_ReleaseUnixSemaphore(&obj->MxPthreadSemaphore, &PrevCount); #endif - if (argc > 0) + if (zprevcount != NULL) { - zval_dtor(*zprevcount); - ZVAL_LONG(*zprevcount, count); + count = (PORTABLE_ZPP_ARG_long)PrevCount; + + zval_dtor(PORTABLE_ZPP_ARG_zval_ref_deref(zprevcount)); + ZVAL_LONG(PORTABLE_ZPP_ARG_zval_ref_deref(zprevcount), count); } if (obj->MxAutoUnlock) obj->MxCount--; @@ -671,92 +1262,76 @@ static const zend_function_entry sync_Semaphore_methods[] = { /* Event */ PHP_SYNC_API zend_class_entry *sync_Event_ce; +static zend_object_handlers sync_Event_object_handlers; -void sync_Event_free_object(void *object TSRMLS_DC); +PORTABLE_free_zend_object_func(sync_Event_free_object); /* {{{ Initialize internal Event structure. */ -zend_object_value sync_Event_create_object(zend_class_entry *ce TSRMLS_DC) +PORTABLE_new_zend_object_func(sync_Event_create_object) { - zend_object_value retval; + PORTABLE_new_zend_object_return_var; 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)); + obj = (sync_Event_object *)PORTABLE_allocate_zend_object(sizeof(sync_Event_object), ce); - /* 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(); + PORTABLE_InitZendObject(obj, &obj->std, PORTABLE_new_zend_object_return_var_ref, sync_Event_free_object, &sync_Event_object_handlers, ce TSRMLS_CC); /* 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; + obj->MxNamed = 0; + obj->MxMem = NULL; #endif - return retval; + PORTABLE_new_zend_object_return(&obj->std); } /* }}} */ /* {{{ Free internal Event structure. */ -void sync_Event_free_object(void *object TSRMLS_DC) +PORTABLE_free_zend_object_func(sync_Event_free_object) { - sync_Event_object *obj = (sync_Event_object *)object; + sync_Event_object *obj = (sync_Event_object *)PORTABLE_free_zend_object_get_object(object); #if defined(PHP_WIN32) if (obj->MxWinWaitEvent != NULL) CloseHandle(obj->MxWinWaitEvent); #else - if (obj->MxAllocated) + if (obj->MxMem != NULL) { - 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); + if (obj->MxNamed) sync_UnmapUnixNamedMem(obj->MxMem, sync_GetUnixEventSize()); + else + { + sync_FreeUnixEvent(&obj->MxPthreadEvent); + + efree(obj->MxMem); + } } #endif + + PORTABLE_free_zend_object_free_object(obj); } /* }}} */ -/* {{{ proto void Sync_Event::__construct([string $name = null, [bool $manual = false]]) +/* {{{ proto void Sync_Event::__construct([string $name = null, [bool $manual = false, [bool $prefire = false]]]) Constructs a named or unnamed event object. */ PHP_METHOD(sync_Event, __construct) { char *name = NULL; - int name_len; - long manual = 0; + PORTABLE_ZPP_ARG_size name_len; + PORTABLE_ZPP_ARG_long manual = 0, prefire = 0; sync_Event_object *obj; #if defined(PHP_WIN32) SECURITY_ATTRIBUTES SecAttr; #else - char *name2; + size_t Pos, TempSize; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &name, &name_len, &manual) == FAILURE) return; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sll", &name, &name_len, &manual, &prefire) == FAILURE) return; - obj = (sync_Event_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + obj = (sync_Event_object *)PORTABLE_zend_object_store_get_object(); + + if (name_len < 1) name = NULL; #if defined(PHP_WIN32) @@ -764,7 +1339,7 @@ PHP_METHOD(sync_Event, __construct) SecAttr.lpSecurityDescriptor = NULL; SecAttr.bInheritHandle = TRUE; - obj->MxWinWaitEvent = CreateEventA(&SecAttr, (BOOL)manual, FALSE, name); + obj->MxWinWaitEvent = CreateEventA(&SecAttr, (BOOL)manual, (prefire ? TRUE : FALSE), name); if (obj->MxWinWaitEvent == NULL) { zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Event object could not be created", 0 TSRMLS_CC); @@ -773,54 +1348,27 @@ PHP_METHOD(sync_Event, __construct) #else - obj->MxManual = (manual ? 1 : 0); + TempSize = sync_GetUnixEventSize(); + obj->MxNamed = (name != NULL ? 1 : 0); + int Result = sync_InitUnixNamedMem(&obj->MxMem, &Pos, "/Sync_Event", name, TempSize); - 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))) + if (Result < 0) { zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Event object could not be created", 0 TSRMLS_CC); + return; } + sync_GetUnixEvent(&obj->MxPthreadEvent, obj->MxMem + Pos); + + /* Handle the first time this event has been opened. */ + if (Result == 0) + { + sync_InitUnixEvent(&obj->MxPthreadEvent, obj->MxNamed, (manual ? 1 : 0), (prefire ? 1 : 0)); + + if (obj->MxNamed) sync_UnixNamedMemReady(obj->MxMem); + } + #endif } /* }}} */ @@ -829,19 +1377,15 @@ PHP_METHOD(sync_Event, __construct) Waits for an event object to fire. */ PHP_METHOD(sync_Event, wait) { - long wait = -1; + PORTABLE_ZPP_ARG_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); + obj = (sync_Event_object *)PORTABLE_zend_object_store_get_object(); #if defined(PHP_WIN32) @@ -850,56 +1394,7 @@ PHP_METHOD(sync_Event, wait) #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; + if (!sync_WaitForUnixEvent(&obj->MxPthreadEvent, (uint32_t)(wait > -1 ? wait : INFINITE))) RETURN_FALSE; #endif @@ -912,12 +1407,8 @@ PHP_METHOD(sync_Event, wait) 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); + obj = (sync_Event_object *)PORTABLE_zend_object_store_get_object(); #if defined(PHP_WIN32) @@ -925,28 +1416,7 @@ PHP_METHOD(sync_Event, fire) #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); - } + if (!sync_FireUnixEvent(&obj->MxPthreadEvent)) RETURN_FALSE; #endif @@ -959,12 +1429,8 @@ PHP_METHOD(sync_Event, fire) 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); + obj = (sync_Event_object *)PORTABLE_zend_object_store_get_object(); #if defined(PHP_WIN32) @@ -972,20 +1438,7 @@ PHP_METHOD(sync_Event, reset) #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); + if (!sync_ResetUnixEvent(&obj->MxPthreadEvent)) RETURN_FALSE; #endif @@ -1021,32 +1474,20 @@ static const zend_function_entry sync_Event_methods[] = { /* Reader-Writer */ PHP_SYNC_API zend_class_entry *sync_ReaderWriter_ce; +static zend_object_handlers sync_ReaderWriter_object_handlers; -void sync_ReaderWriter_free_object(void *object TSRMLS_DC); +PORTABLE_free_zend_object_func(sync_ReaderWriter_free_object); /* {{{ Initialize internal Reader-Writer structure. */ -zend_object_value sync_ReaderWriter_create_object(zend_class_entry *ce TSRMLS_DC) +PORTABLE_new_zend_object_func(sync_ReaderWriter_create_object) { - zend_object_value retval; + PORTABLE_new_zend_object_return_var; 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)); + obj = (sync_ReaderWriter_object *)PORTABLE_allocate_zend_object(sizeof(sync_ReaderWriter_object), ce); - /* 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(); + PORTABLE_InitZendObject(obj, &obj->std, PORTABLE_new_zend_object_return_var_ref, sync_ReaderWriter_free_object, &sync_ReaderWriter_object_handlers, ce TSRMLS_CC); /* Initialize Reader-Writer information. */ #if defined(PHP_WIN32) @@ -1055,18 +1496,16 @@ zend_object_value sync_ReaderWriter_create_object(zend_class_entry *ce TSRMLS_DC 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; + obj->MxNamed = 0; + obj->MxMem = NULL; + obj->MxRCount = NULL; #endif obj->MxAutoUnlock = 1; obj->MxReadLocks = 0; obj->MxWriteLock = 0; - return retval; + PORTABLE_new_zend_object_return(&obj->std); } /* }}} */ @@ -1108,46 +1547,38 @@ int sync_ReaderWriter_readunlock_internal(sync_ReaderWriter_object *obj) /* Release the semaphore mutex. */ ReleaseSemaphore(obj->MxWinRSemMutex, 1, NULL); - return 1; - #else - int Result, Val; + if (obj->MxMem == NULL) return 0; - if (obj->MxSemRSemMutex == NULL || obj->MxSemRSemaphore == NULL || obj->MxSemRWaitEvent == NULL) return 0; - - /* Acquire the semaphore mutex. */ - if (!sync_WaitForSemaphore(obj->MxSemRSemMutex, INFINITE)) return 0; + /* Acquire the counter mutex. */ + if (!sync_WaitForUnixSemaphore(&obj->MxPthreadRCountMutex, INFINITE)) return 0; if (obj->MxReadLocks) obj->MxReadLocks--; - /* Release the semaphore. */ - Result = sem_post(obj->MxSemRSemaphore); - if (Result != 0) + /* Decrease the number of readers. */ + if (obj->MxRCount[0]) obj->MxRCount[0]--; + else { - sem_post(obj->MxSemRSemMutex); + sync_ReleaseUnixSemaphore(&obj->MxPthreadRCountMutex, NULL); return 0; } /* Update the event state. */ - sem_getvalue(obj->MxSemRSemaphore, &Val); - if (Val == SEM_VALUE_MAX) + if (!obj->MxRCount[0] && !sync_FireUnixEvent(&obj->MxPthreadRWaitEvent)) { - if (sem_post(obj->MxSemRWaitEvent) != 0) - { - sem_post(obj->MxSemRSemMutex); + sync_ReleaseUnixSemaphore(&obj->MxPthreadRCountMutex, NULL); - return 0; - } + return 0; } - /* Release the semaphore mutex. */ - sem_post(obj->MxSemRSemMutex); - - return (Result == 0); + /* Release the counter mutex. */ + sync_ReleaseUnixSemaphore(&obj->MxPthreadRCountMutex, NULL); #endif + + return 1; } /* }}} */ @@ -1165,12 +1596,12 @@ int sync_ReaderWriter_writeunlock_internal(sync_ReaderWriter_object *obj) #else - if (obj->MxSemWWaitMutex == NULL) return 0; + if (obj->MxMem == NULL) return 0; obj->MxWriteLock = 0; /* Release the write lock. */ - sem_post(obj->MxSemWWaitMutex); + sync_ReleaseUnixSemaphore(&obj->MxPthreadWWaitMutex, NULL); #endif @@ -1179,9 +1610,9 @@ int sync_ReaderWriter_writeunlock_internal(sync_ReaderWriter_object *obj) /* }}} */ /* {{{ Free internal Reader-Writer structure. */ -void sync_ReaderWriter_free_object(void *object TSRMLS_DC) +PORTABLE_free_zend_object_func(sync_ReaderWriter_free_object) { - sync_ReaderWriter_object *obj = (sync_ReaderWriter_object *)object; + sync_ReaderWriter_object *obj = (sync_ReaderWriter_object *)PORTABLE_free_zend_object_get_object(object); if (obj->MxAutoUnlock) { @@ -1196,21 +1627,21 @@ void sync_ReaderWriter_free_object(void *object TSRMLS_DC) if (obj->MxWinRSemaphore != NULL) CloseHandle(obj->MxWinRSemaphore); if (obj->MxWinRSemMutex != NULL) CloseHandle(obj->MxWinRSemMutex); #else - if (obj->MxAllocated) + if (obj->MxMem != NULL) { - 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); + if (obj->MxNamed) sync_UnmapUnixNamedMem(obj->MxMem, sync_GetUnixSemaphoreSize() + sync_AlignUnixSize(sizeof(uint32_t)) + sync_GetUnixEventSize() + sync_GetUnixSemaphoreSize()); + else + { + sync_FreeUnixSemaphore(&obj->MxPthreadRCountMutex); + sync_FreeUnixEvent(&obj->MxPthreadRWaitEvent); + sync_FreeUnixSemaphore(&obj->MxPthreadWWaitMutex); + + efree(obj->MxMem); + } } #endif + + PORTABLE_free_zend_object_free_object(obj); } /* }}} */ @@ -1219,90 +1650,89 @@ void sync_ReaderWriter_free_object(void *object TSRMLS_DC) PHP_METHOD(sync_ReaderWriter, __construct) { char *name = NULL; - int name_len; - long autounlock = 1; + PORTABLE_ZPP_ARG_size name_len; + PORTABLE_ZPP_ARG_long autounlock = 1; sync_ReaderWriter_object *obj; - char *name2; #if defined(PHP_WIN32) + char *name2; SECURITY_ATTRIBUTES SecAttr; #else + size_t Pos, TempSize; #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 = (sync_ReaderWriter_object *)PORTABLE_zend_object_store_get_object(); + + if (name_len < 1) name = NULL; obj->MxAutoUnlock = (autounlock ? 1 : 0); +#if defined(PHP_WIN32) + 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); + if (name2 != NULL) sprintf(name2, "%s-Sync_ReadWrite-0", name); obj->MxWinRSemMutex = CreateSemaphoreA(&SecAttr, 1, 1, name2); - if (name2 != NULL) sprintf(name2, "Sync_ReadWrite|%s|1", name); + if (name2 != NULL) sprintf(name2, "%s-Sync_ReadWrite-1", name); obj->MxWinRSemaphore = CreateSemaphoreA(&SecAttr, LONG_MAX, LONG_MAX, name2); - if (name2 != NULL) sprintf(name2, "Sync_ReadWrite|%s|2", name); + if (name2 != NULL) sprintf(name2, "%s-Sync_ReadWrite-2", name); obj->MxWinRWaitEvent = CreateEventA(&SecAttr, TRUE, TRUE, name2); - if (name2 != NULL) sprintf(name2, "Sync_ReadWrite|%s|3", name); + if (name2 != NULL) sprintf(name2, "%s-Sync_ReadWrite-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) + TempSize = sync_GetUnixSemaphoreSize() + sync_AlignUnixSize(sizeof(uint32_t)) + sync_GetUnixEventSize() + sync_GetUnixSemaphoreSize(); + obj->MxNamed = (name != NULL ? 1 : 0); + int Result = sync_InitUnixNamedMem(&obj->MxMem, &Pos, "/Sync_ReadWrite", name, TempSize); + + if (Result < 0) { zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Reader-Writer object could not be created", 0 TSRMLS_CC); + return; } + /* Load the pointers. */ + char *MemPtr = obj->MxMem + Pos; + sync_GetUnixSemaphore(&obj->MxPthreadRCountMutex, MemPtr); + MemPtr += sync_GetUnixSemaphoreSize(); + + obj->MxRCount = (volatile uint32_t *)(MemPtr); + MemPtr += sync_AlignUnixSize(sizeof(uint32_t)); + + sync_GetUnixEvent(&obj->MxPthreadRWaitEvent, MemPtr); + MemPtr += sync_GetUnixEventSize(); + + sync_GetUnixSemaphore(&obj->MxPthreadWWaitMutex, MemPtr); + + /* Handle the first time this reader/writer lock has been opened. */ + if (Result == 0) + { + sync_InitUnixSemaphore(&obj->MxPthreadRCountMutex, obj->MxNamed, 1, 1); + obj->MxRCount[0] = 0; + sync_InitUnixEvent(&obj->MxPthreadRWaitEvent, obj->MxNamed, 1, 1); + sync_InitUnixSemaphore(&obj->MxPthreadWWaitMutex, obj->MxNamed, 1, 1); + + if (obj->MxNamed) sync_UnixNamedMemReady(obj->MxMem); + } + #endif } /* }}} */ @@ -1311,19 +1741,17 @@ PHP_METHOD(sync_ReaderWriter, __construct) Read locks a reader-writer object. */ PHP_METHOD(sync_ReaderWriter, readlock) { - long wait = -1; + PORTABLE_ZPP_ARG_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); + obj = (sync_ReaderWriter_object *)PORTABLE_zend_object_store_get_object(); WaitAmt = (uint32_t)(wait > -1 ? wait : INFINITE); @@ -1389,59 +1817,34 @@ PHP_METHOD(sync_ReaderWriter, readlock) #else /* Acquire the write lock mutex. Guarantees that readers can't starve the writer. */ - if (!sync_WaitForSemaphore(obj->MxSemWWaitMutex, WaitAmt)) RETURN_FALSE; + if (!sync_WaitForUnixSemaphore(&obj->MxPthreadWWaitMutex, WaitAmt)) RETURN_FALSE; - /* Acquire the semaphore mutex. */ + /* Acquire the counter mutex. */ CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000); - if (WaitAmt < CurrTime - StartTime) + if (WaitAmt < CurrTime - StartTime || !sync_WaitForUnixSemaphore(&obj->MxPthreadRCountMutex, 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); + sync_ReleaseUnixSemaphore(&obj->MxPthreadWWaitMutex, NULL); RETURN_FALSE; } /* Update the event state. */ - sem_getvalue(obj->MxSemRSemaphore, &Val); - if (Val == SEM_VALUE_MAX - 1) + if (!sync_ResetUnixEvent(&obj->MxPthreadRWaitEvent)) { - if (!sync_WaitForSemaphore(obj->MxSemRWaitEvent, INFINITE)) - { - sem_post(obj->MxSemRSemaphore); - sem_post(obj->MxSemRSemMutex); - sem_post(obj->MxSemWWaitMutex); + sync_ReleaseUnixSemaphore(&obj->MxPthreadRCountMutex, NULL); + sync_ReleaseUnixSemaphore(&obj->MxPthreadWWaitMutex, NULL); - RETURN_FALSE; - } + RETURN_FALSE; } + /* Increment the number of readers. */ + obj->MxRCount[0]++; + obj->MxReadLocks++; /* Release the mutexes. */ - sem_post(obj->MxSemRSemMutex); - sem_post(obj->MxSemWWaitMutex); + sync_ReleaseUnixSemaphore(&obj->MxPthreadRCountMutex, NULL); + sync_ReleaseUnixSemaphore(&obj->MxPthreadWWaitMutex, NULL); #endif @@ -1453,7 +1856,7 @@ PHP_METHOD(sync_ReaderWriter, readlock) Write locks a reader-writer object. */ PHP_METHOD(sync_ReaderWriter, writelock) { - long wait = -1; + PORTABLE_ZPP_ARG_long wait = -1; sync_ReaderWriter_object *obj; uint32_t WaitAmt; uint64_t StartTime, CurrTime; @@ -1464,7 +1867,7 @@ PHP_METHOD(sync_ReaderWriter, writelock) 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); + obj = (sync_ReaderWriter_object *)PORTABLE_zend_object_store_get_object(); WaitAmt = (uint32_t)(wait > -1 ? wait : INFINITE); @@ -1490,20 +1893,17 @@ PHP_METHOD(sync_ReaderWriter, writelock) #else /* Acquire the write lock mutex. */ - if (!sync_WaitForSemaphore(obj->MxSemWWaitMutex, WaitAmt)) RETURN_FALSE; + if (!sync_WaitForUnixSemaphore(&obj->MxPthreadWWaitMutex, WaitAmt)) RETURN_FALSE; /* Wait for readers to reach zero. */ CurrTime = (WaitAmt == INFINITE ? 0 : sync_GetUnixMicrosecondTime() / 1000000); - if (!sync_WaitForSemaphore(obj->MxSemRWaitEvent, WaitAmt - (CurrTime - StartTime))) + if (WaitAmt < CurrTime - StartTime || !sync_WaitForUnixEvent(&obj->MxPthreadRWaitEvent, WaitAmt - (CurrTime - StartTime))) { - sem_post(obj->MxSemWWaitMutex); + sync_ReleaseUnixSemaphore(&obj->MxPthreadWWaitMutex, NULL); RETURN_FALSE; } - /* Release the semaphore to avoid a later deadlock. */ - sem_post(obj->MxSemRWaitEvent); - #endif obj->MxWriteLock = 1; @@ -1518,7 +1918,7 @@ PHP_METHOD(sync_ReaderWriter, readunlock) { sync_ReaderWriter_object *obj; - obj = (sync_ReaderWriter_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + obj = (sync_ReaderWriter_object *)PORTABLE_zend_object_store_get_object(); if (!sync_ReaderWriter_readunlock_internal(obj)) RETURN_FALSE; @@ -1532,7 +1932,7 @@ PHP_METHOD(sync_ReaderWriter, writeunlock) { sync_ReaderWriter_object *obj; - obj = (sync_ReaderWriter_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + obj = (sync_ReaderWriter_object *)PORTABLE_zend_object_store_get_object(); if (!sync_ReaderWriter_writeunlock_internal(obj)) RETURN_FALSE; @@ -1571,28 +1971,326 @@ static const zend_function_entry sync_ReaderWriter_methods[] = { +/* Shared Memory */ +PHP_SYNC_API zend_class_entry *sync_SharedMemory_ce; +static zend_object_handlers sync_SharedMemory_object_handlers; + +PORTABLE_free_zend_object_func(sync_SharedMemory_free_object); + +/* {{{ Initialize internal Shared Memory structure. */ +PORTABLE_new_zend_object_func(sync_SharedMemory_create_object) +{ + PORTABLE_new_zend_object_return_var; + sync_SharedMemory_object *obj; + + /* Create the object. */ + obj = (sync_SharedMemory_object *)PORTABLE_allocate_zend_object(sizeof(sync_SharedMemory_object), ce); + + PORTABLE_InitZendObject(obj, &obj->std, PORTABLE_new_zend_object_return_var_ref, sync_SharedMemory_free_object, &sync_SharedMemory_object_handlers, ce TSRMLS_CC); + + /* Initialize Shared Memory information. */ +#if defined(PHP_WIN32) + obj->MxFile = NULL; +#else + obj->MxMemInternal = NULL; +#endif + + obj->MxFirst = 0; + obj->MxSize = 0; + obj->MxMem = NULL; + + PORTABLE_new_zend_object_return(&obj->std); +} +/* }}} */ + +/* {{{ Free internal Shared Memory structure. */ +PORTABLE_free_zend_object_func(sync_SharedMemory_free_object) +{ + sync_SharedMemory_object *obj = (sync_SharedMemory_object *)PORTABLE_free_zend_object_get_object(object); + +#if defined(PHP_WIN32) + if (obj->MxMem != NULL) UnmapViewOfFile(obj->MxMem); + if (obj->MxFile != NULL) CloseHandle(obj->MxFile); +#else + if (obj->MxMemInternal != NULL) sync_UnmapUnixNamedMem(obj->MxMemInternal, obj->MxSize); +#endif + + PORTABLE_free_zend_object_free_object(obj); +} +/* }}} */ + +/* {{{ proto void Sync_SharedMemory::__construct(string $name, int $size) + Constructs a named shared memory object. */ +PHP_METHOD(sync_SharedMemory, __construct) +{ + char *name; + PORTABLE_ZPP_ARG_size name_len; + PORTABLE_ZPP_ARG_long size; + sync_SharedMemory_object *obj; +#if defined(PHP_WIN32) + char *name2; + SECURITY_ATTRIBUTES SecAttr; +#else + size_t Pos, TempSize; +#endif + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &name, &name_len, &size) == FAILURE) return; + + if (name_len < 1) + { + zend_throw_exception(zend_exception_get_default(TSRMLS_C), "An invalid name was passed", 0 TSRMLS_CC); + + return; + } + + obj = (sync_SharedMemory_object *)PORTABLE_zend_object_store_get_object(); + +#if defined(PHP_WIN32) + + name2 = emalloc(name_len + 30); + + SecAttr.nLength = sizeof(SecAttr); + SecAttr.lpSecurityDescriptor = NULL; + SecAttr.bInheritHandle = TRUE; + + /* Create the file mapping object backed by the system page file. */ + sprintf(name2, "%s-%u-Sync_SharedMem", name, (unsigned int)size); + obj->MxFile = CreateFileMappingA(INVALID_HANDLE_VALUE, &SecAttr, PAGE_READWRITE, 0, (DWORD)size, name2); + if (obj->MxFile == NULL) + { + obj->MxFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, TRUE, name2); + + if (obj->MxFile == NULL) + { + zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Shared memory file mapping could not be created/opened", 0 TSRMLS_CC); + + return; + } + } + else if (GetLastError() != ERROR_ALREADY_EXISTS) + { + obj->MxFirst = 1; + } + + efree(name2); + + obj->MxMem = (char *)MapViewOfFile(obj->MxFile, FILE_MAP_ALL_ACCESS, 0, 0, (DWORD)size); + + if (obj->MxMem == NULL) + { + zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Shared memory segment could not be mapped", 0 TSRMLS_CC); + + return; + } + + obj->MxSize = (size_t)size; + +#else + + TempSize = (size_t)size; + int Result = sync_InitUnixNamedMem(&obj->MxMemInternal, &Pos, "/Sync_SharedMem", name, TempSize); + + if (Result < 0) + { + zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Shared memory object could not be created/opened", 0 TSRMLS_CC); + + return; + } + + /* Load the pointers. */ + obj->MxMem = obj->MxMemInternal + Pos; + obj->MxSize = (size_t)size; + + /* Handle the first time this named memory has been opened. */ + if (Result == 0) + { + sync_UnixNamedMemReady(obj->MxMemInternal); + + obj->MxFirst = 1; + } + +#endif +} +/* }}} */ + +/* {{{ proto bool Sync_SharedMemory::first() + Returns whether or not this shared memory segment is the first time accessed (i.e. not initialized). */ +PHP_METHOD(sync_SharedMemory, first) +{ + sync_SharedMemory_object *obj; + + obj = (sync_SharedMemory_object *)PORTABLE_zend_object_store_get_object(); + + RETURN_BOOL(obj->MxFirst); +} +/* }}} */ + +/* {{{ proto int Sync_SharedMemory::size() + Returns the shared memory size. */ +PHP_METHOD(sync_SharedMemory, size) +{ + sync_SharedMemory_object *obj; + + obj = (sync_SharedMemory_object *)PORTABLE_zend_object_store_get_object(); + + RETURN_LONG((PORTABLE_ZPP_ARG_long)obj->MxSize); +} +/* }}} */ + +/* {{{ proto int Sync_SharedMemory::write(string $string, [int $start = 0]) + Copies data to shared memory. */ +PHP_METHOD(sync_SharedMemory, write) +{ + char *str; + PORTABLE_ZPP_ARG_size str_len; + PORTABLE_ZPP_ARG_long start = 0, maxval; + sync_SharedMemory_object *obj; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &start) == FAILURE) return; + + obj = (sync_SharedMemory_object *)PORTABLE_zend_object_store_get_object(); + maxval = (PORTABLE_ZPP_ARG_long)obj->MxSize; + + if (start < 0) start += maxval; + if (start < 0) start = 0; + if (start > maxval) start = maxval; + + if (start + str_len > maxval) str_len = maxval - start; + + memcpy(obj->MxMem + (size_t)start, str, str_len); + + RETURN_LONG((PORTABLE_ZPP_ARG_long)str_len); +} +/* }}} */ + +/* {{{ proto string Sync_SharedMemory::read([int $start = 0, [int $length = null]]) + Copies data from shared memory. */ +PHP_METHOD(sync_SharedMemory, read) +{ + PORTABLE_ZPP_ARG_long start = 0; + PORTABLE_ZPP_ARG_long length, maxval; + sync_SharedMemory_object *obj; + + obj = (sync_SharedMemory_object *)PORTABLE_zend_object_store_get_object(); + maxval = (PORTABLE_ZPP_ARG_long)obj->MxSize; + length = maxval; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &start, &length) == FAILURE) return; + + if (start < 0) start += maxval; + if (start < 0) start = 0; + if (start > maxval) start = maxval; + + if (length < 0) length += maxval - start; + if (length < 0) length = 0; + if (start + length > maxval) length = maxval - start; + + PORTABLE_RETURN_STRINGL(obj->MxMem + start, length); +} +/* }}} */ + + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_sharedmemory___construct, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, size) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_sharedmemory_first, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_sharedmemory_size, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_sharedmemory_write, 0, 0, 1) + ZEND_ARG_INFO(0, string) + ZEND_ARG_INFO(0, start) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sync_sharedmemory_read, 0, 0, 0) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, length) +ZEND_END_ARG_INFO() + +static const zend_function_entry sync_SharedMemory_methods[] = { + PHP_ME(sync_SharedMemory, __construct, arginfo_sync_sharedmemory___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(sync_SharedMemory, first, arginfo_sync_sharedmemory_first, ZEND_ACC_PUBLIC) + PHP_ME(sync_SharedMemory, size, arginfo_sync_sharedmemory_size, ZEND_ACC_PUBLIC) + PHP_ME(sync_SharedMemory, write, arginfo_sync_sharedmemory_write, ZEND_ACC_PUBLIC) + PHP_ME(sync_SharedMemory, read, arginfo_sync_sharedmemory_read, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + + /* {{{ PHP_MINIT_FUNCTION(sync) */ PHP_MINIT_FUNCTION(sync) { zend_class_entry ce; + /* Mutex */ + memcpy(&sync_Mutex_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + sync_Mutex_object_handlers.clone_obj = NULL; +#if PHP_MAJOR_VERSION >= 7 + sync_Mutex_object_handlers.offset = XtOffsetOf(sync_Mutex_object, PORTABLE_default_zend_object_name); + sync_Mutex_object_handlers.free_obj = sync_Mutex_free_object; +#endif + 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); + + /* Semaphore */ + memcpy(&sync_Semaphore_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + sync_Semaphore_object_handlers.clone_obj = NULL; +#if PHP_MAJOR_VERSION >= 7 + sync_Semaphore_object_handlers.offset = XtOffsetOf(sync_Semaphore_object, PORTABLE_default_zend_object_name); + sync_Semaphore_object_handlers.free_obj = sync_Semaphore_free_object; +#endif + 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); + + /* Event */ + memcpy(&sync_Event_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + sync_Event_object_handlers.clone_obj = NULL; +#if PHP_MAJOR_VERSION >= 7 + sync_Event_object_handlers.offset = XtOffsetOf(sync_Event_object, PORTABLE_default_zend_object_name); + sync_Event_object_handlers.free_obj = sync_Event_free_object; +#endif + 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); + + /* Reader-Writer */ + memcpy(&sync_ReaderWriter_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + sync_ReaderWriter_object_handlers.clone_obj = NULL; +#if PHP_MAJOR_VERSION >= 7 + sync_ReaderWriter_object_handlers.offset = XtOffsetOf(sync_ReaderWriter_object, PORTABLE_default_zend_object_name); + sync_ReaderWriter_object_handlers.free_obj = sync_ReaderWriter_free_object; +#endif + 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); + + /* Named Shared Memory */ + memcpy(&sync_SharedMemory_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + sync_SharedMemory_object_handlers.clone_obj = NULL; +#if PHP_MAJOR_VERSION >= 7 + sync_SharedMemory_object_handlers.offset = XtOffsetOf(sync_SharedMemory_object, PORTABLE_default_zend_object_name); + sync_SharedMemory_object_handlers.free_obj = sync_SharedMemory_free_object; +#endif + + INIT_CLASS_ENTRY(ce, "SyncSharedMemory", sync_SharedMemory_methods); + ce.create_object = sync_SharedMemory_create_object; + sync_SharedMemory_ce = zend_register_internal_class(&ce TSRMLS_CC); + return SUCCESS; } /* }}} */ diff --git a/tests/003.phpt b/tests/003.phpt index 6cffb74..77c6ad2 100644 --- a/tests/003.phpt +++ b/tests/003.phpt @@ -4,7 +4,7 @@ SyncMutex - named mutex allocation, locking, and unlocking. --FILE-- lock(0)); var_dump($mutex->lock(0)); diff --git a/tests/006.phpt b/tests/006.phpt index a59f0aa..1c3abf5 100644 --- a/tests/006.phpt +++ b/tests/006.phpt @@ -4,7 +4,7 @@ SyncSemaphore (1) - named semaphore allocation, locking, and unlocking. --FILE-- lock()); var_dump($semaphore->unlock()); diff --git a/tests/007.phpt b/tests/007.phpt index a6eb678..204e375 100644 --- a/tests/007.phpt +++ b/tests/007.phpt @@ -4,7 +4,7 @@ SyncSemaphore (2) - named semaphore allocation, locking, and unlocking. --FILE-- lock()); var_dump($semaphore->unlock()); diff --git a/tests/009.phpt b/tests/009.phpt index ac497bf..b683e28 100644 --- a/tests/009.phpt +++ b/tests/009.phpt @@ -4,7 +4,7 @@ SyncEvent - named automatic event object allocation and firing. --FILE-- wait(0)); var_dump($event->fire()); diff --git a/tests/011.phpt b/tests/011.phpt index c39249a..b0a4f32 100644 --- a/tests/011.phpt +++ b/tests/011.phpt @@ -4,7 +4,7 @@ SyncEvent - named manual event object allocation and firing. --FILE-- wait(0)); var_dump($event->fire()); diff --git a/tests/013.phpt b/tests/013.phpt index 7de446a..47312ed 100644 --- a/tests/013.phpt +++ b/tests/013.phpt @@ -4,7 +4,7 @@ SyncReaderWriter - named reader-writer allocation, locking, and unlocking. --FILE-- readlock(0)); var_dump($readwrite->readlock(0)); diff --git a/tests/014.phpt b/tests/014.phpt index 9634035..b41518d 100644 --- a/tests/014.phpt +++ b/tests/014.phpt @@ -4,7 +4,7 @@ SyncReaderWriter - named reader-writer allocation, locking, and unlocking freeze --FILE-- readlock(0)); var_dump($readwrite->readlock(0)); diff --git a/tests/015.phpt b/tests/015.phpt new file mode 100644 index 0000000..646cf3a --- /dev/null +++ b/tests/015.phpt @@ -0,0 +1,18 @@ +--TEST-- +SyncSharedMemory - named shared memory allocation, size, reading, and writing test. +--SKIPIF-- + +--FILE-- +first()); + var_dump($shm->size()); + var_dump($shm->write("Everything is awesome.", 1)); + var_dump($shm->read(1, 22)); +?> +--EXPECT-- +bool(true) +int(150) +int(22) +string(22) "Everything is awesome." diff --git a/tests/016.phpt b/tests/016.phpt new file mode 100644 index 0000000..fc83132 --- /dev/null +++ b/tests/016.phpt @@ -0,0 +1,42 @@ +--TEST-- +SyncSharedMemory - named shared memory allocation reuse test. +--SKIPIF-- + +--FILE-- +first()); + var_dump($shm->size()); + var_dump($shm->write("Everything is awesome.", 1)); + var_dump($shm->read(1, 22)); + + $shm2 = new SyncSharedMemory("Awesome_" . PHP_INT_SIZE, 150); + + var_dump($shm2->first()); + var_dump($shm2->size()); + var_dump($shm2->read(1, 22)); + + unset($shm2); + unset($shm); + + // Should be brand new. + $shm = new SyncSharedMemory("Awesome_" . PHP_INT_SIZE, 150); + + var_dump($shm->first()); + var_dump($shm->size()); + var_dump($shm->write("Everything is awesome.", 1)); + var_dump($shm->read(1, 22)); +?> +--EXPECT-- +bool(true) +int(150) +int(22) +string(22) "Everything is awesome." +bool(false) +int(150) +string(22) "Everything is awesome." +bool(true) +int(150) +int(22) +string(22) "Everything is awesome."