The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
7.2 KiB
237 lines
7.2 KiB
/* |
|
* |
|
* Copyright 2014, 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/channel/client_setup.h" |
|
#include "src/core/channel/channel_args.h" |
|
#include "src/core/channel/channel_stack.h" |
|
#include "src/core/iomgr/alarm.h" |
|
#include <grpc/support/alloc.h> |
|
#include <grpc/support/log.h> |
|
#include <grpc/support/time.h> |
|
|
|
struct grpc_client_setup { |
|
grpc_transport_setup base; /* must be first */ |
|
void (*initiate)(void *user_data, grpc_client_setup_request *request); |
|
void (*done)(void *user_data); |
|
void *user_data; |
|
grpc_channel_args *args; |
|
grpc_mdctx *mdctx; |
|
grpc_alarm backoff_alarm; |
|
gpr_timespec current_backoff_interval; |
|
int in_alarm; |
|
|
|
gpr_mu mu; |
|
grpc_client_setup_request *active_request; |
|
int refs; |
|
}; |
|
|
|
struct grpc_client_setup_request { |
|
/* pointer back to the setup object */ |
|
grpc_client_setup *setup; |
|
gpr_timespec deadline; |
|
}; |
|
|
|
gpr_timespec grpc_client_setup_request_deadline(grpc_client_setup_request *r) { |
|
return r->deadline; |
|
} |
|
|
|
static void destroy_setup(grpc_client_setup *s) { |
|
gpr_mu_destroy(&s->mu); |
|
s->done(s->user_data); |
|
grpc_channel_args_destroy(s->args); |
|
gpr_free(s); |
|
} |
|
|
|
/* initiate handshaking */ |
|
static void setup_initiate(grpc_transport_setup *sp) { |
|
grpc_client_setup *s = (grpc_client_setup *)sp; |
|
grpc_client_setup_request *r = gpr_malloc(sizeof(grpc_client_setup_request)); |
|
int in_alarm = 0; |
|
|
|
r->setup = s; |
|
/* TODO(klempner): Actually set a deadline */ |
|
r->deadline = gpr_inf_future; |
|
|
|
gpr_mu_lock(&s->mu); |
|
GPR_ASSERT(s->refs > 0); |
|
/* there might be more than one request outstanding if the caller calls |
|
initiate in some kind of rapid-fire way: we try to connect each time, |
|
and keep track of the latest request (which is the only one that gets |
|
to finish) */ |
|
if (!s->in_alarm) { |
|
s->active_request = r; |
|
s->refs++; |
|
} else { |
|
/* TODO(klempner): Maybe do something more clever here */ |
|
in_alarm = 1; |
|
} |
|
gpr_mu_unlock(&s->mu); |
|
|
|
if (!in_alarm) { |
|
s->initiate(s->user_data, r); |
|
} else { |
|
gpr_free(r); |
|
} |
|
} |
|
|
|
/* cancel handshaking: cancel all requests, and shutdown (the caller promises |
|
not to initiate again) */ |
|
static void setup_cancel(grpc_transport_setup *sp) { |
|
grpc_client_setup *s = (grpc_client_setup *)sp; |
|
|
|
gpr_mu_lock(&s->mu); |
|
|
|
GPR_ASSERT(s->refs > 0); |
|
/* effectively cancels the current request (if any) */ |
|
s->active_request = NULL; |
|
if (s->in_alarm) { |
|
grpc_alarm_cancel(&s->backoff_alarm); |
|
} |
|
if (--s->refs == 0) { |
|
gpr_mu_unlock(&s->mu); |
|
destroy_setup(s); |
|
} else { |
|
gpr_mu_unlock(&s->mu); |
|
} |
|
} |
|
|
|
/* vtable for transport setup */ |
|
static const grpc_transport_setup_vtable setup_vtable = {setup_initiate, |
|
setup_cancel}; |
|
|
|
void grpc_client_setup_create_and_attach( |
|
grpc_channel_stack *newly_minted_channel, const grpc_channel_args *args, |
|
grpc_mdctx *mdctx, |
|
void (*initiate)(void *user_data, grpc_client_setup_request *request), |
|
void (*done)(void *user_data), void *user_data) { |
|
grpc_client_setup *s = gpr_malloc(sizeof(grpc_client_setup)); |
|
|
|
s->base.vtable = &setup_vtable; |
|
gpr_mu_init(&s->mu); |
|
s->refs = 1; |
|
s->mdctx = mdctx; |
|
s->initiate = initiate; |
|
s->done = done; |
|
s->user_data = user_data; |
|
s->active_request = NULL; |
|
s->args = grpc_channel_args_copy(args); |
|
s->current_backoff_interval = gpr_time_from_micros(1000000); |
|
s->in_alarm = 0; |
|
|
|
grpc_client_channel_set_transport_setup(newly_minted_channel, &s->base); |
|
} |
|
|
|
int grpc_client_setup_request_should_continue(grpc_client_setup_request *r) { |
|
int result; |
|
if (gpr_time_cmp(gpr_now(), r->deadline) > 0) { |
|
return 0; |
|
} |
|
gpr_mu_lock(&r->setup->mu); |
|
result = r->setup->active_request == r; |
|
gpr_mu_unlock(&r->setup->mu); |
|
return result; |
|
} |
|
|
|
static void backoff_alarm_done(void *arg /* grpc_client_setup */, |
|
grpc_iomgr_cb_status status) { |
|
grpc_client_setup *s = arg; |
|
grpc_client_setup_request *r = gpr_malloc(sizeof(grpc_client_setup_request)); |
|
r->setup = s; |
|
/* TODO(klempner): Set this to something useful */ |
|
r->deadline = gpr_inf_future; |
|
/* Handle status cancelled? */ |
|
gpr_mu_lock(&s->mu); |
|
s->active_request = r; |
|
s->in_alarm = 0; |
|
if (status != GRPC_CALLBACK_SUCCESS) { |
|
if (0 == --s->refs) { |
|
gpr_mu_unlock(&s->mu); |
|
destroy_setup(s); |
|
gpr_free(r); |
|
return; |
|
} else { |
|
gpr_mu_unlock(&s->mu); |
|
return; |
|
} |
|
} |
|
gpr_mu_unlock(&s->mu); |
|
s->initiate(s->user_data, r); |
|
} |
|
|
|
void grpc_client_setup_request_finish(grpc_client_setup_request *r, |
|
int was_successful) { |
|
int retry = !was_successful; |
|
grpc_client_setup *s = r->setup; |
|
|
|
gpr_mu_lock(&s->mu); |
|
if (s->active_request == r) { |
|
s->active_request = NULL; |
|
} else { |
|
retry = 0; |
|
} |
|
if (!retry && 0 == --s->refs) { |
|
gpr_mu_unlock(&s->mu); |
|
destroy_setup(s); |
|
gpr_free(r); |
|
return; |
|
} |
|
|
|
gpr_free(r); |
|
|
|
if (retry) { |
|
/* TODO(klempner): Replace these values with further consideration. 2x is |
|
probably too aggressive of a backoff. */ |
|
gpr_timespec max_backoff = gpr_time_from_micros(120000000); |
|
GPR_ASSERT(!s->in_alarm); |
|
s->in_alarm = 1; |
|
grpc_alarm_init(&s->backoff_alarm, backoff_alarm_done, s); |
|
grpc_alarm_add(&s->backoff_alarm, |
|
gpr_time_add(s->current_backoff_interval, gpr_now())); |
|
s->current_backoff_interval = |
|
gpr_time_add(s->current_backoff_interval, s->current_backoff_interval); |
|
if (gpr_time_cmp(s->current_backoff_interval, max_backoff) > 0) { |
|
s->current_backoff_interval = max_backoff; |
|
} |
|
} |
|
|
|
gpr_mu_unlock(&s->mu); |
|
} |
|
|
|
const grpc_channel_args *grpc_client_setup_get_channel_args( |
|
grpc_client_setup_request *r) { |
|
return r->setup->args; |
|
} |
|
|
|
grpc_mdctx *grpc_client_setup_get_mdctx(grpc_client_setup_request *r) { |
|
return r->setup->mdctx; |
|
}
|
|
|