public function testLockAndRelease() { $sm = new SharedMemory(); $sm['test1'] = 'value1'; $sm->lock(); $this->assertEquals('value1', $sm['test1']); $sm['test2'] = $sm['test1'] . "+value2"; $sm->release(); $this->assertEquals('value1+value2', $sm['test2']); }
public function Instance() { if (!isset(self::$mrInstance)) { self::$mrInstance = new SharedMemory(); } return self::$mrInstance; }
/** * Ensures that we can put objects of various types in shared memory and * retrieve them unchanged (between processes). */ public function testInterProcessUsage() { $mem = new SharedMemory(self::$_mutex, SharedMemory::getRequiredBytes(array_values(self::$_testValues))); $bootstrapFile = realpath(__DIR__ . '/../classLoader.inc.php'); $this->assertNotFalse($bootstrapFile); $cmd = 'env phpunit --no-configuration --bootstrap=' . $bootstrapFile . ' --filter=testPutValues ' . __FILE__ . ' > /dev/null'; pclose(popen($cmd, 'r')); $this->_runTests($mem); }
function Load($configFilename = '') { if (!class_exists('SharedMemory')) { require_once Configuration::Instance()->GetValue('application', 'gtfw_base') . 'main/lib/gtfw/shared_memory/SharedMemory.class.php'; } if (SharedMemory::Instance()->VarIsSet($configFilename)) { $var_name = basename($configFilename, $this->mConfigFilenameBase); $res = array($var_name, SharedMemory::Instance()->Get($configFilename)); return $res; } else { return array(false, false); } }
/** * Determines information about the run environment and creates the shared * memory segment (an actual SysV shared memory segment on Unix, otherwise * a file). * * @param Mutex $mutex * @param int $segSize */ public function __construct(Mutex $mutex, $segSize = null) { $this->_mutex = $mutex; if (self::$_useSysV === null) { self::$_useSysV = $this->_mutex->hasSysV(); if (!self::$_useSysV) { self::$_shmFileBase = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'shm.{SEGMENT_KEY}.tmp'; self::$_UNIT_SEPARATOR = chr(31); self::$_RECORD_SEPARATOR = chr(30); } self::$_activeShmResources = new SplQueue(); } // We share the segment key with the mutex that governs access to it $this->_segmentKey = $this->_mutex->getLockID(); if (!$segSize) { // Default to 10000 bytes $segSize = 10000; } else { /* Otherwise, allocate the extra space we'll need to store the attached process count and variable count. Assume a three-digit number of each (should be way more than enough). */ $segSize += self::getRequiredBytes(array('100000')); } if (self::$_useSysV) { $this->_shmResource = shm_attach($this->_segmentKey, $segSize); } else { $this->_shmFile = str_replace('{SEGMENT_KEY}', $this->_segmentKey, self::$_shmFileBase); $this->_shmResource = fopen($this->_shmFile, 'a+b'); } if (!$this->_shmResource) { throw new SharedMemoryException('Failed to obtain shared memory resource.'); } /* Add the resource to the queue before the file name (if any); that way if we need to clean up, we can iterate through the queue and know that we will have closed any open file handles before deleting the corresponding file. */ self::$_activeShmResources->enqueue($this->_shmResource); if ($this->_shmFile) { self::$_activeShmResources->enqueue($this->_shmFile); } /* Keep track of the number of processes (or rather, the number of SharedMemory instances across all processes) attached to this memory segment so we can remove it when they all disappear. */ /* This is somewhat of a misnomer; we're not counting processes, we are counting instances across all processes. */ $this->addToVar('_proccount', 1); if (!$this->hasVar('_varcount')) { $this->putVar('_varcount', 0); } }
/** * Get any exceptions thrown by the threads. * * @return \Throwable[] */ public function getExceptions() : array { return $this->memory->getExceptions(); }
/** * Handles certain setup tasks. This is the kind of thing that a * constructor would handle, but since this is an abstract class and I want * child classes to be free to accept any arguments they want, I'm not * using a constructor here. */ private function _setupInstance() { if (!self::$_testedGenericSettings) { \PFXUtils::validateSettings(self::$_SETTINGS, self::$_SETTING_TESTS); self::_buildDefaultHTTPActionMap(); self::$_validator = new \Validator(); } /* If there is an exception type configured, validate that it really exists. Otherwise default to RuntimeException. */ if ($this->_EXCEPTION_TYPE) { if (!class_exists($this->_EXCEPTION_TYPE) || !is_a($this->_EXCEPTION_TYPE, 'Exception', true)) { throw new \Exception('Exception types registered in descendants of this ' . 'class must be valid Exception subclasses.'); } } else { $this->_EXCEPTION_TYPE = 'RuntimeException'; } if ($this->_responseFormat && array_key_exists($this->_responseFormat, self::$_STANDARD_PARSE_CALLBACKS)) { $callbackData = self::$_STANDARD_PARSE_CALLBACKS[$this->_responseFormat]; $callback = array_shift($callbackData); $this->_registerParseCallback($callback, $callbackData); } self::$_validator->number($this->_responseTries, 'The number of attempts to use when connecting to API URLs ' . 'must be a non-zero integer.', \Validator::ASSERT_INT_DEFAULT); self::$_validator->number($this->_repeatPauseInterval, 'The number of seconds to wait before repeating an API ' . 'request must be a positive integer.', \Validator::ASSERT_INT | \Validator::ASSERT_POSITIVE); if ($this->_requestDelayInterval !== null) { self::$_validator->number($this->_requestDelayInterval, 'Delay intervals must be positive integers.', \Validator::ASSERT_INT | \Validator::ASSERT_POSITIVE); /* In order to observe this delay across processes, we need to have a mutex and use shared memory. */ try { if (!$this->_mutex) { $this->_mutex = new \Mutex($this); } /* We will be keeping track of a millisecond timestamp, which means 13 digits. */ $allocBytes = \SharedMemory::getRequiredBytes(array(1000000000000)); $this->_shmSegment = new \SharedMemory($this->_mutex, $allocBytes); } catch (\Exception $e) { throw new $this->_EXCEPTION_TYPE('Caught error while configuring shared memory segment.', null, $e); } /* Not only do we need to keep track of the delay interval, but we also need a variable name that reflects the specific subclass for which we are tracking this interval. */ $this->_lastRequestTimeVarName = 'req' . get_class($this); } foreach (self::$_HTTP_RESPONSE_DEFAULT_MAP as $code => $action) { /* If the child has already registered certain actions in any of these positions, skip them. */ if (!array_key_exists($code, $this->_httpResponseActionMap)) { $this->_httpResponseActionMap[$code] = $action; } } $this->_instanceSetupComplete = true; }