Organized modules, added indirection for FFI vs extension loading (#10)

* mv src -> ext, lib -> src to be more in line with PHP module expectations

* Fixed dependency for ffi test

* Fixed path in format-check to C source

* CRT will now try the extension first, then FFI
This commit is contained in:
Justin Boswell
2021-02-16 10:31:42 -08:00
committed by GitHub
parent aee57a929d
commit c30c1486fe
17 changed files with 141 additions and 46 deletions
+3
View File
@@ -0,0 +1,3 @@
api.h
pkgconfig/
*.so*
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace AWS\CRT;
use AWS\CRT\Internal\Extension;
use AWS\CRT\Internal\FFI;
use \RuntimeException;
/**
* Wrapper for the interface to the CRT. There only ever needs to be one of these, but
* additional instances won't cost anything other than their memory.
* Creating an instance of any NativeResource will activate the CRT binding. User code
* should only need to create one of these if they are only accessing CRT:: static functions.
*/
final class CRT {
private static $impl = null;
private static $refcount = 0;
function __construct() {
if (is_null(self::$impl)) {
try {
self::$impl = new Extension();
} catch (RuntimeException $rex) {
try {
self::$impl = new FFI();
} catch (RuntimeException $frex) {
throw new RuntimeException('Unable to initialize AWS CRT via extension or FFI', -1, $frex);
}
}
}
++self::$refcount;
}
function __destruct() {
if (--self::$refcount == 0) {
self::$impl = null;
}
}
/**
* @return integer last error code reported within the CRT
*/
public static function last_error() {
return self::$impl->aws_crt_last_error();
}
/**
* @param integer $error Error code from the CRT, usually delivered via callback or {@see last_error}
* @return string Human-readable description of the provided error code
*/
public static function error_str($error) {
return self::$impl->aws_crt_error_str((int) $error);
}
/**
* @param integer $error Error code from the CRT, usually delivered via callback or {@see last_error}
* @return string Name/enum identifier for the provided error code
*/
public static function error_name($error) {
return self::$impl->aws_crt_error_name((int) $error);
}
function event_loop_group_new($num_threads) {
return self::$impl->aws_crt_event_loop_group_new($num_threads);
}
function event_loop_group_release($elg) {
return self::$impl->aws_crt_event_loop_group_release($elg);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace AWS\CRT\IO;
use AWS\CRT\NativeResource as NativeResource;
/**
* Represents 1 or more event loops (1 per thread) for doing I/O and background tasks.
* Typically, every application has one EventLoopGroup.
*
* @param array options:
* - int num_threads - Number of worker threads in the EventLoopGroup. Defaults to 0/1 per logical core.
*/
final class EventLoopGroup extends NativeResource {
static function defaults() {
return array(
'num_threads' => 0,
);
}
function __construct(array $options = array()) {
parent::__construct();
if (count($options) == 0) {
$options = self::defaults();
}
$this->acquire(self::$crt->event_loop_group_new($options['num_threads']));
}
function __destruct() {
self::$crt->event_loop_group_release($this->release());
parent::__destruct();
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace AWS\CRT\Internal;
use \RuntimeException;
/**
* @internal
* Forwards calls on to awscrt PHP extension functions
*/
final class Extension {
function __construct() {
if (!extension_loaded('awscrt')) {
throw new RuntimeException('awscrt extension is not loaded');
}
}
/**
* Forwards any call made on this object to the extension function of the
* same name with the supplied arguments. Argument type hinting and checking
* occurs at the CRT wrapper.
*/
function __call(string $name, $args) {
return call_user_func_array($name, $args);
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace AWS\CRT\Internal;
use \Exception;
use \RuntimeException;
/**
* @internal
* Forwards calls on to libaws-crt-ffi via FFI
*/
final class FFI {
private static $ffi = null;
private static $refcount = 0;
function __construct() {
if (is_null(self::$ffi)) {
try {
self::$ffi = \FFI::cdef(file_get_contents(__DIR__ . "/../../../api.h"), __DIR__ . "/../../../libaws-crt-ffi.so");
self::init();
} catch (Exception $e) {
throw new RuntimeException('Exception while initializing CRT via FFI', 0, $e);
}
}
++self::$refcount;
}
function __destruct() {
if (--self::$refcount == 0) {
self::clean_up();
self::$ffi = null;
}
}
/**
* Forwards any call made on this object to the FFI function of the
* same name with the supplied arguments. Argument type hinting and checking
* occurs at the CRT wrapper.
*/
function __call(string $name, $args) {
return call_user_func_array(array(self::$ffi, $name), $args);
}
private static function init() {
return self::$ffi->aws_crt_init();
}
private static function clean_up() {
return self::$ffi->aws_crt_clean_up();
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace AWS\CRT;
use AWS\CRT\CRT as CRT;
/**
* Base class for all native resources, tracks all outstanding resources
* and provides basic leak checking
*/
abstract class NativeResource {
protected static $crt = null;
protected static $resources = [];
protected $native = null;
function __construct() {
if (is_null(self::$crt)) {
self::$crt = new CRT();
}
self::$resources[spl_object_hash($this)] = 1;
}
protected function acquire($handle) {
$this->native = $handle;
}
protected function release() {
$native = $this->native;
$this->native = null;
return $native;
}
function __destruct() {
// Should have been destroyed and released by derived resource
assert($this->native == null);
unset(self::$resources[spl_object_hash($this)]);
}
}
-73
View File
@@ -1,73 +0,0 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include "php_aws_crt.h"
ZEND_DECLARE_MODULE_GLOBALS(awscrt);
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY(
"awscrt.log_level",
"",
PHP_INI_ALL,
OnUpdateLongGEZero,
log_level,
zend_awscrt_globals,
awscrt_globals)
PHP_INI_END()
static PHP_MINIT_FUNCTION(awscrt) {
REGISTER_INI_ENTRIES();
return SUCCESS;
}
static PHP_MSHUTDOWN_FUNCTION(awscrt) {
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
static PHP_GINIT_FUNCTION(awscrt) {
#if defined(COMPILE_DL_ASTKIT) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
awscrt_globals->log_level = 0;
}
ZEND_BEGIN_ARG_INFO(awscrt_version_arginfo, 0)
ZEND_END_ARG_INFO()
PHP_FUNCTION(awscrt_version) {
static const char *version = "1.0.0-dev";
AWS_RETURN_STRING(version);
}
/* clang-format off */
const zend_function_entry awscrt_functions[] = {
PHP_FE(awscrt_version, awscrt_version_arginfo)
PHP_FE_END
};
zend_module_entry awscrt_module_entry = {
STANDARD_MODULE_HEADER,
"awscrt",
awscrt_functions, /* functions */
PHP_MINIT(awscrt),
PHP_MSHUTDOWN(awscrt),
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
NO_VERSION_YET,
PHP_MODULE_GLOBALS(awscrt),
PHP_GINIT(awscrt),
NULL, /* GSHUTDOWN */
NULL, /* RPOSTSHUTDOWN */
STANDARD_MODULE_PROPERTIES_EX};
/* clang-format on */
#ifdef COMPILE_DL_AWSCRT
ZEND_GET_MODULE(awscrt)
#endif
+2
View File
@@ -0,0 +1,2 @@
ffi.enable=preload
opcache.preload=preload.php
-39
View File
@@ -1,39 +0,0 @@
#ifndef PHP_AWS_CRT_H
#define PHP_AWS_CRT_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */
#if ZEND_EXTENSION_API_NO < 220131226
# error "PHP >= 5.6 is required"
#endif
#define AWS_PHP_AT_LEAST_7 defined(ZEND_EXTENSION_API_NO_7_0_X) || (ZEND_EXTENSION_API_NO >= 320151012)
ZEND_BEGIN_MODULE_GLOBALS(awscrt)
long log_level;
ZEND_END_MODULE_GLOBALS(awscrt)
ZEND_EXTERN_MODULE_GLOBALS(awscrt)
#define AWSCRT_GLOBAL(v) ZEND_MODULE_GLOBALS_ACCESSOR(awscrt, v)
/* PHP 7 removed the string duplicate parameter */
#if AWS_PHP_AT_LEAST_7
# define AWS_RETURN_STRING(s) RETURN_STRING(s)
#else
# define AWS_RETURN_STRING(s) RETURN_STRING(s, 1)
#endif
#endif /* PHP_AWS_CRT_H */
+26
View File
@@ -0,0 +1,26 @@
<?php
use AWS\CRT\CRT;
final class ErrorTest extends PHPUnit_Framework_TestCase {
private static $crt = null;
public static function setUpBeforeClass() {
self::$crt = new CRT();
}
public static function tearDownAfterClass() {
self::$crt = null;
}
public function testNoInitialError() {
$this->assertEquals(0, CRT::last_error());
}
public function testCanResolveErrorName() {
$this->assertEquals("AWS_ERROR_SUCCESS", CRT::error_name(0));
}
public function testCanResolveErrorStr() {
$this->assertEquals("Success.", CRT::error_str(0));
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
use AWS\CRT\IO\EventLoopGroup as EventLoopGroup;
final class EventLoopGroupTest extends PHPUnit_Framework_TestCase {
public function testLifetime() {
$elg = new EventLoopGroup();
$this->assertNotNull($elg, "Failed to create default EventLoopGroup");
$elg = null;
}
public function testConstructionWithOptions() {
$options = EventLoopGroup::defaults();
$options['num_threads'] = 1;
$elg = new EventLoopGroup($options);
$this->assertNotNull($elg, "Failed to create EventLoopGroup with 1 thread");
$elg = null;
}
}