From b53e5d1f2b3a20299a2b65afffed6958a77ebfb6 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 18 Oct 2016 09:55:28 -0700 Subject: [PATCH 01/30] Create benchmark client and server for Node Express --- package.json | 12 +- .../performance/benchmark_client_express.js | 289 ++++++++++++++++++ src/node/performance/benchmark_server.js | 5 + .../performance/benchmark_server_express.js | 107 +++++++ src/node/performance/worker.js | 10 +- src/node/performance/worker_service_impl.js | 228 +++++++------- .../run_tests/performance/scenario_config.py | 61 +++- tools/run_tests/run_tests.py | 48 +++ 8 files changed, 645 insertions(+), 115 deletions(-) create mode 100644 src/node/performance/benchmark_client_express.js create mode 100644 src/node/performance/benchmark_server_express.js diff --git a/package.json b/package.json index 9afba318162..4827aac3985 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,9 @@ "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test", "install": "./node_modules/.bin/node-pre-gyp install --fallback-to-build" }, - "bundledDependencies": ["node-pre-gyp"], + "bundledDependencies": [ + "node-pre-gyp" + ], "dependencies": { "arguejs": "^0.2.3", "lodash": "^3.9.3", @@ -34,6 +36,7 @@ }, "devDependencies": { "async": "^1.5.0", + "express": "^4.14.0", "google-auth-library": "^0.9.2", "google-protobuf": "^3.0.0", "istanbul": "^0.3.21", @@ -50,11 +53,10 @@ }, "binary": { "module_name": "grpc_node", - "module_path": "./build/Release/", + "module_path": "src/node/extension_binary", "host": "https://storage.googleapis.com/", "remote_path": "grpc-precompiled-binaries/node/{name}/v{version}", - "package_name": "{node_abi}-{platform}-{arch}.tar.gz", - "module_path": "src/node/extension_binary" + "package_name": "{node_abi}-{platform}-{arch}.tar.gz" }, "files": [ "LICENSE", @@ -75,7 +77,7 @@ ], "main": "src/node/index.js", "license": "BSD-3-Clause", - "jshintConfig" : { + "jshintConfig": { "bitwise": true, "curly": true, "eqeqeq": true, diff --git a/src/node/performance/benchmark_client_express.js b/src/node/performance/benchmark_client_express.js new file mode 100644 index 00000000000..15bc1132d29 --- /dev/null +++ b/src/node/performance/benchmark_client_express.js @@ -0,0 +1,289 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * Benchmark client module + * @module + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var util = require('util'); +var EventEmitter = require('events'); +var http = require('http'); +var https = require('https'); + +var async = require('async'); +var _ = require('lodash'); +var PoissonProcess = require('poisson-process'); +var Histogram = require('./histogram'); + +/** + * Convert a time difference, as returned by process.hrtime, to a number of + * nanoseconds. + * @param {Array.} time_diff The time diff, represented as + * [seconds, nanoseconds] + * @return {number} The total number of nanoseconds + */ +function timeDiffToNanos(time_diff) { + return time_diff[0] * 1e9 + time_diff[1]; +} + +function BenchmarkClient(server_targets, channels, histogram_params, + security_params) { + var options = { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + } + }; + var protocol; + if (security_params) { + var ca_path; + protocol = https; + this.request = _.bind(https.request, https); + if (security_params.use_test_ca) { + ca_path = path.join(__dirname, '../test/data/ca.pem'); + var ca_data = fs.readFileSync(ca_path); + options.ca = ca_data; + } + if (security_params.server_host_override) { + var host_override = security_params.server_host_override; + options.servername = host_override; + } + } else { + protocol = http; + } + + this.request = _.bind(protocol.request, protocol); + + this.client_options = []; + + for (var i = 0; i < channels; i++) { + var new_options = _.assign({host: server_targets[i]}, options); + new_options.agent = new protocol.Agent(new_options); + this.client_options[i] = new_options; + } + + this.histogram = new Histogram(histogram_params.resolution, + histogram_params.max_possible); + + this.running = false; + + this.pending_calls = 0; +} + +util.inherits(BenchmarkClient, EventEmitter); + +function startAllClients(client_options_list, outstanding_rpcs_per_channel, + makeCall, emitter) { + _.each(client_options_list, function(client_options) { + _.times(outstanding_rpcs_per_channel, function() { + makeCall(client_options); + }); + }); +} + +BenchmarkClient.prototype.startClosedLoop = function( + outstanding_rpcs_per_channel, rpc_type, req_size, resp_size, generic) { + var self = this; + + var options = {}; + + self.running = true; + + if (rpc_type == 'UNARY') { + options.path = '/serviceProto.BenchmarkService.service/unaryCall'; + } else { + self.emit('error', new Error('Unsupported rpc_type: ' + rpc_type)); + } + + if (generic) { + self.emit('error', new Error('Generic client not supported')); + } + + self.last_wall_time = process.hrtime(); + + var argument = { + response_size: resp_size, + payload: { + body: '0'.repeat(req_size) + } + }; + + function makeCall(client_options) { + if (self.running) { + self.pending_calls++; + var start_time = process.hrtime(); + var req = self.request(client_options, function(res) { + var res_data = ''; + res.on('data', function(data) { + res_data += data; + }); + res.on('end', function() { + JSON.parse(res_data); + var time_diff = process.hrtime(start_time); + self.histogram.add(timeDiffToNanos(time_diff)); + makeCall(client_options); + self.pending_calls--; + if ((!self.running) && self.pending_calls == 0) { + self.emit('finished'); + } + }); + }); + req.write(JSON.stringify(argument)); + req.end(); + req.on('error', function(error) { + self.emit('error', new Error('Client error: ' + error.message)); + self.running = false; + }); + } + } + + startAllClients(_.assign(options, self.client_options), + outstanding_rpcs_per_channel, makeCall, self); +}; + +BenchmarkClient.prototype.startPoisson = function( + outstanding_rpcs_per_channel, rpc_type, req_size, resp_size, offered_load, + generic) { + var self = this; + + var options = {}; + + self.running = true; + + if (rpc_type == 'UNARY') { + options.path = '/serviceProto.BenchmarkService.service/unaryCall'; + } else { + self.emit('error', new Error('Unsupported rpc_type: ' + rpc_type)); + } + + if (generic) { + self.emit('error', new Error('Generic client not supported')); + } + + self.last_wall_time = process.hrtime(); + + var argument = { + response_size: resp_size, + payload: { + body: '0'.repeat(req_size) + } + }; + + function makeCall(client_options, poisson) { + if (self.running) { + self.pending_calls++; + var start_time = process.hrtime(); + var req = self.request(client_options, function(res) { + var res_data = ''; + res.on('data', function(data) { + res_data += data; + }); + res.on('end', function() { + JSON.parse(res_data); + var time_diff = process.hrtime(start_time); + self.histogram.add(timeDiffToNanos(time_diff)); + self.pending_calls--; + if ((!self.running) && self.pending_calls == 0) { + self.emit('finished'); + } + }); + }); + req.write(JSON.stringify(argument)); + req.end(); + req.on('error', function(error) { + self.emit('error', new Error('Client error: ' + error.message)); + self.running = false; + }); + } else { + poisson.stop(); + } + } + + var averageIntervalMs = (1 / offered_load) * 1000; + + startAllClients(_.assign(options, self.client_options), + outstanding_rpcs_per_channel, function(opts){ + var p = PoissonProcess.create(averageIntervalMs, function() { + makeCall(opts, p); + }); + p.start(); + }, self); +}; + +/** + * Return curent statistics for the client. If reset is set, restart + * statistic collection. + * @param {boolean} reset Indicates that statistics should be reset + * @return {object} Client statistics + */ +BenchmarkClient.prototype.mark = function(reset) { + var wall_time_diff = process.hrtime(this.last_wall_time); + var histogram = this.histogram; + if (reset) { + this.last_wall_time = process.hrtime(); + this.histogram = new Histogram(histogram.resolution, + histogram.max_possible); + } + + return { + latencies: { + bucket: histogram.getContents(), + min_seen: histogram.minimum(), + max_seen: histogram.maximum(), + sum: histogram.getSum(), + sum_of_squares: histogram.sumOfSquares(), + count: histogram.getCount() + }, + time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9, + // Not sure how to measure these values + time_user: 0, + time_system: 0 + }; +}; + +/** + * Stop the clients. + * @param {function} callback Called when the clients have finished shutting + * down + */ +BenchmarkClient.prototype.stop = function(callback) { + this.running = false; + this.on('finished', callback); +}; + +module.exports = BenchmarkClient; diff --git a/src/node/performance/benchmark_server.js b/src/node/performance/benchmark_server.js index 70cee9979b1..6abde2e17af 100644 --- a/src/node/performance/benchmark_server.js +++ b/src/node/performance/benchmark_server.js @@ -40,6 +40,8 @@ var fs = require('fs'); var path = require('path'); +var EventEmitter = require('events'); +var util = require('util'); var genericService = require('./generic_service'); @@ -138,12 +140,15 @@ function BenchmarkServer(host, port, tls, generic, response_size) { this.server = server; } +util.inherits(BenchmarkServer, EventEmitter); + /** * Start the benchmark server. */ BenchmarkServer.prototype.start = function() { this.server.start(); this.last_wall_time = process.hrtime(); + this.emit('started'); }; /** diff --git a/src/node/performance/benchmark_server_express.js b/src/node/performance/benchmark_server_express.js new file mode 100644 index 00000000000..07a559f0226 --- /dev/null +++ b/src/node/performance/benchmark_server_express.js @@ -0,0 +1,107 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * Benchmark server module + * @module + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var http = require('http'); +var https = require('https'); +var EventEmitter = require('events'); +var util = require('util'); + +var express = require('express'); + +function unaryCall(req, res) { + var reqObj = JSON.parse(req.body); + var payload = {body: '0'.repeat(reqObj.response_size)}; + res.send(JSON.dumps(payload)); +} + +function BenchmarkServer(host, port, tls, generic, response_size) { + var app = express(); + app.put('/serviceProto.BenchmarkService.service/unaryCall', unaryCall); + this.input_host = host; + this.input_port = port; + if (tls) { + var credentials = {}; + var key_path = path.join(__dirname, '../test/data/server1.key'); + var pem_path = path.join(__dirname, '../test/data/server1.pem'); + + var key_data = fs.readFileSync(key_path); + var pem_data = fs.readFileSync(pem_path); + credentials['key'] = key_data; + credentials['cert'] = pem_data; + this.server = https.createServer(credentials, app); + } else { + this.server = http.createServer(app); + } +} + +util.inherits(BenchmarkServer, EventEmitter); + +BenchmarkServer.prototype.start = function() { + var self = this; + this.server.listen(this.input_port, this.input_hostname, function() { + this.last_wall_time = process.hrtime(); + self.emit('started'); + }); +}; + +BenchmarkServer.prototype.getPort = function() { + return this.server.address().port; +}; + +BenchmarkServer.prototype.mark = function(reset) { + var wall_time_diff = process.hrtime(this.last_wall_time); + if (reset) { + this.last_wall_time = process.hrtime(); + } + return { + time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9, + // Not sure how to measure these values + time_user: 0, + time_system: 0 + }; +}; + +BenchmarkServer.prototype.stop = function(callback) { + this.server.close(callback); +}; + +module.exports = BenchmarkServer; diff --git a/src/node/performance/worker.js b/src/node/performance/worker.js index 7ef9b84fe76..030bf7d7ba0 100644 --- a/src/node/performance/worker.js +++ b/src/node/performance/worker.js @@ -34,18 +34,18 @@ 'use strict'; var console = require('console'); -var worker_service_impl = require('./worker_service_impl'); +var WorkerServiceImpl = require('./worker_service_impl'); var grpc = require('../../../'); var serviceProto = grpc.load({ root: __dirname + '/../../..', file: 'src/proto/grpc/testing/services.proto'}).grpc.testing; -function runServer(port) { +function runServer(port, benchmark_impl) { var server_creds = grpc.ServerCredentials.createInsecure(); var server = new grpc.Server(); server.addProtoService(serviceProto.WorkerService.service, - worker_service_impl); + new WorkerServiceImpl(benchmark_impl, server)); var address = '0.0.0.0:' + port; server.bind(address, server_creds); server.start(); @@ -57,9 +57,9 @@ if (require.main === module) { Error.stackTraceLimit = Infinity; var parseArgs = require('minimist'); var argv = parseArgs(process.argv, { - string: ['driver_port'] + string: ['driver_port', 'benchmark_impl'] }); - runServer(argv.driver_port); + runServer(argv.driver_port, argv.benchmark_impl); } exports.runServer = runServer; diff --git a/src/node/performance/worker_service_impl.js b/src/node/performance/worker_service_impl.js index 4b5cb8f9c21..73dcb7afad8 100644 --- a/src/node/performance/worker_service_impl.js +++ b/src/node/performance/worker_service_impl.js @@ -38,121 +38,141 @@ var console = require('console'); var BenchmarkClient = require('./benchmark_client'); var BenchmarkServer = require('./benchmark_server'); -exports.quitWorker = function quitWorker(call, callback) { - callback(null, {}); - process.exit(0); -} +module.exports = function WorkerServiceImpl(benchmark_impl, server) { + var BenchmarkClient; + var BenchmarkServer; + switch (benchmark_impl) { + case 'grpc': + BenchmarkClient = require('./benchmark_client'); + BenchmarkServer = require('./benchmark_server'); + break; + case 'express': + BenchmarkClient = require('./benchmark_client_express'); + BenchmarkServer = require('./benchmark_server_express'); + break; + default: + throw new Error('Unrecognized benchmark impl: ' + benchmark_impl); + } -exports.runClient = function runClient(call) { - var client; - call.on('data', function(request) { - var stats; - switch (request.argtype) { - case 'setup': - var setup = request.setup; - console.log('ClientConfig %j', setup); - client = new BenchmarkClient(setup.server_targets, - setup.client_channels, - setup.histogram_params, - setup.security_params); - client.on('error', function(error) { - call.emit('error', error); - }); - var req_size, resp_size, generic; - switch (setup.payload_config.payload) { - case 'bytebuf_params': - req_size = setup.payload_config.bytebuf_params.req_size; - resp_size = setup.payload_config.bytebuf_params.resp_size; - generic = true; + this.quitWorker = function quitWorker(call, callback) { + server.tryShutdown(function() { + callback(null, {}); + }); + }; + + this.runClient = function runClient(call) { + var client; + call.on('data', function(request) { + var stats; + switch (request.argtype) { + case 'setup': + var setup = request.setup; + console.log('ClientConfig %j', setup); + client = new BenchmarkClient(setup.server_targets, + setup.client_channels, + setup.histogram_params, + setup.security_params); + client.on('error', function(error) { + call.emit('error', error); + }); + var req_size, resp_size, generic; + switch (setup.payload_config.payload) { + case 'bytebuf_params': + req_size = setup.payload_config.bytebuf_params.req_size; + resp_size = setup.payload_config.bytebuf_params.resp_size; + generic = true; + break; + case 'simple_params': + req_size = setup.payload_config.simple_params.req_size; + resp_size = setup.payload_config.simple_params.resp_size; + generic = false; + break; + default: + call.emit('error', new Error('Unsupported PayloadConfig type' + + setup.payload_config.payload)); + } + switch (setup.load_params.load) { + case 'closed_loop': + client.startClosedLoop(setup.outstanding_rpcs_per_channel, + setup.rpc_type, req_size, resp_size, generic); + break; + case 'poisson': + client.startPoisson(setup.outstanding_rpcs_per_channel, + setup.rpc_type, req_size, resp_size, + setup.load_params.poisson.offered_load, generic); + break; + default: + call.emit('error', new Error('Unsupported LoadParams type' + + setup.load_params.load)); + } + stats = client.mark(); + call.write({ + stats: stats + }); break; - case 'simple_params': - req_size = setup.payload_config.simple_params.req_size; - resp_size = setup.payload_config.simple_params.resp_size; - generic = false; + case 'mark': + if (client) { + stats = client.mark(request.mark.reset); + call.write({ + stats: stats + }); + } else { + call.emit('error', new Error('Got Mark before ClientConfig')); + } break; default: - call.emit('error', new Error('Unsupported PayloadConfig type' + - setup.payload_config.payload)); + throw new Error('Nonexistent client argtype option: ' + request.argtype); } - switch (setup.load_params.load) { - case 'closed_loop': - client.startClosedLoop(setup.outstanding_rpcs_per_channel, - setup.rpc_type, req_size, resp_size, generic); + }); + call.on('end', function() { + client.stop(function() { + call.end(); + }); + }); + }; + + this.runServer = function runServer(call) { + var server; + call.on('data', function(request) { + var stats; + switch (request.argtype) { + case 'setup': + console.log('ServerConfig %j', request.setup); + server = new BenchmarkServer('[::]', request.setup.port, + request.setup.security_params); + server.start(); + server.on('started', function() { + stats = server.mark(); + call.write({ + stats: stats, + port: server.getPort() + }); + }); break; - case 'poisson': - client.startPoisson(setup.outstanding_rpcs_per_channel, - setup.rpc_type, req_size, resp_size, - setup.load_params.poisson.offered_load, generic); + case 'mark': + if (server) { + stats = server.mark(request.mark.reset); + call.write({ + stats: stats, + port: server.getPort(), + cores: 1 + }); + } else { + call.emit('error', new Error('Got Mark before ServerConfig')); + } break; default: - call.emit('error', new Error('Unsupported LoadParams type' + - setup.load_params.load)); + throw new Error('Nonexistent server argtype option'); } - stats = client.mark(); - call.write({ - stats: stats - }); - break; - case 'mark': - if (client) { - stats = client.mark(request.mark.reset); - call.write({ - stats: stats - }); - } else { - call.emit('error', new Error('Got Mark before ClientConfig')); - } - break; - default: - throw new Error('Nonexistent client argtype option: ' + request.argtype); - } - }); - call.on('end', function() { - client.stop(function() { - call.end(); }); - }); -}; - -exports.runServer = function runServer(call) { - var server; - call.on('data', function(request) { - var stats; - switch (request.argtype) { - case 'setup': - console.log('ServerConfig %j', request.setup); - server = new BenchmarkServer('[::]', request.setup.port, - request.setup.security_params); - server.start(); - stats = server.mark(); - call.write({ - stats: stats, - port: server.getPort() + call.on('end', function() { + server.stop(function() { + call.end(); }); - break; - case 'mark': - if (server) { - stats = server.mark(request.mark.reset); - call.write({ - stats: stats, - port: server.getPort(), - cores: 1 - }); - } else { - call.emit('error', new Error('Got Mark before ServerConfig')); - } - break; - default: - throw new Error('Nonexistent server argtype option'); - } - }); - call.on('end', function() { - server.stop(function() { - call.end(); }); - }); -}; + }; -exports.coreCount = function coreCount(call, callback) { - callback(null, {cores: os.cpus().length}); + this.coreCount = function coreCount(call, callback) { + callback(null, {cores: os.cpus().length}); + }; }; diff --git a/tools/run_tests/performance/scenario_config.py b/tools/run_tests/performance/scenario_config.py index 725ca299bd4..ea86c7a61b0 100644 --- a/tools/run_tests/performance/scenario_config.py +++ b/tools/run_tests/performance/scenario_config.py @@ -338,7 +338,8 @@ class NodeLanguage: self.safename = str(self) def worker_cmdline(self): - return ['tools/run_tests/performance/run_worker_node.sh'] + return ['tools/run_tests/performance/run_worker_node.sh', + '--benchmark_impl=grpc'] def worker_port_offset(self): return 200 @@ -636,11 +637,69 @@ class GoLanguage: def __str__(self): return 'go' +class NodeExpressLanguage: + + def __init__(self): + pass + self.safename = str(self) + + def worker_cmdline(self): + return ['tools/run_tests/performance/run_worker_node.sh', + '--benchmark_impl=express'] + + def worker_port_offset(self): + return 700 + + def scenarios(self): + # TODO(jtattermusch): make this scenario work + #yield _ping_pong_scenario( + # 'node_generic_async_streaming_ping_pong', rpc_type='STREAMING', + # client_type='ASYNC_CLIENT', server_type='ASYNC_GENERIC_SERVER', + # use_generic_payload=True) + + # TODO(jtattermusch): make this scenario work + #yield _ping_pong_scenario( + # 'node_protobuf_async_streaming_ping_pong', rpc_type='STREAMING', + # client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER') + + yield _ping_pong_scenario( + 'node_protobuf_unary_ping_pong', rpc_type='UNARY', + client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', + categories=[SCALABLE, SMOKETEST]) + + yield _ping_pong_scenario( + 'node_protobuf_async_unary_qps_unconstrained', rpc_type='UNARY', + client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', + unconstrained_client='async', + categories=[SCALABLE, SMOKETEST]) + + # TODO(jtattermusch): make this scenario work + #yield _ping_pong_scenario( + # 'node_protobuf_async_streaming_qps_unconstrained', rpc_type='STREAMING', + # client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', + # unconstrained_client='async') + + # TODO(jtattermusch): make this scenario work + #yield _ping_pong_scenario( + # 'node_to_cpp_protobuf_async_unary_ping_pong', rpc_type='UNARY', + # client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', + # server_language='c++', server_core_limit=1, async_server_threads=1) + + # TODO(jtattermusch): make this scenario work + #yield _ping_pong_scenario( + # 'node_to_cpp_protobuf_async_streaming_ping_pong', rpc_type='STREAMING', + # client_type='ASYNC_CLIENT', server_type='ASYNC_SERVER', + # server_language='c++', server_core_limit=1, async_server_threads=1) + + def __str__(self): + return 'node_express' + LANGUAGES = { 'c++' : CXXLanguage(), 'csharp' : CSharpLanguage(), 'node' : NodeLanguage(), + 'node_express': NodeExpressLanguage(), 'ruby' : RubyLanguage(), 'java' : JavaLanguage(), 'python' : PythonLanguage(), diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index dd070b1fe01..fd792001c15 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -825,6 +825,53 @@ class Sanity(object): def __str__(self): return 'sanity' +class NodeExpressLanguage(object): + """Dummy Node express test target to enable running express performance + benchmarks""" + + def __init__(self): + self.platform = platform_string() + + def configure(self, config, args): + self.config = config + self.args = args + _check_compiler(self.args.compiler, ['default', 'node0.12', + 'node4', 'node5', 'node6']) + if self.args.compiler == 'default': + self.node_version = '4' + else: + # Take off the word "node" + self.node_version = self.args.compiler[4:] + + def test_specs(self): + return [] + + def pre_build_steps(self): + if self.platform == 'windows': + return [['tools\\run_tests\\pre_build_node.bat']] + else: + return [['tools/run_tests/pre_build_node.sh', self.node_version]] + + def make_targets(self): + return [] + + def make_options(self): + return [] + + def build_steps(self): + return [] + + def post_tests_steps(self): + return [] + + def makefile_name(self): + return 'Makefile' + + def dockerfile_dir(self): + return 'tools/dockerfile/test/node_jessie_%s' % _docker_arch_suffix(self.args.arch) + + def __str__(self): + return 'node_express' # different configurations we can run under with open('tools/run_tests/configs.json') as f: @@ -835,6 +882,7 @@ _LANGUAGES = { 'c++': CLanguage('cxx', 'c++'), 'c': CLanguage('c', 'c'), 'node': NodeLanguage(), + 'node_express': NodeExpressLanguage(), 'php': PhpLanguage(), 'php7': Php7Language(), 'python': PythonLanguage(), From 3dc67018a0be747917c2d43a72bc072d2b7f2d00 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 18 Oct 2016 15:56:44 -0700 Subject: [PATCH 02/30] Fix issues with express benchmark and synchronize package.json with template --- package.json | 1 + src/node/performance/benchmark_client_express.js | 8 +++++--- src/node/performance/benchmark_server_express.js | 8 +++++--- src/node/performance/worker_service_impl.js | 2 +- templates/package.json.template | 2 ++ 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 4827aac3985..30b4b6787c2 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "devDependencies": { "async": "^1.5.0", + "body-parser": "^1.15.2", "express": "^4.14.0", "google-auth-library": "^0.9.2", "google-protobuf": "^3.0.0", diff --git a/src/node/performance/benchmark_client_express.js b/src/node/performance/benchmark_client_express.js index 15bc1132d29..675eb5f2884 100644 --- a/src/node/performance/benchmark_client_express.js +++ b/src/node/performance/benchmark_client_express.js @@ -92,7 +92,9 @@ function BenchmarkClient(server_targets, channels, histogram_params, this.client_options = []; for (var i = 0; i < channels; i++) { - var new_options = _.assign({host: server_targets[i]}, options); + var host_port; + host_port = server_targets[i % server_targets.length].split(':') + var new_options = _.assign({hostname: host_port[0], port: +host_port[1]}, options); new_options.agent = new protocol.Agent(new_options); this.client_options[i] = new_options; } @@ -172,7 +174,7 @@ BenchmarkClient.prototype.startClosedLoop = function( } } - startAllClients(_.assign(options, self.client_options), + startAllClients(_.map(self.client_options, _.partial(_.assign, options)), outstanding_rpcs_per_channel, makeCall, self); }; @@ -236,7 +238,7 @@ BenchmarkClient.prototype.startPoisson = function( var averageIntervalMs = (1 / offered_load) * 1000; - startAllClients(_.assign(options, self.client_options), + startAllClients(_.map(self.client_options, _.partial(_.assign, options)), outstanding_rpcs_per_channel, function(opts){ var p = PoissonProcess.create(averageIntervalMs, function() { makeCall(opts, p); diff --git a/src/node/performance/benchmark_server_express.js b/src/node/performance/benchmark_server_express.js index 07a559f0226..065bcf660b6 100644 --- a/src/node/performance/benchmark_server_express.js +++ b/src/node/performance/benchmark_server_express.js @@ -46,15 +46,17 @@ var EventEmitter = require('events'); var util = require('util'); var express = require('express'); +var bodyParser = require('body-parser') function unaryCall(req, res) { - var reqObj = JSON.parse(req.body); + var reqObj = req.body; var payload = {body: '0'.repeat(reqObj.response_size)}; - res.send(JSON.dumps(payload)); + res.json(payload); } function BenchmarkServer(host, port, tls, generic, response_size) { var app = express(); + app.use(bodyParser.json()) app.put('/serviceProto.BenchmarkService.service/unaryCall', unaryCall); this.input_host = host; this.input_port = port; @@ -78,7 +80,7 @@ util.inherits(BenchmarkServer, EventEmitter); BenchmarkServer.prototype.start = function() { var self = this; this.server.listen(this.input_port, this.input_hostname, function() { - this.last_wall_time = process.hrtime(); + self.last_wall_time = process.hrtime(); self.emit('started'); }); }; diff --git a/src/node/performance/worker_service_impl.js b/src/node/performance/worker_service_impl.js index 73dcb7afad8..3f317f64294 100644 --- a/src/node/performance/worker_service_impl.js +++ b/src/node/performance/worker_service_impl.js @@ -140,7 +140,6 @@ module.exports = function WorkerServiceImpl(benchmark_impl, server) { console.log('ServerConfig %j', request.setup); server = new BenchmarkServer('[::]', request.setup.port, request.setup.security_params); - server.start(); server.on('started', function() { stats = server.mark(); call.write({ @@ -148,6 +147,7 @@ module.exports = function WorkerServiceImpl(benchmark_impl, server) { port: server.getPort() }); }); + server.start(); break; case 'mark': if (server) { diff --git a/templates/package.json.template b/templates/package.json.template index e9596d4d4cb..2b3d32ec994 100644 --- a/templates/package.json.template +++ b/templates/package.json.template @@ -36,6 +36,8 @@ }, "devDependencies": { "async": "^1.5.0", + "body-parser": "^1.15.2", + "express": "^4.14.0", "google-auth-library": "^0.9.2", "google-protobuf": "^3.0.0", "istanbul": "^0.3.21", From 21bca1e01729ef2ce5f8f15c7ee54a715fea2aba Mon Sep 17 00:00:00 2001 From: Sree Kuchibhotla Date: Tue, 18 Oct 2016 19:37:12 -0700 Subject: [PATCH 03/30] stress test framework design doc --- doc/images/stress_test_framework.png | Bin 0 -> 63366 bytes doc/stress_test_framework.md | 92 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 doc/images/stress_test_framework.png create mode 100644 doc/stress_test_framework.md diff --git a/doc/images/stress_test_framework.png b/doc/images/stress_test_framework.png new file mode 100644 index 0000000000000000000000000000000000000000..ef8f95ba600474a85159c7405903188e2fc07f05 GIT binary patch literal 63366 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 azIuJCjr7cSU literal 0 HcmV?d00001 diff --git a/doc/stress_test_framework.md b/doc/stress_test_framework.md new file mode 100644 index 00000000000..18f545e090e --- /dev/null +++ b/doc/stress_test_framework.md @@ -0,0 +1,92 @@ +# Stress Test framework for gRPC + +(Sree Kuchibhotla - sreek@) + +> Status: This is implemented. More details at [README.md](https://github.com/grpc/grpc/blob/master/tools/run_tests/stress_test/README.md) + + +**I. GOALS** + +1) Build a stress test suite for gRPC: + +* Build a stress test suite that can Identify bugs by testing the system (gRPC server/client) under extreme conditions: + * High load + * High concurrency + * Limited resources + * Intermittent failures +* Should be integrated with Jenkins CI + +2) Make it generic enough (i.e build a generic test framework) that can be used for: + +* Executing M instances of a client against N instances of a server with an arbitrarily defined connection matrix + * Execute heterogenous test configurations - for example: Java stress test clients against C++ servers or Node clients against Python servers or TSAN C++ clients vs ASAN C++ Servers etc. + * Easy and Flexible enough that Devs can use it to recreate complex test scenarios + +The implementation effort is divided into two parts: + +* Building a "Stress Test Framework" to run the stress test suites- More details in **Section II** (The idea is that the Stress Test framework is generic enough that it would be easier to modify it to run other suites like interop-tests or custom test scenarios) +* Building a 'Stress test suite' - More details in **section III** + +**Terminology:** + +GCE - Google compute engine +GKE - Google Container engine +Kubernetes - Google's open source service scheduler / orchestrator. + +**Note:** The terms GKE and Kubernetes are used interchangeably in this document + +# II. STRESS TEST FRAMEWORK + +(The details of each step are explained below)) +![image](images/stress_test_framework.png) +**Figure 1** + +### Step 1 Read the test config, generate base docker images + +**_Test Config:_** The test configuration contains the following information: + +* _GKE info:_ GKE project and cluster info +* _Docker images:_ Instructions to build docker images +* _Client templates:_ One or more client templates each containing the following information: + * Which docker image to use + * Path to the client program to launch (within the docker image) + * Parameters to the client program +* _Server templates:_ Similar to Client templates - except that these are for servers +* Test matrix containing the following: + * _Server groups:_ One or more groups of servers containing the following info for each group + * Which server template to use + * How many instances to launch + * _Client groups:_ One or more groups of clients containing the following (for each group): + * Which client template to use + * How many instances to launch + * Which server group to talk to (all clients in this group will talk to all servers in the server group) + +The first step is to read the test config and build the docker images + +**_Stress server docker image:_** The following are the main files in the server docker images + +* _Interop_server:_ The server program +* `run_server.py`: This is a python script which is the entry point of the docker image (i.e this is the script that is called when the docker image is run in GKE). This script launches the interop server and also updates the status in BigQuery. If the interop_server fails for whatever reason, the script launch_server.py logs that status in BigQuery + +**_Stress client docker image:_** + +* Stress client: The stress test client. In addition to talking to the interop_server, the stress client also exports metrics (which can be queried by the metrics_client described below) +* Metrics client: Metrics client connects to the stress_client to get the current qps metrics. +* `run_client.py`: This is a python script which is the entry point of the docker image (i.e this is the script that is called when the docker image is run in GKE). This script launches the stress client and also updates the status in BigQuery. The script then periodically launches metrics client to query the qps from the stress client and then uploads the qps to BigQuery. + +### Step 2) Upload the docker images to GKE +The docker images are uploaded to the GKE registry + +### Step 3) Launch the tests in GKE +The test driver reads the test matrix (described in step 1) and creates the necessary server and client pods in GKE. + +### Step 4) Tests are run in GKE +GKE starts running the tests by calling the entry points in *each* docker image (i.e `run_server.py` or `run_client.py` depending on whcih docker image it is) + +### Step 5) Upload the status to GKE and Monitor the status in GKE +* 5.1 The tests periodically update their status in BigQuery +* 5.2 The test driver periodically checks the status in Bigquery to see if any tests failed. If any tests failed, the driver immediately stops the tests. If not, the driver continues to run the tests for a configurable amount of time. + +### Step 6) Create a summary report +The test driver creates a final summary report containing details about any test failures and information about how to connect the failed pods in GKE for debugging. + From c1f006e67d7fe348c0e6e95f1204835053da8cb3 Mon Sep 17 00:00:00 2001 From: Sree Kuchibhotla Date: Tue, 18 Oct 2016 19:39:48 -0700 Subject: [PATCH 04/30] update image --- doc/images/stress_test_framework.png | Bin 63366 -> 63744 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/images/stress_test_framework.png b/doc/images/stress_test_framework.png index ef8f95ba600474a85159c7405903188e2fc07f05..a5f49f8e1064a36d73bdca40ef25df58bc203068 100644 GIT binary patch literal 63744 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 literal 63366 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 azIuJCjr7cSU From 98abfd3d64d6a887f56f3dd78eec6c32bde369df Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Fri, 21 Oct 2016 08:10:51 -0700 Subject: [PATCH 05/30] Pass channel args through resolver. --- src/core/ext/client_config/client_channel.c | 6 +- .../ext/client_config/lb_policy_factory.h | 3 +- src/core/ext/client_config/resolver_factory.h | 5 +- .../ext/client_config/resolver_registry.c | 12 ++-- .../ext/client_config/resolver_registry.h | 3 +- src/core/ext/client_config/resolver_result.c | 12 ++-- src/core/ext/client_config/resolver_result.h | 6 +- src/core/ext/lb_policy/grpclb/grpclb.c | 6 +- .../ext/lb_policy/pick_first/pick_first.c | 2 +- .../ext/lb_policy/round_robin/round_robin.c | 2 +- .../ext/resolver/dns/native/dns_resolver.c | 10 ++- .../ext/resolver/sockaddr/sockaddr_resolver.c | 6 +- .../chttp2/client/insecure/channel_create.c | 57 ++++++---------- .../client/secure/secure_channel_create.c | 66 ++++++++----------- 14 files changed, 91 insertions(+), 105 deletions(-) diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c index beaa9637c3b..d02dd5aacf1 100644 --- a/src/core/ext/client_config/client_channel.c +++ b/src/core/ext/client_config/client_channel.c @@ -188,8 +188,8 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, grpc_resolver_result_get_server_name(chand->resolver_result); lb_policy_args.addresses = grpc_resolver_result_get_addresses(chand->resolver_result); - lb_policy_args.additional_args = - grpc_resolver_result_get_lb_policy_args(chand->resolver_result); + lb_policy_args.args = + grpc_resolver_result_get_channel_args(chand->resolver_result); lb_policy_args.client_channel_factory = chand->client_channel_factory; // Special case: If all of the addresses are balancer addresses, @@ -227,7 +227,7 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, grpc_lb_policy_check_connectivity(exec_ctx, lb_policy, &state_error); } const grpc_arg *channel_arg = grpc_channel_args_find( - lb_policy_args.additional_args, GRPC_ARG_SERVICE_CONFIG); + lb_policy_args.args, GRPC_ARG_SERVICE_CONFIG); if (channel_arg != NULL) { GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); method_config_table = grpc_method_config_table_ref( diff --git a/src/core/ext/client_config/lb_policy_factory.h b/src/core/ext/client_config/lb_policy_factory.h index ade55704f25..4e11501e019 100644 --- a/src/core/ext/client_config/lb_policy_factory.h +++ b/src/core/ext/client_config/lb_policy_factory.h @@ -93,8 +93,7 @@ typedef struct grpc_lb_policy_args { const char *server_name; grpc_lb_addresses *addresses; grpc_client_channel_factory *client_channel_factory; - /* Can be used to pass implementation-specific parameters to the LB policy. */ - grpc_channel_args *additional_args; + grpc_channel_args *args; } grpc_lb_policy_args; struct grpc_lb_policy_factory_vtable { diff --git a/src/core/ext/client_config/resolver_factory.h b/src/core/ext/client_config/resolver_factory.h index 9ec5b9a70e7..e70e0565772 100644 --- a/src/core/ext/client_config/resolver_factory.h +++ b/src/core/ext/client_config/resolver_factory.h @@ -47,7 +47,10 @@ struct grpc_resolver_factory { const grpc_resolver_factory_vtable *vtable; }; -typedef struct grpc_resolver_args { grpc_uri *uri; } grpc_resolver_args; +typedef struct grpc_resolver_args { + grpc_uri *uri; + const grpc_channel_args *args; +} grpc_resolver_args; struct grpc_resolver_factory_vtable { void (*ref)(grpc_resolver_factory *factory); diff --git a/src/core/ext/client_config/resolver_registry.c b/src/core/ext/client_config/resolver_registry.c index bd5c683878d..0a168efbc94 100644 --- a/src/core/ext/client_config/resolver_registry.c +++ b/src/core/ext/client_config/resolver_registry.c @@ -131,14 +131,16 @@ static grpc_resolver_factory *resolve_factory(const char *target, return factory; } -grpc_resolver *grpc_resolver_create(const char *target) { +grpc_resolver *grpc_resolver_create(const char *target, + const grpc_channel_args *args) { grpc_uri *uri = NULL; grpc_resolver_factory *factory = resolve_factory(target, &uri); grpc_resolver *resolver; - grpc_resolver_args args; - memset(&args, 0, sizeof(args)); - args.uri = uri; - resolver = grpc_resolver_factory_create_resolver(factory, &args); + grpc_resolver_args resolver_args; + memset(&resolver_args, 0, sizeof(resolver_args)); + resolver_args.uri = uri; + resolver_args.args = args; + resolver = grpc_resolver_factory_create_resolver(factory, &resolver_args); grpc_uri_destroy(uri); return resolver; } diff --git a/src/core/ext/client_config/resolver_registry.h b/src/core/ext/client_config/resolver_registry.h index 4c6279b9789..dfe95b4a11d 100644 --- a/src/core/ext/client_config/resolver_registry.h +++ b/src/core/ext/client_config/resolver_registry.h @@ -58,7 +58,8 @@ void grpc_register_resolver_type(grpc_resolver_factory *factory); If a resolver factory was found, use it to instantiate a resolver and return it. If a resolver factory was not found, return NULL. */ -grpc_resolver *grpc_resolver_create(const char *target); +grpc_resolver *grpc_resolver_create(const char *target, + const grpc_channel_args* args); /** Find a resolver factory given a name and return an (owned-by-the-caller) * reference to it */ diff --git a/src/core/ext/client_config/resolver_result.c b/src/core/ext/client_config/resolver_result.c index 63480d152b7..5f3600e7d96 100644 --- a/src/core/ext/client_config/resolver_result.c +++ b/src/core/ext/client_config/resolver_result.c @@ -43,19 +43,19 @@ struct grpc_resolver_result { char* server_name; grpc_lb_addresses* addresses; char* lb_policy_name; - grpc_channel_args* lb_policy_args; + grpc_channel_args* channel_args; }; grpc_resolver_result* grpc_resolver_result_create( const char* server_name, grpc_lb_addresses* addresses, - const char* lb_policy_name, grpc_channel_args* lb_policy_args) { + const char* lb_policy_name, grpc_channel_args* args) { grpc_resolver_result* result = gpr_malloc(sizeof(*result)); memset(result, 0, sizeof(*result)); gpr_ref_init(&result->refs, 1); result->server_name = gpr_strdup(server_name); result->addresses = addresses; result->lb_policy_name = gpr_strdup(lb_policy_name); - result->lb_policy_args = lb_policy_args; + result->channel_args = args; return result; } @@ -69,7 +69,7 @@ void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx, gpr_free(result->server_name); grpc_lb_addresses_destroy(result->addresses, NULL /* user_data_destroy */); gpr_free(result->lb_policy_name); - grpc_channel_args_destroy(result->lb_policy_args); + grpc_channel_args_destroy(result->channel_args); gpr_free(result); } } @@ -88,7 +88,7 @@ const char* grpc_resolver_result_get_lb_policy_name( return result->lb_policy_name; } -grpc_channel_args* grpc_resolver_result_get_lb_policy_args( +grpc_channel_args* grpc_resolver_result_get_channel_args( grpc_resolver_result* result) { - return result->lb_policy_args; + return result->channel_args; } diff --git a/src/core/ext/client_config/resolver_result.h b/src/core/ext/client_config/resolver_result.h index a7ea7c0f4b8..f081c0448a6 100644 --- a/src/core/ext/client_config/resolver_result.h +++ b/src/core/ext/client_config/resolver_result.h @@ -48,10 +48,10 @@ /// Results reported from a grpc_resolver. typedef struct grpc_resolver_result grpc_resolver_result; -/// Takes ownership of \a addresses and \a lb_policy_args. +/// Takes ownership of \a addresses and \a args. grpc_resolver_result* grpc_resolver_result_create( const char* server_name, grpc_lb_addresses* addresses, - const char* lb_policy_name, grpc_channel_args* lb_policy_args); + const char* lb_policy_name, grpc_channel_args* args); void grpc_resolver_result_ref(grpc_resolver_result* result); void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx, @@ -63,7 +63,7 @@ grpc_lb_addresses* grpc_resolver_result_get_addresses( grpc_resolver_result* result); const char* grpc_resolver_result_get_lb_policy_name( grpc_resolver_result* result); -grpc_channel_args* grpc_resolver_result_get_lb_policy_args( +grpc_channel_args* grpc_resolver_result_get_channel_args( grpc_resolver_result* result); #endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H */ diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c index b24b522449d..663f1d33004 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/lb_policy/grpclb/grpclb.c @@ -459,7 +459,7 @@ static grpc_lb_policy *create_rr_locked( args.server_name = glb_policy->server_name; args.client_channel_factory = glb_policy->cc_factory; args.addresses = process_serverlist(serverlist); - args.additional_args = glb_policy->args; + args.args = glb_policy->args; grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args); @@ -587,7 +587,7 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, * Create a client channel over them to communicate with a LB service */ glb_policy->server_name = gpr_strdup(args->server_name); glb_policy->cc_factory = args->client_channel_factory; - glb_policy->args = grpc_channel_args_copy(args->additional_args); + glb_policy->args = grpc_channel_args_copy(args->args); GPR_ASSERT(glb_policy->cc_factory != NULL); /* construct a target from the addresses in args, given in the form @@ -621,7 +621,7 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, /* will pick using pick_first */ glb_policy->lb_channel = grpc_client_channel_factory_create_channel( exec_ctx, glb_policy->cc_factory, target_uri_str, - GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, NULL); + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, args->args); gpr_free(target_uri_str); for (size_t i = 0; i < num_grpclb_addrs; i++) { diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c index da20b9281ba..3683079cf46 100644 --- a/src/core/ext/lb_policy/pick_first/pick_first.c +++ b/src/core/ext/lb_policy/pick_first/pick_first.c @@ -466,7 +466,7 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, sc_args.addr = (struct sockaddr *)(&args->addresses->addresses[i].address.addr); sc_args.addr_len = args->addresses->addresses[i].address.len; - sc_args.args = args->additional_args; + sc_args.args = args->args; grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( exec_ctx, args->client_channel_factory, &sc_args); diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c index b8f4f67aeba..00a18974dfc 100644 --- a/src/core/ext/lb_policy/round_robin/round_robin.c +++ b/src/core/ext/lb_policy/round_robin/round_robin.c @@ -629,7 +629,7 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, sc_args.addr = (struct sockaddr *)(&args->addresses->addresses[i].address.addr); sc_args.addr_len = args->addresses->addresses[i].address.len; - sc_args.args = args->additional_args; + sc_args.args = args->args; grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( exec_ctx, args->client_channel_factory, &sc_args); diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index fa33ffd7bd5..a4f6ed8c4e3 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -40,6 +40,7 @@ #include "src/core/ext/client_config/http_connect_handshaker.h" #include "src/core/ext/client_config/lb_policy_registry.h" #include "src/core/ext/client_config/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/support/backoff.h" @@ -59,6 +60,8 @@ typedef struct { char *name_to_resolve; /** default port to use */ char *default_port; + /** channel args. */ + grpc_channel_args *channel_args; /** mutex guarding the rest of the state */ gpr_mu mu; @@ -176,8 +179,9 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, NULL /* balancer_name */, NULL /* user_data */); } grpc_resolved_addresses_destroy(r->addresses); - result = grpc_resolver_result_create(r->target_name, addresses, - NULL /* lb_policy_name */, NULL); + result = grpc_resolver_result_create( + r->target_name, addresses, NULL /* lb_policy_name */, + grpc_channel_args_copy(r->channel_args)); } else { gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); @@ -241,6 +245,7 @@ static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { gpr_free(r->target_name); gpr_free(r->name_to_resolve); gpr_free(r->default_port); + grpc_channel_args_destroy(r->channel_args); gpr_free(r); } @@ -263,6 +268,7 @@ static grpc_resolver *dns_create(grpc_resolver_args *args, r->target_name = gpr_strdup(path); r->name_to_resolve = proxy_name == NULL ? gpr_strdup(path) : proxy_name; r->default_port = gpr_strdup(default_port); + r->channel_args = grpc_channel_args_copy(args->args); gpr_backoff_init(&r->backoff_state, BACKOFF_MULTIPLIER, BACKOFF_JITTER, BACKOFF_MIN_SECONDS * 1000, BACKOFF_MAX_SECONDS * 1000); return &r->base; diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c index 28dd2569e81..eaaa2e47c28 100644 --- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c +++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c @@ -55,6 +55,8 @@ typedef struct { char *target_name; /** the addresses that we've 'resolved' */ grpc_lb_addresses *addresses; + /** channel args */ + grpc_channel_args *channel_args; /** mutex guarding the rest of the state */ gpr_mu mu; /** have we published? */ @@ -121,7 +123,7 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, *r->target_result = grpc_resolver_result_create( r->target_name, grpc_lb_addresses_copy(r->addresses, NULL /* user_data_copy */), - NULL /* lb_policy_name */, NULL /* lb_policy_args */); + NULL /* lb_policy_name */, grpc_channel_args_copy(r->channel_args)); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; } @@ -132,6 +134,7 @@ static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { gpr_mu_destroy(&r->mu); gpr_free(r->target_name); grpc_lb_addresses_destroy(r->addresses, NULL /* user_data_destroy */); + grpc_channel_args_destroy(r->channel_args); gpr_free(r); } @@ -201,6 +204,7 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, memset(r, 0, sizeof(*r)); r->target_name = gpr_strdup(args->uri->path); r->addresses = addresses; + r->channel_args = grpc_channel_args_copy(args->args); gpr_mu_init(&r->mu); grpc_resolver_init(&r->base, &sockaddr_resolver_vtable); return &r->base; diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c index c2b59569fd3..df1bb161665 100644 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c @@ -52,6 +52,10 @@ #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/channel.h" +// +// connector +// + typedef struct { grpc_connector base; gpr_refcount refs; @@ -157,35 +161,20 @@ static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, static const grpc_connector_vtable connector_vtable = { connector_ref, connector_unref, connector_shutdown, connector_connect}; -typedef struct { - grpc_client_channel_factory base; - gpr_refcount refs; - grpc_channel_args *merge_args; -} client_channel_factory; +// +// client_channel_factory +// static void client_channel_factory_ref( - grpc_client_channel_factory *cc_factory) { - client_channel_factory *f = (client_channel_factory *)cc_factory; - gpr_ref(&f->refs); -} + grpc_client_channel_factory *cc_factory) {} static void client_channel_factory_unref( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) { - client_channel_factory *f = (client_channel_factory *)cc_factory; - if (gpr_unref(&f->refs)) { - grpc_channel_args_destroy(f->merge_args); - gpr_free(f); - } -} + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {} static grpc_subchannel *client_channel_factory_create_subchannel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, grpc_subchannel_args *args) { - client_channel_factory *f = (client_channel_factory *)cc_factory; connector *c = gpr_malloc(sizeof(*c)); - grpc_channel_args *final_args = - grpc_channel_args_merge(args->args, f->merge_args); - grpc_subchannel *s; memset(c, 0, sizeof(*c)); c->base.vtable = &connector_vtable; gpr_ref_init(&c->refs, 1); @@ -197,10 +186,8 @@ static grpc_subchannel *client_channel_factory_create_subchannel( grpc_http_connect_handshaker_create(proxy_name, args->server_name)); gpr_free(proxy_name); } - args->args = final_args; - s = grpc_subchannel_create(exec_ctx, &c->base, args); + grpc_subchannel *s = grpc_subchannel_create(exec_ctx, &c->base, args); grpc_connector_unref(exec_ctx, &c->base); - grpc_channel_args_destroy(final_args); return s; } @@ -208,12 +195,9 @@ static grpc_channel *client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, const char *target, grpc_client_channel_type type, grpc_channel_args *args) { - client_channel_factory *f = (client_channel_factory *)cc_factory; - grpc_channel_args *final_args = grpc_channel_args_merge(args, f->merge_args); - grpc_channel *channel = grpc_channel_create(exec_ctx, target, final_args, + grpc_channel *channel = grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); - grpc_channel_args_destroy(final_args); - grpc_resolver *resolver = grpc_resolver_create(target); + grpc_resolver *resolver = grpc_resolver_create(target, args); if (!resolver) { GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "client_channel_factory_create_channel"); @@ -221,7 +205,7 @@ static grpc_channel *client_channel_factory_create_channel( } grpc_client_channel_finish_initialization( - exec_ctx, grpc_channel_get_channel_stack(channel), resolver, &f->base); + exec_ctx, grpc_channel_get_channel_stack(channel), resolver, cc_factory); GRPC_RESOLVER_UNREF(exec_ctx, resolver, "create_channel"); return channel; @@ -232,6 +216,9 @@ static const grpc_client_channel_factory_vtable client_channel_factory_vtable = client_channel_factory_create_subchannel, client_channel_factory_create_channel}; +static grpc_client_channel_factory client_channel_factory = { + &client_channel_factory_vtable}; + /* Create a client channel: Asynchronously: - resolve target - connect to it (trying alternatives as presented) @@ -245,16 +232,12 @@ grpc_channel *grpc_insecure_channel_create(const char *target, (target, args, reserved)); GPR_ASSERT(!reserved); - client_channel_factory *f = gpr_malloc(sizeof(*f)); - memset(f, 0, sizeof(*f)); - f->base.vtable = &client_channel_factory_vtable; - gpr_ref_init(&f->refs, 1); - f->merge_args = grpc_channel_args_copy(args); - + grpc_client_channel_factory *factory = + (grpc_client_channel_factory*)&client_channel_factory; grpc_channel *channel = client_channel_factory_create_channel( - &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL); + &exec_ctx, factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL); - grpc_client_channel_factory_unref(&exec_ctx, &f->base); + grpc_client_channel_factory_unref(&exec_ctx, factory); grpc_exec_ctx_finish(&exec_ctx); return channel != NULL ? channel : grpc_lame_client_channel_create( diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c index 31c54ff74c6..ac0a929de66 100644 --- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c +++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c @@ -54,6 +54,10 @@ #include "src/core/lib/surface/channel.h" #include "src/core/lib/tsi/transport_security_interface.h" +// +// connector +// + typedef struct { grpc_connector base; gpr_refcount refs; @@ -216,10 +220,13 @@ static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, static const grpc_connector_vtable connector_vtable = { connector_ref, connector_unref, connector_shutdown, connector_connect}; +// +// client_channel_factory +// + typedef struct { grpc_client_channel_factory base; gpr_refcount refs; - grpc_channel_args *merge_args; grpc_channel_security_connector *security_connector; } client_channel_factory; @@ -235,7 +242,6 @@ static void client_channel_factory_unref( if (gpr_unref(&f->refs)) { GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, "client_channel_factory"); - grpc_channel_args_destroy(f->merge_args); gpr_free(f); } } @@ -245,9 +251,6 @@ static grpc_subchannel *client_channel_factory_create_subchannel( grpc_subchannel_args *args) { client_channel_factory *f = (client_channel_factory *)cc_factory; connector *c = gpr_malloc(sizeof(*c)); - grpc_channel_args *final_args = - grpc_channel_args_merge(args->args, f->merge_args); - grpc_subchannel *s; memset(c, 0, sizeof(*c)); c->base.vtable = &connector_vtable; c->security_connector = f->security_connector; @@ -261,10 +264,8 @@ static grpc_subchannel *client_channel_factory_create_subchannel( } gpr_mu_init(&c->mu); gpr_ref_init(&c->refs, 1); - args->args = final_args; - s = grpc_subchannel_create(exec_ctx, &c->base, args); + grpc_subchannel *s = grpc_subchannel_create(exec_ctx, &c->base, args); grpc_connector_unref(exec_ctx, &c->base); - grpc_channel_args_destroy(final_args); return s; } @@ -273,13 +274,9 @@ static grpc_channel *client_channel_factory_create_channel( const char *target, grpc_client_channel_type type, grpc_channel_args *args) { client_channel_factory *f = (client_channel_factory *)cc_factory; - - grpc_channel_args *final_args = grpc_channel_args_merge(args, f->merge_args); - grpc_channel *channel = grpc_channel_create(exec_ctx, target, final_args, + grpc_channel *channel = grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); - grpc_channel_args_destroy(final_args); - - grpc_resolver *resolver = grpc_resolver_create(target); + grpc_resolver *resolver = grpc_resolver_create(target, args); if (resolver != NULL) { grpc_client_channel_finish_initialization( exec_ctx, grpc_channel_get_channel_stack(channel), resolver, &f->base); @@ -289,7 +286,6 @@ static grpc_channel *client_channel_factory_create_channel( "client_channel_factory_create_channel"); channel = NULL; } - GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, "client_channel_factory_create_channel"); return channel; @@ -308,19 +304,13 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, const char *target, const grpc_channel_args *args, void *reserved) { - grpc_arg connector_arg; - grpc_channel_args *args_copy; - grpc_channel_args *new_args_from_connector; - grpc_channel_security_connector *security_connector; - client_channel_factory *f; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE( "grpc_secure_channel_create(creds=%p, target=%s, args=%p, " "reserved=%p)", 4, (creds, target, args, reserved)); GPR_ASSERT(reserved == NULL); - + // Make sure security connector does not already exist in args. if (grpc_find_security_connector_in_args(args) != NULL) { gpr_log(GPR_ERROR, "Cannot set security context in channel args."); grpc_exec_ctx_finish(&exec_ctx); @@ -328,7 +318,9 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, target, GRPC_STATUS_INTERNAL, "Security connector exists in channel args."); } - + // Create security connector and construct new channel args. + grpc_channel_security_connector *security_connector; + grpc_channel_args *new_args_from_connector; if (grpc_channel_credentials_create_security_connector( creds, target, args, &security_connector, &new_args_from_connector) != GRPC_SECURITY_OK) { @@ -336,32 +328,28 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, return grpc_lame_client_channel_create( target, GRPC_STATUS_INTERNAL, "Failed to create security connector."); } - - connector_arg = grpc_security_connector_to_arg(&security_connector->base); - args_copy = grpc_channel_args_copy_and_add( + grpc_arg connector_arg = + grpc_security_connector_to_arg(&security_connector->base); + grpc_channel_args *new_args = grpc_channel_args_copy_and_add( new_args_from_connector != NULL ? new_args_from_connector : args, &connector_arg, 1); - - f = gpr_malloc(sizeof(*f)); - memset(f, 0, sizeof(*f)); - f->base.vtable = &client_channel_factory_vtable; - gpr_ref_init(&f->refs, 1); - - f->merge_args = grpc_channel_args_copy(args_copy); - grpc_channel_args_destroy(args_copy); if (new_args_from_connector != NULL) { grpc_channel_args_destroy(new_args_from_connector); } - + // Create client channel factory. + client_channel_factory *f = gpr_malloc(sizeof(*f)); + memset(f, 0, sizeof(*f)); + f->base.vtable = &client_channel_factory_vtable; + gpr_ref_init(&f->refs, 1); GRPC_SECURITY_CONNECTOR_REF(&security_connector->base, "grpc_secure_channel_create"); f->security_connector = security_connector; - + // Create channel. grpc_channel *channel = client_channel_factory_create_channel( - &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL); - + &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args); + // Clean up. + grpc_channel_args_destroy(new_args); grpc_client_channel_factory_unref(&exec_ctx, &f->base); grpc_exec_ctx_finish(&exec_ctx); - return channel; /* may be NULL */ } From 16883a37efa49ef2d58ea57c6dbbba66497f2232 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Fri, 21 Oct 2016 10:30:58 -0700 Subject: [PATCH 06/30] Set user data vtable when creating grpc_lb_addresses. --- .../ext/client_config/lb_policy_factory.c | 55 +++++++++++++++---- .../ext/client_config/lb_policy_factory.h | 23 ++++++-- src/core/ext/client_config/resolver_result.c | 2 +- src/core/ext/lb_policy/grpclb/grpclb.c | 27 ++++++--- .../ext/resolver/dns/native/dns_resolver.c | 4 +- .../ext/resolver/sockaddr/sockaddr_resolver.c | 10 ++-- test/core/end2end/fake_resolver.c | 10 ++-- 7 files changed, 93 insertions(+), 38 deletions(-) diff --git a/src/core/ext/client_config/lb_policy_factory.c b/src/core/ext/client_config/lb_policy_factory.c index c17af91a09e..55e346180de 100644 --- a/src/core/ext/client_config/lb_policy_factory.c +++ b/src/core/ext/client_config/lb_policy_factory.c @@ -38,19 +38,20 @@ #include "src/core/ext/client_config/lb_policy_factory.h" -grpc_lb_addresses* grpc_lb_addresses_create(size_t num_addresses) { +grpc_lb_addresses* grpc_lb_addresses_create( + size_t num_addresses, const grpc_lb_user_data_vtable* user_data_vtable) { grpc_lb_addresses* addresses = gpr_malloc(sizeof(grpc_lb_addresses)); addresses->num_addresses = num_addresses; + addresses->user_data_vtable = user_data_vtable; const size_t addresses_size = sizeof(grpc_lb_address) * num_addresses; addresses->addresses = gpr_malloc(addresses_size); memset(addresses->addresses, 0, addresses_size); return addresses; } -grpc_lb_addresses* grpc_lb_addresses_copy(grpc_lb_addresses* addresses, - void* (*user_data_copy)(void*)) { - grpc_lb_addresses* new_addresses = - grpc_lb_addresses_create(addresses->num_addresses); +grpc_lb_addresses* grpc_lb_addresses_copy(const grpc_lb_addresses* addresses) { + grpc_lb_addresses* new_addresses = grpc_lb_addresses_create( + addresses->num_addresses, addresses->user_data_vtable); memcpy(new_addresses->addresses, addresses->addresses, sizeof(grpc_lb_address) * addresses->num_addresses); for (size_t i = 0; i < addresses->num_addresses; ++i) { @@ -58,9 +59,10 @@ grpc_lb_addresses* grpc_lb_addresses_copy(grpc_lb_addresses* addresses, new_addresses->addresses[i].balancer_name = gpr_strdup(new_addresses->addresses[i].balancer_name); } - if (user_data_copy != NULL) { + if (new_addresses->addresses[i].user_data != NULL) { new_addresses->addresses[i].user_data = - user_data_copy(new_addresses->addresses[i].user_data); + addresses->user_data_vtable->copy( + new_addresses->addresses[i].user_data); } } return new_addresses; @@ -71,6 +73,7 @@ void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index, bool is_balancer, char* balancer_name, void* user_data) { GPR_ASSERT(index < addresses->num_addresses); + if (user_data != NULL) GPR_ASSERT(addresses->user_data_vtable != NULL); grpc_lb_address* target = &addresses->addresses[index]; memcpy(target->address.addr, address, address_len); target->address.len = address_len; @@ -79,12 +82,42 @@ void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index, target->user_data = user_data; } -void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses, - void (*user_data_destroy)(void*)) { +int grpc_lb_addresses_cmp(const grpc_lb_addresses* addresses1, + const grpc_lb_addresses* addresses2) { + if (addresses1->num_addresses > addresses2->num_addresses) return 1; + if (addresses1->num_addresses < addresses2->num_addresses) return -1; + if (addresses1->user_data_vtable > addresses2->user_data_vtable) return 1; + if (addresses1->user_data_vtable < addresses2->user_data_vtable) return -1; + for (size_t i = 0; i < addresses1->num_addresses; ++i) { + const grpc_lb_address* target1 = &addresses1->addresses[i]; + const grpc_lb_address* target2 = &addresses2->addresses[i]; + if (target1->address.len > target2->address.len) return 1; + if (target1->address.len < target2->address.len) return -1; + int retval = memcmp( + target1->address.addr, target2->address.addr, target1->address.len); + if (retval != 0) return retval; + if (target1->is_balancer > target2->is_balancer) return 1; + if (target1->is_balancer < target2->is_balancer) return -1; + const char* balancer_name1 = target1->balancer_name != NULL + ? target1->balancer_name : ""; + const char* balancer_name2 = target2->balancer_name != NULL + ? target2->balancer_name : ""; + retval = strcmp(balancer_name1, balancer_name2); + if (retval != 0) return retval; + if (addresses1->user_data_vtable != NULL) { + retval = addresses1->user_data_vtable->cmp(target1->user_data, + target2->user_data); + if (retval != 0) return retval; + } + } + return 0; +} + +void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses) { for (size_t i = 0; i < addresses->num_addresses; ++i) { gpr_free(addresses->addresses[i].balancer_name); - if (user_data_destroy != NULL) { - user_data_destroy(addresses->addresses[i].user_data); + if (addresses->addresses[i].user_data != NULL) { + addresses->user_data_vtable->destroy(addresses->addresses[i].user_data); } } gpr_free(addresses->addresses); diff --git a/src/core/ext/client_config/lb_policy_factory.h b/src/core/ext/client_config/lb_policy_factory.h index 4e11501e019..61c3f419d13 100644 --- a/src/core/ext/client_config/lb_policy_factory.h +++ b/src/core/ext/client_config/lb_policy_factory.h @@ -59,19 +59,27 @@ typedef struct grpc_lb_address { void *user_data; } grpc_lb_address; +typedef struct grpc_lb_user_data_vtable { + void* (*copy)(void*); + void (*destroy)(void*); + int (*cmp)(void*, void*); +} grpc_lb_user_data_vtable; + typedef struct grpc_lb_addresses { size_t num_addresses; grpc_lb_address *addresses; + const grpc_lb_user_data_vtable *user_data_vtable; } grpc_lb_addresses; /** Returns a grpc_addresses struct with enough space for - * \a num_addresses addresses. */ -grpc_lb_addresses *grpc_lb_addresses_create(size_t num_addresses); + \a num_addresses addresses. The \a user_data_vtable argument may be + NULL if no user data will be added. */ +grpc_lb_addresses *grpc_lb_addresses_create( + size_t num_addresses, const grpc_lb_user_data_vtable* user_data_vtable); /** Creates a copy of \a addresses. If \a user_data_copy is not NULL, * it will be invoked to copy the \a user_data field of each address. */ -grpc_lb_addresses *grpc_lb_addresses_copy(grpc_lb_addresses *addresses, - void *(*user_data_copy)(void *)); +grpc_lb_addresses *grpc_lb_addresses_copy(const grpc_lb_addresses *addresses); /** Sets the value of the address at index \a index of \a addresses. * \a address is a socket address of length \a address_len. @@ -81,10 +89,13 @@ void grpc_lb_addresses_set_address(grpc_lb_addresses *addresses, size_t index, bool is_balancer, char *balancer_name, void *user_data); +/** Compares \a addresses1 and \a addresses2. */ +int grpc_lb_addresses_cmp(const grpc_lb_addresses *addresses1, + const grpc_lb_addresses *addresses2); + /** Destroys \a addresses. If \a user_data_destroy is not NULL, it will * be invoked to destroy the \a user_data field of each address. */ -void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses, - void (*user_data_destroy)(void *)); +void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses); /** Arguments passed to LB policies. */ /* TODO(roth, ctiller): Consider replacing this struct with diff --git a/src/core/ext/client_config/resolver_result.c b/src/core/ext/client_config/resolver_result.c index 5f3600e7d96..e6a6f2da620 100644 --- a/src/core/ext/client_config/resolver_result.c +++ b/src/core/ext/client_config/resolver_result.c @@ -67,7 +67,7 @@ void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx, grpc_resolver_result* result) { if (gpr_unref(&result->refs)) { gpr_free(result->server_name); - grpc_lb_addresses_destroy(result->addresses, NULL /* user_data_destroy */); + grpc_lb_addresses_destroy(result->addresses); gpr_free(result->lb_policy_name); grpc_channel_args_destroy(result->channel_args); gpr_free(result); diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c index 663f1d33004..1467508d6d0 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/lb_policy/grpclb/grpclb.c @@ -338,6 +338,21 @@ static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, return true; } +/* vtable for LB tokens in grpc_lb_addresses. */ +static void* lb_token_copy(void *token) { + return token == NULL ? NULL : GRPC_MDELEM_REF(token); +} +static void lb_token_destroy(void *token) { + if (token != NULL) GRPC_MDELEM_UNREF(token); +} +static int lb_token_cmp(void* token1, void* token2) { + if (token1 > token2) return 1; + if (token1 < token2) return -1; + return 0; +} +static const grpc_lb_user_data_vtable lb_token_vtable = { + lb_token_copy, lb_token_destroy, lb_token_cmp}; + /* Returns addresses extracted from \a serverlist. */ static grpc_lb_addresses *process_serverlist( const grpc_grpclb_serverlist *serverlist) { @@ -349,7 +364,8 @@ static grpc_lb_addresses *process_serverlist( } if (num_valid == 0) return NULL; - grpc_lb_addresses *lb_addresses = grpc_lb_addresses_create(num_valid); + grpc_lb_addresses *lb_addresses = + grpc_lb_addresses_create(num_valid, &lb_token_vtable); /* second pass: actually populate the addresses and LB tokens (aka user data * to the outside world) to be read by the RR policy during its creation. @@ -410,11 +426,6 @@ static grpc_lb_addresses *process_serverlist( return lb_addresses; } -/* A plugin for grpc_lb_addresses_destroy that unrefs the LB token metadata. */ -static void lb_token_destroy(void *token) { - if (token != NULL) GRPC_MDELEM_UNREF(token); -} - /* perform a pick over \a rr_policy. Given that a pick can return immediately * (ignoring its completion callback) we need to perform the cleanups this * callback would be otherwise resposible for */ @@ -465,7 +476,7 @@ static grpc_lb_policy *create_rr_locked( if (glb_policy->addresses != NULL) { /* dispose of the previous version */ - grpc_lb_addresses_destroy(glb_policy->addresses, lb_token_destroy); + grpc_lb_addresses_destroy(glb_policy->addresses); } glb_policy->addresses = args.addresses; @@ -662,7 +673,7 @@ static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { grpc_grpclb_destroy_serverlist(glb_policy->serverlist); } gpr_mu_destroy(&glb_policy->mu); - grpc_lb_addresses_destroy(glb_policy->addresses, lb_token_destroy); + grpc_lb_addresses_destroy(glb_policy->addresses); gpr_free(glb_policy); } diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index a4f6ed8c4e3..0694d8b6b77 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -170,8 +170,8 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, GPR_ASSERT(r->resolving); r->resolving = false; if (r->addresses != NULL) { - grpc_lb_addresses *addresses = - grpc_lb_addresses_create(r->addresses->naddrs); + grpc_lb_addresses *addresses = grpc_lb_addresses_create( + r->addresses->naddrs, NULL /* user_data_vtable */); for (size_t i = 0; i < r->addresses->naddrs; ++i) { grpc_lb_addresses_set_address( addresses, i, &r->addresses->addrs[i].addr, diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c index eaaa2e47c28..d34094f8e8b 100644 --- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c +++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c @@ -121,8 +121,7 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, if (r->next_completion != NULL && !r->published) { r->published = true; *r->target_result = grpc_resolver_result_create( - r->target_name, - grpc_lb_addresses_copy(r->addresses, NULL /* user_data_copy */), + r->target_name, grpc_lb_addresses_copy(r->addresses), NULL /* lb_policy_name */, grpc_channel_args_copy(r->channel_args)); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; @@ -133,7 +132,7 @@ static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { sockaddr_resolver *r = (sockaddr_resolver *)gr; gpr_mu_destroy(&r->mu); gpr_free(r->target_name); - grpc_lb_addresses_destroy(r->addresses, NULL /* user_data_destroy */); + grpc_lb_addresses_destroy(r->addresses); grpc_channel_args_destroy(r->channel_args); gpr_free(r); } @@ -178,7 +177,8 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, gpr_slice_buffer path_parts; gpr_slice_buffer_init(&path_parts); gpr_slice_split(path_slice, ",", &path_parts); - grpc_lb_addresses *addresses = grpc_lb_addresses_create(path_parts.count); + grpc_lb_addresses *addresses = grpc_lb_addresses_create( + path_parts.count, NULL /* user_data_vtable */); bool errors_found = false; for (size_t i = 0; i < addresses->num_addresses; i++) { grpc_uri ith_uri = *args->uri; @@ -196,7 +196,7 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, gpr_slice_buffer_destroy(&path_parts); gpr_slice_unref(path_slice); if (errors_found) { - grpc_lb_addresses_destroy(addresses, NULL /* user_data_destroy */); + grpc_lb_addresses_destroy(addresses); return NULL; } /* Instantiate resolver. */ diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c index 32dc9e2711d..67337f4f841 100644 --- a/test/core/end2end/fake_resolver.c +++ b/test/core/end2end/fake_resolver.c @@ -78,7 +78,7 @@ static void fake_resolver_destroy(grpc_exec_ctx* exec_ctx, grpc_resolver* gr) { fake_resolver* r = (fake_resolver*)gr; gpr_mu_destroy(&r->mu); gpr_free(r->target_name); - grpc_lb_addresses_destroy(r->addresses, NULL /* user_data_destroy */); + grpc_lb_addresses_destroy(r->addresses); gpr_free(r->lb_policy_name); grpc_method_config_table_unref(r->method_config_table); gpr_free(r); @@ -107,8 +107,7 @@ static void fake_resolver_maybe_finish_next_locked(grpc_exec_ctx* exec_ctx, lb_policy_args = grpc_channel_args_copy_and_add(NULL /* src */, &arg, 1); } *r->target_result = grpc_resolver_result_create( - r->target_name, - grpc_lb_addresses_copy(r->addresses, NULL /* user_data_copy */), + r->target_name, grpc_lb_addresses_copy(r->addresses), r->lb_policy_name, lb_policy_args); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; @@ -168,7 +167,8 @@ static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory, gpr_slice_buffer path_parts; gpr_slice_buffer_init(&path_parts); gpr_slice_split(path_slice, ",", &path_parts); - grpc_lb_addresses* addresses = grpc_lb_addresses_create(path_parts.count); + grpc_lb_addresses* addresses = grpc_lb_addresses_create( + path_parts.count, NULL /* user_data_vtable */); bool errors_found = false; for (size_t i = 0; i < addresses->num_addresses; i++) { grpc_uri ith_uri = *args->uri; @@ -187,7 +187,7 @@ static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory, gpr_slice_buffer_destroy(&path_parts); gpr_slice_unref(path_slice); if (errors_found) { - grpc_lb_addresses_destroy(addresses, NULL /* user_data_destroy */); + grpc_lb_addresses_destroy(addresses); return NULL; } // Construct method config table. From 3686996786faa2671e487707c78b32c0e4a63d80 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Fri, 21 Oct 2016 10:43:45 -0700 Subject: [PATCH 07/30] Encode server name, LB policy name, and addresses in channel args. --- include/grpc/impl/codegen/grpc_types.h | 7 +++++ .../ext/client_config/lb_policy_factory.c | 22 ++++++++++++++++ .../ext/client_config/lb_policy_factory.h | 4 +++ .../ext/resolver/dns/native/dns_resolver.c | 14 +++++++--- .../ext/resolver/sockaddr/sockaddr_resolver.c | 13 ++++++++-- test/core/end2end/fake_resolver.c | 26 ++++++++++++++++--- 6 files changed, 77 insertions(+), 9 deletions(-) diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index a3fc683e579..0be7ab2ad23 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -204,6 +204,13 @@ typedef struct { /** Service config data, to be passed to subchannels. Not intended for external use. */ #define GRPC_ARG_SERVICE_CONFIG "grpc.service_config" +/** LB policy name. */ +#define GRPC_ARG_LB_POLICY_NAME "grpc.lb_policy_name" +/** Server name. Not intended for external use. */ +#define GRPC_ARG_SERVER_NAME "grpc.server_name" +/** Resolved addresses in a form used by the LB policy. + Not intended for external use. */ +#define GRPC_ARG_LB_ADDRESSES "grpc.lb_addresses" /** \} */ /** Result of a grpc call. If the caller satisfies the prerequisites of a diff --git a/src/core/ext/client_config/lb_policy_factory.c b/src/core/ext/client_config/lb_policy_factory.c index 55e346180de..676fdaf555f 100644 --- a/src/core/ext/client_config/lb_policy_factory.c +++ b/src/core/ext/client_config/lb_policy_factory.c @@ -124,6 +124,28 @@ void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses) { gpr_free(addresses); } +static void* lb_addresses_copy(void* addresses) { + return grpc_lb_addresses_copy(addresses); +} +static void lb_addresses_destroy(void* addresses) { + grpc_lb_addresses_destroy(addresses); +} +static int lb_addresses_cmp(void* addresses1, void* addresses2) { + return grpc_lb_addresses_cmp(addresses1, addresses2); +} +static const grpc_arg_pointer_vtable lb_addresses_arg_vtable = { + lb_addresses_copy, lb_addresses_destroy, lb_addresses_cmp}; + +grpc_arg grpc_lb_addresses_create_channel_arg( + const grpc_lb_addresses *addresses) { + grpc_arg arg; + arg.type = GRPC_ARG_POINTER; + arg.key = GRPC_ARG_LB_ADDRESSES; + arg.value.pointer.p = (void*)addresses; + arg.value.pointer.vtable = &lb_addresses_arg_vtable; + return arg; +} + void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) { factory->vtable->ref(factory); } diff --git a/src/core/ext/client_config/lb_policy_factory.h b/src/core/ext/client_config/lb_policy_factory.h index 61c3f419d13..f0798a31674 100644 --- a/src/core/ext/client_config/lb_policy_factory.h +++ b/src/core/ext/client_config/lb_policy_factory.h @@ -97,6 +97,10 @@ int grpc_lb_addresses_cmp(const grpc_lb_addresses *addresses1, * be invoked to destroy the \a user_data field of each address. */ void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses); +/** Returns a channel arg containing \a addresses. */ +grpc_arg grpc_lb_addresses_create_channel_arg( + const grpc_lb_addresses *addresses); + /** Arguments passed to LB policies. */ /* TODO(roth, ctiller): Consider replacing this struct with grpc_channel_args. See comment in resolver_result.h for details. */ diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index 0694d8b6b77..039fb2225d5 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -55,6 +55,7 @@ typedef struct { /** base class: must be first */ grpc_resolver base; /** target name */ +// FIXME: remove target_name when resolver_result goes away char *target_name; /** name to resolve (usually the same as target_name) */ char *name_to_resolve; @@ -178,10 +179,12 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, r->addresses->addrs[i].len, false /* is_balancer */, NULL /* balancer_name */, NULL /* user_data */); } + grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses); + grpc_channel_args* args = + grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); grpc_resolved_addresses_destroy(r->addresses); result = grpc_resolver_result_create( - r->target_name, addresses, NULL /* lb_policy_name */, - grpc_channel_args_copy(r->channel_args)); + r->target_name, addresses, NULL /* lb_policy_name */, args); } else { gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); @@ -268,7 +271,12 @@ static grpc_resolver *dns_create(grpc_resolver_args *args, r->target_name = gpr_strdup(path); r->name_to_resolve = proxy_name == NULL ? gpr_strdup(path) : proxy_name; r->default_port = gpr_strdup(default_port); - r->channel_args = grpc_channel_args_copy(args->args); + grpc_arg server_name_arg; + server_name_arg.type = GRPC_ARG_STRING; + server_name_arg.key = GRPC_ARG_SERVER_NAME; + server_name_arg.value.string = (char*)path; + r->channel_args = + grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1); gpr_backoff_init(&r->backoff_state, BACKOFF_MULTIPLIER, BACKOFF_JITTER, BACKOFF_MIN_SECONDS * 1000, BACKOFF_MAX_SECONDS * 1000); return &r->base; diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c index d34094f8e8b..93a34bf305f 100644 --- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c +++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c @@ -52,6 +52,7 @@ typedef struct { /** base class: must be first */ grpc_resolver base; /** the path component of the uri passed in */ +// FIXME: remove target_name when resolver_result goes away char *target_name; /** the addresses that we've 'resolved' */ grpc_lb_addresses *addresses; @@ -120,9 +121,12 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, sockaddr_resolver *r) { if (r->next_completion != NULL && !r->published) { r->published = true; + grpc_arg arg = grpc_lb_addresses_create_channel_arg(r->addresses); + grpc_channel_args* args = + grpc_channel_args_copy_and_add(r->channel_args, &arg, 1); *r->target_result = grpc_resolver_result_create( r->target_name, grpc_lb_addresses_copy(r->addresses), - NULL /* lb_policy_name */, grpc_channel_args_copy(r->channel_args)); + NULL /* lb_policy_name */, args); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; } @@ -204,7 +208,12 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, memset(r, 0, sizeof(*r)); r->target_name = gpr_strdup(args->uri->path); r->addresses = addresses; - r->channel_args = grpc_channel_args_copy(args->args); + grpc_arg server_name_arg; + server_name_arg.type = GRPC_ARG_STRING; + server_name_arg.key = GRPC_ARG_SERVER_NAME; + server_name_arg.value.string = args->uri->path; + r->channel_args = + grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1); gpr_mu_init(&r->mu); grpc_resolver_init(&r->base, &sockaddr_resolver_vtable); return &r->base; diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c index 67337f4f841..f77f3eb27d2 100644 --- a/test/core/end2end/fake_resolver.c +++ b/test/core/end2end/fake_resolver.c @@ -59,7 +59,9 @@ typedef struct { grpc_resolver base; // passed-in parameters +// FIXME: remove target_name once resolver_result is removed char* target_name; // the path component of the uri passed in + grpc_channel_args* channel_args; grpc_lb_addresses* addresses; char* lb_policy_name; grpc_method_config_table* method_config_table; @@ -78,6 +80,7 @@ static void fake_resolver_destroy(grpc_exec_ctx* exec_ctx, grpc_resolver* gr) { fake_resolver* r = (fake_resolver*)gr; gpr_mu_destroy(&r->mu); gpr_free(r->target_name); + grpc_channel_args_destroy(r->channel_args); grpc_lb_addresses_destroy(r->addresses); gpr_free(r->lb_policy_name); grpc_method_config_table_unref(r->method_config_table); @@ -100,15 +103,24 @@ static void fake_resolver_maybe_finish_next_locked(grpc_exec_ctx* exec_ctx, fake_resolver* r) { if (r->next_completion != NULL && !r->published) { r->published = true; - grpc_channel_args* lb_policy_args = NULL; + grpc_arg new_args[3]; + size_t num_args = 0; + new_args[num_args++] = grpc_lb_addresses_create_channel_arg(r->addresses); if (r->method_config_table != NULL) { - const grpc_arg arg = + new_args[num_args++] = grpc_method_config_table_create_channel_arg(r->method_config_table); - lb_policy_args = grpc_channel_args_copy_and_add(NULL /* src */, &arg, 1); } + if (r->lb_policy_name != NULL) { + new_args[num_args].type = GRPC_ARG_STRING; + new_args[num_args].key = GRPC_ARG_LB_POLICY_NAME; + new_args[num_args].value.string = r->lb_policy_name; + ++num_args; + } + grpc_channel_args* args = + grpc_channel_args_copy_and_add(r->channel_args, new_args, num_args); *r->target_result = grpc_resolver_result_create( r->target_name, grpc_lb_addresses_copy(r->addresses), - r->lb_policy_name, lb_policy_args); + r->lb_policy_name, args); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; } @@ -233,6 +245,12 @@ static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory, fake_resolver* r = gpr_malloc(sizeof(fake_resolver)); memset(r, 0, sizeof(*r)); r->target_name = gpr_strdup(args->uri->path); + grpc_arg server_name_arg; + server_name_arg.type = GRPC_ARG_STRING; + server_name_arg.key = GRPC_ARG_SERVER_NAME; + server_name_arg.value.string = r->target_name; + r->channel_args = + grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1); r->addresses = addresses; r->lb_policy_name = gpr_strdup(grpc_uri_get_query_arg(args->uri, "lb_policy")); From 5bd7be0c55d4149cc6e2d5ee90f33fe5f7f6a7de Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Fri, 21 Oct 2016 14:19:50 -0700 Subject: [PATCH 08/30] Change LB policies to get their input from channel args. --- src/core/ext/client_config/client_channel.c | 10 ++- src/core/ext/lb_policy/grpclb/grpclb.c | 61 +++++++++++++++---- .../ext/lb_policy/pick_first/pick_first.c | 31 +++++++--- .../ext/lb_policy/round_robin/round_robin.c | 30 ++++++--- src/core/lib/channel/channel_args.c | 55 ++++++++++++++--- src/core/lib/channel/channel_args.h | 11 ++++ 6 files changed, 155 insertions(+), 43 deletions(-) diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c index d02dd5aacf1..f23da5f35d2 100644 --- a/src/core/ext/client_config/client_channel.c +++ b/src/core/ext/client_config/client_channel.c @@ -192,11 +192,17 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, grpc_resolver_result_get_channel_args(chand->resolver_result); lb_policy_args.client_channel_factory = chand->client_channel_factory; + // Find LB policy name. + const char *lb_policy_name = NULL; + const grpc_arg *lb_policy_name_arg = + grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_POLICY_NAME); + if (lb_policy_name_arg != NULL) { + GPR_ASSERT(lb_policy_name_arg->type == GRPC_ARG_STRING); + lb_policy_name = lb_policy_name_arg->value.string; + } // Special case: If all of the addresses are balancer addresses, // assume that we should use the grpclb policy, regardless of what the // resolver actually specified. - const char *lb_policy_name = - grpc_resolver_result_get_lb_policy_name(chand->resolver_result); bool found_backend_address = false; for (size_t i = 0; i < lb_policy_args.addresses->num_addresses; ++i) { if (!lb_policy_args.addresses->addresses[i].is_balancer) { diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c index 1467508d6d0..fdc0bec9968 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/lb_policy/grpclb/grpclb.c @@ -470,9 +470,17 @@ static grpc_lb_policy *create_rr_locked( args.server_name = glb_policy->server_name; args.client_channel_factory = glb_policy->cc_factory; args.addresses = process_serverlist(serverlist); - args.args = glb_policy->args; + + // Replace the LB addresses in the channel args that we pass down to + // the subchannel. + static const char* keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES}; + const grpc_arg arg = grpc_lb_addresses_create_channel_arg(args.addresses); + args.args = grpc_channel_args_copy_and_add_and_remove( + glb_policy->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &arg, + 1); grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args); + grpc_channel_args_destroy(args.args); if (glb_policy->addresses != NULL) { /* dispose of the previous version */ @@ -574,6 +582,13 @@ static void glb_rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args) { + /* Get server name. */ + const grpc_arg* arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); + const char* server_name = + arg != NULL && arg->type == GRPC_ARG_STRING + ? arg->value.string : NULL; + /* Count the number of gRPC-LB addresses. There must be at least one. * TODO(roth): For now, we ignore non-balancer addresses, but in the * future, we may change the behavior such that we fall back to using @@ -581,22 +596,25 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, * time, this should be changed to allow a list with no balancer addresses, * since the resolver might fail to return a balancer address even when * this is the right LB policy to use. */ + arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses* addresses = arg->value.pointer.p; size_t num_grpclb_addrs = 0; - for (size_t i = 0; i < args->addresses->num_addresses; ++i) { - if (args->addresses->addresses[i].is_balancer) ++num_grpclb_addrs; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; } if (num_grpclb_addrs == 0) return NULL; glb_lb_policy *glb_policy = gpr_malloc(sizeof(*glb_policy)); memset(glb_policy, 0, sizeof(*glb_policy)); - /* All input addresses in args->addresses come from a resolver that claims + /* All input addresses in addresses come from a resolver that claims * they are LB services. It's the resolver's responsibility to make sure * this * policy is only instantiated and used in that case. * * Create a client channel over them to communicate with a LB service */ - glb_policy->server_name = gpr_strdup(args->server_name); + glb_policy->server_name = gpr_strdup(server_name); glb_policy->cc_factory = args->client_channel_factory; glb_policy->args = grpc_channel_args_copy(args->args); GPR_ASSERT(glb_policy->cc_factory != NULL); @@ -606,20 +624,20 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, * TODO(dgq): support mixed ip version */ char **addr_strs = gpr_malloc(sizeof(char *) * num_grpclb_addrs); size_t addr_index = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { - if (args->addresses->addresses[i].user_data != NULL) { + for (size_t i = 0; i < addresses->num_addresses; i++) { + if (addresses->addresses[i].user_data != NULL) { gpr_log(GPR_ERROR, "This LB policy doesn't support user data. It will be ignored"); } - if (args->addresses->addresses[i].is_balancer) { + if (addresses->addresses[i].is_balancer) { if (addr_index == 0) { addr_strs[addr_index++] = grpc_sockaddr_to_uri( - (const struct sockaddr *)&args->addresses->addresses[i] + (const struct sockaddr *)&addresses->addresses[i] .address.addr); } else { GPR_ASSERT(grpc_sockaddr_to_string( &addr_strs[addr_index++], - (const struct sockaddr *)&args->addresses->addresses[i] + (const struct sockaddr *)&addresses->addresses[i] .address.addr, true) > 0); } @@ -629,10 +647,29 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, char *target_uri_str = gpr_strjoin_sep((const char **)addr_strs, num_grpclb_addrs, ",", &uri_path_len); - /* will pick using pick_first */ + /* Create a channel to talk to the LBs. + * + * We strip out the channel arg for the LB policy name, since we want + * to use the default (pick_first) in this case. + * + * We also strip out the channel arg for the resolved addresses, since + * that will be generated by the name resolver used in the LB channel. + * Note that the LB channel will use the sockaddr resolver, so this + * won't actually generate a query to DNS (or some other name service). + * However, the addresses returned by the sockaddr resolver will have + * is_balancer=false, whereas our own addresses have is_balancer=true. + * We need the LB channel to return addresses with is_balancer=false + * so that it does not wind up recursively using the grpclb LB policy, + * as per the special case logic in client_channel.c. + */ + static const char* keys_to_remove[] = { + GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES}; + grpc_channel_args *new_args = grpc_channel_args_copy_and_remove( + args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove)); glb_policy->lb_channel = grpc_client_channel_factory_create_channel( exec_ctx, glb_policy->cc_factory, target_uri_str, - GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, args->args); + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, new_args); + grpc_channel_args_destroy(new_args); gpr_free(target_uri_str); for (size_t i = 0; i < num_grpclb_addrs; i++) { diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c index 3683079cf46..d95f0310b62 100644 --- a/src/core/ext/lb_policy/pick_first/pick_first.c +++ b/src/core/ext/lb_policy/pick_first/pick_first.c @@ -34,7 +34,9 @@ #include #include + #include "src/core/ext/client_config/lb_policy_registry.h" +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/transport/connectivity_state.h" typedef struct pending_pick { @@ -432,14 +434,23 @@ static void pick_first_factory_unref(grpc_lb_policy_factory *factory) {} static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args) { - GPR_ASSERT(args->addresses != NULL); GPR_ASSERT(args->client_channel_factory != NULL); + /* Get server name. */ + const grpc_arg* arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); + const char* server_name = + arg != NULL && arg->type == GRPC_ARG_STRING + ? arg->value.string : NULL; + /* Find the number of backend addresses. We ignore balancer * addresses, since we don't know how to handle them. */ + arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses* addresses = arg->value.pointer.p; size_t num_addrs = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { - if (!args->addresses->addresses[i].is_balancer) ++num_addrs; + for (size_t i = 0; i < addresses->num_addresses; i++) { + if (!addresses->addresses[i].is_balancer) ++num_addrs; } if (num_addrs == 0) return NULL; @@ -450,22 +461,22 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, memset(p->subchannels, 0, sizeof(*p->subchannels) * num_addrs); grpc_subchannel_args sc_args; size_t subchannel_idx = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { + for (size_t i = 0; i < addresses->num_addresses; i++) { /* Skip balancer addresses, since we only know how to handle backends. */ - if (args->addresses->addresses[i].is_balancer) continue; + if (addresses->addresses[i].is_balancer) continue; - if (args->addresses->addresses[i].user_data != NULL) { + if (addresses->addresses[i].user_data != NULL) { gpr_log(GPR_ERROR, "This LB policy doesn't support user data. It will be ignored"); } memset(&sc_args, 0, sizeof(grpc_subchannel_args)); /* server_name will be copied as part of the subchannel creation. This makes - * the copying of args->server_name (a borrowed pointer) OK. */ - sc_args.server_name = args->server_name; + * the copying of server_name (a borrowed pointer) OK. */ + sc_args.server_name = server_name; sc_args.addr = - (struct sockaddr *)(&args->addresses->addresses[i].address.addr); - sc_args.addr_len = args->addresses->addresses[i].address.len; + (struct sockaddr *)(&addresses->addresses[i].address.addr); + sc_args.addr_len = addresses->addresses[i].address.len; sc_args.args = args->args; grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c index 00a18974dfc..2deb0af99cf 100644 --- a/src/core/ext/lb_policy/round_robin/round_robin.c +++ b/src/core/ext/lb_policy/round_robin/round_robin.c @@ -64,6 +64,7 @@ #include #include "src/core/ext/client_config/lb_policy_registry.h" +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/static_metadata.h" @@ -598,14 +599,23 @@ static void round_robin_factory_unref(grpc_lb_policy_factory *factory) {} static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args) { - GPR_ASSERT(args->addresses != NULL); GPR_ASSERT(args->client_channel_factory != NULL); + /* Get server name. */ + const grpc_arg* arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); + const char* server_name = + arg != NULL && arg->type == GRPC_ARG_STRING + ? arg->value.string : NULL; + /* Find the number of backend addresses. We ignore balancer * addresses, since we don't know how to handle them. */ + arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses* addresses = arg->value.pointer.p; size_t num_addrs = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { - if (!args->addresses->addresses[i].is_balancer) ++num_addrs; + for (size_t i = 0; i < addresses->num_addresses; i++) { + if (!addresses->addresses[i].is_balancer) ++num_addrs; } if (num_addrs == 0) return NULL; @@ -618,17 +628,17 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, grpc_subchannel_args sc_args; size_t subchannel_idx = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { + for (size_t i = 0; i < addresses->num_addresses; i++) { /* Skip balancer addresses, since we only know how to handle backends. */ - if (args->addresses->addresses[i].is_balancer) continue; + if (addresses->addresses[i].is_balancer) continue; memset(&sc_args, 0, sizeof(grpc_subchannel_args)); /* server_name will be copied as part of the subchannel creation. This makes - * the copying of args->server_name (a borrowed pointer) OK. */ - sc_args.server_name = args->server_name; + * the copying of server_name (a borrowed pointer) OK. */ + sc_args.server_name = server_name; sc_args.addr = - (struct sockaddr *)(&args->addresses->addresses[i].address.addr); - sc_args.addr_len = args->addresses->addresses[i].address.len; + (struct sockaddr *)(&addresses->addresses[i].address.addr); + sc_args.addr_len = addresses->addresses[i].address.len; sc_args.args = args->args; grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( @@ -641,7 +651,7 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, sd->policy = p; sd->index = subchannel_idx; sd->subchannel = subchannel; - sd->user_data = args->addresses->addresses[i].user_data; + sd->user_data = addresses->addresses[i].user_data; ++subchannel_idx; grpc_closure_init(&sd->connectivity_changed_closure, rr_connectivity_changed, sd); diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c index 2957d2c8188..0270d6f2246 100644 --- a/src/core/lib/channel/channel_args.c +++ b/src/core/lib/channel/channel_args.c @@ -66,22 +66,59 @@ static grpc_arg copy_arg(const grpc_arg *src) { grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, const grpc_arg *to_add, size_t num_to_add) { + return grpc_channel_args_copy_and_add_and_remove(src, NULL, 0, to_add, + num_to_add); +} + +grpc_channel_args *grpc_channel_args_copy_and_remove( + const grpc_channel_args *src, const char** to_remove, + size_t num_to_remove) { + return grpc_channel_args_copy_and_add_and_remove(src, to_remove, + num_to_remove, NULL, 0); +} + +static bool should_remove_arg(const grpc_arg* arg, const char** to_remove, + size_t num_to_remove) { + for (size_t i = 0; i < num_to_remove; ++i) { + if (strcmp(arg->key, to_remove[i]) == 0) return true; + } + return false; +} + +grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( + const grpc_channel_args *src, const char** to_remove, size_t num_to_remove, + const grpc_arg *to_add, size_t num_to_add) { + // Figure out how many args we'll be copying. + size_t num_args_to_copy = 0; + if (src != NULL) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + ++num_args_to_copy; + } + } + } + // Create result. grpc_channel_args *dst = gpr_malloc(sizeof(grpc_channel_args)); - size_t i; - size_t src_num_args = (src == NULL) ? 0 : src->num_args; - if (!src && !to_add) { - dst->num_args = 0; + dst->num_args = num_args_to_copy + num_to_add; + if (dst->num_args == 0) { dst->args = NULL; return dst; } - dst->num_args = src_num_args + num_to_add; dst->args = gpr_malloc(sizeof(grpc_arg) * dst->num_args); - for (i = 0; i < src_num_args; i++) { - dst->args[i] = copy_arg(&src->args[i]); + // Copy args from src that are not being removed. + size_t dst_idx = 0; + if (src != NULL) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + dst->args[dst_idx++] = copy_arg(&src->args[i]); + } + } } - for (i = 0; i < num_to_add; i++) { - dst->args[i + src_num_args] = copy_arg(&to_add[i]); + // Add args from to_add. + for (size_t i = 0; i < num_to_add; ++i) { + dst->args[dst_idx++] = copy_arg(&to_add[i]); } + GPR_ASSERT(dst_idx == dst->num_args); return dst; } diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h index a80340c0faf..1508d237484 100644 --- a/src/core/lib/channel/channel_args.h +++ b/src/core/lib/channel/channel_args.h @@ -51,6 +51,17 @@ grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, const grpc_arg *to_add, size_t num_to_add); +/** Copies the arguments in \a src except for those whose keys are in + \a to_remove. */ +grpc_channel_args *grpc_channel_args_copy_and_remove( + const grpc_channel_args *src, const char** to_remove, size_t num_to_remove); + +/** Copies the arguments from \a src except for those whose keys are in + \a to_remove and appends the arguments in \a to_add. */ +grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( + const grpc_channel_args *src, const char** to_remove, size_t num_to_remove, + const grpc_arg *to_add, size_t num_to_add); + /** Concatenate args from \a a and \a b into a new instance */ grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a, const grpc_channel_args *b); From af842451318f73d0fd2dcb55f02baaa70c82f3f9 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Fri, 21 Oct 2016 15:05:15 -0700 Subject: [PATCH 09/30] Remove resolver_result. --- BUILD | 8 - CMakeLists.txt | 3 - Makefile | 3 - binding.gyp | 1 - build.yaml | 2 - config.m4 | 1 - gRPC-Core.podspec | 3 - grpc.gemspec | 2 - package.xml | 2 - src/core/ext/client_config/README.md | 21 +- src/core/ext/client_config/client_channel.c | 57 ++-- .../ext/client_config/lb_policy_factory.h | 4 - src/core/ext/client_config/resolver.c | 2 +- src/core/ext/client_config/resolver.h | 13 +- src/core/ext/client_config/resolver_factory.h | 2 - src/core/ext/client_config/resolver_result.c | 94 ------ src/core/ext/client_config/resolver_result.h | 69 ---- src/core/ext/lb_policy/grpclb/grpclb.c | 17 +- .../ext/resolver/dns/native/dns_resolver.c | 36 +- .../ext/resolver/sockaddr/sockaddr_resolver.c | 17 +- src/python/grpcio/grpc_core_dependencies.py | 1 - test/core/end2end/fake_resolver.c | 16 +- tools/doxygen/Doxyfile.core.internal | 2 - tools/run_tests/sources_and_headers.json | 3 - tools/run_tests/tests.json | 312 +++++++++--------- vsprojects/vcxproj/grpc/grpc.vcxproj | 3 - vsprojects/vcxproj/grpc/grpc.vcxproj.filters | 6 - .../grpc_unsecure/grpc_unsecure.vcxproj | 3 - .../grpc_unsecure.vcxproj.filters | 6 - 29 files changed, 232 insertions(+), 477 deletions(-) delete mode 100644 src/core/ext/client_config/resolver_result.c delete mode 100644 src/core/ext/client_config/resolver_result.h diff --git a/BUILD b/BUILD index 5c4333463c6..801310422e6 100644 --- a/BUILD +++ b/BUILD @@ -304,7 +304,6 @@ cc_library( "src/core/ext/client_config/resolver.h", "src/core/ext/client_config/resolver_factory.h", "src/core/ext/client_config/resolver_registry.h", - "src/core/ext/client_config/resolver_result.h", "src/core/ext/client_config/subchannel.h", "src/core/ext/client_config/subchannel_index.h", "src/core/ext/client_config/uri_parser.h", @@ -486,7 +485,6 @@ cc_library( "src/core/ext/client_config/resolver.c", "src/core/ext/client_config/resolver_factory.c", "src/core/ext/client_config/resolver_registry.c", - "src/core/ext/client_config/resolver_result.c", "src/core/ext/client_config/subchannel.c", "src/core/ext/client_config/subchannel_index.c", "src/core/ext/client_config/uri_parser.c", @@ -686,7 +684,6 @@ cc_library( "src/core/ext/client_config/resolver.h", "src/core/ext/client_config/resolver_factory.h", "src/core/ext/client_config/resolver_registry.h", - "src/core/ext/client_config/resolver_result.h", "src/core/ext/client_config/subchannel.h", "src/core/ext/client_config/subchannel_index.h", "src/core/ext/client_config/uri_parser.h", @@ -850,7 +847,6 @@ cc_library( "src/core/ext/client_config/resolver.c", "src/core/ext/client_config/resolver_factory.c", "src/core/ext/client_config/resolver_registry.c", - "src/core/ext/client_config/resolver_result.c", "src/core/ext/client_config/subchannel.c", "src/core/ext/client_config/subchannel_index.c", "src/core/ext/client_config/uri_parser.c", @@ -1045,7 +1041,6 @@ cc_library( "src/core/ext/client_config/resolver.h", "src/core/ext/client_config/resolver_factory.h", "src/core/ext/client_config/resolver_registry.h", - "src/core/ext/client_config/resolver_result.h", "src/core/ext/client_config/subchannel.h", "src/core/ext/client_config/subchannel_index.h", "src/core/ext/client_config/uri_parser.h", @@ -1202,7 +1197,6 @@ cc_library( "src/core/ext/client_config/resolver.c", "src/core/ext/client_config/resolver_factory.c", "src/core/ext/client_config/resolver_registry.c", - "src/core/ext/client_config/resolver_result.c", "src/core/ext/client_config/subchannel.c", "src/core/ext/client_config/subchannel_index.c", "src/core/ext/client_config/uri_parser.c", @@ -1993,7 +1987,6 @@ objc_library( "src/core/ext/client_config/resolver.c", "src/core/ext/client_config/resolver_factory.c", "src/core/ext/client_config/resolver_registry.c", - "src/core/ext/client_config/resolver_result.c", "src/core/ext/client_config/subchannel.c", "src/core/ext/client_config/subchannel_index.c", "src/core/ext/client_config/uri_parser.c", @@ -2195,7 +2188,6 @@ objc_library( "src/core/ext/client_config/resolver.h", "src/core/ext/client_config/resolver_factory.h", "src/core/ext/client_config/resolver_registry.h", - "src/core/ext/client_config/resolver_result.h", "src/core/ext/client_config/subchannel.h", "src/core/ext/client_config/subchannel_index.h", "src/core/ext/client_config/uri_parser.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 893aac36fbb..09febb191ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -451,7 +451,6 @@ add_library(grpc src/core/ext/client_config/resolver.c src/core/ext/client_config/resolver_factory.c src/core/ext/client_config/resolver_registry.c - src/core/ext/client_config/resolver_result.c src/core/ext/client_config/subchannel.c src/core/ext/client_config/subchannel_index.c src/core/ext/client_config/uri_parser.c @@ -686,7 +685,6 @@ add_library(grpc_cronet src/core/ext/client_config/resolver.c src/core/ext/client_config/resolver_factory.c src/core/ext/client_config/resolver_registry.c - src/core/ext/client_config/resolver_result.c src/core/ext/client_config/subchannel.c src/core/ext/client_config/subchannel_index.c src/core/ext/client_config/uri_parser.c @@ -919,7 +917,6 @@ add_library(grpc_unsecure src/core/ext/client_config/resolver.c src/core/ext/client_config/resolver_factory.c src/core/ext/client_config/resolver_registry.c - src/core/ext/client_config/resolver_result.c src/core/ext/client_config/subchannel.c src/core/ext/client_config/subchannel_index.c src/core/ext/client_config/uri_parser.c diff --git a/Makefile b/Makefile index 38be9e658ce..7c3feedd4e6 100644 --- a/Makefile +++ b/Makefile @@ -2708,7 +2708,6 @@ LIBGRPC_SRC = \ src/core/ext/client_config/resolver.c \ src/core/ext/client_config/resolver_factory.c \ src/core/ext/client_config/resolver_registry.c \ - src/core/ext/client_config/resolver_result.c \ src/core/ext/client_config/subchannel.c \ src/core/ext/client_config/subchannel_index.c \ src/core/ext/client_config/uri_parser.c \ @@ -2961,7 +2960,6 @@ LIBGRPC_CRONET_SRC = \ src/core/ext/client_config/resolver.c \ src/core/ext/client_config/resolver_factory.c \ src/core/ext/client_config/resolver_registry.c \ - src/core/ext/client_config/resolver_result.c \ src/core/ext/client_config/subchannel.c \ src/core/ext/client_config/subchannel_index.c \ src/core/ext/client_config/uri_parser.c \ @@ -3425,7 +3423,6 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/client_config/resolver.c \ src/core/ext/client_config/resolver_factory.c \ src/core/ext/client_config/resolver_registry.c \ - src/core/ext/client_config/resolver_result.c \ src/core/ext/client_config/subchannel.c \ src/core/ext/client_config/subchannel_index.c \ src/core/ext/client_config/uri_parser.c \ diff --git a/binding.gyp b/binding.gyp index 397bb1b639b..03239060161 100644 --- a/binding.gyp +++ b/binding.gyp @@ -726,7 +726,6 @@ 'src/core/ext/client_config/resolver.c', 'src/core/ext/client_config/resolver_factory.c', 'src/core/ext/client_config/resolver_registry.c', - 'src/core/ext/client_config/resolver_result.c', 'src/core/ext/client_config/subchannel.c', 'src/core/ext/client_config/subchannel_index.c', 'src/core/ext/client_config/uri_parser.c', diff --git a/build.yaml b/build.yaml index 2a066531036..ee3d2727431 100644 --- a/build.yaml +++ b/build.yaml @@ -363,7 +363,6 @@ filegroups: - src/core/ext/client_config/resolver.h - src/core/ext/client_config/resolver_factory.h - src/core/ext/client_config/resolver_registry.h - - src/core/ext/client_config/resolver_result.h - src/core/ext/client_config/subchannel.h - src/core/ext/client_config/subchannel_index.h - src/core/ext/client_config/uri_parser.h @@ -384,7 +383,6 @@ filegroups: - src/core/ext/client_config/resolver.c - src/core/ext/client_config/resolver_factory.c - src/core/ext/client_config/resolver_registry.c - - src/core/ext/client_config/resolver_result.c - src/core/ext/client_config/subchannel.c - src/core/ext/client_config/subchannel_index.c - src/core/ext/client_config/uri_parser.c diff --git a/config.m4 b/config.m4 index d8716753b66..49dc222fac9 100644 --- a/config.m4 +++ b/config.m4 @@ -245,7 +245,6 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/client_config/resolver.c \ src/core/ext/client_config/resolver_factory.c \ src/core/ext/client_config/resolver_registry.c \ - src/core/ext/client_config/resolver_result.c \ src/core/ext/client_config/subchannel.c \ src/core/ext/client_config/subchannel_index.c \ src/core/ext/client_config/uri_parser.c \ diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index bb1bbc5f0eb..91358feab66 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -391,7 +391,6 @@ Pod::Spec.new do |s| 'src/core/ext/client_config/resolver.h', 'src/core/ext/client_config/resolver_factory.h', 'src/core/ext/client_config/resolver_registry.h', - 'src/core/ext/client_config/resolver_result.h', 'src/core/ext/client_config/subchannel.h', 'src/core/ext/client_config/subchannel_index.h', 'src/core/ext/client_config/uri_parser.h', @@ -577,7 +576,6 @@ Pod::Spec.new do |s| 'src/core/ext/client_config/resolver.c', 'src/core/ext/client_config/resolver_factory.c', 'src/core/ext/client_config/resolver_registry.c', - 'src/core/ext/client_config/resolver_result.c', 'src/core/ext/client_config/subchannel.c', 'src/core/ext/client_config/subchannel_index.c', 'src/core/ext/client_config/uri_parser.c', @@ -768,7 +766,6 @@ Pod::Spec.new do |s| 'src/core/ext/client_config/resolver.h', 'src/core/ext/client_config/resolver_factory.h', 'src/core/ext/client_config/resolver_registry.h', - 'src/core/ext/client_config/resolver_result.h', 'src/core/ext/client_config/subchannel.h', 'src/core/ext/client_config/subchannel_index.h', 'src/core/ext/client_config/uri_parser.h', diff --git a/grpc.gemspec b/grpc.gemspec index 85172922cc8..dbf8502b7d0 100755 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -311,7 +311,6 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/client_config/resolver.h ) s.files += %w( src/core/ext/client_config/resolver_factory.h ) s.files += %w( src/core/ext/client_config/resolver_registry.h ) - s.files += %w( src/core/ext/client_config/resolver_result.h ) s.files += %w( src/core/ext/client_config/subchannel.h ) s.files += %w( src/core/ext/client_config/subchannel_index.h ) s.files += %w( src/core/ext/client_config/uri_parser.h ) @@ -497,7 +496,6 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/client_config/resolver.c ) s.files += %w( src/core/ext/client_config/resolver_factory.c ) s.files += %w( src/core/ext/client_config/resolver_registry.c ) - s.files += %w( src/core/ext/client_config/resolver_result.c ) s.files += %w( src/core/ext/client_config/subchannel.c ) s.files += %w( src/core/ext/client_config/subchannel_index.c ) s.files += %w( src/core/ext/client_config/uri_parser.c ) diff --git a/package.xml b/package.xml index 31a2822a758..d7517c01adf 100644 --- a/package.xml +++ b/package.xml @@ -318,7 +318,6 @@ - @@ -504,7 +503,6 @@ - diff --git a/src/core/ext/client_config/README.md b/src/core/ext/client_config/README.md index eda01e3e715..7c209db12e3 100644 --- a/src/core/ext/client_config/README.md +++ b/src/core/ext/client_config/README.md @@ -5,28 +5,27 @@ This library provides high level configuration machinery to construct client channels and load balance between them. Each grpc_channel is created with a grpc_resolver. It is the resolver's duty -to resolve a name into configuration data for the channel. Such configuration -data might include: +to resolve a name into a set of arguments for the channel. Such arguments +might include: - a list of (ip, port) addresses to connect to - a load balancing policy to decide which server to send a request to - a set of filters to mutate outgoing requests (say, by adding metadata) -The resolver provides this data as a stream of grpc_resolver_result objects to -the channel. We represent configuration as a stream so that it can be changed -by the resolver during execution, by reacting to external events (such as a -new configuration file being pushed to some store). +The resolver provides this data as a stream of grpc_channel_args objects to +the channel. We represent arguments as a stream so that they can be changed +by the resolver during execution, by reacting to external events (such as +new service configuration data being pushed to some store). Load Balancing -------------- -Load balancing configuration is provided by a grpc_lb_policy object, stored as -part of grpc_resolver_result. +Load balancing configuration is provided by a grpc_lb_policy object. -The primary job of the load balancing policies is to pick a target server given only the -initial metadata for a request. It does this by providing a grpc_subchannel -object to the owning channel. +The primary job of the load balancing policies is to pick a target server +given only the initial metadata for a request. It does this by providing +a grpc_subchannel object to the owning channel. Sub-Channels diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c index f23da5f35d2..f1fca822e8a 100644 --- a/src/core/ext/client_config/client_channel.c +++ b/src/core/ext/client_config/client_channel.c @@ -79,7 +79,7 @@ typedef struct client_channel_channel_data { /** method config table */ grpc_method_config_table *method_config_table; /** incoming resolver result - set by resolver.next() */ - grpc_resolver_result *resolver_result; + grpc_channel_args *resolver_result; /** a list of closures that are all waiting for config to come in */ grpc_closure_list waiting_for_config_closures; /** resolver callback */ @@ -184,41 +184,42 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, if (chand->resolver_result != NULL) { grpc_lb_policy_args lb_policy_args; - lb_policy_args.server_name = - grpc_resolver_result_get_server_name(chand->resolver_result); - lb_policy_args.addresses = - grpc_resolver_result_get_addresses(chand->resolver_result); - lb_policy_args.args = - grpc_resolver_result_get_channel_args(chand->resolver_result); + lb_policy_args.args = chand->resolver_result; lb_policy_args.client_channel_factory = chand->client_channel_factory; // Find LB policy name. const char *lb_policy_name = NULL; - const grpc_arg *lb_policy_name_arg = + const grpc_arg *channel_arg = grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_POLICY_NAME); - if (lb_policy_name_arg != NULL) { - GPR_ASSERT(lb_policy_name_arg->type == GRPC_ARG_STRING); - lb_policy_name = lb_policy_name_arg->value.string; + if (channel_arg != NULL) { + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + lb_policy_name = channel_arg->value.string; } // Special case: If all of the addresses are balancer addresses, // assume that we should use the grpclb policy, regardless of what the // resolver actually specified. - bool found_backend_address = false; - for (size_t i = 0; i < lb_policy_args.addresses->num_addresses; ++i) { - if (!lb_policy_args.addresses->addresses[i].is_balancer) { - found_backend_address = true; - break; + channel_arg = + grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_ADDRESSES); + if (channel_arg != NULL) { + GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses* addresses = channel_arg->value.pointer.p; + bool found_backend_address = false; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (!addresses->addresses[i].is_balancer) { + found_backend_address = true; + break; + } } - } - if (!found_backend_address) { - if (lb_policy_name != NULL && strcmp(lb_policy_name, "grpclb") != 0) { - gpr_log(GPR_INFO, - "resolver requested LB policy %s but provided only balancer " - "addresses, no backend addresses -- forcing use of grpclb LB " - "policy", - (lb_policy_name == NULL ? "(none)" : lb_policy_name)); + if (!found_backend_address) { + if (lb_policy_name != NULL && strcmp(lb_policy_name, "grpclb") != 0) { + gpr_log(GPR_INFO, + "resolver requested LB policy %s but provided only balancer " + "addresses, no backend addresses -- forcing use of grpclb LB " + "policy", + (lb_policy_name == NULL ? "(none)" : lb_policy_name)); + } + lb_policy_name = "grpclb"; } - lb_policy_name = "grpclb"; } // Use pick_first if nothing was specified and we didn't select grpclb // above. @@ -232,14 +233,14 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, state = grpc_lb_policy_check_connectivity(exec_ctx, lb_policy, &state_error); } - const grpc_arg *channel_arg = grpc_channel_args_find( - lb_policy_args.args, GRPC_ARG_SERVICE_CONFIG); + channel_arg = + grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_SERVICE_CONFIG); if (channel_arg != NULL) { GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); method_config_table = grpc_method_config_table_ref( (grpc_method_config_table *)channel_arg->value.pointer.p); } - grpc_resolver_result_unref(exec_ctx, chand->resolver_result); + grpc_channel_args_destroy(chand->resolver_result); chand->resolver_result = NULL; } diff --git a/src/core/ext/client_config/lb_policy_factory.h b/src/core/ext/client_config/lb_policy_factory.h index f0798a31674..26fe8f17ae4 100644 --- a/src/core/ext/client_config/lb_policy_factory.h +++ b/src/core/ext/client_config/lb_policy_factory.h @@ -102,11 +102,7 @@ grpc_arg grpc_lb_addresses_create_channel_arg( const grpc_lb_addresses *addresses); /** Arguments passed to LB policies. */ -/* TODO(roth, ctiller): Consider replacing this struct with - grpc_channel_args. See comment in resolver_result.h for details. */ typedef struct grpc_lb_policy_args { - const char *server_name; - grpc_lb_addresses *addresses; grpc_client_channel_factory *client_channel_factory; grpc_channel_args *args; } grpc_lb_policy_args; diff --git a/src/core/ext/client_config/resolver.c b/src/core/ext/client_config/resolver.c index 7534ea62af5..d3ca12484f4 100644 --- a/src/core/ext/client_config/resolver.c +++ b/src/core/ext/client_config/resolver.c @@ -76,7 +76,7 @@ void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, } void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **result, + grpc_channel_args **result, grpc_closure *on_complete) { resolver->vtable->next(exec_ctx, resolver, result, on_complete); } diff --git a/src/core/ext/client_config/resolver.h b/src/core/ext/client_config/resolver.h index 88ac262d513..b5979a5bfd1 100644 --- a/src/core/ext/client_config/resolver.h +++ b/src/core/ext/client_config/resolver.h @@ -34,15 +34,13 @@ #ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H #define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H -#include "src/core/ext/client_config/resolver_result.h" #include "src/core/ext/client_config/subchannel.h" #include "src/core/lib/iomgr/iomgr.h" typedef struct grpc_resolver grpc_resolver; typedef struct grpc_resolver_vtable grpc_resolver_vtable; -/** grpc_resolver provides grpc_resolver_result objects to grpc_channel - objects */ +/** grpc_resolver provides grpc_channel_args objects to grpc_channel objects */ struct grpc_resolver { const grpc_resolver_vtable *vtable; gpr_refcount refs; @@ -53,7 +51,7 @@ struct grpc_resolver_vtable { void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void (*channel_saw_error)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void (*next)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **result, grpc_closure *on_complete); + grpc_channel_args **result, grpc_closure *on_complete); }; #ifdef GRPC_RESOLVER_REFCOUNT_DEBUG @@ -81,14 +79,13 @@ void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); -/** Get the next client config. Called by the channel to fetch a new - configuration. Expected to set *result with a new configuration, - and then schedule on_complete for execution. +/** Get the next result from the resolver. Expected to set *result with + new channel args and then schedule on_complete for execution. If resolution is fatally broken, set *result to NULL and schedule on_complete. */ void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **result, + grpc_channel_args **result, grpc_closure *on_complete); #endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H */ diff --git a/src/core/ext/client_config/resolver_factory.h b/src/core/ext/client_config/resolver_factory.h index e70e0565772..e97281c922b 100644 --- a/src/core/ext/client_config/resolver_factory.h +++ b/src/core/ext/client_config/resolver_factory.h @@ -41,8 +41,6 @@ typedef struct grpc_resolver_factory grpc_resolver_factory; typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable; -/** grpc_resolver provides grpc_resolver_result objects to grpc_channel - objects */ struct grpc_resolver_factory { const grpc_resolver_factory_vtable *vtable; }; diff --git a/src/core/ext/client_config/resolver_result.c b/src/core/ext/client_config/resolver_result.c deleted file mode 100644 index e6a6f2da620..00000000000 --- a/src/core/ext/client_config/resolver_result.c +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -#include "src/core/ext/client_config/resolver_result.h" - -#include - -#include -#include - -#include "src/core/lib/channel/channel_args.h" - -struct grpc_resolver_result { - gpr_refcount refs; - char* server_name; - grpc_lb_addresses* addresses; - char* lb_policy_name; - grpc_channel_args* channel_args; -}; - -grpc_resolver_result* grpc_resolver_result_create( - const char* server_name, grpc_lb_addresses* addresses, - const char* lb_policy_name, grpc_channel_args* args) { - grpc_resolver_result* result = gpr_malloc(sizeof(*result)); - memset(result, 0, sizeof(*result)); - gpr_ref_init(&result->refs, 1); - result->server_name = gpr_strdup(server_name); - result->addresses = addresses; - result->lb_policy_name = gpr_strdup(lb_policy_name); - result->channel_args = args; - return result; -} - -void grpc_resolver_result_ref(grpc_resolver_result* result) { - gpr_ref(&result->refs); -} - -void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx, - grpc_resolver_result* result) { - if (gpr_unref(&result->refs)) { - gpr_free(result->server_name); - grpc_lb_addresses_destroy(result->addresses); - gpr_free(result->lb_policy_name); - grpc_channel_args_destroy(result->channel_args); - gpr_free(result); - } -} - -const char* grpc_resolver_result_get_server_name(grpc_resolver_result* result) { - return result->server_name; -} - -grpc_lb_addresses* grpc_resolver_result_get_addresses( - grpc_resolver_result* result) { - return result->addresses; -} - -const char* grpc_resolver_result_get_lb_policy_name( - grpc_resolver_result* result) { - return result->lb_policy_name; -} - -grpc_channel_args* grpc_resolver_result_get_channel_args( - grpc_resolver_result* result) { - return result->channel_args; -} diff --git a/src/core/ext/client_config/resolver_result.h b/src/core/ext/client_config/resolver_result.h deleted file mode 100644 index f081c0448a6..00000000000 --- a/src/core/ext/client_config/resolver_result.h +++ /dev/null @@ -1,69 +0,0 @@ -// -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H - -#include "src/core/ext/client_config/lb_policy_factory.h" -#include "src/core/lib/iomgr/resolve_address.h" - -// TODO(roth, ctiller): In the long term, we are considering replacing -// the resolver_result data structure with grpc_channel_args. The idea is -// that the resolver will return a set of channel args that contains the -// information that is currently in the resolver_result struct. For -// example, there will be specific args indicating the set of addresses -// and the name of the LB policy to instantiate. Note that if we did -// this, we would probably want to change the data structure of -// grpc_channel_args such to a hash table or AVL or some other data -// structure that does not require linear search to find keys. - -/// Results reported from a grpc_resolver. -typedef struct grpc_resolver_result grpc_resolver_result; - -/// Takes ownership of \a addresses and \a args. -grpc_resolver_result* grpc_resolver_result_create( - const char* server_name, grpc_lb_addresses* addresses, - const char* lb_policy_name, grpc_channel_args* args); - -void grpc_resolver_result_ref(grpc_resolver_result* result); -void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx, - grpc_resolver_result* result); - -/// Accessors. Caller does NOT take ownership of results. -const char* grpc_resolver_result_get_server_name(grpc_resolver_result* result); -grpc_lb_addresses* grpc_resolver_result_get_addresses( - grpc_resolver_result* result); -const char* grpc_resolver_result_get_lb_policy_name( - grpc_resolver_result* result); -grpc_channel_args* grpc_resolver_result_get_channel_args( - grpc_resolver_result* result); - -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H */ diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c index fdc0bec9968..9784150eef7 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/lb_policy/grpclb/grpclb.c @@ -465,16 +465,21 @@ static grpc_lb_policy *create_rr_locked( glb_lb_policy *glb_policy) { GPR_ASSERT(serverlist != NULL && serverlist->num_servers > 0); + if (glb_policy->addresses != NULL) { + /* dispose of the previous version */ + grpc_lb_addresses_destroy(glb_policy->addresses); + } + glb_policy->addresses = process_serverlist(serverlist); + grpc_lb_policy_args args; memset(&args, 0, sizeof(args)); - args.server_name = glb_policy->server_name; args.client_channel_factory = glb_policy->cc_factory; - args.addresses = process_serverlist(serverlist); // Replace the LB addresses in the channel args that we pass down to // the subchannel. static const char* keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES}; - const grpc_arg arg = grpc_lb_addresses_create_channel_arg(args.addresses); + const grpc_arg arg = + grpc_lb_addresses_create_channel_arg(glb_policy->addresses); args.args = grpc_channel_args_copy_and_add_and_remove( glb_policy->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &arg, 1); @@ -482,12 +487,6 @@ static grpc_lb_policy *create_rr_locked( grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args); grpc_channel_args_destroy(args.args); - if (glb_policy->addresses != NULL) { - /* dispose of the previous version */ - grpc_lb_addresses_destroy(glb_policy->addresses); - } - glb_policy->addresses = args.addresses; - return rr; } diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index 039fb2225d5..cb3d8cea27d 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -54,10 +54,7 @@ typedef struct { /** base class: must be first */ grpc_resolver base; - /** target name */ -// FIXME: remove target_name when resolver_result goes away - char *target_name; - /** name to resolve (usually the same as target_name) */ + /** name to resolve */ char *name_to_resolve; /** default port to use */ char *default_port; @@ -75,9 +72,9 @@ typedef struct { /** pending next completion, or NULL */ grpc_closure *next_completion; /** target result address for next completion */ - grpc_resolver_result **target_result; + grpc_channel_args **target_result; /** current (fully resolved) result */ - grpc_resolver_result *resolved_result; + grpc_channel_args *resolved_result; /** retry timer */ bool have_retry_timer; grpc_timer retry_timer; @@ -98,7 +95,7 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete); static const grpc_resolver_vtable dns_resolver_vtable = { @@ -131,7 +128,7 @@ static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, } static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete) { dns_resolver *r = (dns_resolver *)resolver; gpr_mu_lock(&r->mu); @@ -166,7 +163,7 @@ static void dns_on_retry_timer(grpc_exec_ctx *exec_ctx, void *arg, static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { dns_resolver *r = arg; - grpc_resolver_result *result = NULL; + grpc_channel_args *result = NULL; gpr_mu_lock(&r->mu); GPR_ASSERT(r->resolving); r->resolving = false; @@ -180,11 +177,9 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, NULL /* balancer_name */, NULL /* user_data */); } grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses); - grpc_channel_args* args = - grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); + result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); grpc_resolved_addresses_destroy(r->addresses); - result = grpc_resolver_result_create( - r->target_name, addresses, NULL /* lb_policy_name */, args); + grpc_lb_addresses_destroy(addresses); } else { gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); @@ -204,8 +199,8 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_timer_init(exec_ctx, &r->retry_timer, next_try, dns_on_retry_timer, r, now); } - if (r->resolved_result) { - grpc_resolver_result_unref(exec_ctx, r->resolved_result); + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(r->resolved_result); } r->resolved_result = result; r->resolved_version++; @@ -229,10 +224,7 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, dns_resolver *r) { if (r->next_completion != NULL && r->resolved_version != r->published_version) { - *r->target_result = r->resolved_result; - if (r->resolved_result) { - grpc_resolver_result_ref(r->resolved_result); - } + *r->target_result = grpc_channel_args_copy(r->resolved_result); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; r->published_version = r->resolved_version; @@ -242,10 +234,9 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { dns_resolver *r = (dns_resolver *)gr; gpr_mu_destroy(&r->mu); - if (r->resolved_result) { - grpc_resolver_result_unref(exec_ctx, r->resolved_result); + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(r->resolved_result); } - gpr_free(r->target_name); gpr_free(r->name_to_resolve); gpr_free(r->default_port); grpc_channel_args_destroy(r->channel_args); @@ -268,7 +259,6 @@ static grpc_resolver *dns_create(grpc_resolver_args *args, memset(r, 0, sizeof(*r)); gpr_mu_init(&r->mu); grpc_resolver_init(&r->base, &dns_resolver_vtable); - r->target_name = gpr_strdup(path); r->name_to_resolve = proxy_name == NULL ? gpr_strdup(path) : proxy_name; r->default_port = gpr_strdup(default_port); grpc_arg server_name_arg; diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c index 93a34bf305f..3f161f2ebdf 100644 --- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c +++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c @@ -41,6 +41,7 @@ #include #include +#include "src/core/ext/client_config/lb_policy_factory.h" #include "src/core/ext/client_config/parse_address.h" #include "src/core/ext/client_config/resolver_registry.h" #include "src/core/lib/channel/channel_args.h" @@ -51,9 +52,6 @@ typedef struct { /** base class: must be first */ grpc_resolver base; - /** the path component of the uri passed in */ -// FIXME: remove target_name when resolver_result goes away - char *target_name; /** the addresses that we've 'resolved' */ grpc_lb_addresses *addresses; /** channel args */ @@ -65,7 +63,7 @@ typedef struct { /** pending next completion, or NULL */ grpc_closure *next_completion; /** target result address for next completion */ - grpc_resolver_result **target_result; + grpc_channel_args **target_result; } sockaddr_resolver; static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); @@ -77,7 +75,7 @@ static void sockaddr_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete); static const grpc_resolver_vtable sockaddr_resolver_vtable = { @@ -106,7 +104,7 @@ static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, } static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete) { sockaddr_resolver *r = (sockaddr_resolver *)resolver; gpr_mu_lock(&r->mu); @@ -122,11 +120,8 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, if (r->next_completion != NULL && !r->published) { r->published = true; grpc_arg arg = grpc_lb_addresses_create_channel_arg(r->addresses); - grpc_channel_args* args = + *r->target_result = grpc_channel_args_copy_and_add(r->channel_args, &arg, 1); - *r->target_result = grpc_resolver_result_create( - r->target_name, grpc_lb_addresses_copy(r->addresses), - NULL /* lb_policy_name */, args); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; } @@ -135,7 +130,6 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { sockaddr_resolver *r = (sockaddr_resolver *)gr; gpr_mu_destroy(&r->mu); - gpr_free(r->target_name); grpc_lb_addresses_destroy(r->addresses); grpc_channel_args_destroy(r->channel_args); gpr_free(r); @@ -206,7 +200,6 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, /* Instantiate resolver. */ sockaddr_resolver *r = gpr_malloc(sizeof(sockaddr_resolver)); memset(r, 0, sizeof(*r)); - r->target_name = gpr_strdup(args->uri->path); r->addresses = addresses; grpc_arg server_name_arg; server_name_arg.type = GRPC_ARG_STRING; diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index a40edfb0905..d44b826fb80 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -239,7 +239,6 @@ CORE_SOURCE_FILES = [ 'src/core/ext/client_config/resolver.c', 'src/core/ext/client_config/resolver_factory.c', 'src/core/ext/client_config/resolver_registry.c', - 'src/core/ext/client_config/resolver_result.c', 'src/core/ext/client_config/subchannel.c', 'src/core/ext/client_config/subchannel_index.c', 'src/core/ext/client_config/uri_parser.c', diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c index f77f3eb27d2..8e0d8a45b3b 100644 --- a/test/core/end2end/fake_resolver.c +++ b/test/core/end2end/fake_resolver.c @@ -42,6 +42,7 @@ #include #include +#include "src/core/ext/client_config/lb_policy_factory.h" #include "src/core/ext/client_config/method_config.h" #include "src/core/ext/client_config/parse_address.h" #include "src/core/ext/client_config/resolver_registry.h" @@ -59,8 +60,6 @@ typedef struct { grpc_resolver base; // passed-in parameters -// FIXME: remove target_name once resolver_result is removed - char* target_name; // the path component of the uri passed in grpc_channel_args* channel_args; grpc_lb_addresses* addresses; char* lb_policy_name; @@ -73,13 +72,12 @@ typedef struct { // pending next completion, or NULL grpc_closure* next_completion; // target result address for next completion - grpc_resolver_result** target_result; + grpc_channel_args** target_result; } fake_resolver; static void fake_resolver_destroy(grpc_exec_ctx* exec_ctx, grpc_resolver* gr) { fake_resolver* r = (fake_resolver*)gr; gpr_mu_destroy(&r->mu); - gpr_free(r->target_name); grpc_channel_args_destroy(r->channel_args); grpc_lb_addresses_destroy(r->addresses); gpr_free(r->lb_policy_name); @@ -116,11 +114,8 @@ static void fake_resolver_maybe_finish_next_locked(grpc_exec_ctx* exec_ctx, new_args[num_args].value.string = r->lb_policy_name; ++num_args; } - grpc_channel_args* args = + *r->target_result = grpc_channel_args_copy_and_add(r->channel_args, new_args, num_args); - *r->target_result = grpc_resolver_result_create( - r->target_name, grpc_lb_addresses_copy(r->addresses), - r->lb_policy_name, args); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; } @@ -136,7 +131,7 @@ static void fake_resolver_channel_saw_error(grpc_exec_ctx* exec_ctx, } static void fake_resolver_next(grpc_exec_ctx* exec_ctx, grpc_resolver* resolver, - grpc_resolver_result** target_result, + grpc_channel_args** target_result, grpc_closure* on_complete) { fake_resolver* r = (fake_resolver*)resolver; gpr_mu_lock(&r->mu); @@ -244,11 +239,10 @@ static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory, // Instantiate resolver. fake_resolver* r = gpr_malloc(sizeof(fake_resolver)); memset(r, 0, sizeof(*r)); - r->target_name = gpr_strdup(args->uri->path); grpc_arg server_name_arg; server_name_arg.type = GRPC_ARG_STRING; server_name_arg.key = GRPC_ARG_SERVER_NAME; - server_name_arg.value.string = r->target_name; + server_name_arg.value.string = args->uri->path; r->channel_args = grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1); r->addresses = addresses; diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index e5c91cbb131..63d5b04ecc6 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -928,7 +928,6 @@ src/core/ext/client_config/parse_address.h \ src/core/ext/client_config/resolver.h \ src/core/ext/client_config/resolver_factory.h \ src/core/ext/client_config/resolver_registry.h \ -src/core/ext/client_config/resolver_result.h \ src/core/ext/client_config/subchannel.h \ src/core/ext/client_config/subchannel_index.h \ src/core/ext/client_config/uri_parser.h \ @@ -1114,7 +1113,6 @@ src/core/ext/client_config/parse_address.c \ src/core/ext/client_config/resolver.c \ src/core/ext/client_config/resolver_factory.c \ src/core/ext/client_config/resolver_registry.c \ -src/core/ext/client_config/resolver_result.c \ src/core/ext/client_config/subchannel.c \ src/core/ext/client_config/subchannel_index.c \ src/core/ext/client_config/uri_parser.c \ diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json index 7cfb1d4c17e..e8ceb294ef7 100644 --- a/tools/run_tests/sources_and_headers.json +++ b/tools/run_tests/sources_and_headers.json @@ -6735,7 +6735,6 @@ "src/core/ext/client_config/resolver.h", "src/core/ext/client_config/resolver_factory.h", "src/core/ext/client_config/resolver_registry.h", - "src/core/ext/client_config/resolver_result.h", "src/core/ext/client_config/subchannel.h", "src/core/ext/client_config/subchannel_index.h", "src/core/ext/client_config/uri_parser.h" @@ -6773,8 +6772,6 @@ "src/core/ext/client_config/resolver_factory.h", "src/core/ext/client_config/resolver_registry.c", "src/core/ext/client_config/resolver_registry.h", - "src/core/ext/client_config/resolver_result.c", - "src/core/ext/client_config/resolver_result.h", "src/core/ext/client_config/subchannel.c", "src/core/ext/client_config/subchannel.h", "src/core/ext/client_config/subchannel_index.c", diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 9ca4908ecac..d831d6df0c3 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -17002,7 +17002,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17023,7 +17025,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17044,7 +17048,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17065,7 +17071,9 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17086,7 +17094,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17107,7 +17117,9 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17128,7 +17140,9 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17149,7 +17163,9 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17170,7 +17186,9 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17191,7 +17209,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17212,7 +17232,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17233,7 +17255,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17254,7 +17278,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17275,7 +17301,9 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17296,7 +17324,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17317,7 +17347,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17338,7 +17370,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17359,7 +17393,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17380,7 +17416,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17401,7 +17439,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17422,7 +17462,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17443,7 +17485,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17464,7 +17508,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17485,7 +17531,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17506,7 +17554,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17527,7 +17577,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17548,7 +17600,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17569,7 +17623,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17590,7 +17646,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17611,7 +17669,9 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17632,7 +17692,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17653,7 +17715,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17674,7 +17738,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17695,7 +17761,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17716,7 +17784,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17737,7 +17807,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17758,7 +17830,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17779,7 +17853,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17800,7 +17876,9 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [], + "exclude_configs": [ + "msan" + ], "flaky": false, "language": "c", "name": "h2_sockpair_1byte_test", @@ -17822,9 +17900,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -17846,9 +17922,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -17870,9 +17944,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -17894,9 +17966,7 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -17918,9 +17988,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -17942,9 +18010,7 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -17966,9 +18032,7 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -17990,9 +18054,7 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18014,9 +18076,7 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18038,9 +18098,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18128,9 +18186,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18152,9 +18208,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18176,9 +18230,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18200,9 +18252,7 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18224,9 +18274,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18248,9 +18296,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18272,9 +18318,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18318,9 +18362,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18342,9 +18384,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18366,9 +18406,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18390,9 +18428,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18414,9 +18450,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18438,9 +18472,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18462,9 +18494,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18486,9 +18516,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18510,9 +18538,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18534,9 +18560,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18558,9 +18582,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18582,9 +18604,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18606,9 +18626,7 @@ "posix" ], "cpu_cost": 0.1, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18630,9 +18648,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18654,9 +18670,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18678,9 +18692,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18702,9 +18714,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18726,9 +18736,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18772,9 +18780,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18796,9 +18802,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18820,9 +18824,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", @@ -18844,9 +18846,7 @@ "posix" ], "cpu_cost": 1.0, - "exclude_configs": [ - "msan" - ], + "exclude_configs": [], "flaky": false, "language": "c", "name": "h2_ssl_test", diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj index b8c0049db50..d7bbc43828d 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj @@ -437,7 +437,6 @@ - @@ -787,8 +786,6 @@ - - diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters index fb1f904811d..2c19b37099c 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters @@ -487,9 +487,6 @@ src\core\ext\client_config - - src\core\ext\client_config - src\core\ext\client_config @@ -1100,9 +1097,6 @@ src\core\ext\client_config - - src\core\ext\client_config - src\core\ext\client_config diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj index 519d7317bad..8ee7471e2ce 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj @@ -403,7 +403,6 @@ - @@ -703,8 +702,6 @@ - - diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters index d30df5c03d5..c0582e8a4fc 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters @@ -412,9 +412,6 @@ src\core\ext\client_config - - src\core\ext\client_config - src\core\ext\client_config @@ -938,9 +935,6 @@ src\core\ext\client_config - - src\core\ext\client_config - src\core\ext\client_config From 557c990c36dd281812016ad81fa79309078e807d Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Mon, 24 Oct 2016 11:12:05 -0700 Subject: [PATCH 10/30] clang-format --- src/core/ext/client_channel/client_channel.c | 2 +- .../ext/client_channel/lb_policy_factory.c | 19 ++++++----- .../ext/client_channel/lb_policy_factory.h | 8 ++--- src/core/ext/client_channel/resolver.c | 3 +- src/core/ext/client_channel/resolver.h | 3 +- .../ext/client_channel/resolver_registry.h | 2 +- src/core/ext/lb_policy/grpclb/grpclb.c | 32 +++++++++---------- .../ext/lb_policy/pick_first/pick_first.c | 14 ++++---- .../ext/lb_policy/round_robin/round_robin.c | 12 +++---- .../ext/resolver/dns/native/dns_resolver.c | 2 +- .../ext/resolver/sockaddr/sockaddr_resolver.c | 4 +-- .../chttp2/client/insecure/channel_create.c | 6 ++-- .../client/secure/secure_channel_create.c | 4 +-- src/core/lib/channel/channel_args.c | 6 ++-- src/core/lib/channel/channel_args.h | 4 +-- test/core/end2end/fake_resolver.c | 4 +-- 16 files changed, 58 insertions(+), 67 deletions(-) diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c index 48459de44d6..acebabe8b02 100644 --- a/src/core/ext/client_channel/client_channel.c +++ b/src/core/ext/client_channel/client_channel.c @@ -202,7 +202,7 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_ADDRESSES); if (channel_arg != NULL) { GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); - grpc_lb_addresses* addresses = channel_arg->value.pointer.p; + grpc_lb_addresses *addresses = channel_arg->value.pointer.p; bool found_backend_address = false; for (size_t i = 0; i < addresses->num_addresses; ++i) { if (!addresses->addresses[i].is_balancer) { diff --git a/src/core/ext/client_channel/lb_policy_factory.c b/src/core/ext/client_channel/lb_policy_factory.c index 67ff71fdaec..8a474c88185 100644 --- a/src/core/ext/client_channel/lb_policy_factory.c +++ b/src/core/ext/client_channel/lb_policy_factory.c @@ -60,9 +60,8 @@ grpc_lb_addresses* grpc_lb_addresses_copy(const grpc_lb_addresses* addresses) { gpr_strdup(new_addresses->addresses[i].balancer_name); } if (new_addresses->addresses[i].user_data != NULL) { - new_addresses->addresses[i].user_data = - addresses->user_data_vtable->copy( - new_addresses->addresses[i].user_data); + new_addresses->addresses[i].user_data = addresses->user_data_vtable->copy( + new_addresses->addresses[i].user_data); } } return new_addresses; @@ -93,15 +92,15 @@ int grpc_lb_addresses_cmp(const grpc_lb_addresses* addresses1, const grpc_lb_address* target2 = &addresses2->addresses[i]; if (target1->address.len > target2->address.len) return 1; if (target1->address.len < target2->address.len) return -1; - int retval = memcmp( - target1->address.addr, target2->address.addr, target1->address.len); + int retval = memcmp(target1->address.addr, target2->address.addr, + target1->address.len); if (retval != 0) return retval; if (target1->is_balancer > target2->is_balancer) return 1; if (target1->is_balancer < target2->is_balancer) return -1; - const char* balancer_name1 = target1->balancer_name != NULL - ? target1->balancer_name : ""; - const char* balancer_name2 = target2->balancer_name != NULL - ? target2->balancer_name : ""; + const char* balancer_name1 = + target1->balancer_name != NULL ? target1->balancer_name : ""; + const char* balancer_name2 = + target2->balancer_name != NULL ? target2->balancer_name : ""; retval = strcmp(balancer_name1, balancer_name2); if (retval != 0) return retval; if (addresses1->user_data_vtable != NULL) { @@ -137,7 +136,7 @@ static const grpc_arg_pointer_vtable lb_addresses_arg_vtable = { lb_addresses_copy, lb_addresses_destroy, lb_addresses_cmp}; grpc_arg grpc_lb_addresses_create_channel_arg( - const grpc_lb_addresses *addresses) { + const grpc_lb_addresses* addresses) { grpc_arg arg; arg.type = GRPC_ARG_POINTER; arg.key = GRPC_ARG_LB_ADDRESSES; diff --git a/src/core/ext/client_channel/lb_policy_factory.h b/src/core/ext/client_channel/lb_policy_factory.h index 27b0a4de015..c19126d04fd 100644 --- a/src/core/ext/client_channel/lb_policy_factory.h +++ b/src/core/ext/client_channel/lb_policy_factory.h @@ -60,9 +60,9 @@ typedef struct grpc_lb_address { } grpc_lb_address; typedef struct grpc_lb_user_data_vtable { - void* (*copy)(void*); - void (*destroy)(void*); - int (*cmp)(void*, void*); + void *(*copy)(void *); + void (*destroy)(void *); + int (*cmp)(void *, void *); } grpc_lb_user_data_vtable; typedef struct grpc_lb_addresses { @@ -75,7 +75,7 @@ typedef struct grpc_lb_addresses { \a num_addresses addresses. The \a user_data_vtable argument may be NULL if no user data will be added. */ grpc_lb_addresses *grpc_lb_addresses_create( - size_t num_addresses, const grpc_lb_user_data_vtable* user_data_vtable); + size_t num_addresses, const grpc_lb_user_data_vtable *user_data_vtable); /** Creates a copy of \a addresses. If \a user_data_copy is not NULL, * it will be invoked to copy the \a user_data field of each address. */ diff --git a/src/core/ext/client_channel/resolver.c b/src/core/ext/client_channel/resolver.c index 3b9b6d56414..2ae4fe862e9 100644 --- a/src/core/ext/client_channel/resolver.c +++ b/src/core/ext/client_channel/resolver.c @@ -76,7 +76,6 @@ void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, } void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_channel_args **result, - grpc_closure *on_complete) { + grpc_channel_args **result, grpc_closure *on_complete) { resolver->vtable->next(exec_ctx, resolver, result, on_complete); } diff --git a/src/core/ext/client_channel/resolver.h b/src/core/ext/client_channel/resolver.h index a7097935d2d..0bebcf00b36 100644 --- a/src/core/ext/client_channel/resolver.h +++ b/src/core/ext/client_channel/resolver.h @@ -85,7 +85,6 @@ void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, If resolution is fatally broken, set *result to NULL and schedule on_complete. */ void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_channel_args **result, - grpc_closure *on_complete); + grpc_channel_args **result, grpc_closure *on_complete); #endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_H */ diff --git a/src/core/ext/client_channel/resolver_registry.h b/src/core/ext/client_channel/resolver_registry.h index 60e6d003fb3..ca57bef6fbe 100644 --- a/src/core/ext/client_channel/resolver_registry.h +++ b/src/core/ext/client_channel/resolver_registry.h @@ -59,7 +59,7 @@ void grpc_register_resolver_type(grpc_resolver_factory *factory); return it. If a resolver factory was not found, return NULL. */ grpc_resolver *grpc_resolver_create(const char *target, - const grpc_channel_args* args); + const grpc_channel_args *args); /** Find a resolver factory given a name and return an (owned-by-the-caller) * reference to it */ diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c index 45947935189..b0ec5633a72 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/lb_policy/grpclb/grpclb.c @@ -339,13 +339,13 @@ static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, } /* vtable for LB tokens in grpc_lb_addresses. */ -static void* lb_token_copy(void *token) { +static void *lb_token_copy(void *token) { return token == NULL ? NULL : GRPC_MDELEM_REF(token); } static void lb_token_destroy(void *token) { if (token != NULL) GRPC_MDELEM_UNREF(token); } -static int lb_token_cmp(void* token1, void* token2) { +static int lb_token_cmp(void *token1, void *token2) { if (token1 > token2) return 1; if (token1 < token2) return -1; return 0; @@ -477,7 +477,7 @@ static grpc_lb_policy *create_rr_locked( // Replace the LB addresses in the channel args that we pass down to // the subchannel. - static const char* keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES}; + static const char *keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES}; const grpc_arg arg = grpc_lb_addresses_create_channel_arg(glb_policy->addresses); args.args = grpc_channel_args_copy_and_add_and_remove( @@ -582,11 +582,10 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args) { /* Get server name. */ - const grpc_arg* arg = + const grpc_arg *arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); - const char* server_name = - arg != NULL && arg->type == GRPC_ARG_STRING - ? arg->value.string : NULL; + const char *server_name = + arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL; /* Count the number of gRPC-LB addresses. There must be at least one. * TODO(roth): For now, we ignore non-balancer addresses, but in the @@ -597,7 +596,7 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, * this is the right LB policy to use. */ arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); - grpc_lb_addresses* addresses = arg->value.pointer.p; + grpc_lb_addresses *addresses = arg->value.pointer.p; size_t num_grpclb_addrs = 0; for (size_t i = 0; i < addresses->num_addresses; ++i) { if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; @@ -631,14 +630,13 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, if (addresses->addresses[i].is_balancer) { if (addr_index == 0) { addr_strs[addr_index++] = grpc_sockaddr_to_uri( - (const struct sockaddr *)&addresses->addresses[i] - .address.addr); + (const struct sockaddr *)&addresses->addresses[i].address.addr); } else { - GPR_ASSERT(grpc_sockaddr_to_string( - &addr_strs[addr_index++], - (const struct sockaddr *)&addresses->addresses[i] - .address.addr, - true) > 0); + GPR_ASSERT( + grpc_sockaddr_to_string( + &addr_strs[addr_index++], + (const struct sockaddr *)&addresses->addresses[i].address.addr, + true) > 0); } } } @@ -661,8 +659,8 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, * so that it does not wind up recursively using the grpclb LB policy, * as per the special case logic in client_channel.c. */ - static const char* keys_to_remove[] = { - GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES}; + static const char *keys_to_remove[] = {GRPC_ARG_LB_POLICY_NAME, + GRPC_ARG_LB_ADDRESSES}; grpc_channel_args *new_args = grpc_channel_args_copy_and_remove( args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove)); glb_policy->lb_channel = grpc_client_channel_factory_create_channel( diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c index d3166f34168..da4341d30de 100644 --- a/src/core/ext/lb_policy/pick_first/pick_first.c +++ b/src/core/ext/lb_policy/pick_first/pick_first.c @@ -35,8 +35,8 @@ #include -#include "src/core/lib/channel/channel_args.h" #include "src/core/ext/client_channel/lb_policy_registry.h" +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/transport/connectivity_state.h" typedef struct pending_pick { @@ -437,17 +437,16 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, GPR_ASSERT(args->client_channel_factory != NULL); /* Get server name. */ - const grpc_arg* arg = + const grpc_arg *arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); - const char* server_name = - arg != NULL && arg->type == GRPC_ARG_STRING - ? arg->value.string : NULL; + const char *server_name = + arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL; /* Find the number of backend addresses. We ignore balancer * addresses, since we don't know how to handle them. */ arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); - grpc_lb_addresses* addresses = arg->value.pointer.p; + grpc_lb_addresses *addresses = arg->value.pointer.p; size_t num_addrs = 0; for (size_t i = 0; i < addresses->num_addresses; i++) { if (!addresses->addresses[i].is_balancer) ++num_addrs; @@ -474,8 +473,7 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, /* server_name will be copied as part of the subchannel creation. This makes * the copying of server_name (a borrowed pointer) OK. */ sc_args.server_name = server_name; - sc_args.addr = - (struct sockaddr *)(&addresses->addresses[i].address.addr); + sc_args.addr = (struct sockaddr *)(&addresses->addresses[i].address.addr); sc_args.addr_len = addresses->addresses[i].address.len; sc_args.args = args->args; diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c index 9f35ebf8be8..b2676a7c869 100644 --- a/src/core/ext/lb_policy/round_robin/round_robin.c +++ b/src/core/ext/lb_policy/round_robin/round_robin.c @@ -602,17 +602,16 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, GPR_ASSERT(args->client_channel_factory != NULL); /* Get server name. */ - const grpc_arg* arg = + const grpc_arg *arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); - const char* server_name = - arg != NULL && arg->type == GRPC_ARG_STRING - ? arg->value.string : NULL; + const char *server_name = + arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL; /* Find the number of backend addresses. We ignore balancer * addresses, since we don't know how to handle them. */ arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); - grpc_lb_addresses* addresses = arg->value.pointer.p; + grpc_lb_addresses *addresses = arg->value.pointer.p; size_t num_addrs = 0; for (size_t i = 0; i < addresses->num_addresses; i++) { if (!addresses->addresses[i].is_balancer) ++num_addrs; @@ -636,8 +635,7 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, /* server_name will be copied as part of the subchannel creation. This makes * the copying of server_name (a borrowed pointer) OK. */ sc_args.server_name = server_name; - sc_args.addr = - (struct sockaddr *)(&addresses->addresses[i].address.addr); + sc_args.addr = (struct sockaddr *)(&addresses->addresses[i].address.addr); sc_args.addr_len = addresses->addresses[i].address.len; sc_args.args = args->args; diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index fa6a254c595..4ca8a29b3a6 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -264,7 +264,7 @@ static grpc_resolver *dns_create(grpc_resolver_args *args, grpc_arg server_name_arg; server_name_arg.type = GRPC_ARG_STRING; server_name_arg.key = GRPC_ARG_SERVER_NAME; - server_name_arg.value.string = (char*)path; + server_name_arg.value.string = (char *)path; r->channel_args = grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1); gpr_backoff_init(&r->backoff_state, BACKOFF_MULTIPLIER, BACKOFF_JITTER, diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c index b81b44099e0..921e8f8f15f 100644 --- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c +++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c @@ -175,8 +175,8 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, gpr_slice_buffer path_parts; gpr_slice_buffer_init(&path_parts); gpr_slice_split(path_slice, ",", &path_parts); - grpc_lb_addresses *addresses = grpc_lb_addresses_create( - path_parts.count, NULL /* user_data_vtable */); + grpc_lb_addresses *addresses = + grpc_lb_addresses_create(path_parts.count, NULL /* user_data_vtable */); bool errors_found = false; for (size_t i = 0; i < addresses->num_addresses; i++) { grpc_uri ith_uri = *args->uri; diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c index cf017d0ea5d..b3e1a71987e 100644 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c @@ -195,8 +195,8 @@ static grpc_channel *client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, const char *target, grpc_client_channel_type type, grpc_channel_args *args) { - grpc_channel *channel = grpc_channel_create(exec_ctx, target, args, - GRPC_CLIENT_CHANNEL, NULL); + grpc_channel *channel = + grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); grpc_resolver *resolver = grpc_resolver_create(target, args); if (!resolver) { GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, @@ -233,7 +233,7 @@ grpc_channel *grpc_insecure_channel_create(const char *target, GPR_ASSERT(!reserved); grpc_client_channel_factory *factory = - (grpc_client_channel_factory*)&client_channel_factory; + (grpc_client_channel_factory *)&client_channel_factory; grpc_channel *channel = client_channel_factory_create_channel( &exec_ctx, factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL); diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c index 99929eb123a..37e7b0bcf93 100644 --- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c +++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c @@ -274,8 +274,8 @@ static grpc_channel *client_channel_factory_create_channel( const char *target, grpc_client_channel_type type, grpc_channel_args *args) { client_channel_factory *f = (client_channel_factory *)cc_factory; - grpc_channel *channel = grpc_channel_create(exec_ctx, target, args, - GRPC_CLIENT_CHANNEL, NULL); + grpc_channel *channel = + grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); grpc_resolver *resolver = grpc_resolver_create(target, args); if (resolver != NULL) { grpc_client_channel_finish_initialization( diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c index 0270d6f2246..cfc072c0b56 100644 --- a/src/core/lib/channel/channel_args.c +++ b/src/core/lib/channel/channel_args.c @@ -71,13 +71,13 @@ grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, } grpc_channel_args *grpc_channel_args_copy_and_remove( - const grpc_channel_args *src, const char** to_remove, + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove) { return grpc_channel_args_copy_and_add_and_remove(src, to_remove, num_to_remove, NULL, 0); } -static bool should_remove_arg(const grpc_arg* arg, const char** to_remove, +static bool should_remove_arg(const grpc_arg *arg, const char **to_remove, size_t num_to_remove) { for (size_t i = 0; i < num_to_remove; ++i) { if (strcmp(arg->key, to_remove[i]) == 0) return true; @@ -86,7 +86,7 @@ static bool should_remove_arg(const grpc_arg* arg, const char** to_remove, } grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( - const grpc_channel_args *src, const char** to_remove, size_t num_to_remove, + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove, const grpc_arg *to_add, size_t num_to_add) { // Figure out how many args we'll be copying. size_t num_args_to_copy = 0; diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h index 1508d237484..1e05303471d 100644 --- a/src/core/lib/channel/channel_args.h +++ b/src/core/lib/channel/channel_args.h @@ -54,12 +54,12 @@ grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, /** Copies the arguments in \a src except for those whose keys are in \a to_remove. */ grpc_channel_args *grpc_channel_args_copy_and_remove( - const grpc_channel_args *src, const char** to_remove, size_t num_to_remove); + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove); /** Copies the arguments from \a src except for those whose keys are in \a to_remove and appends the arguments in \a to_add. */ grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( - const grpc_channel_args *src, const char** to_remove, size_t num_to_remove, + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove, const grpc_arg *to_add, size_t num_to_add); /** Concatenate args from \a a and \a b into a new instance */ diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c index 0dcfdcf43b0..c5a916ef8b3 100644 --- a/test/core/end2end/fake_resolver.c +++ b/test/core/end2end/fake_resolver.c @@ -174,8 +174,8 @@ static grpc_resolver* fake_resolver_create(grpc_resolver_factory* factory, gpr_slice_buffer path_parts; gpr_slice_buffer_init(&path_parts); gpr_slice_split(path_slice, ",", &path_parts); - grpc_lb_addresses* addresses = grpc_lb_addresses_create( - path_parts.count, NULL /* user_data_vtable */); + grpc_lb_addresses* addresses = + grpc_lb_addresses_create(path_parts.count, NULL /* user_data_vtable */); bool errors_found = false; for (size_t i = 0; i < addresses->num_addresses; i++) { grpc_uri ith_uri = *args->uri; From 25db523baa162b6b5d0fa2a652aca2d37dfa6dba Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Mon, 24 Oct 2016 12:53:01 -0700 Subject: [PATCH 11/30] Fix sockaddr_resolver_test. --- .../resolvers/sockaddr_resolver_test.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/core/client_channel/resolvers/sockaddr_resolver_test.c b/test/core/client_channel/resolvers/sockaddr_resolver_test.c index c39052cd9dd..20c42b7387e 100644 --- a/test/core/client_channel/resolvers/sockaddr_resolver_test.c +++ b/test/core/client_channel/resolvers/sockaddr_resolver_test.c @@ -38,21 +38,23 @@ #include #include "src/core/ext/client_channel/resolver_registry.h" -#include "src/core/ext/client_channel/resolver_result.h" +#include "src/core/lib/channel/channel_args.h" #include "test/core/util/test_config.h" typedef struct on_resolution_arg { char *expected_server_name; - grpc_resolver_result *resolver_result; + grpc_channel_args *resolver_result; } on_resolution_arg; void on_resolution_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { on_resolution_arg *res = arg; - const char *server_name = - grpc_resolver_result_get_server_name(res->resolver_result); - GPR_ASSERT(strcmp(res->expected_server_name, server_name) == 0); - grpc_resolver_result_unref(exec_ctx, res->resolver_result); + const grpc_arg* channel_arg = + grpc_channel_args_find(res->resolver_result, GRPC_ARG_SERVER_NAME); + GPR_ASSERT(channel_arg != NULL); + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + GPR_ASSERT(strcmp(res->expected_server_name, channel_arg->value.string) == 0); + grpc_channel_args_destroy(res->resolver_result); } static void test_succeeds(grpc_resolver_factory *factory, const char *string) { From b367c1bed706bf408ac4c8f3664e1213505ddc3c Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Mon, 24 Oct 2016 12:59:07 -0700 Subject: [PATCH 12/30] Fix dns_resolver_connectivity_test. --- src/core/ext/resolver/dns/native/dns_resolver.c | 4 +++- .../resolvers/dns_resolver_connectivity_test.c | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index 4ca8a29b3a6..958b8af8b28 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -224,7 +224,9 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, dns_resolver *r) { if (r->next_completion != NULL && r->resolved_version != r->published_version) { - *r->target_result = grpc_channel_args_copy(r->resolved_result); + *r->target_result = r->resolved_result == NULL + ? NULL + : grpc_channel_args_copy(r->resolved_result); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; r->published_version = r->resolved_version; diff --git a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c index 07723c229d2..ffa167a0e78 100644 --- a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c +++ b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c @@ -37,6 +37,7 @@ #include #include "src/core/ext/client_channel/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/timer.h" #include "test/core/util/test_config.h" @@ -103,7 +104,7 @@ int main(int argc, char **argv) { grpc_resolver *resolver = create_resolver("dns:test"); - grpc_resolver_result *result = (grpc_resolver_result *)1; + grpc_channel_args *result = (grpc_channel_args *)1; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; gpr_event ev1; @@ -122,7 +123,7 @@ int main(int argc, char **argv) { GPR_ASSERT(wait_loop(30, &ev2)); GPR_ASSERT(result != NULL); - grpc_resolver_result_unref(&exec_ctx, result); + grpc_channel_args_destroy(result); GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "test"); grpc_exec_ctx_finish(&exec_ctx); From 5f40e5ddf597169a5cf88d1b0471831f1db1edc0 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Mon, 24 Oct 2016 13:09:05 -0700 Subject: [PATCH 13/30] Minor clean-up. --- src/core/ext/client_channel/client_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c index acebabe8b02..dfa84b0d770 100644 --- a/src/core/ext/client_channel/client_channel.c +++ b/src/core/ext/client_channel/client_channel.c @@ -216,7 +216,7 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, "resolver requested LB policy %s but provided only balancer " "addresses, no backend addresses -- forcing use of grpclb LB " "policy", - (lb_policy_name == NULL ? "(none)" : lb_policy_name)); + lb_policy_name); } lb_policy_name = "grpclb"; } From e3a21005bfa42d974ee2b8a0776797ae2b7eea23 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Mon, 24 Oct 2016 13:29:05 -0700 Subject: [PATCH 14/30] Fix propagation of channel args for insecure channels. --- src/core/ext/client_channel/client_channel_factory.c | 4 ++-- src/core/ext/client_channel/client_channel_factory.h | 9 +++++---- src/core/ext/client_channel/subchannel.c | 2 +- src/core/ext/client_channel/subchannel.h | 2 +- src/core/ext/client_channel/subchannel_index.c | 6 +++--- src/core/ext/client_channel/subchannel_index.h | 4 ++-- .../transport/chttp2/client/insecure/channel_create.c | 6 +++--- .../chttp2/client/secure/secure_channel_create.c | 4 ++-- test/cpp/end2end/end2end_test.cc | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/core/ext/client_channel/client_channel_factory.c b/src/core/ext/client_channel/client_channel_factory.c index db1cc9093c6..4900832d573 100644 --- a/src/core/ext/client_channel/client_channel_factory.c +++ b/src/core/ext/client_channel/client_channel_factory.c @@ -44,14 +44,14 @@ void grpc_client_channel_factory_unref(grpc_exec_ctx* exec_ctx, grpc_subchannel* grpc_client_channel_factory_create_subchannel( grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, - grpc_subchannel_args* args) { + const grpc_subchannel_args* args) { return factory->vtable->create_subchannel(exec_ctx, factory, args); } grpc_channel* grpc_client_channel_factory_create_channel( grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, const char* target, grpc_client_channel_type type, - grpc_channel_args* args) { + const grpc_channel_args* args) { return factory->vtable->create_client_channel(exec_ctx, factory, target, type, args); } diff --git a/src/core/ext/client_channel/client_channel_factory.h b/src/core/ext/client_channel/client_channel_factory.h index 28828c2eb60..2b8fc577b3b 100644 --- a/src/core/ext/client_channel/client_channel_factory.h +++ b/src/core/ext/client_channel/client_channel_factory.h @@ -60,12 +60,12 @@ struct grpc_client_channel_factory_vtable { void (*unref)(grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory); grpc_subchannel *(*create_subchannel)(grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, - grpc_subchannel_args *args); + const grpc_subchannel_args *args); grpc_channel *(*create_client_channel)(grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, const char *target, grpc_client_channel_type type, - grpc_channel_args *args); + const grpc_channel_args *args); }; void grpc_client_channel_factory_ref(grpc_client_channel_factory *factory); @@ -75,11 +75,12 @@ void grpc_client_channel_factory_unref(grpc_exec_ctx *exec_ctx, /** Create a new grpc_subchannel */ grpc_subchannel *grpc_client_channel_factory_create_subchannel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, - grpc_subchannel_args *args); + const grpc_subchannel_args *args); /** Create a new grpc_channel */ grpc_channel *grpc_client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, - const char *target, grpc_client_channel_type type, grpc_channel_args *args); + const char *target, grpc_client_channel_type type, + const grpc_channel_args *args); #endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H */ diff --git a/src/core/ext/client_channel/subchannel.c b/src/core/ext/client_channel/subchannel.c index 672e5c3a913..b35bb79bc7f 100644 --- a/src/core/ext/client_channel/subchannel.c +++ b/src/core/ext/client_channel/subchannel.c @@ -298,7 +298,7 @@ void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, grpc_connector *connector, - grpc_subchannel_args *args) { + const grpc_subchannel_args *args) { grpc_subchannel_key *key = grpc_subchannel_key_create(connector, args); grpc_subchannel *c = grpc_subchannel_index_find(exec_ctx, key); if (c) { diff --git a/src/core/ext/client_channel/subchannel.h b/src/core/ext/client_channel/subchannel.h index c4a7da0c24a..0f34cff14b9 100644 --- a/src/core/ext/client_channel/subchannel.h +++ b/src/core/ext/client_channel/subchannel.h @@ -174,6 +174,6 @@ struct grpc_subchannel_args { /** create a subchannel given a connector */ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, grpc_connector *connector, - grpc_subchannel_args *args); + const grpc_subchannel_args *args); #endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_SUBCHANNEL_H */ diff --git a/src/core/ext/client_channel/subchannel_index.c b/src/core/ext/client_channel/subchannel_index.c index bf22975fba4..7c547f8edb8 100644 --- a/src/core/ext/client_channel/subchannel_index.c +++ b/src/core/ext/client_channel/subchannel_index.c @@ -73,7 +73,7 @@ static grpc_exec_ctx *current_ctx() { } static grpc_subchannel_key *create_key( - grpc_connector *connector, grpc_subchannel_args *args, + grpc_connector *connector, const grpc_subchannel_args *args, grpc_channel_args *(*copy_channel_args)(const grpc_channel_args *args)) { grpc_subchannel_key *k = gpr_malloc(sizeof(*k)); k->connector = grpc_connector_ref(connector); @@ -96,8 +96,8 @@ static grpc_subchannel_key *create_key( return k; } -grpc_subchannel_key *grpc_subchannel_key_create(grpc_connector *connector, - grpc_subchannel_args *args) { +grpc_subchannel_key *grpc_subchannel_key_create( + grpc_connector *connector, const grpc_subchannel_args *args) { return create_key(connector, args, grpc_channel_args_normalize); } diff --git a/src/core/ext/client_channel/subchannel_index.h b/src/core/ext/client_channel/subchannel_index.h index 5b3af6b0546..a67bd5e2195 100644 --- a/src/core/ext/client_channel/subchannel_index.h +++ b/src/core/ext/client_channel/subchannel_index.h @@ -43,8 +43,8 @@ typedef struct grpc_subchannel_key grpc_subchannel_key; /** Create a key that can be used to uniquely identify a subchannel */ -grpc_subchannel_key *grpc_subchannel_key_create(grpc_connector *con, - grpc_subchannel_args *args); +grpc_subchannel_key *grpc_subchannel_key_create( + grpc_connector *con, const grpc_subchannel_args *args); /** Destroy a subchannel key */ void grpc_subchannel_key_destroy(grpc_exec_ctx *exec_ctx, diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c index b3e1a71987e..6dc73169918 100644 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c @@ -173,7 +173,7 @@ static void client_channel_factory_unref( static grpc_subchannel *client_channel_factory_create_subchannel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - grpc_subchannel_args *args) { + const grpc_subchannel_args *args) { connector *c = gpr_malloc(sizeof(*c)); memset(c, 0, sizeof(*c)); c->base.vtable = &connector_vtable; @@ -194,7 +194,7 @@ static grpc_subchannel *client_channel_factory_create_subchannel( static grpc_channel *client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, const char *target, grpc_client_channel_type type, - grpc_channel_args *args) { + const grpc_channel_args *args) { grpc_channel *channel = grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); grpc_resolver *resolver = grpc_resolver_create(target, args); @@ -235,7 +235,7 @@ grpc_channel *grpc_insecure_channel_create(const char *target, grpc_client_channel_factory *factory = (grpc_client_channel_factory *)&client_channel_factory; grpc_channel *channel = client_channel_factory_create_channel( - &exec_ctx, factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL); + &exec_ctx, factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, args); grpc_client_channel_factory_unref(&exec_ctx, factory); grpc_exec_ctx_finish(&exec_ctx); diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c index 37e7b0bcf93..a4f1858aa2b 100644 --- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c +++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c @@ -248,7 +248,7 @@ static void client_channel_factory_unref( static grpc_subchannel *client_channel_factory_create_subchannel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - grpc_subchannel_args *args) { + const grpc_subchannel_args *args) { client_channel_factory *f = (client_channel_factory *)cc_factory; connector *c = gpr_malloc(sizeof(*c)); memset(c, 0, sizeof(*c)); @@ -272,7 +272,7 @@ static grpc_subchannel *client_channel_factory_create_subchannel( static grpc_channel *client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, const char *target, grpc_client_channel_type type, - grpc_channel_args *args) { + const grpc_channel_args *args) { client_channel_factory *f = (client_channel_factory *)cc_factory; grpc_channel *channel = grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index b1d3ce92f6a..7af0fb997e2 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -636,7 +636,7 @@ TEST_P(End2endTest, SimpleRpcWithCustomeUserAgentPrefix) { auto iter = trailing_metadata.find("user-agent"); EXPECT_TRUE(iter != trailing_metadata.end()); grpc::string expected_prefix = user_agent_prefix_ + " grpc-c++/"; - EXPECT_TRUE(iter->second.starts_with(expected_prefix)); + EXPECT_TRUE(iter->second.starts_with(expected_prefix)) << iter->second; } TEST_P(End2endTest, MultipleRpcsWithVariedBinaryMetadataValue) { From 3620287bbe5fc9a955b0f176b48c527d931aa679 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 24 Oct 2016 16:02:29 -0700 Subject: [PATCH 15/30] Sanitize --- package.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 30b4b6787c2..c6b2600209b 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,7 @@ "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test", "install": "./node_modules/.bin/node-pre-gyp install --fallback-to-build" }, - "bundledDependencies": [ - "node-pre-gyp" - ], + "bundledDependencies": ["node-pre-gyp"], "dependencies": { "arguejs": "^0.2.3", "lodash": "^3.9.3", @@ -54,10 +52,11 @@ }, "binary": { "module_name": "grpc_node", - "module_path": "src/node/extension_binary", + "module_path": "./build/Release/", "host": "https://storage.googleapis.com/", "remote_path": "grpc-precompiled-binaries/node/{name}/v{version}", - "package_name": "{node_abi}-{platform}-{arch}.tar.gz" + "package_name": "{node_abi}-{platform}-{arch}.tar.gz", + "module_path": "src/node/extension_binary" }, "files": [ "LICENSE", @@ -78,7 +77,7 @@ ], "main": "src/node/index.js", "license": "BSD-3-Clause", - "jshintConfig": { + "jshintConfig" : { "bitwise": true, "curly": true, "eqeqeq": true, From e917f5d96a58cdfc0aceaa1337d505eef1cef885 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Tue, 25 Oct 2016 07:48:28 -0700 Subject: [PATCH 16/30] Update comments. --- src/core/ext/client_channel/lb_policy_factory.h | 6 ++---- src/core/ext/client_channel/resolver.h | 10 +++++----- src/core/ext/client_channel/resolver_registry.h | 4 +++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/ext/client_channel/lb_policy_factory.h b/src/core/ext/client_channel/lb_policy_factory.h index c19126d04fd..e2b8080a329 100644 --- a/src/core/ext/client_channel/lb_policy_factory.h +++ b/src/core/ext/client_channel/lb_policy_factory.h @@ -77,8 +77,7 @@ typedef struct grpc_lb_addresses { grpc_lb_addresses *grpc_lb_addresses_create( size_t num_addresses, const grpc_lb_user_data_vtable *user_data_vtable); -/** Creates a copy of \a addresses. If \a user_data_copy is not NULL, - * it will be invoked to copy the \a user_data field of each address. */ +/** Creates a copy of \a addresses. */ grpc_lb_addresses *grpc_lb_addresses_copy(const grpc_lb_addresses *addresses); /** Sets the value of the address at index \a index of \a addresses. @@ -93,8 +92,7 @@ void grpc_lb_addresses_set_address(grpc_lb_addresses *addresses, size_t index, int grpc_lb_addresses_cmp(const grpc_lb_addresses *addresses1, const grpc_lb_addresses *addresses2); -/** Destroys \a addresses. If \a user_data_destroy is not NULL, it will - * be invoked to destroy the \a user_data field of each address. */ +/** Destroys \a addresses. */ void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses); /** Returns a channel arg containing \a addresses. */ diff --git a/src/core/ext/client_channel/resolver.h b/src/core/ext/client_channel/resolver.h index 0bebcf00b36..96ece92b9d8 100644 --- a/src/core/ext/client_channel/resolver.h +++ b/src/core/ext/client_channel/resolver.h @@ -40,7 +40,7 @@ typedef struct grpc_resolver grpc_resolver; typedef struct grpc_resolver_vtable grpc_resolver_vtable; -/** grpc_resolver provides grpc_channel_args objects to grpc_channel objects */ +/** \a grpc_resolver provides \a grpc_channel_args objects to its caller */ struct grpc_resolver { const grpc_resolver_vtable *vtable; gpr_refcount refs; @@ -79,11 +79,11 @@ void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); -/** Get the next result from the resolver. Expected to set *result with - new channel args and then schedule on_complete for execution. +/** Get the next result from the resolver. Expected to set \a *result with + new channel args and then schedule \a on_complete for execution. - If resolution is fatally broken, set *result to NULL and - schedule on_complete. */ + If resolution is fatally broken, set \a *result to NULL and + schedule \a on_complete. */ void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_channel_args **result, grpc_closure *on_complete); diff --git a/src/core/ext/client_channel/resolver_registry.h b/src/core/ext/client_channel/resolver_registry.h index ca57bef6fbe..2a95a669f0d 100644 --- a/src/core/ext/client_channel/resolver_registry.h +++ b/src/core/ext/client_channel/resolver_registry.h @@ -57,7 +57,9 @@ void grpc_register_resolver_type(grpc_resolver_factory *factory); was not NULL). If a resolver factory was found, use it to instantiate a resolver and return it. - If a resolver factory was not found, return NULL. */ + If a resolver factory was not found, return NULL. + \a args is a set of channel arguments to be included in the result + (typically the set of arguments passed in from the client API). */ grpc_resolver *grpc_resolver_create(const char *target, const grpc_channel_args *args); From f0b2ec0435aebf64c6c4f8c2fe39917acfb23ef1 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Tue, 25 Oct 2016 07:49:36 -0700 Subject: [PATCH 17/30] clang-format --- test/core/client_channel/resolvers/sockaddr_resolver_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/client_channel/resolvers/sockaddr_resolver_test.c b/test/core/client_channel/resolvers/sockaddr_resolver_test.c index 20c42b7387e..ebf311ab83f 100644 --- a/test/core/client_channel/resolvers/sockaddr_resolver_test.c +++ b/test/core/client_channel/resolvers/sockaddr_resolver_test.c @@ -49,7 +49,7 @@ typedef struct on_resolution_arg { void on_resolution_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { on_resolution_arg *res = arg; - const grpc_arg* channel_arg = + const grpc_arg *channel_arg = grpc_channel_args_find(res->resolver_result, GRPC_ARG_SERVER_NAME); GPR_ASSERT(channel_arg != NULL); GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); From b477cebbe77fca069fb2a4bb1e9806e0891ead83 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Tue, 25 Oct 2016 08:04:54 -0700 Subject: [PATCH 18/30] Fix asan bug. --- .../ext/transport/chttp2/client/secure/secure_channel_create.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c index a4f1858aa2b..00eb82e12a3 100644 --- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c +++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c @@ -346,6 +346,8 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, grpc_channel *channel = client_channel_factory_create_channel( &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args); // Clean up. + GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, + "client_channel_factory_create_channel"); grpc_channel_args_destroy(new_args); grpc_client_channel_factory_unref(&exec_ctx, &f->base); grpc_exec_ctx_finish(&exec_ctx); From a83ee60b932a5cbb0a575f18036fc2a88bd7ed55 Mon Sep 17 00:00:00 2001 From: Sree Kuchibhotla Date: Tue, 25 Oct 2016 11:54:52 -0700 Subject: [PATCH 19/30] New streaming scenario --- .../run_tests/performance/scenario_config.py | 9 ++++ tools/run_tests/tests.json | 42 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/tools/run_tests/performance/scenario_config.py b/tools/run_tests/performance/scenario_config.py index e1c1bc65b6b..d0c28f28b15 100644 --- a/tools/run_tests/performance/scenario_config.py +++ b/tools/run_tests/performance/scenario_config.py @@ -229,6 +229,15 @@ class CXXLanguage: secure=secure, categories=smoketest_categories + [SCALABLE]) + yield _ping_pong_scenario( + 'cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_%s' % secstr, + rpc_type='STREAMING', + client_type='ASYNC_CLIENT', + server_type='SYNC_SERVER', + unconstrained_client='async', + secure=secure, + categories=smoketest_categories+[SCALABLE]) + for rpc_type in ['unary', 'streaming']: for synchronicity in ['sync', 'async']: yield _ping_pong_scenario( diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 938aa2edde2..4171dbc4786 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -33595,6 +33595,27 @@ "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_secure", "timeout_seconds": 180 }, + { + "args": [ + "--scenarios_json", + "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_secure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": {\"use_test_ca\": true, \"server_host_override\": \"foo.test.google.fr\"}, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}" + ], + "boringssl": true, + "ci_platforms": [ + "linux" + ], + "cpu_cost": 8, + "defaults": "boringssl", + "exclude_configs": [], + "flaky": false, + "language": "c++", + "name": "json_run_localhost", + "platforms": [ + "linux" + ], + "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_secure", + "timeout_seconds": 180 + }, { "args": [ "--scenarios_json", @@ -33847,6 +33868,27 @@ "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_unary_qps_unconstrained_insecure", "timeout_seconds": 180 }, + { + "args": [ + "--scenarios_json", + "{\"scenarios\": [{\"name\": \"cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure\", \"warmup_seconds\": 0, \"benchmark_seconds\": 1, \"num_servers\": 1, \"server_config\": {\"async_server_threads\": 0, \"core_limit\": 0, \"security_params\": null, \"server_type\": \"SYNC_SERVER\"}, \"client_config\": {\"client_type\": \"ASYNC_CLIENT\", \"security_params\": null, \"payload_config\": {\"simple_params\": {\"resp_size\": 0, \"req_size\": 0}}, \"client_channels\": 64, \"async_client_threads\": 0, \"outstanding_rpcs_per_channel\": 100, \"rpc_type\": \"STREAMING\", \"load_params\": {\"closed_loop\": {}}, \"histogram_params\": {\"max_possible\": 60000000000.0, \"resolution\": 0.01}}, \"num_clients\": 0}]}" + ], + "boringssl": true, + "ci_platforms": [ + "linux" + ], + "cpu_cost": 8, + "defaults": "boringssl", + "exclude_configs": [], + "flaky": false, + "language": "c++", + "name": "json_run_localhost", + "platforms": [ + "linux" + ], + "shortname": "json_run_localhost:cpp_protobuf_async_client_sync_server_streaming_qps_unconstrained_insecure", + "timeout_seconds": 180 + }, { "args": [ "--scenarios_json", From 2da4666bdd0a1da32521ef86c23b20c3b2608954 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Tue, 25 Oct 2016 15:57:48 -0700 Subject: [PATCH 20/30] Don't set up Python doc packages every time --- setup.py | 8 +++++++- tools/distrib/python/docgen.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7a65698cbb4..cdd3bb3f0dd 100644 --- a/setup.py +++ b/setup.py @@ -79,6 +79,11 @@ BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False) ENABLE_CYTHON_TRACING = os.environ.get( 'GRPC_PYTHON_ENABLE_CYTHON_TRACING', False) +# Environment variable specifying whether or not there's interest in setting up +# documentation building. +ENABLE_DOCUMENTATION_BUILD = os.environ.get( + 'GRPC_PYTHON_ENABLE_DOCUMENTATION_BUILD', False) + # There are some situations (like on Windows) where CC, CFLAGS, and LDFLAGS are # entirely ignored/dropped/forgotten by distutils and its Cygwin/MinGW support. # We use these environment variables to thus get around that without locking @@ -210,7 +215,8 @@ SETUP_REQUIRES = INSTALL_REQUIRES + ( 'sphinx>=1.3', 'sphinx_rtd_theme>=0.1.8', 'six>=1.10', -) + ) if ENABLE_DOCUMENTATION_BUILD else () + if BUILD_WITH_CYTHON: sys.stderr.write( "You requested a Cython build via GRPC_PYTHON_BUILD_WITH_CYTHON, " diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py index 15bd8d855f6..622317920d4 100755 --- a/tools/distrib/python/docgen.py +++ b/tools/distrib/python/docgen.py @@ -67,6 +67,7 @@ environment.update({ 'LDFLAGS': '-L{}'.format(LIBRARY_PATH), 'LD_LIBRARY_PATH': LIBRARY_PATH, 'GRPC_PYTHON_BUILD_WITH_CYTHON': '1', + 'GRPC_PYTHON_ENABLE_DOCUMENTATION_BUILD': '1', }) subprocess_arguments_list = [ From e5293c3f7b77c2ff7bef602e830e87794b12cdc2 Mon Sep 17 00:00:00 2001 From: Alex Polcyn Date: Sun, 31 Jul 2016 19:08:56 -0700 Subject: [PATCH 21/30] combine more core batch ops --- src/ruby/lib/grpc/generic/active_call.rb | 146 ++++++++++++++-------- src/ruby/lib/grpc/generic/client_stub.rb | 12 +- src/ruby/lib/grpc/generic/rpc_desc.rb | 43 +++++-- src/ruby/spec/generic/active_call_spec.rb | 22 ++-- src/ruby/spec/generic/client_stub_spec.rb | 123 ++++++++++++------ src/ruby/spec/generic/rpc_desc_spec.rb | 24 ++-- src/ruby/spec/generic/rpc_server_spec.rb | 1 + src/ruby/spec/pb/health/checker_spec.rb | 38 +++--- 8 files changed, 272 insertions(+), 137 deletions(-) diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb index dfc2644c460..f5c426ebfc6 100644 --- a/src/ruby/lib/grpc/generic/active_call.rb +++ b/src/ruby/lib/grpc/generic/active_call.rb @@ -43,7 +43,8 @@ class Struct GRPC.logger.debug("Failing with status #{status}") # raise BadStatus, propagating the metadata if present. md = status.metadata - fail GRPC::BadStatus.new(status.code, status.details, md) + fail GRPC::BadStatus.new(status.code, status.details, md), + "status code: #{status.code}, details: #{status.details}" end status end @@ -156,41 +157,25 @@ module GRPC Operation.new(self) end - # writes_done indicates that all writes are completed. - # - # It blocks until the remote endpoint acknowledges with at status unless - # assert_finished is set to false. Any calls to #remote_send after this - # call will fail. - # - # @param assert_finished [true, false] when true(default), waits for - # FINISHED. - def writes_done(assert_finished = true) - ops = { - SEND_CLOSE_FROM_CLIENT => nil - } - ops[RECV_STATUS_ON_CLIENT] = nil if assert_finished - batch_result = @call.run_batch(ops) - return unless assert_finished - unless batch_result.status.nil? - @call.trailing_metadata = batch_result.status.metadata - end - @call.status = batch_result.status - op_is_done - batch_result.check_status - end - # finished waits until a client call is completed. # # It blocks until the remote endpoint acknowledges by sending a status. def finished batch_result = @call.run_batch(RECV_STATUS_ON_CLIENT => nil) - unless batch_result.status.nil? - @call.trailing_metadata = batch_result.status.metadata + attach_status_results_and_complete_call(batch_result) + end + + def attach_status_results_and_complete_call(recv_status_batch_result) + unless recv_status_batch_result.status.nil? + @call.trailing_metadata = recv_status_batch_result.status.metadata end - @call.status = batch_result.status - op_is_done - batch_result.check_status + @call.status = recv_status_batch_result.status @call.close + op_is_done + + # The RECV_STATUS in run_batch always succeeds + # Check the status for a bad status or failed run batch + recv_status_batch_result.check_status end # remote_send sends a request to the remote endpoint. @@ -226,6 +211,23 @@ module GRPC nil end + def server_unary_response(req, trailing_metadata: {}, + code: Core::StatusCodes::OK, details: 'OK') + ops = {} + @send_initial_md_mutex.synchronize do + ops[SEND_INITIAL_METADATA] = @metadata_to_send unless @metadata_sent + @metadata_sent = true + end + + payload = @marshal.call(req) + ops[SEND_MESSAGE] = payload + ops[SEND_STATUS_FROM_SERVER] = Struct::Status.new( + code, details, trailing_metadata) + ops[RECV_CLOSE_ON_SERVER] = nil + + @call.run_batch(ops) + end + # remote_read reads a response from the remote endpoint. # # It blocks until the remote endpoint replies with a message or status. @@ -240,9 +242,13 @@ module GRPC @call.metadata = batch_result.metadata @metadata_received = true end - unless batch_result.nil? || batch_result.message.nil? - res = @unmarshal.call(batch_result.message) - return res + get_message_from_batch_result(batch_result) + end + + def get_message_from_batch_result(recv_message_batch_result) + unless recv_message_batch_result.nil? || + recv_message_batch_result.message.nil? + return @unmarshal.call(recv_message_batch_result.message) end GRPC.logger.debug('found nil; the final response has been sent') nil @@ -298,7 +304,6 @@ module GRPC return enum_for(:each_remote_read_then_finish) unless block_given? loop do resp = remote_read - break if resp.is_a? Struct::Status # is an OK status if resp.nil? # the last response was received, but not finished yet finished break @@ -315,15 +320,25 @@ module GRPC # a list, multiple metadata for its key are sent # @return [Object] the response received from the server def request_response(req, metadata: {}) - merge_metadata_to_send(metadata) && send_initial_metadata - remote_send(req) - writes_done(false) - response = remote_read - finished unless response.is_a? Struct::Status - response - rescue GRPC::Core::CallError => e - finished # checks for Cancelled - raise e + ops = { + SEND_MESSAGE => @marshal.call(req), + SEND_CLOSE_FROM_CLIENT => nil, + RECV_INITIAL_METADATA => nil, + RECV_MESSAGE => nil, + RECV_STATUS_ON_CLIENT => nil + } + @send_initial_md_mutex.synchronize do + # Metadata might have already been sent if this is an operation view + unless @metadata_sent + ops[SEND_INITIAL_METADATA] = @metadata_to_send.merge!(metadata) + end + @metadata_sent = true + end + batch_result = @call.run_batch(ops) + + @call.metadata = batch_result.metadata + attach_status_results_and_complete_call(batch_result) + get_message_from_batch_result(batch_result) end # client_streamer sends a stream of requests to a GRPC server, and @@ -339,12 +354,20 @@ module GRPC # a list, multiple metadata for its key are sent # @return [Object] the response received from the server def client_streamer(requests, metadata: {}) - merge_metadata_to_send(metadata) && send_initial_metadata - requests.each { |r| remote_send(r) } - writes_done(false) - response = remote_read - finished unless response.is_a? Struct::Status - response + # Metadata might have already been sent if this is an operation view + merge_metadata_and_send_if_not_already_sent(metadata) + + requests.each { |r| @call.run_batch(SEND_MESSAGE => @marshal.call(r)) } + batch_result = @call.run_batch( + SEND_CLOSE_FROM_CLIENT => nil, + RECV_INITIAL_METADATA => nil, + RECV_MESSAGE => nil, + RECV_STATUS_ON_CLIENT => nil + ) + + @call.metadata = batch_result.metadata + attach_status_results_and_complete_call(batch_result) + get_message_from_batch_result(batch_result) rescue GRPC::Core::CallError => e finished # checks for Cancelled raise e @@ -365,9 +388,18 @@ module GRPC # a list, multiple metadata for its key are sent # @return [Enumerator|nil] a response Enumerator def server_streamer(req, metadata: {}) - merge_metadata_to_send(metadata) && send_initial_metadata - remote_send(req) - writes_done(false) + ops = { + SEND_MESSAGE => @marshal.call(req), + SEND_CLOSE_FROM_CLIENT => nil + } + @send_initial_md_mutex.synchronize do + # Metadata might have already been sent if this is an operation view + unless @metadata_sent + ops[SEND_INITIAL_METADATA] = @metadata_to_send.merge!(metadata) + end + @metadata_sent = true + end + @call.run_batch(ops) replies = enum_for(:each_remote_read_then_finish) return replies unless block_given? replies.each { |r| yield r } @@ -404,7 +436,8 @@ module GRPC # a list, multiple metadata for its key are sent # @return [Enumerator, nil] a response Enumerator def bidi_streamer(requests, metadata: {}, &blk) - merge_metadata_to_send(metadata) && send_initial_metadata + # Metadata might have already been sent if this is an operation view + merge_metadata_and_send_if_not_already_sent(metadata) bd = BidiCall.new(@call, @marshal, @unmarshal, @@ -457,6 +490,15 @@ module GRPC end end + def merge_metadata_and_send_if_not_already_sent(new_metadata = {}) + @send_initial_md_mutex.synchronize do + return if @metadata_sent + @metadata_to_send.merge!(new_metadata) + @call.run_batch(SEND_INITIAL_METADATA => @metadata_to_send) + @metadata_sent = true + end + end + private # Starts the call if not already started diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb index 0d7c1f7805e..6934257cbc0 100644 --- a/src/ruby/lib/grpc/generic/client_stub.rb +++ b/src/ruby/lib/grpc/generic/client_stub.rb @@ -168,6 +168,7 @@ module GRPC # return the operation view of the active_call; define #execute as a # new method for this instance that invokes #request_response. + c.merge_metadata_to_send(metadata) op = c.operation op.define_singleton_method(:execute) do c.request_response(req, metadata: metadata) @@ -231,9 +232,10 @@ module GRPC # return the operation view of the active_call; define #execute as a # new method for this instance that invokes #client_streamer. + c.merge_metadata_to_send(metadata) op = c.operation op.define_singleton_method(:execute) do - c.client_streamer(requests, metadata: metadata) + c.client_streamer(requests) end op end @@ -309,9 +311,10 @@ module GRPC # return the operation view of the active_call; define #execute # as a new method for this instance that invokes #server_streamer + c.merge_metadata_to_send(metadata) op = c.operation op.define_singleton_method(:execute) do - c.server_streamer(req, metadata: metadata, &blk) + c.server_streamer(req, &blk) end op end @@ -417,15 +420,15 @@ module GRPC deadline: deadline, parent: parent, credentials: credentials) - return c.bidi_streamer(requests, metadata: metadata, &blk) unless return_op # return the operation view of the active_call; define #execute # as a new method for this instance that invokes #bidi_streamer + c.merge_metadata_to_send(metadata) op = c.operation op.define_singleton_method(:execute) do - c.bidi_streamer(requests, metadata: metadata, &blk) + c.bidi_streamer(requests, &blk) end op end @@ -445,7 +448,6 @@ module GRPC deadline: nil, parent: nil, credentials: nil) - deadline = from_relative_time(@timeout) if deadline.nil? # Provide each new client call with its own completion queue call = @ch.create_call(parent, # parent call diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb index 584fe781698..cd17aed8e7c 100644 --- a/src/ruby/lib/grpc/generic/rpc_desc.rb +++ b/src/ruby/lib/grpc/generic/rpc_desc.rb @@ -62,25 +62,44 @@ module GRPC proc { |o| unmarshal_class.method(unmarshal_method).call(o) } end + def handle_request_response(active_call, mth) + req = active_call.remote_read + resp = mth.call(req, active_call.single_req_view) + active_call.server_unary_response( + resp, trailing_metadata: active_call.output_metadata) + end + + def handle_client_streamer(active_call, mth) + resp = mth.call(active_call.multi_req_view) + active_call.server_unary_response( + resp, trailing_metadata: active_call.output_metadata) + end + + def handle_server_streamer(active_call, mth) + req = active_call.remote_read + replys = mth.call(req, active_call.single_req_view) + replys.each { |r| active_call.remote_send(r) } + send_status(active_call, OK, 'OK', active_call.output_metadata) + end + + def handle_bidi_streamer(active_call, mth) + active_call.run_server_bidi(mth) + send_status(active_call, OK, 'OK', active_call.output_metadata) + end + def run_server_method(active_call, mth) # While a server method is running, it might be cancelled, its deadline # might be reached, the handler could throw an unknown error, or a # well-behaved handler could throw a StatusError. if request_response? - req = active_call.remote_read - resp = mth.call(req, active_call.single_req_view) - active_call.remote_send(resp) + handle_request_response(active_call, mth) elsif client_streamer? - resp = mth.call(active_call.multi_req_view) - active_call.remote_send(resp) + handle_client_streamer(active_call, mth) elsif server_streamer? - req = active_call.remote_read - replys = mth.call(req, active_call.single_req_view) - replys.each { |r| active_call.remote_send(r) } + handle_server_streamer(active_call, mth) else # is a bidi_stream - active_call.run_server_bidi(mth) + handle_bidi_streamer(active_call, mth) end - send_status(active_call, OK, 'OK', active_call.output_metadata) rescue BadStatus => e # this is raised by handlers that want GRPC to send an application error # code and detail message and some additional app-specific metadata. @@ -91,7 +110,7 @@ module GRPC # Log it, but don't notify the other endpoint.. GRPC.logger.warn("failed call: #{active_call}\n#{e}") rescue Core::OutOfTime - # This is raised when active_call#method.call exceeeds the deadline + # This is raised when active_call#method.call exceeds the deadline # event. Send a status of deadline exceeded GRPC.logger.warn("late call: #{active_call}") send_status(active_call, DEADLINE_EXCEEDED, 'late') @@ -100,7 +119,7 @@ module GRPC # Send back a UNKNOWN status to the client GRPC.logger.warn("failed handler: #{active_call}; sending status:UNKNOWN") GRPC.logger.warn(e) - send_status(active_call, UNKNOWN, 'no reason given') + send_status(active_call, UNKNOWN, 'unkown error handling call on server') end def assert_arity_matches(mth) diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb index 5ae4f255374..aa51d9d7b17 100644 --- a/src/ruby/spec/generic/active_call_spec.rb +++ b/src/ruby/spec/generic/active_call_spec.rb @@ -402,7 +402,7 @@ describe GRPC::ActiveCall do @pass_through, deadline) msg = 'message is a string' client_call.remote_send(msg) - client_call.writes_done(false) + call.run_batch(CallOps::SEND_CLOSE_FROM_CLIENT => nil) server_call = expect_server_to_receive(msg) server_call.remote_send('server_response') server_call.send_status(OK, 'OK') @@ -460,7 +460,7 @@ describe GRPC::ActiveCall do msg = 'message is a string' reply = 'server_response' client_call.remote_send(msg) - client_call.writes_done(false) + call.run_batch(CallOps::SEND_CLOSE_FROM_CLIENT => nil) server_call = expect_server_to_receive(msg) e = client_call.each_remote_read n = 3 # arbitrary value > 1 @@ -473,7 +473,7 @@ describe GRPC::ActiveCall do end end - describe '#writes_done' do + describe '#closing the call from the client' do it 'finishes ok if the server sends a status response' do call = make_test_call ActiveCall.client_invoke(call) @@ -481,7 +481,9 @@ describe GRPC::ActiveCall do @pass_through, deadline) msg = 'message is a string' client_call.remote_send(msg) - expect { client_call.writes_done(false) }.to_not raise_error + expect do + call.run_batch(CallOps::SEND_CLOSE_FROM_CLIENT => nil) + end.to_not raise_error server_call = expect_server_to_receive(msg) server_call.remote_send('server_response') expect(client_call.remote_read).to eq('server_response') @@ -500,11 +502,13 @@ describe GRPC::ActiveCall do server_call.remote_send('server_response') server_call.send_status(OK, 'status code is OK') expect(client_call.remote_read).to eq('server_response') - expect { client_call.writes_done(false) }.to_not raise_error + expect do + call.run_batch(CallOps::SEND_CLOSE_FROM_CLIENT => nil) + end.to_not raise_error expect { client_call.finished }.to_not raise_error end - it 'finishes ok if writes_done is true' do + it 'finishes ok if SEND_CLOSE and RECV_STATUS has been sent' do call = make_test_call ActiveCall.client_invoke(call) client_call = ActiveCall.new(call, @pass_through, @@ -515,7 +519,11 @@ describe GRPC::ActiveCall do server_call.remote_send('server_response') server_call.send_status(OK, 'status code is OK') expect(client_call.remote_read).to eq('server_response') - expect { client_call.writes_done(true) }.to_not raise_error + expect do + call.run_batch( + CallOps::SEND_CLOSE_FROM_CLIENT => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil) + end.to_not raise_error end end diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index 6034b5419c9..e68b8db7ab7 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -180,30 +180,44 @@ describe 'ClientStub' do end describe 'via a call operation' do - def get_response(stub) + def get_response(stub, run_start_call_first: false) op = stub.request_response(@method, @sent_msg, noop, noop, return_op: true, metadata: { k1: 'v1', k2: 'v2' }, deadline: from_relative_time(2)) expect(op).to be_a(GRPC::ActiveCall::Operation) - op.execute + op.start_call if run_start_call_first + result = op.execute + op.wait # make sure wait doesn't hang + result end it_behaves_like 'request response' - end - end - describe '#client_streamer' do - shared_examples 'client streaming' do - before(:each) do + it 'sends metadata to the server ok when running start_call first' do server_port = create_test_server host = "localhost:#{server_port}" - @stub = GRPC::ClientStub.new(host, :this_channel_is_insecure) - @metadata = { k1: 'v1', k2: 'v2' } - @sent_msgs = Array.new(3) { |i| 'msg_' + (i + 1).to_s } - @resp = 'a_reply' + th = run_request_response(@sent_msg, @resp, @pass, + k1: 'v1', k2: 'v2') + stub = GRPC::ClientStub.new(host, :this_channel_is_insecure) + expect(get_response(stub)).to eq(@resp) + th.join end + end + end + + describe '#client_streamer' do + before(:each) do + Thread.abort_on_exception = true + server_port = create_test_server + host = "localhost:#{server_port}" + @stub = GRPC::ClientStub.new(host, :this_channel_is_insecure) + @metadata = { k1: 'v1', k2: 'v2' } + @sent_msgs = Array.new(3) { |i| 'msg_' + (i + 1).to_s } + @resp = 'a_reply' + end + shared_examples 'client streaming' do it 'should send requests to/receive a reply from a server' do th = run_client_streamer(@sent_msgs, @resp, @pass) expect(get_response(@stub)).to eq(@resp) @@ -242,24 +256,33 @@ describe 'ClientStub' do end describe 'via a call operation' do - def get_response(stub) + def get_response(stub, run_start_call_first: false) op = stub.client_streamer(@method, @sent_msgs, noop, noop, return_op: true, metadata: @metadata) expect(op).to be_a(GRPC::ActiveCall::Operation) - op.execute + op.start_call if run_start_call_first + result = op.execute + op.wait # make sure wait doesn't hang + result end it_behaves_like 'client streaming' + + it 'sends metadata to the server ok when running start_call first' do + th = run_client_streamer(@sent_msgs, @resp, @pass, **@metadata) + expect(get_response(@stub, run_start_call_first: true)).to eq(@resp) + th.join + end end end describe '#server_streamer' do - shared_examples 'server streaming' do - before(:each) do - @sent_msg = 'a_msg' - @replys = Array.new(3) { |i| 'reply_' + (i + 1).to_s } - end + before(:each) do + @sent_msg = 'a_msg' + @replys = Array.new(3) { |i| 'reply_' + (i + 1).to_s } + end + shared_examples 'server streaming' do it 'should send a request to/receive replies from a server' do server_port = create_test_server host = "localhost:#{server_port}" @@ -303,29 +326,44 @@ describe 'ClientStub' do end describe 'via a call operation' do - def get_responses(stub) - op = stub.server_streamer(@method, @sent_msg, noop, noop, - return_op: true, - metadata: { k1: 'v1', k2: 'v2' }) - expect(op).to be_a(GRPC::ActiveCall::Operation) - e = op.execute + after(:each) do + @op.wait # make sure wait doesn't hang + end + def get_responses(stub, run_start_call_first: false) + @op = stub.server_streamer(@method, @sent_msg, noop, noop, + return_op: true, + metadata: { k1: 'v1', k2: 'v2' }) + expect(@op).to be_a(GRPC::ActiveCall::Operation) + @op.start_call if run_start_call_first + e = @op.execute expect(e).to be_a(Enumerator) e end it_behaves_like 'server streaming' + + it 'should send metadata to the server ok when start_call is run first' do + server_port = create_test_server + host = "localhost:#{server_port}" + th = run_server_streamer(@sent_msg, @replys, @fail, + k1: 'v1', k2: 'v2') + stub = GRPC::ClientStub.new(host, :this_channel_is_insecure) + e = get_responses(stub, run_start_call_first: true) + expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus) + th.join + end end end describe '#bidi_streamer' do - shared_examples 'bidi streaming' do - before(:each) do - @sent_msgs = Array.new(3) { |i| 'msg_' + (i + 1).to_s } - @replys = Array.new(3) { |i| 'reply_' + (i + 1).to_s } - server_port = create_test_server - @host = "localhost:#{server_port}" - end + before(:each) do + @sent_msgs = Array.new(3) { |i| 'msg_' + (i + 1).to_s } + @replys = Array.new(3) { |i| 'reply_' + (i + 1).to_s } + server_port = create_test_server + @host = "localhost:#{server_port}" + end + shared_examples 'bidi streaming' do it 'supports sending all the requests first', bidi: true do th = run_bidi_streamer_handle_inputs_first(@sent_msgs, @replys, @pass) @@ -363,16 +401,29 @@ describe 'ClientStub' do end describe 'via a call operation' do - def get_responses(stub) - op = stub.bidi_streamer(@method, @sent_msgs, noop, noop, - return_op: true) - expect(op).to be_a(GRPC::ActiveCall::Operation) - e = op.execute + after(:each) do + @op.wait # make sure wait doesn't hang + end + def get_responses(stub, run_start_call_first: false) + @op = stub.bidi_streamer(@method, @sent_msgs, noop, noop, + return_op: true) + expect(@op).to be_a(GRPC::ActiveCall::Operation) + @op.start_call if run_start_call_first + e = @op.execute expect(e).to be_a(Enumerator) e end it_behaves_like 'bidi streaming' + + it 'can run start_call before executing the call' do + th = run_bidi_streamer_handle_inputs_first(@sent_msgs, @replys, + @pass) + stub = GRPC::ClientStub.new(@host, :this_channel_is_insecure) + e = get_responses(stub, run_start_call_first: true) + expect(e.collect { |r| r }).to eq(@replys) + th.join + end end end diff --git a/src/ruby/spec/generic/rpc_desc_spec.rb b/src/ruby/spec/generic/rpc_desc_spec.rb index 1a895005bc3..a3f0efa6036 100644 --- a/src/ruby/spec/generic/rpc_desc_spec.rb +++ b/src/ruby/spec/generic/rpc_desc_spec.rb @@ -48,7 +48,7 @@ describe GRPC::RpcDesc do @bidi_streamer = RpcDesc.new('ss', Stream.new(Object.new), Stream.new(Object.new), 'encode', 'decode') @bs_code = INTERNAL - @no_reason = 'no reason given' + @no_reason = 'unkown error handling call on server' @ok_response = Object.new end @@ -83,6 +83,7 @@ describe GRPC::RpcDesc do before(:each) do @call = double('active_call') allow(@call).to receive(:single_req_view).and_return(@call) + allow(@call).to receive(:output_metadata).and_return(@call) end it_behaves_like 'it handles errors' @@ -90,10 +91,10 @@ describe GRPC::RpcDesc do it 'sends a response and closes the stream if there no errors' do req = Object.new expect(@call).to receive(:remote_read).once.and_return(req) - expect(@call).to receive(:remote_send).once.with(@ok_response) - expect(@call).to receive(:output_metadata).and_return(fake_md) - expect(@call).to receive(:send_status).once.with(OK, 'OK', true, - metadata: fake_md) + expect(@call).to receive(:output_metadata).once.and_return(fake_md) + expect(@call).to receive(:server_unary_response).once + .with(@ok_response, trailing_metadata: fake_md) + this_desc.run_server_method(@call, method(:fake_reqresp)) end end @@ -117,7 +118,9 @@ describe GRPC::RpcDesc do end it 'absorbs CallError with no further action' do - expect(@call).to receive(:remote_send).once.and_raise(CallError) + expect(@call).to receive(:server_unary_response).once.and_raise( + CallError) + allow(@call).to receive(:output_metadata).and_return({}) blk = proc do @client_streamer.run_server_method(@call, method(:fake_clstream)) end @@ -125,10 +128,11 @@ describe GRPC::RpcDesc do end it 'sends a response and closes the stream if there no errors' do - expect(@call).to receive(:remote_send).once.with(@ok_response) - expect(@call).to receive(:output_metadata).and_return(fake_md) - expect(@call).to receive(:send_status).once.with(OK, 'OK', true, - metadata: fake_md) + expect(@call).to receive(:output_metadata).and_return( + fake_md) + expect(@call).to receive(:server_unary_response).once + .with(@ok_response, trailing_metadata: fake_md) + @client_streamer.run_server_method(@call, method(:fake_clstream)) end end diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb index d362e48deee..c5694790fdf 100644 --- a/src/ruby/spec/generic/rpc_server_spec.rb +++ b/src/ruby/spec/generic/rpc_server_spec.rb @@ -462,6 +462,7 @@ describe GRPC::RpcServer do 'connect_k1' => 'connect_v1' } wanted_md.each do |key, value| + puts "key: #{key}" expect(op.metadata[key]).to eq(value) end @srv.stop diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb index 1b2fa968271..4711e09e88a 100644 --- a/src/ruby/spec/pb/health/checker_spec.rb +++ b/src/ruby/spec/pb/health/checker_spec.rb @@ -97,15 +97,17 @@ describe Grpc::Health::Checker do context 'initialization' do it 'can be constructed with no args' do - expect(subject).to_not be(nil) + checker = Grpc::Health::Checker.new + expect(checker).to_not be(nil) end end context 'method `add_status` and `check`' do success_tests.each do |t| it "should succeed when #{t[:desc]}" do - subject.add_status(t[:service], ServingStatus::NOT_SERVING) - got = subject.check(HCReq.new(service: t[:service]), nil) + checker = Grpc::Health::Checker.new + checker.add_status(t[:service], ServingStatus::NOT_SERVING) + got = checker.check(HCReq.new(service: t[:service]), nil) want = HCResp.new(status: ServingStatus::NOT_SERVING) expect(got).to eq(want) end @@ -115,8 +117,9 @@ describe Grpc::Health::Checker do context 'method `check`' do success_tests.each do |t| it "should fail with NOT_FOUND when #{t[:desc]}" do + checker = Grpc::Health::Checker.new blk = proc do - subject.check(HCReq.new(service: t[:service]), nil) + checker.check(HCReq.new(service: t[:service]), nil) end expected_msg = /#{StatusCodes::NOT_FOUND}/ expect(&blk).to raise_error GRPC::BadStatus, expected_msg @@ -127,14 +130,15 @@ describe Grpc::Health::Checker do context 'method `clear_status`' do success_tests.each do |t| it "should fail after clearing status when #{t[:desc]}" do - subject.add_status(t[:service], ServingStatus::NOT_SERVING) - got = subject.check(HCReq.new(service: t[:service]), nil) + checker = Grpc::Health::Checker.new + checker.add_status(t[:service], ServingStatus::NOT_SERVING) + got = checker.check(HCReq.new(service: t[:service]), nil) want = HCResp.new(status: ServingStatus::NOT_SERVING) expect(got).to eq(want) - subject.clear_status(t[:service]) + checker.clear_status(t[:service]) blk = proc do - subject.check(HCReq.new(service: t[:service]), nil) + checker.check(HCReq.new(service: t[:service]), nil) end expected_msg = /#{StatusCodes::NOT_FOUND}/ expect(&blk).to raise_error GRPC::BadStatus, expected_msg @@ -144,18 +148,19 @@ describe Grpc::Health::Checker do context 'method `clear_all`' do it 'should return NOT_FOUND after being invoked' do + checker = Grpc::Health::Checker.new success_tests.each do |t| - subject.add_status(t[:service], ServingStatus::NOT_SERVING) - got = subject.check(HCReq.new(service: t[:service]), nil) + checker.add_status(t[:service], ServingStatus::NOT_SERVING) + got = checker.check(HCReq.new(service: t[:service]), nil) want = HCResp.new(status: ServingStatus::NOT_SERVING) expect(got).to eq(want) end - subject.clear_all + checker.clear_all success_tests.each do |t| blk = proc do - subject.check(HCReq.new(service: t[:service]), nil) + checker.check(HCReq.new(service: t[:service]), nil) end expected_msg = /#{StatusCodes::NOT_FOUND}/ expect(&blk).to raise_error GRPC::BadStatus, expected_msg @@ -184,8 +189,10 @@ describe Grpc::Health::Checker do end it 'should receive the correct status', server: true do - @srv.handle(subject) - subject.add_status('', ServingStatus::NOT_SERVING) + Thread.abort_on_exception = true + checker = Grpc::Health::Checker.new + @srv.handle(checker) + checker.add_status('', ServingStatus::NOT_SERVING) t = Thread.new { @srv.run } @srv.wait_till_running @@ -198,7 +205,8 @@ describe Grpc::Health::Checker do end it 'should fail on unknown services', server: true do - @srv.handle(subject) + checker = Grpc::Health::Checker.new + @srv.handle(checker) t = Thread.new { @srv.run } @srv.wait_till_running blk = proc do From 49f89f0c05e25e0b01fc12844ff88b62579a5501 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Wed, 26 Oct 2016 11:16:59 -0700 Subject: [PATCH 22/30] clang-format --- src/core/ext/lb_policy/grpclb/grpclb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c index 6d73c48cc3d..6da4febf266 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/lb_policy/grpclb/grpclb.c @@ -637,9 +637,9 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, addr_strs[addr_index++] = grpc_sockaddr_to_uri(&addresses->addresses[i].address); } else { - GPR_ASSERT(grpc_sockaddr_to_string( - &addr_strs[addr_index++], - &addresses->addresses[i].address, true) > 0); + GPR_ASSERT(grpc_sockaddr_to_string(&addr_strs[addr_index++], + &addresses->addresses[i].address, + true) > 0); } } } From 49607a8afa2fa58e3546c6e3b7b3cf90bb74414b Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Wed, 26 Oct 2016 11:17:37 -0700 Subject: [PATCH 23/30] Ran generate_projects.sh --- tools/run_tests/tests.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index c86b14f63eb..5cb50cc999c 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -1306,6 +1306,7 @@ ], "cpu_cost": 1.0, "exclude_configs": [], + "exclude_iomgrs": [], "flaky": false, "gtest": false, "language": "c", @@ -1321,6 +1322,7 @@ ], "cpu_cost": 1.0, "exclude_configs": [], + "exclude_iomgrs": [], "flaky": false, "gtest": false, "language": "c", From bfe56801ad916248f43c2dcfaa96bd7442a65ae2 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Wed, 26 Oct 2016 12:45:20 -0700 Subject: [PATCH 24/30] Move method_config module from ext/client_channel to lib/transport. --- BUILD | 16 ++++++++-------- CMakeLists.txt | 6 +++--- Makefile | 7 ++++--- binding.gyp | 2 +- build.yaml | 4 ++-- config.m4 | 2 +- gRPC-Core.podspec | 6 +++--- grpc.gemspec | 4 ++-- package.xml | 4 ++-- src/core/ext/client_channel/client_channel.c | 2 +- src/core/lib/channel/message_size_filter.c | 2 +- .../transport}/method_config.c | 2 +- .../transport}/method_config.h | 6 +++--- src/python/grpcio/grpc_core_dependencies.py | 2 +- test/core/end2end/fake_resolver.c | 2 +- tools/doxygen/Doxyfile.core.internal | 4 ++-- tools/run_tests/sources_and_headers.json | 6 +++--- tools/run_tests/tests.json | 2 ++ vsprojects/vcxproj/grpc/grpc.vcxproj | 6 +++--- vsprojects/vcxproj/grpc/grpc.vcxproj.filters | 12 ++++++------ .../grpc_test_util/grpc_test_util.vcxproj | 3 +++ .../grpc_test_util.vcxproj.filters | 6 ++++++ .../vcxproj/grpc_unsecure/grpc_unsecure.vcxproj | 6 +++--- .../grpc_unsecure/grpc_unsecure.vcxproj.filters | 12 ++++++------ 24 files changed, 68 insertions(+), 56 deletions(-) rename src/core/{ext/client_channel => lib/transport}/method_config.c (99%) rename src/core/{ext/client_channel => lib/transport}/method_config.h (97%) diff --git a/BUILD b/BUILD index e4ee8e93f71..241215e0d64 100644 --- a/BUILD +++ b/BUILD @@ -249,6 +249,7 @@ cc_library( "src/core/lib/transport/mdstr_hash_table.h", "src/core/lib/transport/metadata.h", "src/core/lib/transport/metadata_batch.h", + "src/core/lib/transport/method_config.h", "src/core/lib/transport/static_metadata.h", "src/core/lib/transport/timeout_encoding.h", "src/core/lib/transport/transport.h", @@ -306,7 +307,6 @@ cc_library( "src/core/ext/client_channel/lb_policy.h", "src/core/ext/client_channel/lb_policy_factory.h", "src/core/ext/client_channel/lb_policy_registry.h", - "src/core/ext/client_channel/method_config.h", "src/core/ext/client_channel/parse_address.h", "src/core/ext/client_channel/resolver.h", "src/core/ext/client_channel/resolver_factory.h", @@ -433,6 +433,7 @@ cc_library( "src/core/lib/transport/mdstr_hash_table.c", "src/core/lib/transport/metadata.c", "src/core/lib/transport/metadata_batch.c", + "src/core/lib/transport/method_config.c", "src/core/lib/transport/static_metadata.c", "src/core/lib/transport/timeout_encoding.c", "src/core/lib/transport/transport.c", @@ -499,7 +500,6 @@ cc_library( "src/core/ext/client_channel/lb_policy.c", "src/core/ext/client_channel/lb_policy_factory.c", "src/core/ext/client_channel/lb_policy_registry.c", - "src/core/ext/client_channel/method_config.c", "src/core/ext/client_channel/parse_address.c", "src/core/ext/client_channel/resolver.c", "src/core/ext/client_channel/resolver_factory.c", @@ -672,6 +672,7 @@ cc_library( "src/core/lib/transport/mdstr_hash_table.h", "src/core/lib/transport/metadata.h", "src/core/lib/transport/metadata_batch.h", + "src/core/lib/transport/method_config.h", "src/core/lib/transport/static_metadata.h", "src/core/lib/transport/timeout_encoding.h", "src/core/lib/transport/transport.h", @@ -706,7 +707,6 @@ cc_library( "src/core/ext/client_channel/lb_policy.h", "src/core/ext/client_channel/lb_policy_factory.h", "src/core/ext/client_channel/lb_policy_registry.h", - "src/core/ext/client_channel/method_config.h", "src/core/ext/client_channel/parse_address.h", "src/core/ext/client_channel/resolver.h", "src/core/ext/client_channel/resolver_factory.h", @@ -841,6 +841,7 @@ cc_library( "src/core/lib/transport/mdstr_hash_table.c", "src/core/lib/transport/metadata.c", "src/core/lib/transport/metadata_batch.c", + "src/core/lib/transport/method_config.c", "src/core/lib/transport/static_metadata.c", "src/core/lib/transport/timeout_encoding.c", "src/core/lib/transport/transport.c", @@ -882,7 +883,6 @@ cc_library( "src/core/ext/client_channel/lb_policy.c", "src/core/ext/client_channel/lb_policy_factory.c", "src/core/ext/client_channel/lb_policy_registry.c", - "src/core/ext/client_channel/method_config.c", "src/core/ext/client_channel/parse_address.c", "src/core/ext/client_channel/resolver.c", "src/core/ext/client_channel/resolver_factory.c", @@ -1050,6 +1050,7 @@ cc_library( "src/core/lib/transport/mdstr_hash_table.h", "src/core/lib/transport/metadata.h", "src/core/lib/transport/metadata_batch.h", + "src/core/lib/transport/method_config.h", "src/core/lib/transport/static_metadata.h", "src/core/lib/transport/timeout_encoding.h", "src/core/lib/transport/transport.h", @@ -1083,7 +1084,6 @@ cc_library( "src/core/ext/client_channel/lb_policy.h", "src/core/ext/client_channel/lb_policy_factory.h", "src/core/ext/client_channel/lb_policy_registry.h", - "src/core/ext/client_channel/method_config.h", "src/core/ext/client_channel/parse_address.h", "src/core/ext/client_channel/resolver.h", "src/core/ext/client_channel/resolver_factory.h", @@ -1211,6 +1211,7 @@ cc_library( "src/core/lib/transport/mdstr_hash_table.c", "src/core/lib/transport/metadata.c", "src/core/lib/transport/metadata_batch.c", + "src/core/lib/transport/method_config.c", "src/core/lib/transport/static_metadata.c", "src/core/lib/transport/timeout_encoding.c", "src/core/lib/transport/transport.c", @@ -1252,7 +1253,6 @@ cc_library( "src/core/ext/client_channel/lb_policy.c", "src/core/ext/client_channel/lb_policy_factory.c", "src/core/ext/client_channel/lb_policy_registry.c", - "src/core/ext/client_channel/method_config.c", "src/core/ext/client_channel/parse_address.c", "src/core/ext/client_channel/resolver.c", "src/core/ext/client_channel/resolver_factory.c", @@ -2136,6 +2136,7 @@ objc_library( "src/core/lib/transport/mdstr_hash_table.c", "src/core/lib/transport/metadata.c", "src/core/lib/transport/metadata_batch.c", + "src/core/lib/transport/method_config.c", "src/core/lib/transport/static_metadata.c", "src/core/lib/transport/timeout_encoding.c", "src/core/lib/transport/transport.c", @@ -2202,7 +2203,6 @@ objc_library( "src/core/ext/client_channel/lb_policy.c", "src/core/ext/client_channel/lb_policy_factory.c", "src/core/ext/client_channel/lb_policy_registry.c", - "src/core/ext/client_channel/method_config.c", "src/core/ext/client_channel/parse_address.c", "src/core/ext/client_channel/resolver.c", "src/core/ext/client_channel/resolver_factory.c", @@ -2354,6 +2354,7 @@ objc_library( "src/core/lib/transport/mdstr_hash_table.h", "src/core/lib/transport/metadata.h", "src/core/lib/transport/metadata_batch.h", + "src/core/lib/transport/method_config.h", "src/core/lib/transport/static_metadata.h", "src/core/lib/transport/timeout_encoding.h", "src/core/lib/transport/transport.h", @@ -2411,7 +2412,6 @@ objc_library( "src/core/ext/client_channel/lb_policy.h", "src/core/ext/client_channel/lb_policy_factory.h", "src/core/ext/client_channel/lb_policy_registry.h", - "src/core/ext/client_channel/method_config.h", "src/core/ext/client_channel/parse_address.h", "src/core/ext/client_channel/resolver.h", "src/core/ext/client_channel/resolver_factory.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f301f54b9d..707efd78115 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -391,6 +391,7 @@ add_library(grpc src/core/lib/transport/mdstr_hash_table.c src/core/lib/transport/metadata.c src/core/lib/transport/metadata_batch.c + src/core/lib/transport/method_config.c src/core/lib/transport/static_metadata.c src/core/lib/transport/timeout_encoding.c src/core/lib/transport/transport.c @@ -457,7 +458,6 @@ add_library(grpc src/core/ext/client_channel/lb_policy.c src/core/ext/client_channel/lb_policy_factory.c src/core/ext/client_channel/lb_policy_registry.c - src/core/ext/client_channel/method_config.c src/core/ext/client_channel/parse_address.c src/core/ext/client_channel/resolver.c src/core/ext/client_channel/resolver_factory.c @@ -663,6 +663,7 @@ add_library(grpc_cronet src/core/lib/transport/mdstr_hash_table.c src/core/lib/transport/metadata.c src/core/lib/transport/metadata_batch.c + src/core/lib/transport/method_config.c src/core/lib/transport/static_metadata.c src/core/lib/transport/timeout_encoding.c src/core/lib/transport/transport.c @@ -704,7 +705,6 @@ add_library(grpc_cronet src/core/ext/client_channel/lb_policy.c src/core/ext/client_channel/lb_policy_factory.c src/core/ext/client_channel/lb_policy_registry.c - src/core/ext/client_channel/method_config.c src/core/ext/client_channel/parse_address.c src/core/ext/client_channel/resolver.c src/core/ext/client_channel/resolver_factory.c @@ -907,6 +907,7 @@ add_library(grpc_unsecure src/core/lib/transport/mdstr_hash_table.c src/core/lib/transport/metadata.c src/core/lib/transport/metadata_batch.c + src/core/lib/transport/method_config.c src/core/lib/transport/static_metadata.c src/core/lib/transport/timeout_encoding.c src/core/lib/transport/transport.c @@ -948,7 +949,6 @@ add_library(grpc_unsecure src/core/ext/client_channel/lb_policy.c src/core/ext/client_channel/lb_policy_factory.c src/core/ext/client_channel/lb_policy_registry.c - src/core/ext/client_channel/method_config.c src/core/ext/client_channel/parse_address.c src/core/ext/client_channel/resolver.c src/core/ext/client_channel/resolver_factory.c diff --git a/Makefile b/Makefile index f0dec1a4a36..d5c92077247 100644 --- a/Makefile +++ b/Makefile @@ -2676,6 +2676,7 @@ LIBGRPC_SRC = \ src/core/lib/transport/mdstr_hash_table.c \ src/core/lib/transport/metadata.c \ src/core/lib/transport/metadata_batch.c \ + src/core/lib/transport/method_config.c \ src/core/lib/transport/static_metadata.c \ src/core/lib/transport/timeout_encoding.c \ src/core/lib/transport/transport.c \ @@ -2742,7 +2743,6 @@ LIBGRPC_SRC = \ src/core/ext/client_channel/lb_policy.c \ src/core/ext/client_channel/lb_policy_factory.c \ src/core/ext/client_channel/lb_policy_registry.c \ - src/core/ext/client_channel/method_config.c \ src/core/ext/client_channel/parse_address.c \ src/core/ext/client_channel/resolver.c \ src/core/ext/client_channel/resolver_factory.c \ @@ -2966,6 +2966,7 @@ LIBGRPC_CRONET_SRC = \ src/core/lib/transport/mdstr_hash_table.c \ src/core/lib/transport/metadata.c \ src/core/lib/transport/metadata_batch.c \ + src/core/lib/transport/method_config.c \ src/core/lib/transport/static_metadata.c \ src/core/lib/transport/timeout_encoding.c \ src/core/lib/transport/transport.c \ @@ -3007,7 +3008,6 @@ LIBGRPC_CRONET_SRC = \ src/core/ext/client_channel/lb_policy.c \ src/core/ext/client_channel/lb_policy_factory.c \ src/core/ext/client_channel/lb_policy_registry.c \ - src/core/ext/client_channel/method_config.c \ src/core/ext/client_channel/parse_address.c \ src/core/ext/client_channel/resolver.c \ src/core/ext/client_channel/resolver_factory.c \ @@ -3247,6 +3247,7 @@ LIBGRPC_TEST_UTIL_SRC = \ src/core/lib/transport/mdstr_hash_table.c \ src/core/lib/transport/metadata.c \ src/core/lib/transport/metadata_batch.c \ + src/core/lib/transport/method_config.c \ src/core/lib/transport/static_metadata.c \ src/core/lib/transport/timeout_encoding.c \ src/core/lib/transport/transport.c \ @@ -3455,6 +3456,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/lib/transport/mdstr_hash_table.c \ src/core/lib/transport/metadata.c \ src/core/lib/transport/metadata_batch.c \ + src/core/lib/transport/method_config.c \ src/core/lib/transport/static_metadata.c \ src/core/lib/transport/timeout_encoding.c \ src/core/lib/transport/transport.c \ @@ -3496,7 +3498,6 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/client_channel/lb_policy.c \ src/core/ext/client_channel/lb_policy_factory.c \ src/core/ext/client_channel/lb_policy_registry.c \ - src/core/ext/client_channel/method_config.c \ src/core/ext/client_channel/parse_address.c \ src/core/ext/client_channel/resolver.c \ src/core/ext/client_channel/resolver_factory.c \ diff --git a/binding.gyp b/binding.gyp index 390cc52c8f3..a752d4c6b33 100644 --- a/binding.gyp +++ b/binding.gyp @@ -669,6 +669,7 @@ 'src/core/lib/transport/mdstr_hash_table.c', 'src/core/lib/transport/metadata.c', 'src/core/lib/transport/metadata_batch.c', + 'src/core/lib/transport/method_config.c', 'src/core/lib/transport/static_metadata.c', 'src/core/lib/transport/timeout_encoding.c', 'src/core/lib/transport/transport.c', @@ -735,7 +736,6 @@ 'src/core/ext/client_channel/lb_policy.c', 'src/core/ext/client_channel/lb_policy_factory.c', 'src/core/ext/client_channel/lb_policy_registry.c', - 'src/core/ext/client_channel/method_config.c', 'src/core/ext/client_channel/parse_address.c', 'src/core/ext/client_channel/resolver.c', 'src/core/ext/client_channel/resolver_factory.c', diff --git a/build.yaml b/build.yaml index 17828ae8eb6..149514f9c10 100644 --- a/build.yaml +++ b/build.yaml @@ -253,6 +253,7 @@ filegroups: - src/core/lib/transport/mdstr_hash_table.h - src/core/lib/transport/metadata.h - src/core/lib/transport/metadata_batch.h + - src/core/lib/transport/method_config.h - src/core/lib/transport/static_metadata.h - src/core/lib/transport/timeout_encoding.h - src/core/lib/transport/transport.h @@ -359,6 +360,7 @@ filegroups: - src/core/lib/transport/mdstr_hash_table.c - src/core/lib/transport/metadata.c - src/core/lib/transport/metadata_batch.c + - src/core/lib/transport/method_config.c - src/core/lib/transport/static_metadata.c - src/core/lib/transport/timeout_encoding.c - src/core/lib/transport/transport.c @@ -377,7 +379,6 @@ filegroups: - src/core/ext/client_channel/lb_policy.h - src/core/ext/client_channel/lb_policy_factory.h - src/core/ext/client_channel/lb_policy_registry.h - - src/core/ext/client_channel/method_config.h - src/core/ext/client_channel/parse_address.h - src/core/ext/client_channel/resolver.h - src/core/ext/client_channel/resolver_factory.h @@ -398,7 +399,6 @@ filegroups: - src/core/ext/client_channel/lb_policy.c - src/core/ext/client_channel/lb_policy_factory.c - src/core/ext/client_channel/lb_policy_registry.c - - src/core/ext/client_channel/method_config.c - src/core/ext/client_channel/parse_address.c - src/core/ext/client_channel/resolver.c - src/core/ext/client_channel/resolver_factory.c diff --git a/config.m4 b/config.m4 index 298708a8397..854ec569c80 100644 --- a/config.m4 +++ b/config.m4 @@ -185,6 +185,7 @@ if test "$PHP_GRPC" != "no"; then src/core/lib/transport/mdstr_hash_table.c \ src/core/lib/transport/metadata.c \ src/core/lib/transport/metadata_batch.c \ + src/core/lib/transport/method_config.c \ src/core/lib/transport/static_metadata.c \ src/core/lib/transport/timeout_encoding.c \ src/core/lib/transport/transport.c \ @@ -251,7 +252,6 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/client_channel/lb_policy.c \ src/core/ext/client_channel/lb_policy_factory.c \ src/core/ext/client_channel/lb_policy_registry.c \ - src/core/ext/client_channel/method_config.c \ src/core/ext/client_channel/parse_address.c \ src/core/ext/client_channel/resolver.c \ src/core/ext/client_channel/resolver_factory.c \ diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 6fc65f6c693..8735a350310 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -336,6 +336,7 @@ Pod::Spec.new do |s| 'src/core/lib/transport/mdstr_hash_table.h', 'src/core/lib/transport/metadata.h', 'src/core/lib/transport/metadata_batch.h', + 'src/core/lib/transport/method_config.h', 'src/core/lib/transport/static_metadata.h', 'src/core/lib/transport/timeout_encoding.h', 'src/core/lib/transport/transport.h', @@ -393,7 +394,6 @@ Pod::Spec.new do |s| 'src/core/ext/client_channel/lb_policy.h', 'src/core/ext/client_channel/lb_policy_factory.h', 'src/core/ext/client_channel/lb_policy_registry.h', - 'src/core/ext/client_channel/method_config.h', 'src/core/ext/client_channel/parse_address.h', 'src/core/ext/client_channel/resolver.h', 'src/core/ext/client_channel/resolver_factory.h', @@ -524,6 +524,7 @@ Pod::Spec.new do |s| 'src/core/lib/transport/mdstr_hash_table.c', 'src/core/lib/transport/metadata.c', 'src/core/lib/transport/metadata_batch.c', + 'src/core/lib/transport/method_config.c', 'src/core/lib/transport/static_metadata.c', 'src/core/lib/transport/timeout_encoding.c', 'src/core/lib/transport/transport.c', @@ -590,7 +591,6 @@ Pod::Spec.new do |s| 'src/core/ext/client_channel/lb_policy.c', 'src/core/ext/client_channel/lb_policy_factory.c', 'src/core/ext/client_channel/lb_policy_registry.c', - 'src/core/ext/client_channel/method_config.c', 'src/core/ext/client_channel/parse_address.c', 'src/core/ext/client_channel/resolver.c', 'src/core/ext/client_channel/resolver_factory.c', @@ -731,6 +731,7 @@ Pod::Spec.new do |s| 'src/core/lib/transport/mdstr_hash_table.h', 'src/core/lib/transport/metadata.h', 'src/core/lib/transport/metadata_batch.h', + 'src/core/lib/transport/method_config.h', 'src/core/lib/transport/static_metadata.h', 'src/core/lib/transport/timeout_encoding.h', 'src/core/lib/transport/transport.h', @@ -788,7 +789,6 @@ Pod::Spec.new do |s| 'src/core/ext/client_channel/lb_policy.h', 'src/core/ext/client_channel/lb_policy_factory.h', 'src/core/ext/client_channel/lb_policy_registry.h', - 'src/core/ext/client_channel/method_config.h', 'src/core/ext/client_channel/parse_address.h', 'src/core/ext/client_channel/resolver.h', 'src/core/ext/client_channel/resolver_factory.h', diff --git a/grpc.gemspec b/grpc.gemspec index d991d561f4d..f70ffb7fdaa 100755 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -256,6 +256,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/transport/mdstr_hash_table.h ) s.files += %w( src/core/lib/transport/metadata.h ) s.files += %w( src/core/lib/transport/metadata_batch.h ) + s.files += %w( src/core/lib/transport/method_config.h ) s.files += %w( src/core/lib/transport/static_metadata.h ) s.files += %w( src/core/lib/transport/timeout_encoding.h ) s.files += %w( src/core/lib/transport/transport.h ) @@ -313,7 +314,6 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/client_channel/lb_policy.h ) s.files += %w( src/core/ext/client_channel/lb_policy_factory.h ) s.files += %w( src/core/ext/client_channel/lb_policy_registry.h ) - s.files += %w( src/core/ext/client_channel/method_config.h ) s.files += %w( src/core/ext/client_channel/parse_address.h ) s.files += %w( src/core/ext/client_channel/resolver.h ) s.files += %w( src/core/ext/client_channel/resolver_factory.h ) @@ -444,6 +444,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/transport/mdstr_hash_table.c ) s.files += %w( src/core/lib/transport/metadata.c ) s.files += %w( src/core/lib/transport/metadata_batch.c ) + s.files += %w( src/core/lib/transport/method_config.c ) s.files += %w( src/core/lib/transport/static_metadata.c ) s.files += %w( src/core/lib/transport/timeout_encoding.c ) s.files += %w( src/core/lib/transport/transport.c ) @@ -510,7 +511,6 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/client_channel/lb_policy.c ) s.files += %w( src/core/ext/client_channel/lb_policy_factory.c ) s.files += %w( src/core/ext/client_channel/lb_policy_registry.c ) - s.files += %w( src/core/ext/client_channel/method_config.c ) s.files += %w( src/core/ext/client_channel/parse_address.c ) s.files += %w( src/core/ext/client_channel/resolver.c ) s.files += %w( src/core/ext/client_channel/resolver_factory.c ) diff --git a/package.xml b/package.xml index 5f637f816e0..27ec0ceffc4 100644 --- a/package.xml +++ b/package.xml @@ -263,6 +263,7 @@ + @@ -320,7 +321,6 @@ - @@ -451,6 +451,7 @@ + @@ -517,7 +518,6 @@ - diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c index 55bb877576f..7c1212f8fa9 100644 --- a/src/core/ext/client_channel/client_channel.c +++ b/src/core/ext/client_channel/client_channel.c @@ -43,7 +43,6 @@ #include #include "src/core/ext/client_channel/lb_policy_registry.h" -#include "src/core/ext/client_channel/method_config.h" #include "src/core/ext/client_channel/subchannel.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/connected_channel.h" @@ -56,6 +55,7 @@ #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/method_config.h" #include "src/core/lib/transport/static_metadata.h" /* Client channel implementation */ diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c index b8b25460356..7dc5ae0df15 100644 --- a/src/core/lib/channel/message_size_filter.c +++ b/src/core/lib/channel/message_size_filter.c @@ -38,8 +38,8 @@ #include #include -#include "src/core/ext/client_channel/method_config.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/transport/method_config.h" #define DEFAULT_MAX_SEND_MESSAGE_LENGTH -1 // Unlimited. // The protobuf library will (by default) start warning at 100 megs. diff --git a/src/core/ext/client_channel/method_config.c b/src/core/lib/transport/method_config.c similarity index 99% rename from src/core/ext/client_channel/method_config.c rename to src/core/lib/transport/method_config.c index 4313ad5e0e6..57d97700bfe 100644 --- a/src/core/ext/client_channel/method_config.c +++ b/src/core/lib/transport/method_config.c @@ -29,7 +29,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#include "src/core/ext/client_channel/method_config.h" +#include "src/core/lib/transport/method_config.h" #include diff --git a/src/core/ext/client_channel/method_config.h b/src/core/lib/transport/method_config.h similarity index 97% rename from src/core/ext/client_channel/method_config.h rename to src/core/lib/transport/method_config.h index 4cbeee56257..58fedd94366 100644 --- a/src/core/ext/client_channel/method_config.h +++ b/src/core/lib/transport/method_config.h @@ -29,8 +29,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_METHOD_CONFIG_H -#define GRPC_CORE_EXT_CLIENT_CHANNEL_METHOD_CONFIG_H +#ifndef GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H +#define GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H #include @@ -133,4 +133,4 @@ grpc_mdstr_hash_table* grpc_method_config_table_convert( void* (*convert_value)(const grpc_method_config* method_config), const grpc_mdstr_hash_table_vtable* vtable); -#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_METHOD_CONFIG_H */ +#endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */ diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 0ffb0e351c3..484e34a1f4b 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -179,6 +179,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/transport/mdstr_hash_table.c', 'src/core/lib/transport/metadata.c', 'src/core/lib/transport/metadata_batch.c', + 'src/core/lib/transport/method_config.c', 'src/core/lib/transport/static_metadata.c', 'src/core/lib/transport/timeout_encoding.c', 'src/core/lib/transport/transport.c', @@ -245,7 +246,6 @@ CORE_SOURCE_FILES = [ 'src/core/ext/client_channel/lb_policy.c', 'src/core/ext/client_channel/lb_policy_factory.c', 'src/core/ext/client_channel/lb_policy_registry.c', - 'src/core/ext/client_channel/method_config.c', 'src/core/ext/client_channel/parse_address.c', 'src/core/ext/client_channel/resolver.c', 'src/core/ext/client_channel/resolver_factory.c', diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c index ed972177507..e89e66674dd 100644 --- a/test/core/end2end/fake_resolver.c +++ b/test/core/end2end/fake_resolver.c @@ -42,13 +42,13 @@ #include #include -#include "src/core/ext/client_channel/method_config.h" #include "src/core/ext/client_channel/parse_address.h" #include "src/core/ext/client_channel/resolver_registry.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" #include "src/core/lib/support/string.h" +#include "src/core/lib/transport/method_config.h" // // fake_resolver diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 827159ab3ee..6e8de9848ee 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -873,6 +873,7 @@ src/core/lib/transport/connectivity_state.h \ src/core/lib/transport/mdstr_hash_table.h \ src/core/lib/transport/metadata.h \ src/core/lib/transport/metadata_batch.h \ +src/core/lib/transport/method_config.h \ src/core/lib/transport/static_metadata.h \ src/core/lib/transport/timeout_encoding.h \ src/core/lib/transport/transport.h \ @@ -930,7 +931,6 @@ src/core/ext/client_channel/initial_connect_string.h \ src/core/ext/client_channel/lb_policy.h \ src/core/ext/client_channel/lb_policy_factory.h \ src/core/ext/client_channel/lb_policy_registry.h \ -src/core/ext/client_channel/method_config.h \ src/core/ext/client_channel/parse_address.h \ src/core/ext/client_channel/resolver.h \ src/core/ext/client_channel/resolver_factory.h \ @@ -1061,6 +1061,7 @@ src/core/lib/transport/connectivity_state.c \ src/core/lib/transport/mdstr_hash_table.c \ src/core/lib/transport/metadata.c \ src/core/lib/transport/metadata_batch.c \ +src/core/lib/transport/method_config.c \ src/core/lib/transport/static_metadata.c \ src/core/lib/transport/timeout_encoding.c \ src/core/lib/transport/transport.c \ @@ -1127,7 +1128,6 @@ src/core/ext/client_channel/initial_connect_string.c \ src/core/ext/client_channel/lb_policy.c \ src/core/ext/client_channel/lb_policy_factory.c \ src/core/ext/client_channel/lb_policy_registry.c \ -src/core/ext/client_channel/method_config.c \ src/core/ext/client_channel/parse_address.c \ src/core/ext/client_channel/resolver.c \ src/core/ext/client_channel/resolver_factory.c \ diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json index 60138a49a2e..b77f4560fde 100644 --- a/tools/run_tests/sources_and_headers.json +++ b/tools/run_tests/sources_and_headers.json @@ -6583,6 +6583,7 @@ "src/core/lib/transport/mdstr_hash_table.h", "src/core/lib/transport/metadata.h", "src/core/lib/transport/metadata_batch.h", + "src/core/lib/transport/method_config.h", "src/core/lib/transport/static_metadata.h", "src/core/lib/transport/timeout_encoding.h", "src/core/lib/transport/transport.h", @@ -6787,6 +6788,8 @@ "src/core/lib/transport/metadata.h", "src/core/lib/transport/metadata_batch.c", "src/core/lib/transport/metadata_batch.h", + "src/core/lib/transport/method_config.c", + "src/core/lib/transport/method_config.h", "src/core/lib/transport/static_metadata.c", "src/core/lib/transport/static_metadata.h", "src/core/lib/transport/timeout_encoding.c", @@ -6813,7 +6816,6 @@ "src/core/ext/client_channel/lb_policy.h", "src/core/ext/client_channel/lb_policy_factory.h", "src/core/ext/client_channel/lb_policy_registry.h", - "src/core/ext/client_channel/method_config.h", "src/core/ext/client_channel/parse_address.h", "src/core/ext/client_channel/resolver.h", "src/core/ext/client_channel/resolver_factory.h", @@ -6846,8 +6848,6 @@ "src/core/ext/client_channel/lb_policy_factory.h", "src/core/ext/client_channel/lb_policy_registry.c", "src/core/ext/client_channel/lb_policy_registry.h", - "src/core/ext/client_channel/method_config.c", - "src/core/ext/client_channel/method_config.h", "src/core/ext/client_channel/parse_address.c", "src/core/ext/client_channel/parse_address.h", "src/core/ext/client_channel/resolver.c", diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index c86b14f63eb..5cb50cc999c 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -1306,6 +1306,7 @@ ], "cpu_cost": 1.0, "exclude_configs": [], + "exclude_iomgrs": [], "flaky": false, "gtest": false, "language": "c", @@ -1321,6 +1322,7 @@ ], "cpu_cost": 1.0, "exclude_configs": [], + "exclude_iomgrs": [], "flaky": false, "gtest": false, "language": "c", diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj index 7870234aec2..7118a95273b 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj @@ -382,6 +382,7 @@ + @@ -439,7 +440,6 @@ - @@ -674,6 +674,8 @@ + + @@ -806,8 +808,6 @@ - - diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters index 760ac313bb8..d5056aa5ec4 100644 --- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters +++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters @@ -307,6 +307,9 @@ src\core\lib\transport + + src\core\lib\transport + src\core\lib\transport @@ -505,9 +508,6 @@ src\core\ext\client_channel - - src\core\ext\client_channel - src\core\ext\client_channel @@ -968,6 +968,9 @@ src\core\lib\transport + + src\core\lib\transport + src\core\lib\transport @@ -1139,9 +1142,6 @@ src\core\ext\client_channel - - src\core\ext\client_channel - src\core\ext\client_channel diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj index 8eb4292e863..35ace6eabbd 100644 --- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj +++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj @@ -275,6 +275,7 @@ + @@ -523,6 +524,8 @@ + + diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters index 90ea7bde6d5..4b104aeb9bf 100644 --- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters @@ -364,6 +364,9 @@ src\core\lib\transport + + src\core\lib\transport + src\core\lib\transport @@ -758,6 +761,9 @@ src\core\lib\transport + + src\core\lib\transport + src\core\lib\transport diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj index 577264d3260..c8532d6c5bb 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj @@ -372,6 +372,7 @@ + @@ -405,7 +406,6 @@ - @@ -642,6 +642,8 @@ + + @@ -724,8 +726,6 @@ - - diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters index 9b3c6943633..032d2726bc2 100644 --- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters @@ -310,6 +310,9 @@ src\core\lib\transport + + src\core\lib\transport + src\core\lib\transport @@ -433,9 +436,6 @@ src\core\ext\client_channel - - src\core\ext\client_channel - src\core\ext\client_channel @@ -881,6 +881,9 @@ src\core\lib\transport + + src\core\lib\transport + src\core\lib\transport @@ -980,9 +983,6 @@ src\core\ext\client_channel - - src\core\ext\client_channel - src\core\ext\client_channel From d7648feb225e8cefd26ff5a611f8ca733a755d6c Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 26 Oct 2016 13:40:50 -0700 Subject: [PATCH 25/30] Fix issue with pointers in udp_server_test --- test/core/iomgr/udp_server_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/iomgr/udp_server_test.c b/test/core/iomgr/udp_server_test.c index d9f9cd748b4..9bea2294668 100644 --- a/test/core/iomgr/udp_server_test.c +++ b/test/core/iomgr/udp_server_test.c @@ -185,7 +185,7 @@ static void test_receive(int number_of_clients) { /* Create a socket, send a packet to the UDP server. */ clifd = socket(addr->ss_family, SOCK_DGRAM, 0); GPR_ASSERT(clifd >= 0); - GPR_ASSERT(connect(clifd, (struct sockaddr *)&addr, + GPR_ASSERT(connect(clifd, (struct sockaddr *)addr, (socklen_t)resolved_addr.len) == 0); GPR_ASSERT(5 == write(clifd, "hello", 5)); while (g_number_of_reads == number_of_reads_before && From cad0908a2b5064d637036e892ca51ed2c2db57c6 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Wed, 26 Oct 2016 15:08:13 -0700 Subject: [PATCH 26/30] Break out of build process if in vanilla MSYS --- tools/run_tests/build_python.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh index b786c479f36..54e2fe53478 100755 --- a/tools/run_tests/build_python.sh +++ b/tools/run_tests/build_python.sh @@ -39,6 +39,14 @@ cd $(dirname $0)/../.. PLATFORM=`uname -s` +function is_msys() { + if [ "${PLATFORM/MSYS}" != "$PLATFORM" ]; then + echo true + else + exit 1 + fi +} + function is_mingw() { if [ "${PLATFORM/MINGW}" != "$PLATFORM" ]; then echo true @@ -108,6 +116,12 @@ VENV=${2:-$(venv $PYTHON)} VENV_RELATIVE_PYTHON=${3:-$(venv_relative_python)} TOOLCHAIN=${4:-$(toolchain)} +if [ $(is_msys) ]; then + echo "MSYS doesn't directly provide the right compiler(s);" + echo "switch to a MinGW shell." + exit 1 +fi + ROOT=`pwd` export CFLAGS="-I$ROOT/include -std=gnu99 -fno-wrapv $CFLAGS" export GRPC_PYTHON_BUILD_WITH_CYTHON=1 From a129adf13af4169b77e4917f08a595ee7d77a70b Mon Sep 17 00:00:00 2001 From: Sree Kuchibhotla Date: Wed, 26 Oct 2016 16:44:44 -0700 Subject: [PATCH 27/30] Remove fd from polling island before deleting it --- src/core/lib/iomgr/ev_epoll_linux.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c index 8381f4a63a4..4358020f9b2 100644 --- a/src/core/lib/iomgr/ev_epoll_linux.c +++ b/src/core/lib/iomgr/ev_epoll_linux.c @@ -1711,6 +1711,12 @@ retry: "pollset_add_fd: Raced creating new polling island. pi_new: %p " "(fd: %d, pollset: %p)", (void *)pi_new, fd->fd, (void *)pollset); + + /* No need to lock 'pi_new' here since this is a new polling island and + no one has a reference to it yet */ + polling_island_remove_all_fds_locked(pi_new, true, &error); + + /* Ref and unref so that the polling island gets deleted during unref */ PI_ADD_REF(pi_new, "dance_of_destruction"); PI_UNREF(exec_ctx, pi_new, "dance_of_destruction"); goto retry; From 485a902264dc96bad9e257611662e429adc8cb75 Mon Sep 17 00:00:00 2001 From: Sree Kuchibhotla Date: Wed, 26 Oct 2016 16:46:55 -0700 Subject: [PATCH 28/30] minor formatting fix --- src/core/lib/iomgr/ev_epoll_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c index 4358020f9b2..db51ec49392 100644 --- a/src/core/lib/iomgr/ev_epoll_linux.c +++ b/src/core/lib/iomgr/ev_epoll_linux.c @@ -1713,7 +1713,7 @@ retry: (void *)pi_new, fd->fd, (void *)pollset); /* No need to lock 'pi_new' here since this is a new polling island and - no one has a reference to it yet */ + * no one has a reference to it yet */ polling_island_remove_all_fds_locked(pi_new, true, &error); /* Ref and unref so that the polling island gets deleted during unref */ From 12e5775bd00b4b55f9a93d73de0f8b53f57eda54 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 27 Oct 2016 14:30:41 -0700 Subject: [PATCH 29/30] Disable Nagle's algorithm in libuv endpoints --- src/core/lib/iomgr/tcp_uv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c index 05d6eeb2408..0dd4beaae18 100644 --- a/src/core/lib/iomgr/tcp_uv.c +++ b/src/core/lib/iomgr/tcp_uv.c @@ -315,6 +315,8 @@ grpc_endpoint *grpc_tcp_create(uv_tcp_t *handle, char *peer_string) { gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp); } + uv_tcp_nodelay(handle, 1); + memset(tcp, 0, sizeof(grpc_tcp)); tcp->base.vtable = &vtable; tcp->handle = handle; From 04d2829fe60adbcead1efe1ff8b5da70898329d0 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 27 Oct 2016 14:36:57 -0700 Subject: [PATCH 30/30] Add comment --- src/core/lib/iomgr/tcp_uv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c index 0dd4beaae18..3860fe3e9b5 100644 --- a/src/core/lib/iomgr/tcp_uv.c +++ b/src/core/lib/iomgr/tcp_uv.c @@ -315,6 +315,7 @@ grpc_endpoint *grpc_tcp_create(uv_tcp_t *handle, char *peer_string) { gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp); } + /* Disable Nagle's Algorithm */ uv_tcp_nodelay(handle, 1); memset(tcp, 0, sizeof(grpc_tcp));