Skip to content

Commit dd4d1cb

Browse files
authored
Merge pull request #188 from MrMage/even-more-improvements
Two important fixes (and possibly more improvements)
2 parents 951a20a + e68cec0 commit dd4d1cb

15 files changed

+256
-95
lines changed

Sources/CgRPC/shim/call.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#include <assert.h>
2222

2323
void cgrpc_call_destroy(cgrpc_call *call) {
24-
//grpc_call_destroy(call->call);
24+
if (call->call) {
25+
grpc_call_unref(call->call);
26+
}
2527
free(call);
2628
}
2729

Sources/CgRPC/shim/cgrpc.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ typedef enum grpc_completion_type {
9191
GRPC_OP_COMPLETE
9292
} grpc_completion_type;
9393

94+
/** Connectivity state of a channel. */
95+
typedef enum grpc_connectivity_state {
96+
/** channel has just been initialized */
97+
GRPC_CHANNEL_INIT = -1,
98+
/** channel is idle */
99+
GRPC_CHANNEL_IDLE,
100+
/** channel is connecting */
101+
GRPC_CHANNEL_CONNECTING,
102+
/** channel is ready for work */
103+
GRPC_CHANNEL_READY,
104+
/** channel has seen a failure but expects to recover */
105+
GRPC_CHANNEL_TRANSIENT_FAILURE,
106+
/** channel has seen a failure that it cannot recover from */
107+
GRPC_CHANNEL_SHUTDOWN
108+
} grpc_connectivity_state;
109+
94110
typedef struct grpc_event {
95111
/** The type of the completion. */
96112
grpc_completion_type type;
@@ -110,6 +126,9 @@ void grpc_shutdown(void);
110126
const char *grpc_version_string(void);
111127
const char *grpc_g_stands_for(void);
112128

129+
void cgrpc_completion_queue_drain(cgrpc_completion_queue *cq);
130+
void grpc_completion_queue_destroy(cgrpc_completion_queue *cq);
131+
113132
// helper
114133
void cgrpc_free_copied_string(char *string);
115134

@@ -126,6 +145,9 @@ cgrpc_call *cgrpc_channel_create_call(cgrpc_channel *channel,
126145
double timeout);
127146
cgrpc_completion_queue *cgrpc_channel_completion_queue(cgrpc_channel *channel);
128147

148+
grpc_connectivity_state cgrpc_channel_check_connectivity_state(
149+
cgrpc_channel *channel, int try_to_connect);
150+
129151
// server support
130152
cgrpc_server *cgrpc_server_create(const char *address);
131153
cgrpc_server *cgrpc_server_create_secure(const char *address,

Sources/CgRPC/shim/channel.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ void cgrpc_channel_destroy(cgrpc_channel *c) {
7171
c->channel = NULL;
7272

7373
grpc_completion_queue_shutdown(c->completion_queue);
74-
cgrpc_completion_queue_drain(c->completion_queue);
75-
grpc_completion_queue_destroy(c->completion_queue);
7674
free(c);
7775
}
7876

@@ -85,6 +83,7 @@ cgrpc_call *cgrpc_channel_create_call(cgrpc_channel *channel,
8583
// create call
8684
host_slice = grpc_slice_from_copied_string(host);
8785
gpr_timespec deadline = cgrpc_deadline_in_seconds_from_now(timeout);
86+
// The resulting call will have a retain call of +1. We'll release it in `cgrpc_call_destroy()`.
8887
grpc_call *channel_call = grpc_channel_create_call(channel->channel,
8988
NULL,
9089
GRPC_PROPAGATE_DEFAULTS,
@@ -102,3 +101,7 @@ cgrpc_call *cgrpc_channel_create_call(cgrpc_channel *channel,
102101
cgrpc_completion_queue *cgrpc_channel_completion_queue(cgrpc_channel *channel) {
103102
return channel->completion_queue;
104103
}
104+
105+
grpc_connectivity_state cgrpc_channel_check_connectivity_state(cgrpc_channel *channel, int try_to_connect) {
106+
return grpc_channel_check_connectivity_state(channel->channel, try_to_connect);
107+
}

Sources/CgRPC/shim/handler.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,10 @@ cgrpc_handler *cgrpc_handler_create_with_server(cgrpc_server *server) {
3333

3434
void cgrpc_handler_destroy(cgrpc_handler *h) {
3535
grpc_completion_queue_shutdown(h->completion_queue);
36-
cgrpc_completion_queue_drain(h->completion_queue);
37-
grpc_completion_queue_destroy(h->completion_queue);
3836
grpc_metadata_array_destroy(&(h->request_metadata_recv));
3937
grpc_call_details_destroy(&(h->call_details));
4038
if (h->server_call) {
41-
//grpc_call_destroy(h->server_call);
39+
grpc_call_unref(h->server_call);
4240
}
4341
free(h);
4442
}
@@ -67,6 +65,10 @@ cgrpc_call *cgrpc_handler_get_call(cgrpc_handler *h) {
6765
cgrpc_call *call = (cgrpc_call *) malloc(sizeof(cgrpc_call));
6866
memset(call, 0, sizeof(cgrpc_call));
6967
call->call = h->server_call;
68+
if (call->call) {
69+
// This retain will be balanced by `cgrpc_call_destroy()`.
70+
grpc_call_ref(call->call);
71+
}
7072
return call;
7173
}
7274

@@ -77,6 +79,11 @@ cgrpc_completion_queue *cgrpc_handler_get_completion_queue(cgrpc_handler *h) {
7779
grpc_call_error cgrpc_handler_request_call(cgrpc_handler *h,
7880
cgrpc_metadata_array *metadata,
7981
long tag) {
82+
if (h->server_call != NULL) {
83+
return GRPC_CALL_OK;
84+
}
85+
// This fills `h->server_call` with a call with retain count of +1.
86+
// We'll release that retain in `cgrpc_handler_destroy()`.
8087
return grpc_server_request_call(h->server->server,
8188
&(h->server_call),
8289
&(h->call_details),
@@ -85,5 +92,3 @@ grpc_call_error cgrpc_handler_request_call(cgrpc_handler *h,
8592
h->server->completion_queue,
8693
cgrpc_create_tag(tag));
8794
}
88-
89-

Sources/CgRPC/shim/server.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ void cgrpc_server_destroy(cgrpc_server *server) {
7777
server->server = NULL;
7878

7979
grpc_completion_queue_shutdown(server->completion_queue);
80-
cgrpc_completion_queue_drain(server->completion_queue);
81-
grpc_completion_queue_destroy(server->completion_queue);
8280
}
8381

8482
void cgrpc_server_start(cgrpc_server *server) {

Sources/SwiftGRPC/Core/Channel.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public class Channel {
3131

3232
/// Default host to use for new calls
3333
public var host: String
34+
35+
public var connectivityState: ConnectivityState? {
36+
return ConnectivityState.fromCEnum(cgrpc_channel_check_connectivity_state(underlyingChannel, 0))
37+
}
3438

3539
/// Initializes a gRPC channel
3640
///

Sources/SwiftGRPC/Core/CompletionQueue.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ class CompletionQueue {
7777
self.underlyingCompletionQueue = underlyingCompletionQueue
7878
self.name = name
7979
}
80+
81+
deinit {
82+
cgrpc_completion_queue_drain(underlyingCompletionQueue)
83+
grpc_completion_queue_destroy(underlyingCompletionQueue)
84+
}
8085

8186
/// Waits for an operation group to complete
8287
///
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2018, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#if SWIFT_PACKAGE
17+
import CgRPC
18+
#endif
19+
import Foundation
20+
21+
public enum ConnectivityState: Int32, Error {
22+
case initializing = -1
23+
case idle
24+
case connecting
25+
case ready
26+
case transient_failure
27+
case shutdown
28+
29+
static func fromCEnum(_ connectivityState: grpc_connectivity_state) -> ConnectivityState? {
30+
return ConnectivityState(rawValue: connectivityState.rawValue)
31+
}
32+
}

Sources/SwiftGRPC/Core/Handler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class Handler {
3232
/// A Call object that can be used to respond to the request
3333
public private(set) lazy var call: Call = {
3434
Call(underlyingCall: cgrpc_handler_get_call(self.underlyingHandler),
35-
owned: false,
35+
owned: true,
3636
completionQueue: self.completionQueue)
3737
}()
3838

Sources/SwiftGRPC/Core/Metadata.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ public class Metadata: CustomStringConvertible {
6969
}
7070

7171
public var description: String {
72-
var result = ""
72+
var lines: [String] = []
7373
for i in 0..<count() {
7474
let key = self.key(i)
7575
let value = self.value(i)
76-
result += (key ?? "(nil)") + ":" + (value ?? "(nil)") + "\n"
76+
lines.append((key ?? "(nil)") + ":" + (value ?? "(nil)"))
7777
}
78-
return result
78+
return lines.joined(separator: "\n")
7979
}
8080

8181
public func copy() -> Metadata {

Sources/SwiftGRPC/Core/Server.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ public class Server {
6666
cgrpc_server_start(underlyingServer)
6767
// run the server on a new background thread
6868
dispatchQueue.async {
69-
var running = true
70-
while running {
69+
spinloop: while true {
7170
do {
7271
let handler = Handler(underlyingServer: self.underlyingServer)
7372
try handler.requestCall(tag: Server.handlerCallTag)
@@ -97,16 +96,17 @@ public class Server {
9796
}
9897
}
9998
} else if event.tag == Server.stopTag || event.tag == Server.destroyTag {
100-
running = false // exit the loop
99+
break spinloop
101100
}
102101
} else if event.type == .queueTimeout {
103102
// everything is fine
103+
continue
104104
} else if event.type == .queueShutdown {
105-
running = false
105+
break spinloop
106106
}
107107
} catch {
108108
print("server call error: \(error)")
109-
running = false
109+
break spinloop
110110
}
111111
}
112112
self.onCompletion?()

Sources/SwiftGRPC/Runtime/ServiceServer.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ open class ServiceServer {
2222
public let address: String
2323
public let server: Server
2424

25+
public var shouldLogRequests = true
26+
2527
/// Create a server that accepts insecure connections.
2628
public init(address: String) {
2729
gRPC.initialize()
@@ -58,13 +60,15 @@ open class ServiceServer {
5860
return
5961
}
6062

61-
let unwrappedHost = handler.host ?? "(nil)"
6263
let unwrappedMethod = handler.method ?? "(nil)"
63-
let unwrappedCaller = handler.caller ?? "(nil)"
64-
print("Server received request to " + unwrappedHost
65-
+ " calling " + unwrappedMethod
66-
+ " from " + unwrappedCaller
67-
+ " with " + handler.requestMetadata.description)
64+
if strongSelf.shouldLogRequests == true {
65+
let unwrappedHost = handler.host ?? "(nil)"
66+
let unwrappedCaller = handler.caller ?? "(nil)"
67+
print("Server received request to " + unwrappedHost
68+
+ " calling " + unwrappedMethod
69+
+ " from " + unwrappedCaller
70+
+ " with " + handler.requestMetadata.description)
71+
}
6872

6973
do {
7074
if !(try strongSelf.handleMethod(unwrappedMethod, handler: handler, queue: queue)) {

Tests/SwiftGRPCTests/EchoTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class EchoTests: BasicEchoTestCase {
2222
static var allTests: [(String, (EchoTests) -> () throws -> Void)] {
2323
return [
2424
("testUnary", testUnary),
25+
("testUnaryLotsOfRequests", testUnaryLotsOfRequests),
2526
("testClientStreaming", testClientStreaming),
2627
("testClientStreamingLotsOfMessages", testClientStreamingLotsOfMessages),
2728
("testServerStreaming", testServerStreaming),
@@ -48,6 +49,22 @@ extension EchoTests {
4849
XCTAssertEqual("Swift echo get: foo", try! client.get(Echo_EchoRequest(text: "foo")).text)
4950
XCTAssertEqual("Swift echo get: foo", try! client.get(Echo_EchoRequest(text: "foo")).text)
5051
}
52+
53+
func testUnaryLotsOfRequests() {
54+
// No need to spam the log with 50k lines.
55+
server.shouldLogRequests = false
56+
// Sending that many requests at once can sometimes trip things up, it seems.
57+
client.timeout = 5.0
58+
let clockStart = clock()
59+
let numberOfRequests = 50_000
60+
for i in 0..<numberOfRequests {
61+
if i % 1_000 == 0 && i > 0 {
62+
print("\(i) requests sent so far, elapsed time: \(Double(clock() - clockStart) / Double(CLOCKS_PER_SEC))")
63+
}
64+
XCTAssertEqual("Swift echo get: foo \(i)", try client.get(Echo_EchoRequest(text: "foo \(i)")).text)
65+
}
66+
print("total time for \(numberOfRequests) requests: \(Double(clock() - clockStart) / Double(CLOCKS_PER_SEC))")
67+
}
5168
}
5269

5370
extension EchoTests {

0 commit comments

Comments
 (0)