diff options
Diffstat (limited to 'src/php')
-rw-r--r-- | src/php/ext/grpc/call.c | 33 | ||||
-rw-r--r-- | src/php/ext/grpc/call.h | 3 | ||||
-rw-r--r-- | src/php/ext/grpc/call_credentials.c | 11 | ||||
-rw-r--r-- | src/php/ext/grpc/channel.c | 135 | ||||
-rwxr-xr-x | src/php/ext/grpc/channel.h | 5 | ||||
-rw-r--r-- | src/php/ext/grpc/channel_credentials.c | 14 | ||||
-rw-r--r-- | src/php/ext/grpc/php7_wrapper.h | 2 | ||||
-rw-r--r-- | src/php/ext/grpc/php_grpc.c | 3 | ||||
-rw-r--r-- | src/php/lib/Grpc/BaseStub.php | 25 | ||||
-rw-r--r-- | src/php/tests/unit_tests/ChannelTest.php | 5 |
10 files changed, 183 insertions, 53 deletions
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index c4997f720d..b802f04f53 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -99,6 +99,7 @@ zval *grpc_parse_metadata_array(grpc_metadata_array 1 TSRMLS_CC); efree(str_key); efree(str_val); + PHP_GRPC_FREE_STD_ZVAL(array); return NULL; } php_grpc_add_next_index_stringl(data, str_val, @@ -127,10 +128,12 @@ bool create_metadata_array(zval *array, grpc_metadata_array *metadata) { HashTable *inner_array_hash; zval *value; zval *inner_array; + grpc_metadata_array_init(metadata); + metadata->count = 0; + metadata->metadata = NULL; if (Z_TYPE_P(array) != IS_ARRAY) { return false; } - grpc_metadata_array_init(metadata); array_hash = Z_ARRVAL_P(array); char *key; @@ -174,6 +177,18 @@ bool create_metadata_array(zval *array, grpc_metadata_array *metadata) { return true; } +void grpc_php_metadata_array_destroy_including_entries( + grpc_metadata_array* array) { + size_t i; + if (array->metadata) { + for (i = 0; i < array->count; i++) { + grpc_slice_unref(array->metadata[i].key); + grpc_slice_unref(array->metadata[i].value); + } + } + grpc_metadata_array_destroy(array); +} + /* Wraps a grpc_call struct in a PHP object. Owned indicates whether the struct should be destroyed at the end of the object's lifecycle */ zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned TSRMLS_DC) { @@ -301,6 +316,11 @@ PHP_METHOD(Call, startBatch) { "batch keys must be integers", 1 TSRMLS_CC); goto cleanup; } + + ops[op_num].op = (grpc_op_type)index; + ops[op_num].flags = 0; + ops[op_num].reserved = NULL; + switch(index) { case GRPC_OP_SEND_INITIAL_METADATA: if (!create_metadata_array(value, &metadata)) { @@ -414,9 +434,6 @@ PHP_METHOD(Call, startBatch) { "Unrecognized key in batch", 1 TSRMLS_CC); goto cleanup; } - ops[op_num].op = (grpc_op_type)index; - ops[op_num].flags = 0; - ops[op_num].reserved = NULL; op_num++; PHP_GRPC_HASH_FOREACH_END() @@ -502,8 +519,8 @@ PHP_METHOD(Call, startBatch) { } cleanup: - grpc_metadata_array_destroy(&metadata); - grpc_metadata_array_destroy(&trailing_metadata); + grpc_php_metadata_array_destroy_including_entries(&metadata); + grpc_php_metadata_array_destroy_including_entries(&trailing_metadata); grpc_metadata_array_destroy(&recv_metadata); grpc_metadata_array_destroy(&recv_trailing_metadata); grpc_slice_unref(recv_status_details); @@ -526,7 +543,9 @@ cleanup: */ PHP_METHOD(Call, getPeer) { wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); - PHP_GRPC_RETURN_STRING(grpc_call_get_peer(call->wrapped), 1); + char *peer = grpc_call_get_peer(call->wrapped); + PHP_GRPC_RETVAL_STRING(peer, 1); + gpr_free(peer); } /** diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h index 5bde5d5390..104ac301c1 100644 --- a/src/php/ext/grpc/call.h +++ b/src/php/ext/grpc/call.h @@ -69,5 +69,6 @@ void grpc_init_call(TSRMLS_D); /* Populates a grpc_metadata_array with the data in a PHP array object. Returns true on success and false on failure */ bool create_metadata_array(zval *array, grpc_metadata_array *metadata); - +void grpc_php_metadata_array_destroy_including_entries( + grpc_metadata_array* array); #endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */ diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c index a395d53614..41c488a79c 100644 --- a/src/php/ext/grpc/call_credentials.c +++ b/src/php/ext/grpc/call_credentials.c @@ -120,6 +120,8 @@ PHP_METHOD(CallCredentials, createFromPlugin) { fci->params, fci->param_count) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "createFromPlugin expects 1 callback", 1 TSRMLS_CC); + free(fci); + free(fci_cache); return; } @@ -183,15 +185,17 @@ int plugin_get_metadata( *status = GRPC_STATUS_OK; *error_details = NULL; + bool should_return = false; grpc_metadata_array metadata; if (retval == NULL || Z_TYPE_P(retval) != IS_ARRAY) { *status = GRPC_STATUS_INVALID_ARGUMENT; - return true; // Synchronous return. + should_return = true; // Synchronous return. } if (!create_metadata_array(retval, &metadata)) { *status = GRPC_STATUS_INVALID_ARGUMENT; - return true; // Synchronous return. + should_return = true; // Synchronous return. + grpc_php_metadata_array_destroy_including_entries(&metadata); } if (retval != NULL) { @@ -204,6 +208,9 @@ int plugin_get_metadata( PHP_GRPC_FREE_STD_ZVAL(retval); #endif } + if (should_return) { + return true; + } if (metadata.count > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) { *status = GRPC_STATUS_INTERNAL; diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index db59869c7f..4054723b43 100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -41,6 +41,7 @@ #include <grpc/grpc.h> #include <grpc/grpc_security.h> +#include <grpc/support/alloc.h> #include "completion_queue.h" #include "channel_credentials.h" @@ -56,22 +57,63 @@ int le_plink; /* Frees and destroys an instance of wrapped_grpc_channel */ PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel) + bool is_last_wrapper = false; + // In_persistent_list is used when the user don't close the channel. + // In this case, le in the list won't be freed. + bool in_persistent_list = true; if (p->wrapper != NULL) { gpr_mu_lock(&p->wrapper->mu); if (p->wrapper->wrapped != NULL) { - php_grpc_zend_resource *rsrc; - php_grpc_int key_len = strlen(p->wrapper->key); - // only destroy the channel here if not found in the persistent list - gpr_mu_lock(&global_persistent_list_mu); - if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key, - key_len, rsrc))) { - grpc_channel_destroy(p->wrapper->wrapped); - free(p->wrapper->target); - free(p->wrapper->args_hashstr); + if (p->wrapper->is_valid) { + php_grpc_zend_resource *rsrc; + php_grpc_int key_len = strlen(p->wrapper->key); + // only destroy the channel here if not found in the persistent list + gpr_mu_lock(&global_persistent_list_mu); + if (!(PHP_GRPC_PERSISTENT_LIST_FIND(&EG(persistent_list), p->wrapper->key, + key_len, rsrc))) { + in_persistent_list = false; + grpc_channel_destroy(p->wrapper->wrapped); + free(p->wrapper->target); + free(p->wrapper->args_hashstr); + if(p->wrapper->creds_hashstr != NULL){ + free(p->wrapper->creds_hashstr); + p->wrapper->creds_hashstr = NULL; + } + } + gpr_mu_unlock(&global_persistent_list_mu); } - gpr_mu_unlock(&global_persistent_list_mu); + } + p->wrapper->ref_count -= 1; + if (p->wrapper->ref_count == 0) { + is_last_wrapper = true; } gpr_mu_unlock(&p->wrapper->mu); + if (is_last_wrapper) { + if (in_persistent_list) { + // If ref_count==0 and the key still in the list, it means the user + // don't call channel->close().persistent list should free the + // allocation in such case, as well as related wrapped channel. + if (p->wrapper->wrapped != NULL) { + gpr_mu_lock(&p->wrapper->mu); + grpc_channel_destroy(p->wrapper->wrapped); + free(p->wrapper->target); + free(p->wrapper->args_hashstr); + if(p->wrapper->creds_hashstr != NULL){ + free(p->wrapper->creds_hashstr); + p->wrapper->creds_hashstr = NULL; + } + p->wrapper->wrapped = NULL; + php_grpc_delete_persistent_list_entry(p->wrapper->key, + strlen(p->wrapper->key) + TSRMLS_CC); + gpr_mu_unlock(&p->wrapper->mu); + } + } + gpr_mu_destroy(&p->wrapper->mu); + free(p->wrapper->key); + free(p->wrapper); + } + p->wrapper = NULL; } PHP_GRPC_FREE_WRAPPED_FUNC_END() @@ -276,9 +318,16 @@ PHP_METHOD(Channel, __construct) { channel->wrapper->key = key; channel->wrapper->target = strdup(target); channel->wrapper->args_hashstr = strdup(sha1str); + channel->wrapper->creds_hashstr = NULL; + channel->wrapper->ref_count = 1; + channel->wrapper->is_valid = true; if (creds != NULL && creds->hashstr != NULL) { - channel->wrapper->creds_hashstr = creds->hashstr; + php_grpc_int creds_hashstr_len = strlen(creds->hashstr); + char *channel_creds_hashstr = malloc(creds_hashstr_len + 1); + strcpy(channel_creds_hashstr, creds->hashstr); + channel->wrapper->creds_hashstr = channel_creds_hashstr; } + gpr_mu_init(&channel->wrapper->mu); smart_str_free(&buf); @@ -303,7 +352,17 @@ PHP_METHOD(Channel, __construct) { channel, target, args, creds, key, key_len TSRMLS_CC); } else { efree(args.args); + if (channel->wrapper->creds_hashstr != NULL){ + free(channel->wrapper->creds_hashstr); + channel->wrapper->creds_hashstr = NULL; + } + free(channel->wrapper->creds_hashstr); + free(channel->wrapper->key); + free(channel->wrapper->target); + free(channel->wrapper->args_hashstr); + free(channel->wrapper); channel->wrapper = le->channel; + channel->wrapper->ref_count += 1; } } } @@ -323,7 +382,8 @@ PHP_METHOD(Channel, getTarget) { } char *target = grpc_channel_get_target(channel->wrapper->wrapped); gpr_mu_unlock(&channel->wrapper->mu); - PHP_GRPC_RETURN_STRING(target, 1); + PHP_GRPC_RETVAL_STRING(target, 1); + gpr_free(target); } /** @@ -411,18 +471,46 @@ PHP_METHOD(Channel, watchConnectivityState) { */ PHP_METHOD(Channel, close) { wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); - gpr_mu_lock(&channel->wrapper->mu); - if (channel->wrapper->wrapped != NULL) { - grpc_channel_destroy(channel->wrapper->wrapped); - free(channel->wrapper->target); - free(channel->wrapper->args_hashstr); - channel->wrapper->wrapped = NULL; - - php_grpc_delete_persistent_list_entry(channel->wrapper->key, - strlen(channel->wrapper->key) - TSRMLS_CC); + bool is_last_wrapper = false; + if (channel->wrapper != NULL) { + // Channel_wrapper hasn't call close before. + gpr_mu_lock(&channel->wrapper->mu); + if (channel->wrapper->wrapped != NULL) { + if (channel->wrapper->is_valid) { + // Wrapped channel hasn't been destoryed by other wrapper. + grpc_channel_destroy(channel->wrapper->wrapped); + free(channel->wrapper->target); + free(channel->wrapper->args_hashstr); + if(channel->wrapper->creds_hashstr != NULL){ + free(channel->wrapper->creds_hashstr); + channel->wrapper->creds_hashstr = NULL; + } + channel->wrapper->wrapped = NULL; + channel->wrapper->is_valid = false; + + php_grpc_delete_persistent_list_entry(channel->wrapper->key, + strlen(channel->wrapper->key) + TSRMLS_CC); + } + } + channel->wrapper->ref_count -= 1; + if(channel->wrapper->ref_count == 0){ + // Mark that the wrapper can be freed because mu should be + // destroyed outside the lock. + is_last_wrapper = true; + } + gpr_mu_unlock(&channel->wrapper->mu); } - gpr_mu_unlock(&channel->wrapper->mu); + gpr_mu_lock(&global_persistent_list_mu); + if (is_last_wrapper) { + gpr_mu_destroy(&channel->wrapper->mu); + free(channel->wrapper->key); + free(channel->wrapper); + } + // Set channel->wrapper to NULL to avoid call close twice for the same + // channel. + channel->wrapper = NULL; + gpr_mu_unlock(&global_persistent_list_mu); } // Delete an entry from the persistent list @@ -437,6 +525,7 @@ void php_grpc_delete_persistent_list_entry(char *key, php_grpc_int key_len le = (channel_persistent_le_t *)rsrc->ptr; le->channel = NULL; php_grpc_zend_hash_del(&EG(persistent_list), key, key_len+1); + free(le); } gpr_mu_unlock(&global_persistent_list_mu); } diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h index 69adc4782c..86bfdea51a 100755 --- a/src/php/ext/grpc/channel.h +++ b/src/php/ext/grpc/channel.h @@ -40,6 +40,11 @@ typedef struct _grpc_channel_wrapper { char *args_hashstr; char *creds_hashstr; gpr_mu mu; + // is_valid is used to check the wrapped channel has been freed + // before to avoid double free. + bool is_valid; + // ref_count is used to let the last wrapper free related channel and key. + size_t ref_count; } grpc_channel_wrapper; /* Wrapper struct for grpc_channel that can be associated with a PHP object */ diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c index d120d6e90f..624d7cc75c 100644 --- a/src/php/ext/grpc/channel_credentials.c +++ b/src/php/ext/grpc/channel_credentials.c @@ -57,8 +57,13 @@ static grpc_ssl_roots_override_result get_ssl_roots_override( /* Frees and destroys an instance of wrapped_grpc_channel_credentials */ PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_channel_credentials) + if (p->hashstr != NULL) { + free(p->hashstr); + p->hashstr = NULL; + } if (p->wrapped != NULL) { grpc_channel_credentials_release(p->wrapped); + p->wrapped = NULL; } PHP_GRPC_FREE_WRAPPED_FUNC_END() @@ -152,7 +157,7 @@ PHP_METHOD(ChannelCredentials, createSsl) { } php_grpc_int hashkey_len = root_certs_length + cert_chain_length; - char *hashkey = emalloc(hashkey_len); + char *hashkey = emalloc(hashkey_len + 1); if (root_certs_length > 0) { strcpy(hashkey, pem_root_certs); } @@ -199,8 +204,13 @@ PHP_METHOD(ChannelCredentials, createComposite) { grpc_channel_credentials *creds = grpc_composite_channel_credentials_create(cred1->wrapped, cred2->wrapped, NULL); + // wrapped_grpc_channel_credentials object should keeps it's own + // allocation. Otherwise it conflicts free hashstr with call.c. + php_grpc_int cred1_len = strlen(cred1->hashstr); + char *cred1_hashstr = malloc(cred1_len+1); + strcpy(cred1_hashstr, cred1->hashstr); zval *creds_object = - grpc_php_wrap_channel_credentials(creds, cred1->hashstr, true + grpc_php_wrap_channel_credentials(creds, cred1_hashstr, true TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); } diff --git a/src/php/ext/grpc/php7_wrapper.h b/src/php/ext/grpc/php7_wrapper.h index 96091f9dad..2f4a53611c 100644 --- a/src/php/ext/grpc/php7_wrapper.h +++ b/src/php/ext/grpc/php7_wrapper.h @@ -33,6 +33,7 @@ #define php_grpc_add_next_index_stringl(data, str, len, b) \ add_next_index_stringl(data, str, len, b) +#define PHP_GRPC_RETVAL_STRING(val, dup) RETVAL_STRING(val, dup) #define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val, dup) #define PHP_GRPC_MAKE_STD_ZVAL(pzv) MAKE_STD_ZVAL(pzv) #define PHP_GRPC_FREE_STD_ZVAL(pzv) @@ -145,6 +146,7 @@ static inline int php_grpc_zend_hash_find(HashTable *ht, char *key, int len, #define php_grpc_add_next_index_stringl(data, str, len, b) \ add_next_index_stringl(data, str, len) +#define PHP_GRPC_RETVAL_STRING(val, dup) RETVAL_STRING(val) #define PHP_GRPC_RETURN_STRING(val, dup) RETURN_STRING(val) #define PHP_GRPC_MAKE_STD_ZVAL(pzv) \ pzv = (zval *)emalloc(sizeof(zval)); diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c index 0f2c5b8114..5971babc00 100644 --- a/src/php/ext/grpc/php_grpc.c +++ b/src/php/ext/grpc/php_grpc.c @@ -253,7 +253,8 @@ PHP_MSHUTDOWN_FUNCTION(grpc) { */ PHP_MINFO_FUNCTION(grpc) { php_info_print_table_start(); - php_info_print_table_header(2, "grpc support", "enabled"); + php_info_print_table_row(2, "grpc support", "enabled"); + php_info_print_table_row(2, "grpc module version", PHP_GRPC_VERSION); php_info_print_table_end(); /* Remove comments if you have entries in php.ini diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php index 67378a34a8..5f3a96feaa 100644 --- a/src/php/lib/Grpc/BaseStub.php +++ b/src/php/lib/Grpc/BaseStub.php @@ -54,6 +54,18 @@ class BaseStub } unset($opts['update_metadata']); } + if (!empty($opts['grpc.ssl_target_name_override'])) { + $this->hostname_override = $opts['grpc.ssl_target_name_override']; + } + if ($channel) { + if (!is_a($channel, 'Grpc\Channel')) { + throw new \Exception('The channel argument is not a'. + 'Channel object'); + } + $this->channel = $channel; + return; + } + $package_config = json_decode( file_get_contents(dirname(__FILE__).'/../../composer.json'), true); if (!empty($opts['grpc.primary_user_agent'])) { @@ -61,9 +73,6 @@ class BaseStub } else { $opts['grpc.primary_user_agent'] = ''; } - if (!empty($opts['grpc.ssl_target_name_override'])) { - $this->hostname_override = $opts['grpc.ssl_target_name_override']; - } $opts['grpc.primary_user_agent'] .= 'grpc-php/'.$package_config['version']; if (!array_key_exists('credentials', $opts)) { @@ -71,15 +80,7 @@ class BaseStub 'required. Please see one of the '. 'ChannelCredentials::create methods'); } - if ($channel) { - if (!is_a($channel, 'Grpc\Channel')) { - throw new \Exception('The channel argument is not a'. - 'Channel object'); - } - $this->channel = $channel; - } else { - $this->channel = new Channel($hostname, $opts); - } + $this->channel = new Channel($hostname, $opts); } /** diff --git a/src/php/tests/unit_tests/ChannelTest.php b/src/php/tests/unit_tests/ChannelTest.php index 13a770caff..5baff1fbd9 100644 --- a/src/php/tests/unit_tests/ChannelTest.php +++ b/src/php/tests/unit_tests/ChannelTest.php @@ -380,11 +380,6 @@ class ChannelTest extends PHPUnit_Framework_TestCase // close channel1 $this->channel1->close(); - // channel2 is now in SHUTDOWN state - $state = $this->channel2->getConnectivityState(); - $this->assertEquals(GRPC\CHANNEL_FATAL_FAILURE, $state); - - // calling it again will result in an exception because the // channel is already closed $state = $this->channel2->getConnectivityState(); } |