blob: 64264f5b15824b1afbda3a200aa493715c8fcd98 [file] [log] [blame]
nnoble097ef9b2014-12-01 17:06:10 -08001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
nnoble097ef9b2014-12-01 17:06:10 -08004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
nnoble097ef9b2014-12-01 17:06:10 -08008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
nnoble097ef9b2014-12-01 17:06:10 -080010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
nnoble097ef9b2014-12-01 17:06:10 -080016 *
17 */
18
Nicolas "Pixel" Nobled51d1212016-01-31 11:33:19 +010019#include <ruby/ruby.h>
Nicolas "Pixel" Noble9fcdc872016-05-05 06:15:34 +020020
nnoble097ef9b2014-12-01 17:06:10 -080021#include "rb_completion_queue.h"
Craig Tiller5b1c5f22017-04-19 09:52:18 -070022#include "rb_grpc_imports.generated.h"
nnoble097ef9b2014-12-01 17:06:10 -080023
Yuki Yugui Sonoda22887912015-04-16 20:57:17 +090024#include <ruby/thread.h>
nnoble097ef9b2014-12-01 17:06:10 -080025
26#include <grpc/grpc.h>
murgatroid999acc40f2016-06-30 13:54:09 -070027#include <grpc/support/log.h>
Craig Tiller5b1c5f22017-04-19 09:52:18 -070028#include <grpc/support/time.h>
nnoble097ef9b2014-12-01 17:06:10 -080029#include "rb_grpc.h"
nnoble097ef9b2014-12-01 17:06:10 -080030
31/* Used to allow grpc_completion_queue_next call to release the GIL */
32typedef struct next_call_stack {
Craig Tillerbaa14a92017-11-03 09:09:36 -070033 grpc_completion_queue* cq;
Craig Tiller3a488082015-05-11 09:02:07 -070034 grpc_event event;
nnoble097ef9b2014-12-01 17:06:10 -080035 gpr_timespec timeout;
Craig Tillerbaa14a92017-11-03 09:09:36 -070036 void* tag;
murgatroid99c0ecedb2016-05-16 16:14:52 -070037 volatile int interrupted;
nnoble097ef9b2014-12-01 17:06:10 -080038} next_call_stack;
39
nnoble097ef9b2014-12-01 17:06:10 -080040/* Calls grpc_completion_queue_pluck without holding the ruby GIL */
Craig Tillerbaa14a92017-11-03 09:09:36 -070041static void* grpc_rb_completion_queue_pluck_no_gil(void* param) {
42 next_call_stack* const next_call = (next_call_stack*)param;
murgatroid99c0ecedb2016-05-16 16:14:52 -070043 gpr_timespec increment = gpr_time_from_millis(20, GPR_TIMESPAN);
44 gpr_timespec deadline;
45 do {
46 deadline = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), increment);
Craig Tiller5b1c5f22017-04-19 09:52:18 -070047 next_call->event = grpc_completion_queue_pluck(
48 next_call->cq, next_call->tag, deadline, NULL);
murgatroid99e68a7152016-05-18 09:46:26 -070049 if (next_call->event.type != GRPC_QUEUE_TIMEOUT ||
50 gpr_time_cmp(deadline, next_call->timeout) > 0) {
murgatroid99c0ecedb2016-05-16 16:14:52 -070051 break;
52 }
53 } while (!next_call->interrupted);
nnoble097ef9b2014-12-01 17:06:10 -080054 return NULL;
55}
56
nnoble097ef9b2014-12-01 17:06:10 -080057/* Helper function to free a completion queue. */
Craig Tillerbaa14a92017-11-03 09:09:36 -070058void grpc_rb_completion_queue_destroy(grpc_completion_queue* cq) {
murgatroid995756c682016-06-30 13:58:18 -070059 /* Every function that adds an event to a queue also synchronously plucks
60 that event from the queue, and holds a reference to the Ruby object that
61 holds the queue, so we only get to this point if all of those functions
62 have completed, and the queue is empty */
murgatroid999acc40f2016-06-30 13:54:09 -070063 grpc_completion_queue_shutdown(cq);
nnoble097ef9b2014-12-01 17:06:10 -080064 grpc_completion_queue_destroy(cq);
65}
66
Craig Tillerbaa14a92017-11-03 09:09:36 -070067static void unblock_func(void* param) {
68 next_call_stack* const next_call = (next_call_stack*)param;
murgatroid99c0ecedb2016-05-16 16:14:52 -070069 next_call->interrupted = 1;
70}
71
murgatroid99ec1588b2016-06-06 15:37:45 -070072/* Does the same thing as grpc_completion_queue_pluck, while properly releasing
73 the GVL and handling interrupts */
Craig Tillerbaa14a92017-11-03 09:09:36 -070074grpc_event rb_completion_queue_pluck(grpc_completion_queue* queue, void* tag,
75 gpr_timespec deadline, void* reserved) {
nnoble097ef9b2014-12-01 17:06:10 -080076 next_call_stack next_call;
77 MEMZERO(&next_call, next_call_stack, 1);
murgatroid99ec1588b2016-06-06 15:37:45 -070078 next_call.cq = queue;
79 next_call.timeout = deadline;
80 next_call.tag = tag;
Craig Tillerde4e3eb2015-05-11 18:51:32 -070081 next_call.event.type = GRPC_QUEUE_TIMEOUT;
murgatroid99ec1588b2016-06-06 15:37:45 -070082 (void)reserved;
murgatroid99c0ecedb2016-05-16 16:14:52 -070083 /* Loop until we finish a pluck without an interruption. The internal
84 pluck function runs either until it is interrupted or it gets an
85 event, or time runs out.
86
87 The basic reason we need this relatively complicated construction is that
88 we need to re-acquire the GVL when an interrupt comes in, so that the ruby
murgatroid99e68a7152016-05-18 09:46:26 -070089 interpreter can do what it needs to do with the interrupt. But we also need
90 to get back to plucking when the interrupt has been handled. */
murgatroid99c0ecedb2016-05-16 16:14:52 -070091 do {
92 next_call.interrupted = 0;
93 rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil,
Craig Tillerbaa14a92017-11-03 09:09:36 -070094 (void*)&next_call, unblock_func,
95 (void*)&next_call);
murgatroid99c0ecedb2016-05-16 16:14:52 -070096 /* If an interrupt prevented pluck from returning useful information, then
97 any plucks that did complete must have timed out */
Craig Tiller5b1c5f22017-04-19 09:52:18 -070098 } while (next_call.interrupted && next_call.event.type == GRPC_QUEUE_TIMEOUT);
Tim Emiola6de558f2015-03-28 01:46:00 -070099 return next_call.event;
nnoble097ef9b2014-12-01 17:06:10 -0800100}