aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ruby/ext
diff options
context:
space:
mode:
Diffstat (limited to 'src/ruby/ext')
-rw-r--r--src/ruby/ext/grpc/extconf.rb92
-rw-r--r--src/ruby/ext/grpc/rb_byte_buffer.c243
-rw-r--r--src/ruby/ext/grpc/rb_byte_buffer.h54
-rw-r--r--src/ruby/ext/grpc/rb_call.c542
-rw-r--r--src/ruby/ext/grpc/rb_call.h59
-rw-r--r--src/ruby/ext/grpc/rb_channel.c235
-rw-r--r--src/ruby/ext/grpc/rb_channel.h49
-rw-r--r--src/ruby/ext/grpc/rb_channel_args.c157
-rw-r--r--src/ruby/ext/grpc/rb_channel_args.h53
-rw-r--r--src/ruby/ext/grpc/rb_completion_queue.c194
-rw-r--r--src/ruby/ext/grpc/rb_completion_queue.h50
-rw-r--r--src/ruby/ext/grpc/rb_event.c284
-rw-r--r--src/ruby/ext/grpc/rb_event.h55
-rw-r--r--src/ruby/ext/grpc/rb_grpc.c230
-rw-r--r--src/ruby/ext/grpc/rb_grpc.h71
-rw-r--r--src/ruby/ext/grpc/rb_metadata.c215
-rw-r--r--src/ruby/ext/grpc/rb_metadata.h53
-rw-r--r--src/ruby/ext/grpc/rb_server.c226
-rw-r--r--src/ruby/ext/grpc/rb_server.h50
-rw-r--r--src/ruby/ext/grpc/rb_status.c243
-rw-r--r--src/ruby/ext/grpc/rb_status.h53
21 files changed, 3208 insertions, 0 deletions
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
new file mode 100644
index 0000000000..06bfad9e6c
--- /dev/null
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -0,0 +1,92 @@
+# 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.
+
+require 'mkmf'
+
+LIBDIR = RbConfig::CONFIG['libdir']
+INCLUDEDIR = RbConfig::CONFIG['includedir']
+
+HEADER_DIRS = [
+ # First search the local development dir
+ ENV['HOME'] + '/grpc_dev/include',
+
+ # Then search /opt/local (Mac)
+ '/opt/local/include',
+
+ # Then search /usr/local (Source install)
+ '/usr/local/include',
+
+ # Check the ruby install locations
+ INCLUDEDIR,
+
+ # Finally fall back to /usr
+ '/usr/include'
+]
+
+LIB_DIRS = [
+ # First search the local development dir
+ ENV['HOME'] + '/grpc_dev/lib',
+
+ # Then search /opt/local for (Mac)
+ '/opt/local/lib',
+
+ # Then search /usr/local (Source install)
+ '/usr/local/lib',
+
+ # Check the ruby install locations
+ LIBDIR,
+
+ # Finally fall back to /usr
+ '/usr/lib'
+]
+
+def crash(msg)
+ print(" extconf failure: %s\n" % msg)
+ exit 1
+end
+
+dir_config('grpc', HEADER_DIRS, LIB_DIRS)
+
+$CFLAGS << ' -std=c89 '
+$CFLAGS << ' -Wno-implicit-function-declaration '
+$CFLAGS << ' -Wno-pointer-sign '
+$CFLAGS << ' -Wno-return-type '
+$CFLAGS << ' -Wall '
+$CFLAGS << ' -pedantic '
+
+$LDFLAGS << ' -lgrpc -lgpr -levent -levent_pthreads -levent_core'
+
+# crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
+#
+# TODO(temiola): figure out why this stopped working, but the so is built OK
+# and the tests pass
+
+have_library('grpc', 'grpc_channel_destroy')
+crash('need gpr lib') unless have_library('gpr', 'gpr_now')
+create_makefile('grpc/grpc')
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c
new file mode 100644
index 0000000000..a520ca44dd
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_byte_buffer.c
@@ -0,0 +1,243 @@
+/*
+ *
+ * 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 "rb_byte_buffer.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/slice.h>
+#include "rb_grpc.h"
+
+/* grpc_rb_byte_buffer wraps a grpc_byte_buffer. It provides a peer ruby
+ * object, 'mark' to minimize copying when a byte_buffer is created from
+ * ruby. */
+typedef struct grpc_rb_byte_buffer {
+ /* Holder of ruby objects involved in constructing the status */
+ VALUE mark;
+ /* The actual status */
+ grpc_byte_buffer *wrapped;
+} grpc_rb_byte_buffer;
+
+
+/* Destroys ByteBuffer instances. */
+static void grpc_rb_byte_buffer_free(void *p) {
+ grpc_rb_byte_buffer *bb = NULL;
+ if (p == NULL) {
+ return;
+ };
+ bb = (grpc_rb_byte_buffer *)p;
+
+ /* Deletes the wrapped object if the mark object is Qnil, which indicates
+ * that no other object is the actual owner. */
+ if (bb->wrapped != NULL && bb->mark == Qnil) {
+ grpc_byte_buffer_destroy(bb->wrapped);
+ }
+
+ xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_byte_buffer_mark(void *p) {
+ grpc_rb_byte_buffer *bb = NULL;
+ if (p == NULL) {
+ return;
+ }
+ bb = (grpc_rb_byte_buffer *)p;
+
+ /* If it's not already cleaned up, mark the mark object */
+ if (bb->mark != Qnil && BUILTIN_TYPE(bb->mark) != T_NONE) {
+ rb_gc_mark(bb->mark);
+ }
+}
+
+/* id_source is the name of the hidden ivar the preserves the original
+ * byte_buffer source string */
+static ID id_source;
+
+/* Allocates ByteBuffer instances.
+
+ Provides safe default values for the byte_buffer fields. */
+static VALUE grpc_rb_byte_buffer_alloc(VALUE cls) {
+ grpc_rb_byte_buffer *wrapper = ALLOC(grpc_rb_byte_buffer);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_byte_buffer_mark,
+ grpc_rb_byte_buffer_free, wrapper);
+}
+
+/* Clones ByteBuffer instances.
+
+ Gives ByteBuffer a consistent implementation of Ruby's object copy/dup
+ protocol. */
+static VALUE grpc_rb_byte_buffer_init_copy(VALUE copy, VALUE orig) {
+ grpc_rb_byte_buffer *orig_bb = NULL;
+ grpc_rb_byte_buffer *copy_bb = NULL;
+
+ if (copy == orig) {
+ return copy;
+ }
+
+ /* Raise an error if orig is not a metadata object or a subclass. */
+ if (TYPE(orig) != T_DATA ||
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_byte_buffer_free) {
+ rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cByteBuffer));
+ }
+
+ Data_Get_Struct(orig, grpc_rb_byte_buffer, orig_bb);
+ Data_Get_Struct(copy, grpc_rb_byte_buffer, copy_bb);
+
+ /* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper
+ * object. */
+ MEMCPY(copy_bb, orig_bb, grpc_rb_byte_buffer, 1);
+ return copy;
+}
+
+/* id_empty is used to return the empty string from to_s when necessary. */
+static ID id_empty;
+
+static VALUE grpc_rb_byte_buffer_to_s(VALUE self) {
+ grpc_rb_byte_buffer *wrapper = NULL;
+ grpc_byte_buffer *bb = NULL;
+ grpc_byte_buffer_reader *reader = NULL;
+ char *output = NULL;
+ size_t length = 0;
+ size_t offset = 0;
+ VALUE output_obj = Qnil;
+ gpr_slice next;
+
+ Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper);
+ output_obj = rb_ivar_get(wrapper->mark, id_source);
+ if (output_obj != Qnil) {
+ /* From ruby, ByteBuffers are immutable so if a source is set, return that
+ * as the to_s value */
+ return output_obj;
+ }
+
+ /* Read the bytes. */
+ bb = wrapper->wrapped;
+ if (bb == NULL) {
+ return rb_id2str(id_empty);
+ }
+ length = grpc_byte_buffer_length(bb);
+ if (length == 0) {
+ return rb_id2str(id_empty);
+ }
+ reader = grpc_byte_buffer_reader_create(bb);
+ output = xmalloc(length);
+ while (grpc_byte_buffer_reader_next(reader, &next) != 0) {
+ memcpy(output + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next));
+ offset += GPR_SLICE_LENGTH(next);
+ }
+ output_obj = rb_str_new(output, length);
+
+ /* Save a references to the computed string in the mark object so that the
+ * calling to_s does not do any allocations. */
+ wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
+ rb_ivar_set(wrapper->mark, id_source, output_obj);
+
+ return output_obj;
+}
+
+
+/* Initializes ByteBuffer instances. */
+static VALUE grpc_rb_byte_buffer_init(VALUE self, VALUE src) {
+ gpr_slice a_slice;
+ grpc_rb_byte_buffer *wrapper = NULL;
+ grpc_byte_buffer *byte_buffer = NULL;
+
+ if (TYPE(src) != T_STRING) {
+ rb_raise(rb_eTypeError, "bad byte_buffer arg: got <%s>, want <String>",
+ rb_obj_classname(src));
+ return Qnil;
+ }
+ Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper);
+ a_slice = gpr_slice_malloc(RSTRING_LEN(src));
+ memcpy(GPR_SLICE_START_PTR(a_slice), RSTRING_PTR(src), RSTRING_LEN(src));
+ byte_buffer = grpc_byte_buffer_create(&a_slice, 1);
+ gpr_slice_unref(a_slice);
+
+ if (byte_buffer == NULL) {
+ rb_raise(rb_eArgError, "could not create a byte_buffer, not sure why");
+ return Qnil;
+ }
+ wrapper->wrapped = byte_buffer;
+
+ /* Save a references to the original string in the mark object so that the
+ * pointers used there is valid for the lifetime of the object. */
+ wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
+ rb_ivar_set(wrapper->mark, id_source, src);
+
+ return self;
+}
+
+/* rb_cByteBuffer is the ruby class that proxies grpc_byte_buffer. */
+VALUE rb_cByteBuffer = Qnil;
+
+void Init_google_rpc_byte_buffer() {
+ rb_cByteBuffer = rb_define_class_under(rb_mGoogleRPC, "ByteBuffer",
+ rb_cObject);
+
+ /* Allocates an object managed by the ruby runtime */
+ rb_define_alloc_func(rb_cByteBuffer, grpc_rb_byte_buffer_alloc);
+
+ /* Provides a ruby constructor and support for dup/clone. */
+ rb_define_method(rb_cByteBuffer, "initialize", grpc_rb_byte_buffer_init, 1);
+ rb_define_method(rb_cByteBuffer, "initialize_copy",
+ grpc_rb_byte_buffer_init_copy, 1);
+
+ /* Provides a to_s method that returns the buffer value */
+ rb_define_method(rb_cByteBuffer, "to_s", grpc_rb_byte_buffer_to_s, 0);
+
+ id_source = rb_intern("__source");
+ id_empty = rb_intern("");
+}
+
+VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb) {
+ grpc_rb_byte_buffer *byte_buffer = NULL;
+ if (bb == NULL) {
+ return Qnil;
+ }
+ byte_buffer = ALLOC(grpc_rb_byte_buffer);
+ byte_buffer->wrapped = bb;
+ byte_buffer->mark = mark;
+ return Data_Wrap_Struct(rb_cByteBuffer, grpc_rb_byte_buffer_mark,
+ grpc_rb_byte_buffer_free, byte_buffer);
+}
+
+/* Gets the wrapped byte_buffer from the ruby wrapper */
+grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v) {
+ grpc_rb_byte_buffer *wrapper = NULL;
+ Data_Get_Struct(v, grpc_rb_byte_buffer, wrapper);
+ return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.h b/src/ruby/ext/grpc/rb_byte_buffer.h
new file mode 100644
index 0000000000..1bdcfe4019
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_byte_buffer.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_BYTE_BUFFER_H_
+#define GRPC_RB_BYTE_BUFFER_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* rb_cByteBuffer is the ByteBuffer class whose instances proxy
+ grpc_byte_buffer. */
+extern VALUE rb_cByteBuffer;
+
+/* Initializes the ByteBuffer class. */
+void Init_google_rpc_byte_buffer();
+
+/* grpc_rb_byte_buffer_create_with_mark creates a grpc_rb_byte_buffer with a
+ * ruby mark object that will be kept alive while the byte_buffer is alive. */
+VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb);
+
+/* Gets the wrapped byte_buffer from its ruby object. */
+grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v);
+
+#endif /* GRPC_RB_BYTE_BUFFER_H_ */
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
new file mode 100644
index 0000000000..07f70e041a
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -0,0 +1,542 @@
+/*
+ *
+ * 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 "rb_call.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_byte_buffer.h"
+#include "rb_completion_queue.h"
+#include "rb_metadata.h"
+#include "rb_status.h"
+#include "rb_grpc.h"
+
+/* id_cq is the name of the hidden ivar that preserves a reference to a
+ * completion queue */
+static ID id_cq;
+
+/* id_flags is the name of the hidden ivar that preserves the value of
+ * the flags used to create metadata from a Hash */
+static ID id_flags;
+
+/* id_input_md is the name of the hidden ivar that preserves the hash used to
+ * create metadata, so that references to the strings it contains last as long
+ * as the call the metadata is added to. */
+static ID id_input_md;
+
+/* id_metadata is name of the attribute used to access the metadata hash
+ * received by the call and subsequently saved on it. */
+static ID id_metadata;
+
+/* id_status is name of the attribute used to access the status object
+ * received by the call and subsequently saved on it. */
+static ID id_status;
+
+/* hash_all_calls is a hash of Call address -> reference count that is used to
+ * track the creation and destruction of rb_call instances.
+ */
+static VALUE hash_all_calls;
+
+/* Destroys a Call. */
+void grpc_rb_call_destroy(void *p) {
+ grpc_call *call = NULL;
+ VALUE ref_count = Qnil;
+ if (p == NULL) {
+ return;
+ };
+ call = (grpc_call *)p;
+
+ ref_count = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)call));
+ if (ref_count == Qnil) {
+ return; /* No longer in the hash, so already deleted */
+ } else if (NUM2UINT(ref_count) == 1) {
+ rb_hash_delete(hash_all_calls, OFFT2NUM((VALUE)call));
+ grpc_call_destroy(call);
+ } else {
+ rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)call),
+ UINT2NUM(NUM2UINT(ref_count) - 1));
+ }
+}
+
+/* Error code details is a hash containing text strings describing errors */
+VALUE rb_error_code_details;
+
+/* Obtains the error detail string for given error code */
+const char* grpc_call_error_detail_of(grpc_call_error err) {
+ VALUE detail_ref = rb_hash_aref(rb_error_code_details, UINT2NUM(err));
+ const char* detail = "unknown error code!";
+ if (detail_ref != Qnil) {
+ detail = StringValueCStr(detail_ref);
+ }
+ return detail;
+}
+
+/* grpc_rb_call_add_metadata_hash_cb is the hash iteration callback used by
+ grpc_rb_call_add_metadata.
+*/
+int grpc_rb_call_add_metadata_hash_cb(VALUE key, VALUE val, VALUE call_obj) {
+ grpc_call *call = NULL;
+ grpc_metadata *md = NULL;
+ VALUE md_obj = Qnil;
+ VALUE md_obj_args[2];
+ VALUE flags = rb_ivar_get(call_obj, id_flags);
+ grpc_call_error err;
+ int array_length;
+ int i;
+
+ /* Construct a metadata object from key and value and add it */
+ Data_Get_Struct(call_obj, grpc_call, call);
+ md_obj_args[0] = key;
+
+ if (TYPE(val) == T_ARRAY) {
+ /* If the value is an array, add each value in the array separately */
+ array_length = RARRAY_LEN(val);
+ for (i = 0; i < array_length; i++) {
+ md_obj_args[1] = rb_ary_entry(val, i);
+ md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
+ md = grpc_rb_get_wrapped_metadata(md_obj);
+ err = grpc_call_add_metadata(call, md, NUM2UINT(flags));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ return ST_STOP;
+ }
+ }
+ } else {
+ md_obj_args[1] = val;
+ md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
+ md = grpc_rb_get_wrapped_metadata(md_obj);
+ err = grpc_call_add_metadata(call, md, NUM2UINT(flags));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ return ST_STOP;
+ }
+ }
+
+ return ST_CONTINUE;
+}
+
+/*
+ call-seq:
+ call.add_metadata(completion_queue, hash_elements, flags=nil)
+
+ Add metadata elements to the call from a ruby hash, to be sent upon
+ invocation. flags is a bit-field combination of the write flags defined
+ above. REQUIRES: grpc_call_start_invoke/grpc_call_accept have not been
+ called on this call. Produces no events. */
+
+static VALUE grpc_rb_call_add_metadata(int argc, VALUE *argv, VALUE self) {
+ VALUE metadata;
+ VALUE flags = Qnil;
+ ID id_size = rb_intern("size");
+
+ /* "11" == 1 mandatory args, 1 (flags) is optional */
+ rb_scan_args(argc, argv, "11", &metadata, &flags);
+ if (NIL_P(flags)) {
+ flags = UINT2NUM(0); /* Default to no flags */
+ }
+ if (TYPE(metadata) != T_HASH) {
+ rb_raise(rb_eTypeError, "add metadata failed: metadata should be a hash");
+ return Qnil;
+ }
+ if (NUM2UINT(rb_funcall(metadata, id_size, 0)) == 0) {
+ return Qnil;
+ }
+ rb_ivar_set(self, id_flags, flags);
+ rb_ivar_set(self, id_input_md, metadata);
+ rb_hash_foreach(metadata, grpc_rb_call_add_metadata_hash_cb, self);
+ return Qnil;
+}
+
+/* Called by clients to cancel an RPC on the server.
+ Can be called multiple times, from any thread. */
+static VALUE grpc_rb_call_cancel(VALUE self) {
+ grpc_call *call = NULL;
+ grpc_call_error err;
+ Data_Get_Struct(self, grpc_call, call);
+ err = grpc_call_cancel(call);
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "cancel failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+
+ return Qnil;
+}
+
+/*
+ call-seq:
+ call.start_invoke(completion_queue, tag, flags=nil)
+
+ Invoke the RPC. Starts sending metadata and request headers on the wire.
+ flags is a bit-field combination of the write flags defined above.
+ REQUIRES: Can be called at most once per call.
+ Can only be called on the client.
+ Produces a GRPC_INVOKE_ACCEPTED event on completion. */
+static VALUE grpc_rb_call_start_invoke(int argc, VALUE *argv, VALUE self) {
+ VALUE cqueue = Qnil;
+ VALUE invoke_accepted_tag = Qnil;
+ VALUE metadata_read_tag = Qnil;
+ VALUE finished_tag = Qnil;
+ VALUE flags = Qnil;
+ grpc_call *call = NULL;
+ grpc_completion_queue *cq = NULL;
+ grpc_call_error err;
+
+ /* "41" == 4 mandatory args, 1 (flags) is optional */
+ rb_scan_args(argc, argv, "41", &cqueue, &invoke_accepted_tag,
+ &metadata_read_tag, &finished_tag, &flags);
+ if (NIL_P(flags)) {
+ flags = UINT2NUM(0); /* Default to no flags */
+ }
+ cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+ Data_Get_Struct(self, grpc_call, call);
+ err = grpc_call_start_invoke(call, cq, ROBJECT(invoke_accepted_tag),
+ ROBJECT(metadata_read_tag),
+ ROBJECT(finished_tag),
+ NUM2UINT(flags));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "invoke failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+
+ /* Add the completion queue as an instance attribute, prevents it from being
+ * GCed until this call object is GCed */
+ rb_ivar_set(self, id_cq, cqueue);
+
+ return Qnil;
+}
+
+/* Initiate a read on a call. Output event contains a byte buffer with the
+ result of the read.
+ REQUIRES: No other reads are pending on the call. It is only safe to start
+ the next read after the corresponding read event is received. */
+static VALUE grpc_rb_call_start_read(VALUE self, VALUE tag) {
+ grpc_call *call = NULL;
+ grpc_call_error err;
+ Data_Get_Struct(self, grpc_call, call);
+ err = grpc_call_start_read(call, ROBJECT(tag));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "start read failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+
+ return Qnil;
+}
+
+/*
+ call-seq:
+ status = call.status
+
+ Gets the status object saved the call. */
+static VALUE grpc_rb_call_get_status(VALUE self) {
+ return rb_ivar_get(self, id_status);
+}
+
+/*
+ call-seq:
+ call.status = status
+
+ Saves a status object on the call. */
+static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) {
+ if (!NIL_P(status) && rb_obj_class(status) != rb_cStatus) {
+ rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Status>",
+ rb_obj_classname(status));
+ return Qnil;
+ }
+
+ return rb_ivar_set(self, id_status, status);
+}
+
+/*
+ call-seq:
+ metadata = call.metadata
+
+ Gets the metadata object saved the call. */
+static VALUE grpc_rb_call_get_metadata(VALUE self) {
+ return rb_ivar_get(self, id_metadata);
+}
+
+/*
+ call-seq:
+ call.metadata = metadata
+
+ Saves the metadata hash on the call. */
+static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) {
+ if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) {
+ rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>",
+ rb_obj_classname(metadata));
+ return Qnil;
+ }
+
+ return rb_ivar_set(self, id_metadata, metadata);
+}
+
+/*
+ call-seq:
+ call.start_write(byte_buffer, tag, flags=nil)
+
+ Queue a byte buffer for writing.
+ flags is a bit-field combination of the write flags defined above.
+ A write with byte_buffer null is allowed, and will not send any bytes on the
+ wire. If this is performed without GRPC_WRITE_BUFFER_HINT flag it provides
+ a mechanism to flush any previously buffered writes to outgoing flow control.
+ REQUIRES: No other writes are pending on the call. It is only safe to
+ start the next write after the corresponding write_accepted event
+ is received.
+ GRPC_INVOKE_ACCEPTED must have been received by the application
+ prior to calling this on the client. On the server,
+ grpc_call_accept must have been called successfully.
+ Produces a GRPC_WRITE_ACCEPTED event. */
+static VALUE grpc_rb_call_start_write(int argc, VALUE *argv, VALUE self) {
+ VALUE byte_buffer = Qnil;
+ VALUE tag = Qnil;
+ VALUE flags = Qnil;
+ grpc_call *call = NULL;
+ grpc_byte_buffer *bfr = NULL;
+ grpc_call_error err;
+
+ /* "21" == 2 mandatory args, 1 (flags) is optional */
+ rb_scan_args(argc, argv, "21", &byte_buffer, &tag, &flags);
+ if (NIL_P(flags)) {
+ flags = UINT2NUM(0); /* Default to no flags */
+ }
+ bfr = grpc_rb_get_wrapped_byte_buffer(byte_buffer);
+ Data_Get_Struct(self, grpc_call, call);
+ err = grpc_call_start_write(call, bfr, ROBJECT(tag), NUM2UINT(flags));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "start write failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+
+ return Qnil;
+}
+
+/* Queue a status for writing.
+ REQUIRES: No other writes are pending on the call. It is only safe to
+ start the next write after the corresponding write_accepted event
+ is received.
+ GRPC_INVOKE_ACCEPTED must have been received by the application
+ prior to calling this.
+ Only callable on the server.
+ Produces a GRPC_FINISHED event when the status is sent and the stream is
+ fully closed */
+static VALUE grpc_rb_call_start_write_status(VALUE self, VALUE status,
+ VALUE tag) {
+ grpc_call *call = NULL;
+ grpc_status *sts = grpc_rb_get_wrapped_status(status);
+ grpc_call_error err;
+ Data_Get_Struct(self, grpc_call, call);
+ err = grpc_call_start_write_status(call, *sts, ROBJECT(tag));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "start write status: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+
+ return Qnil;
+}
+
+/* No more messages to send.
+ REQUIRES: No other writes are pending on the call. */
+static VALUE grpc_rb_call_writes_done(VALUE self, VALUE tag) {
+ grpc_call *call = NULL;
+ grpc_call_error err;
+ Data_Get_Struct(self, grpc_call, call);
+ err = grpc_call_writes_done(call, ROBJECT(tag));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "writes done: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+
+ return Qnil;
+}
+
+/* call-seq:
+ call.accept(completion_queue, flags=nil)
+
+ Accept an incoming RPC, binding a completion queue to it.
+ To be called after adding metadata to the call, but before sending
+ messages.
+ flags is a bit-field combination of the write flags defined above.
+ REQUIRES: Can be called at most once per call.
+ Can only be called on the server.
+ Produces no events. */
+static VALUE grpc_rb_call_accept(int argc, VALUE *argv, VALUE self) {
+ VALUE cqueue = Qnil;
+ VALUE finished_tag = Qnil;
+ VALUE flags = Qnil;
+ grpc_call *call = NULL;
+ grpc_completion_queue *cq = NULL;
+ grpc_call_error err;
+
+ /* "21" == 2 mandatory args, 1 (flags) is optional */
+ rb_scan_args(argc, argv, "21", &cqueue, &finished_tag, &flags);
+ if (NIL_P(flags)) {
+ flags = UINT2NUM(0); /* Default to no flags */
+ }
+ cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+ Data_Get_Struct(self, grpc_call, call);
+ err = grpc_call_accept(call, cq, ROBJECT(finished_tag), NUM2UINT(flags));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "accept failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+
+ /* Add the completion queue as an instance attribute, prevents it from being
+ * GCed until this call object is GCed */
+ rb_ivar_set(self, id_cq, cqueue);
+
+ return Qnil;
+}
+
+/* rb_cCall is the ruby class that proxies grpc_call. */
+VALUE rb_cCall = Qnil;
+
+/* rb_eCallError is the ruby class of the exception thrown during call
+ operations; */
+VALUE rb_eCallError = Qnil;
+
+void Init_google_rpc_error_codes() {
+ /* Constants representing the error codes of grpc_call_error in grpc.h */
+ VALUE rb_RpcErrors = rb_define_module_under(rb_mGoogleRPC, "RpcErrors");
+ rb_define_const(rb_RpcErrors, "OK", UINT2NUM(GRPC_CALL_OK));
+ rb_define_const(rb_RpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR));
+ rb_define_const(rb_RpcErrors, "NOT_ON_SERVER",
+ UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER));
+ rb_define_const(rb_RpcErrors, "NOT_ON_CLIENT",
+ UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT));
+ rb_define_const(rb_RpcErrors, "ALREADY_INVOKED",
+ UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED));
+ rb_define_const(rb_RpcErrors, "NOT_INVOKED",
+ UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED));
+ rb_define_const(rb_RpcErrors, "ALREADY_FINISHED",
+ UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED));
+ rb_define_const(rb_RpcErrors, "TOO_MANY_OPERATIONS",
+ UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
+ rb_define_const(rb_RpcErrors, "INVALID_FLAGS",
+ UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS));
+
+ /* Add the detail strings to a Hash */
+ rb_error_code_details = rb_hash_new();
+ rb_hash_aset(rb_error_code_details,
+ UINT2NUM(GRPC_CALL_OK), rb_str_new2("ok"));
+ rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR),
+ rb_str_new2("unknown error"));
+ rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER),
+ rb_str_new2("not available on a server"));
+ rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT),
+ rb_str_new2("not available on a client"));
+ rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED),
+ rb_str_new2("call is already invoked"));
+ rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED),
+ rb_str_new2("call is not yet invoked"));
+ rb_hash_aset(rb_error_code_details,
+ UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED),
+ rb_str_new2("call is already finished"));
+ rb_hash_aset(rb_error_code_details,
+ UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS),
+ rb_str_new2("outstanding read or write present"));
+ rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS),
+ rb_str_new2("a bad flag was given"));
+ rb_define_const(rb_RpcErrors, "ErrorMessages", rb_error_code_details);
+ rb_obj_freeze(rb_error_code_details);
+}
+
+void Init_google_rpc_call() {
+ /* CallError inherits from Exception to signal that it is non-recoverable */
+ rb_eCallError = rb_define_class_under(rb_mGoogleRPC, "CallError",
+ rb_eException);
+ rb_cCall = rb_define_class_under(rb_mGoogleRPC, "Call", rb_cObject);
+
+ /* Prevent allocation or inialization of the Call class */
+ rb_define_alloc_func(rb_cCall, grpc_rb_cannot_alloc);
+ rb_define_method(rb_cCall, "initialize", grpc_rb_cannot_init, 0);
+ rb_define_method(rb_cCall, "initialize_copy", grpc_rb_cannot_init_copy, 1);
+
+ /* Add ruby analogues of the Call methods. */
+ rb_define_method(rb_cCall, "accept", grpc_rb_call_accept, -1);
+ rb_define_method(rb_cCall, "add_metadata", grpc_rb_call_add_metadata,
+ -1);
+ rb_define_method(rb_cCall, "cancel", grpc_rb_call_cancel, 0);
+ rb_define_method(rb_cCall, "start_invoke", grpc_rb_call_start_invoke, -1);
+ rb_define_method(rb_cCall, "start_read", grpc_rb_call_start_read, 1);
+ rb_define_method(rb_cCall, "start_write", grpc_rb_call_start_write, -1);
+ rb_define_method(rb_cCall, "start_write_status",
+ grpc_rb_call_start_write_status, 2);
+ rb_define_method(rb_cCall, "writes_done", grpc_rb_call_writes_done, 1);
+ rb_define_method(rb_cCall, "status", grpc_rb_call_get_status, 0);
+ rb_define_method(rb_cCall, "status=", grpc_rb_call_set_status, 1);
+ rb_define_method(rb_cCall, "metadata", grpc_rb_call_get_metadata, 0);
+ rb_define_method(rb_cCall, "metadata=", grpc_rb_call_set_metadata, 1);
+
+ /* Ids used to support call attributes */
+ id_metadata = rb_intern("metadata");
+ id_status = rb_intern("status");
+
+ /* Ids used by the c wrapping internals. */
+ id_cq = rb_intern("__cq");
+ id_flags = rb_intern("__flags");
+ id_input_md = rb_intern("__input_md");
+
+ /* The hash for reference counting calls, to ensure they can't be destroyed
+ * more than once */
+ hash_all_calls = rb_hash_new();
+ rb_define_const(rb_cCall, "INTERNAL_ALL_CALLs", hash_all_calls);
+
+ Init_google_rpc_error_codes();
+}
+
+/* Gets the call from the ruby object */
+grpc_call* grpc_rb_get_wrapped_call(VALUE v) {
+ grpc_call *c = NULL;
+ Data_Get_Struct(v, grpc_call, c);
+ return c;
+}
+
+/* Obtains the wrapped object for a given call */
+VALUE grpc_rb_wrap_call(grpc_call* c) {
+ VALUE obj = Qnil;
+ if (c == NULL) {
+ return Qnil;
+ }
+ obj = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)c));
+ if (obj == Qnil) { /* Not in the hash add it */
+ rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c), UINT2NUM(1));
+ } else {
+ rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c),
+ UINT2NUM(NUM2UINT(obj) + 1));
+ }
+ return Data_Wrap_Struct(rb_cCall, GC_NOT_MARKED, grpc_rb_call_destroy,
+ c);
+}
diff --git a/src/ruby/ext/grpc/rb_call.h b/src/ruby/ext/grpc/rb_call.h
new file mode 100644
index 0000000000..422e7e7a6c
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_call.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_CALL_H_
+#define GRPC_RB_CALL_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* Gets the wrapped call from a VALUE. */
+grpc_call* grpc_rb_get_wrapped_call(VALUE v);
+
+/* Gets the VALUE corresponding to given grpc_call. */
+VALUE grpc_rb_wrap_call(grpc_call* c);
+
+/* Provides the details of an call error */
+const char* grpc_call_error_detail_of(grpc_call_error err);
+
+/* rb_cCall is the Call class whose instances proxy grpc_call. */
+extern VALUE rb_cCall;
+
+/* rb_cCallError is the ruby class of the exception thrown during call
+ operations. */
+extern VALUE rb_eCallError;
+
+/* Initializes the Call class. */
+void Init_google_rpc_call();
+
+#endif /* GRPC_RB_CALL_H_ */
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
new file mode 100644
index 0000000000..f4c09a392a
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -0,0 +1,235 @@
+/*
+ *
+ * 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 "rb_channel.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_grpc.h"
+#include "rb_call.h"
+#include "rb_channel_args.h"
+#include "rb_completion_queue.h"
+#include "rb_server.h"
+
+/* id_channel is the name of the hidden ivar that preserves a reference to the
+ * channel on a call, so that calls are not GCed before their channel. */
+static ID id_channel;
+
+/* id_target is the name of the hidden ivar that preserves a reference to the
+ * target string used to create the call, preserved so that is does not get
+ * GCed before the channel */
+static ID id_target;
+
+/* Used during the conversion of a hash to channel args during channel setup */
+static VALUE rb_cChannelArgs;
+
+/* grpc_rb_channel wraps a grpc_channel. It provides a peer ruby object,
+ * 'mark' to minimize copying when a channel is created from ruby. */
+typedef struct grpc_rb_channel {
+ /* Holder of ruby objects involved in constructing the channel */
+ VALUE mark;
+ /* The actual channel */
+ grpc_channel *wrapped;
+} grpc_rb_channel;
+
+/* Destroys Channel instances. */
+static void grpc_rb_channel_free(void *p) {
+ grpc_rb_channel *ch = NULL;
+ if (p == NULL) {
+ return;
+ };
+ ch = (grpc_rb_channel *)p;
+
+ /* Deletes the wrapped object if the mark object is Qnil, which indicates
+ * that no other object is the actual owner. */
+ if (ch->wrapped != NULL && ch->mark == Qnil) {
+ grpc_channel_destroy(ch->wrapped);
+ rb_warning("channel gc: destroyed the c channel");
+ } else {
+ rb_warning("channel gc: did not destroy the c channel");
+ }
+
+ xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_channel_mark(void *p) {
+ grpc_rb_channel *channel = NULL;
+ if (p == NULL) {
+ return;
+ }
+ channel = (grpc_rb_channel *)p;
+ if (channel->mark != Qnil) {
+ rb_gc_mark(channel->mark);
+ }
+}
+
+/* Allocates grpc_rb_channel instances. */
+static VALUE grpc_rb_channel_alloc(VALUE cls) {
+ grpc_rb_channel *wrapper = ALLOC(grpc_rb_channel);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_channel_mark, grpc_rb_channel_free,
+ wrapper);
+}
+
+/* Initializes channel instances */
+static VALUE grpc_rb_channel_init(VALUE self, VALUE target,
+ VALUE channel_args) {
+ grpc_rb_channel *wrapper = NULL;
+ grpc_channel *ch = NULL;
+ char *target_chars = StringValueCStr(target);
+ grpc_channel_args args;
+ MEMZERO(&args, grpc_channel_args, 1);
+
+ Data_Get_Struct(self, grpc_rb_channel, wrapper);
+ grpc_rb_hash_convert_to_channel_args(channel_args, &args);
+ ch = grpc_channel_create(target_chars, &args);
+ if (args.args != NULL) {
+ xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
+ }
+ if (ch == NULL) {
+ rb_raise(rb_eRuntimeError, "could not create an rpc channel to target:%s",
+ target_chars);
+ }
+ rb_ivar_set(self, id_target, target);
+ wrapper->wrapped = ch;
+ return self;
+}
+
+/* Clones Channel instances.
+
+ Gives Channel a consistent implementation of Ruby's object copy/dup
+ protocol. */
+static VALUE grpc_rb_channel_init_copy(VALUE copy, VALUE orig) {
+ grpc_rb_channel *orig_ch = NULL;
+ grpc_rb_channel *copy_ch = NULL;
+
+ if (copy == orig) {
+ return copy;
+ }
+
+ /* Raise an error if orig is not a channel object or a subclass. */
+ if (TYPE(orig) != T_DATA ||
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_channel_free) {
+ rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cChannel));
+ }
+
+ Data_Get_Struct(orig, grpc_rb_channel, orig_ch);
+ Data_Get_Struct(copy, grpc_rb_channel, copy_ch);
+
+ /* use ruby's MEMCPY to make a byte-for-byte copy of the channel wrapper
+ * object. */
+ MEMCPY(copy_ch, orig_ch, grpc_rb_channel, 1);
+ return copy;
+}
+
+/* Create a call given a grpc_channel, in order to call method. The request
+ is not sent until grpc_call_invoke is called. */
+static VALUE grpc_rb_channel_create_call(VALUE self, VALUE method, VALUE host,
+ VALUE deadline) {
+ VALUE res = Qnil;
+ grpc_rb_channel *wrapper = NULL;
+ grpc_channel *ch = NULL;
+ grpc_call *call = NULL;
+ char *method_chars = StringValueCStr(method);
+ char *host_chars = StringValueCStr(host);
+
+ Data_Get_Struct(self, grpc_rb_channel, wrapper);
+ ch = wrapper->wrapped;
+ if (ch == NULL) {
+ rb_raise(rb_eRuntimeError, "closed!");
+ }
+
+ call = grpc_channel_create_call(ch, method_chars, host_chars,
+ grpc_rb_time_timeval(deadline,
+ /* absolute time */ 0));
+ if (call == NULL) {
+ rb_raise(rb_eRuntimeError, "cannot create call with method %s",
+ method_chars);
+ }
+ res = grpc_rb_wrap_call(call);
+
+ /* Make this channel an instance attribute of the call so that is is not GCed
+ * before the call. */
+ rb_ivar_set(res, id_channel, self);
+ return res;
+}
+
+/* Closes the channel, calling it's destroy method */
+static VALUE grpc_rb_channel_destroy(VALUE self) {
+ grpc_rb_channel *wrapper = NULL;
+ grpc_channel *ch = NULL;
+
+ Data_Get_Struct(self, grpc_rb_channel, wrapper);
+ ch = wrapper->wrapped;
+ if (ch != NULL) {
+ grpc_channel_destroy(ch);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ }
+
+ return Qnil;
+}
+
+/* rb_cChannel is the ruby class that proxies grpc_channel. */
+VALUE rb_cChannel = Qnil;
+
+void Init_google_rpc_channel() {
+ rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
+ rb_cChannel = rb_define_class_under(rb_mGoogleRPC, "Channel", rb_cObject);
+
+ /* Allocates an object managed by the ruby runtime */
+ rb_define_alloc_func(rb_cChannel, grpc_rb_channel_alloc);
+
+ /* Provides a ruby constructor and support for dup/clone. */
+ rb_define_method(rb_cChannel, "initialize", grpc_rb_channel_init, 2);
+ rb_define_method(rb_cChannel, "initialize_copy", grpc_rb_channel_init_copy,
+ 1);
+
+ /* Add ruby analogues of the Channel methods. */
+ rb_define_method(rb_cChannel, "create_call", grpc_rb_channel_create_call, 3);
+ rb_define_method(rb_cChannel, "destroy", grpc_rb_channel_destroy, 0);
+ rb_define_alias(rb_cChannel, "close", "destroy");
+
+ id_channel = rb_intern("__channel");
+ id_target = rb_intern("__target");
+}
+
+/* Gets the wrapped channel from the ruby wrapper */
+grpc_channel* grpc_rb_get_wrapped_channel(VALUE v) {
+ grpc_rb_channel *wrapper = NULL;
+ Data_Get_Struct(v, grpc_rb_channel, wrapper);
+ return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_channel.h b/src/ruby/ext/grpc/rb_channel.h
new file mode 100644
index 0000000000..b0a3634474
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_CHANNEL_H_
+#define GRPC_RB_CHANNEL_H_
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+/* rb_cChannel is the Channel class whose instances proxy grpc_channel. */
+extern VALUE rb_cChannel;
+
+/* Initializes the Channel class. */
+void Init_google_rpc_channel();
+
+/* Gets the wrapped channel from the ruby wrapper */
+grpc_channel* grpc_rb_get_wrapped_channel(VALUE v);
+
+#endif /* GRPC_RB_CHANNEL_H_ */
diff --git a/src/ruby/ext/grpc/rb_channel_args.c b/src/ruby/ext/grpc/rb_channel_args.c
new file mode 100644
index 0000000000..eebced0bd8
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel_args.c
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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 "rb_channel_args.h"
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+#include "rb_grpc.h"
+
+/* A callback the processes the hash key values in channel_args hash */
+static int grpc_rb_channel_create_in_process_add_args_hash_cb(VALUE key,
+ VALUE val,
+ VALUE args_obj) {
+ const char* the_key;
+ grpc_channel_args* args;
+
+ switch (TYPE(key)) {
+
+ case T_STRING:
+ the_key = StringValuePtr(key);
+ break;
+
+ case T_SYMBOL:
+ the_key = rb_id2name(SYM2ID(key));
+ break;
+
+ default:
+ rb_raise(rb_eTypeError, "bad chan arg: got <%s>, want <String|Symbol>",
+ rb_obj_classname(key));
+ return ST_STOP;
+ }
+
+ Data_Get_Struct(args_obj, grpc_channel_args, args);
+ if (args->num_args <= 0) {
+ rb_raise(rb_eRuntimeError, "hash_cb bug: num_args is %lu for key:%s",
+ args->num_args, StringValueCStr(key));
+ return ST_STOP;
+ }
+
+ args->args[args->num_args - 1].key = (char *)the_key;
+ switch (TYPE(val)) {
+
+ case T_SYMBOL:
+ args->args[args->num_args - 1].type = GRPC_ARG_STRING;
+ args->args[args->num_args - 1].value.string =
+ (char *)rb_id2name(SYM2ID(val));
+ --args->num_args;
+ return ST_CONTINUE;
+
+ case T_STRING:
+ args->args[args->num_args - 1].type = GRPC_ARG_STRING;
+ args->args[args->num_args - 1].value.string = StringValueCStr(val);
+ --args->num_args;
+ return ST_CONTINUE;
+
+ case T_FIXNUM:
+ args->args[args->num_args - 1].type = GRPC_ARG_INTEGER;
+ args->args[args->num_args - 1].value.integer = NUM2INT(val);
+ --args->num_args;
+ return ST_CONTINUE;
+
+ default:
+ rb_raise(rb_eTypeError, "%s: bad value: got <%s>, want <String|Fixnum>",
+ StringValueCStr(key), rb_obj_classname(val));
+ return ST_STOP;
+ }
+ rb_raise(rb_eRuntimeError, "impl bug: hash_cb reached to far while on key:%s",
+ StringValueCStr(key));
+ return ST_STOP;
+}
+
+/* channel_convert_params allows the call to
+ grpc_rb_hash_convert_to_channel_args to be made within an rb_protect
+ exception-handler. This allows any allocated memory to be freed before
+ propagating any exception that occurs */
+typedef struct channel_convert_params {
+ VALUE src_hash;
+ grpc_channel_args* dst;
+} channel_convert_params;
+
+
+static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) {
+ ID id_size = rb_intern("size");
+ VALUE rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
+ channel_convert_params* params = (channel_convert_params *)as_value;
+ size_t num_args = 0;
+
+ if (!NIL_P(params->src_hash) && TYPE(params->src_hash) != T_HASH) {
+ rb_raise(rb_eTypeError, "bad channel args: got:<%s> want: a hash or nil",
+ rb_obj_classname(params->src_hash));
+ return Qnil;
+ }
+
+ if (TYPE(params->src_hash) == T_HASH) {
+ num_args = NUM2INT(rb_funcall(params->src_hash, id_size, 0));
+ params->dst->num_args = num_args;
+ params->dst->args = ALLOC_N(grpc_arg, num_args);
+ MEMZERO(params->dst->args, grpc_arg, num_args);
+ rb_hash_foreach(params->src_hash,
+ grpc_rb_channel_create_in_process_add_args_hash_cb,
+ Data_Wrap_Struct(rb_cChannelArgs, GC_NOT_MARKED,
+ GC_DONT_FREE, params->dst));
+ /* reset num_args as grpc_rb_channel_create_in_process_add_args_hash_cb
+ * decrements it during has processing */
+ params->dst->num_args = num_args;
+ }
+ return Qnil;
+}
+
+void grpc_rb_hash_convert_to_channel_args(VALUE src_hash,
+ grpc_channel_args* dst) {
+ channel_convert_params params;
+ int status = 0;
+
+ /* Make a protected call to grpc_rb_hash_convert_channel_args */
+ params.src_hash = src_hash;
+ params.dst = dst;
+ rb_protect(grpc_rb_hash_convert_to_channel_args0, (VALUE) &params, &status);
+ if (status != 0) {
+ if (dst->args != NULL) {
+ /* Free any allocated memory before propagating the error */
+ xfree(dst->args);
+ }
+ rb_jump_tag(status);
+ }
+}
diff --git a/src/ruby/ext/grpc/rb_channel_args.h b/src/ruby/ext/grpc/rb_channel_args.h
new file mode 100644
index 0000000000..bbff017c1e
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel_args.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_CHANNEL_ARGS_H_
+#define GRPC_RB_CHANNEL_ARGS_H_
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+/* Converts a hash object containing channel args to a channel args instance.
+ *
+ * This func ALLOCs args->args. The caller is responsible for freeing it. If
+ * a ruby error is raised during processing of the hash values, the func takes
+ * care to deallocate any memory allocated so far, and propagate the error.
+ *
+ * @param src_hash A ruby hash
+ * @param dst the grpc_channel_args that the hash entries will be added to.
+ */
+void grpc_rb_hash_convert_to_channel_args(VALUE src_hash,
+ grpc_channel_args* dst);
+
+
+#endif /* GRPC_RB_CHANNEL_ARGS_H_ */
diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
new file mode 100644
index 0000000000..62d045e971
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_completion_queue.c
@@ -0,0 +1,194 @@
+/*
+ *
+ * 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 "rb_completion_queue.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/time.h>
+#include "rb_grpc.h"
+#include "rb_event.h"
+
+/* Used to allow grpc_completion_queue_next call to release the GIL */
+typedef struct next_call_stack {
+ grpc_completion_queue *cq;
+ grpc_event *event;
+ gpr_timespec timeout;
+ void* tag;
+} next_call_stack;
+
+/* Calls grpc_completion_queue_next without holding the ruby GIL */
+static void *grpc_rb_completion_queue_next_no_gil(
+ next_call_stack *next_call) {
+ next_call->event = grpc_completion_queue_next(next_call->cq,
+ next_call->timeout);
+ return NULL;
+}
+
+/* Calls grpc_completion_queue_pluck without holding the ruby GIL */
+static void *grpc_rb_completion_queue_pluck_no_gil(
+ next_call_stack *next_call) {
+ next_call->event = grpc_completion_queue_pluck(next_call->cq,
+ next_call->tag,
+ next_call->timeout);
+ return NULL;
+}
+
+
+/* Shuts down and drains the completion queue if necessary.
+ *
+ * This is done when the ruby completion queue object is about to be GCed.
+ */
+static void grpc_rb_completion_queue_shutdown_drain(
+ grpc_completion_queue* cq) {
+ next_call_stack next_call;
+ grpc_completion_type type;
+ int drained = 0;
+ MEMZERO(&next_call, next_call_stack, 1);
+
+ grpc_completion_queue_shutdown(cq);
+ next_call.cq = cq;
+ next_call.event = NULL;
+ /* TODO(temiola): the timeout should be a module level constant that defaults
+ * to gpr_inf_future.
+ *
+ * - at the moment this does not work, it stalls. Using a small timeout like
+ * this one works, and leads to fast test run times; a longer timeout was
+ * causing unnecessary delays in the test runs.
+ *
+ * - investigate further, this is probably another example of C-level cleanup
+ * not working consistently in all cases.
+ */
+ next_call.timeout = gpr_time_add(gpr_now(), gpr_time_from_micros(5e3));
+ do {
+ rb_thread_call_without_gvl(grpc_rb_completion_queue_next_no_gil,
+ (void *)&next_call, NULL, NULL);
+ if (next_call.event == NULL) {
+ break;
+ }
+ type = next_call.event->type;
+ if (type != GRPC_QUEUE_SHUTDOWN) {
+ ++drained;
+ rb_warning("completion queue shutdown: %d undrained events", drained);
+ }
+ grpc_event_finish(next_call.event);
+ next_call.event = NULL;
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+/* Helper function to free a completion queue. */
+static void grpc_rb_completion_queue_destroy(void *p) {
+ grpc_completion_queue *cq = NULL;
+ if (p == NULL) {
+ return;
+ }
+ cq = (grpc_completion_queue *)p;
+ grpc_rb_completion_queue_shutdown_drain(cq);
+ grpc_completion_queue_destroy(cq);
+}
+
+/* Allocates a completion queue. */
+static VALUE grpc_rb_completion_queue_alloc(VALUE cls) {
+ grpc_completion_queue* cq = grpc_completion_queue_create();
+ if (cq == NULL) {
+ rb_raise(rb_eArgError,
+ "could not create a completion queue: not sure why");
+ }
+ return Data_Wrap_Struct(cls, GC_NOT_MARKED,
+ grpc_rb_completion_queue_destroy, cq);
+}
+
+/* Blocks until the next event is available, and returns the event. */
+static VALUE grpc_rb_completion_queue_next(VALUE self, VALUE timeout) {
+ next_call_stack next_call;
+ MEMZERO(&next_call, next_call_stack, 1);
+ Data_Get_Struct(self, grpc_completion_queue, next_call.cq);
+ next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
+ next_call.event = NULL;
+ rb_thread_call_without_gvl(grpc_rb_completion_queue_next_no_gil,
+ (void *)&next_call, NULL, NULL);
+ if (next_call.event == NULL) {
+ return Qnil;
+ }
+ return Data_Wrap_Struct(rb_cEvent, GC_NOT_MARKED, grpc_rb_event_finish,
+ next_call.event);
+}
+
+/* Blocks until the next event for given tag is available, and returns the
+ * event. */
+static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag,
+ VALUE timeout) {
+ next_call_stack next_call;
+ MEMZERO(&next_call, next_call_stack, 1);
+ Data_Get_Struct(self, grpc_completion_queue, next_call.cq);
+ next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
+ next_call.tag = ROBJECT(tag);
+ next_call.event = NULL;
+ rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil,
+ (void *)&next_call, NULL, NULL);
+ if (next_call.event == NULL) {
+ return Qnil;
+ }
+ return Data_Wrap_Struct(rb_cEvent, GC_NOT_MARKED, grpc_rb_event_finish,
+ next_call.event);
+}
+
+/* rb_cCompletionQueue is the ruby class that proxies grpc_completion_queue. */
+VALUE rb_cCompletionQueue = Qnil;
+
+void Init_google_rpc_completion_queue() {
+ rb_cCompletionQueue = rb_define_class_under(rb_mGoogleRPC,
+ "CompletionQueue",
+ rb_cObject);
+
+ /* constructor: uses an alloc func without an initializer. Using a simple
+ alloc func works here as the grpc header does not specify any args for
+ this func, so no separate initialization step is necessary. */
+ rb_define_alloc_func(rb_cCompletionQueue, grpc_rb_completion_queue_alloc);
+
+ /* Add the next method that waits for the next event. */
+ rb_define_method(rb_cCompletionQueue, "next",
+ grpc_rb_completion_queue_next, 1);
+
+ /* Add the pluck method that waits for the next event of given tag */
+ rb_define_method(rb_cCompletionQueue, "pluck",
+ grpc_rb_completion_queue_pluck, 2);
+}
+
+/* Gets the wrapped completion queue from the ruby wrapper */
+grpc_completion_queue* grpc_rb_get_wrapped_completion_queue(VALUE v) {
+ grpc_completion_queue *cq = NULL;
+ Data_Get_Struct(v, grpc_completion_queue, cq);
+ return cq;
+}
diff --git a/src/ruby/ext/grpc/rb_completion_queue.h b/src/ruby/ext/grpc/rb_completion_queue.h
new file mode 100644
index 0000000000..1ec2718ed4
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_completion_queue.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_COMPLETION_QUEUE_H_
+#define GRPC_RB_COMPLETION_QUEUE_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* Gets the wrapped completion queue from the ruby wrapper */
+grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v);
+
+/* rb_cCompletionQueue is the CompletionQueue class whose instances proxy
+ grpc_completion_queue. */
+extern VALUE rb_cCompletionQueue;
+
+/* Initializes the CompletionQueue class. */
+void Init_google_rpc_completion_queue();
+
+#endif /* GRPC_RB_COMPLETION_QUEUE_H_ */
diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c
new file mode 100644
index 0000000000..6f542f9eba
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_event.c
@@ -0,0 +1,284 @@
+/*
+ *
+ * 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 "rb_event.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_grpc.h"
+#include "rb_byte_buffer.h"
+#include "rb_call.h"
+#include "rb_metadata.h"
+#include "rb_status.h"
+
+/* rb_mCompletionType is a ruby module that holds the completion type values */
+VALUE rb_mCompletionType = Qnil;
+
+/* Helper function to free an event. */
+void grpc_rb_event_finish(void *p) {
+ grpc_event_finish(p);
+}
+
+static VALUE grpc_rb_event_result(VALUE self);
+
+/* Obtains the type of an event. */
+static VALUE grpc_rb_event_type(VALUE self) {
+ grpc_event *event = NULL;
+ Data_Get_Struct(self, grpc_event, event);
+ switch (event->type) {
+ case GRPC_QUEUE_SHUTDOWN:
+ return rb_const_get(rb_mCompletionType, rb_intern("QUEUE_SHUTDOWN"));
+
+ case GRPC_READ:
+ return rb_const_get(rb_mCompletionType, rb_intern("READ"));
+
+ case GRPC_INVOKE_ACCEPTED:
+ grpc_rb_event_result(self); /* validates the result */
+ return rb_const_get(rb_mCompletionType, rb_intern("INVOKE_ACCEPTED"));
+
+ case GRPC_WRITE_ACCEPTED:
+ grpc_rb_event_result(self); /* validates the result */
+ return rb_const_get(rb_mCompletionType, rb_intern("WRITE_ACCEPTED"));
+
+ case GRPC_FINISH_ACCEPTED:
+ grpc_rb_event_result(self); /* validates the result */
+ return rb_const_get(rb_mCompletionType, rb_intern("FINISH_ACCEPTED"));
+
+ case GRPC_CLIENT_METADATA_READ:
+ return rb_const_get(rb_mCompletionType,
+ rb_intern("CLIENT_METADATA_READ"));
+
+ case GRPC_FINISHED:
+ return rb_const_get(rb_mCompletionType, rb_intern("FINISHED"));
+
+ case GRPC_SERVER_RPC_NEW:
+ return rb_const_get(rb_mCompletionType, rb_intern("SERVER_RPC_NEW"));
+
+ default:
+ rb_raise(rb_eRuntimeError,
+ "unrecognized event code for an rpc event:%d", event->type);
+ }
+ return Qnil; /* should not be reached */
+}
+
+/* Obtains the tag associated with an event. */
+static VALUE grpc_rb_event_tag(VALUE self) {
+ grpc_event *event = NULL;
+ Data_Get_Struct(self, grpc_event, event);
+ if (event->tag == NULL) {
+ return Qnil;
+ }
+ return (VALUE)event->tag;
+}
+
+/* Obtains the call associated with an event. */
+static VALUE grpc_rb_event_call(VALUE self) {
+ grpc_event *ev = NULL;
+ Data_Get_Struct(self, grpc_event, ev);
+ if (ev->call != NULL) {
+ return grpc_rb_wrap_call(ev->call);
+ }
+ return Qnil;
+}
+
+/* Obtains the metadata associated with an event. */
+static VALUE grpc_rb_event_metadata(VALUE self) {
+ grpc_event *event = NULL;
+ grpc_metadata *metadata = NULL;
+ VALUE key = Qnil;
+ VALUE new_ary = Qnil;
+ VALUE result = Qnil;
+ VALUE value = Qnil;
+ size_t count = 0;
+ size_t i = 0;
+
+ /* Figure out which metadata to read. */
+ Data_Get_Struct(self, grpc_event, event);
+ switch (event->type) {
+
+ case GRPC_CLIENT_METADATA_READ:
+ count = event->data.client_metadata_read.count;
+ metadata = event->data.client_metadata_read.elements;
+ break;
+
+ case GRPC_SERVER_RPC_NEW:
+ count = event->data.server_rpc_new.metadata_count;
+ metadata = event->data.server_rpc_new.metadata_elements;
+ break;
+
+ default:
+ rb_raise(rb_eRuntimeError,
+ "bug: bad event type reading server metadata. got %d; want %d",
+ event->type, GRPC_SERVER_RPC_NEW);
+ return Qnil;
+ }
+
+ result = rb_hash_new();
+ for (i = 0; i < count; i++) {
+ key = rb_str_new2(metadata[i].key);
+ value = rb_hash_aref(result, key);
+ if (value == Qnil) {
+ value = rb_str_new(
+ metadata[i].value,
+ metadata[i].value_length);
+ rb_hash_aset(result, key, value);
+ } else if (TYPE(value) == T_ARRAY) {
+ /* Add the string to the returned array */
+ rb_ary_push(value, rb_str_new(
+ metadata[i].value,
+ metadata[i].value_length));
+ } else {
+ /* Add the current value with this key and the new one to an array */
+ new_ary = rb_ary_new();
+ rb_ary_push(new_ary, value);
+ rb_ary_push(new_ary, rb_str_new(
+ metadata[i].value,
+ metadata[i].value_length));
+ rb_hash_aset(result, key, new_ary);
+ }
+ }
+ return result;
+}
+
+/* Obtains the data associated with an event. */
+static VALUE grpc_rb_event_result(VALUE self) {
+ grpc_event *event = NULL;
+ Data_Get_Struct(self, grpc_event, event);
+
+ switch (event->type) {
+
+ case GRPC_QUEUE_SHUTDOWN:
+ return Qnil;
+
+ case GRPC_READ:
+ return grpc_rb_byte_buffer_create_with_mark(self, event->data.read);
+
+ case GRPC_FINISH_ACCEPTED:
+ if (event->data.finish_accepted == GRPC_OP_OK) {
+ return Qnil;
+ }
+ rb_raise(rb_eEventError, "finish failed, not sure why (code=%d)",
+ event->data.finish_accepted);
+ break;
+
+ case GRPC_INVOKE_ACCEPTED:
+ if (event->data.invoke_accepted == GRPC_OP_OK) {
+ return Qnil;
+ }
+ rb_raise(rb_eEventError, "invoke failed, not sure why (code=%d)",
+ event->data.invoke_accepted);
+ break;
+
+ case GRPC_WRITE_ACCEPTED:
+ if (event->data.write_accepted == GRPC_OP_OK) {
+ return Qnil;
+ }
+ rb_raise(rb_eEventError, "write failed, not sure why (code=%d)",
+ event->data.invoke_accepted);
+ break;
+
+ case GRPC_CLIENT_METADATA_READ:
+ return grpc_rb_event_metadata(self);
+
+ case GRPC_FINISHED:
+ return grpc_rb_status_create_with_mark(self, &event->data.finished);
+ break;
+
+ case GRPC_SERVER_RPC_NEW:
+ return rb_struct_new(
+ rb_sNewServerRpc,
+ rb_str_new2(event->data.server_rpc_new.method),
+ rb_str_new2(event->data.server_rpc_new.host),
+ Data_Wrap_Struct(
+ rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE,
+ (void *)&event->data.server_rpc_new.deadline),
+ grpc_rb_event_metadata(self),
+ NULL);
+
+ default:
+ rb_raise(rb_eRuntimeError,
+ "unrecognized event code for an rpc event:%d", event->type);
+ }
+
+ return Qfalse;
+}
+
+/* rb_sNewServerRpc is the struct that holds new server rpc details. */
+VALUE rb_sNewServerRpc = Qnil;
+
+/* rb_cEvent is the Event class whose instances proxy grpc_event */
+VALUE rb_cEvent = Qnil;
+
+/* rb_eEventError is the ruby class of the exception thrown on failures during
+ rpc event processing. */
+VALUE rb_eEventError = Qnil;
+
+void Init_google_rpc_event() {
+ rb_eEventError = rb_define_class_under(rb_mGoogleRPC, "EventError",
+ rb_eStandardError);
+ rb_cEvent = rb_define_class_under(rb_mGoogleRPC, "Event", rb_cObject);
+ rb_sNewServerRpc = rb_struct_define("NewServerRpc", "method", "host",
+ "deadline", "metadata", NULL);
+
+ /* Prevent allocation or inialization from ruby. */
+ rb_define_alloc_func(rb_cEvent, grpc_rb_cannot_alloc);
+ rb_define_method(rb_cEvent, "initialize", grpc_rb_cannot_init, 0);
+ rb_define_method(rb_cEvent, "initialize_copy", grpc_rb_cannot_init_copy, 1);
+
+ /* Accessors for the data available in an event. */
+ rb_define_method(rb_cEvent, "call", grpc_rb_event_call, 0);
+ rb_define_method(rb_cEvent, "result", grpc_rb_event_result, 0);
+ rb_define_method(rb_cEvent, "tag", grpc_rb_event_tag, 0);
+ rb_define_method(rb_cEvent, "type", grpc_rb_event_type, 0);
+
+ /* Constants representing the completion types */
+ rb_mCompletionType = rb_define_module_under(rb_mGoogleRPC, "CompletionType");
+ rb_define_const(rb_mCompletionType, "QUEUE_SHUTDOWN",
+ INT2NUM(GRPC_QUEUE_SHUTDOWN));
+ rb_define_const(rb_mCompletionType, "READ", INT2NUM(GRPC_READ));
+ rb_define_const(rb_mCompletionType, "INVOKE_ACCEPTED",
+ INT2NUM(GRPC_INVOKE_ACCEPTED));
+ rb_define_const(rb_mCompletionType, "WRITE_ACCEPTED",
+ INT2NUM(GRPC_WRITE_ACCEPTED));
+ rb_define_const(rb_mCompletionType, "FINISH_ACCEPTED",
+ INT2NUM(GRPC_FINISH_ACCEPTED));
+ rb_define_const(rb_mCompletionType, "CLIENT_METADATA_READ",
+ INT2NUM(GRPC_CLIENT_METADATA_READ));
+ rb_define_const(rb_mCompletionType, "FINISHED",
+ INT2NUM(GRPC_FINISHED));
+ rb_define_const(rb_mCompletionType, "SERVER_RPC_NEW",
+ INT2NUM(GRPC_SERVER_RPC_NEW));
+ rb_define_const(rb_mCompletionType, "RESERVED",
+ INT2NUM(GRPC_COMPLETION_DO_NOT_USE));
+}
diff --git a/src/ruby/ext/grpc/rb_event.h b/src/ruby/ext/grpc/rb_event.h
new file mode 100644
index 0000000000..c398b6c6c8
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_event.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_EVENT_H_
+#define GRPC_RB_EVENT_H_
+
+#include <ruby.h>
+
+/* rb_sNewServerRpc is the struct that holds new server rpc details. */
+extern VALUE rb_sNewServerRpc;
+
+/* rb_cEvent is the Event class whose instances proxy grpc_event. */
+extern VALUE rb_cEvent;
+
+/* rb_cEventError is the ruby class that acts the exception thrown during rpc
+ event processing. */
+extern VALUE rb_eEventError;
+
+/* Helper function to free an event. */
+void grpc_rb_event_finish(void *p);
+
+/* Initializes the Event and EventError classes. */
+void Init_google_rpc_event();
+
+#endif /* GRPC_RB_EVENT_H_ */
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
new file mode 100644
index 0000000000..5cc45cf743
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -0,0 +1,230 @@
+/*
+ *
+ * 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 "rb_grpc.h"
+
+#include <math.h>
+#include <ruby.h>
+#include <sys/time.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/time.h>
+#include "rb_byte_buffer.h"
+#include "rb_call.h"
+#include "rb_channel.h"
+#include "rb_completion_queue.h"
+#include "rb_event.h"
+#include "rb_metadata.h"
+#include "rb_server.h"
+#include "rb_status.h"
+
+/* Define common vars and funcs declared in rb.h */
+const RUBY_DATA_FUNC GC_NOT_MARKED = NULL;
+const RUBY_DATA_FUNC GC_DONT_FREE = NULL;
+
+VALUE rb_cTimeVal = Qnil;
+
+/* Alloc func that blocks allocation of a given object by raising an
+ * exception. */
+VALUE grpc_rb_cannot_alloc(VALUE cls) {
+ rb_raise(rb_eTypeError,
+ "allocation of %s only allowed from the gRPC native layer",
+ rb_class2name(cls));
+ return Qnil;
+}
+
+/* Init func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init(VALUE self) {
+ rb_raise(rb_eTypeError,
+ "initialization of %s only allowed from the gRPC native layer",
+ rb_obj_classname(self));
+ return Qnil;
+}
+
+/* Init/Clone func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) {
+ rb_raise(rb_eTypeError,
+ "initialization of %s only allowed from the gRPC native layer",
+ rb_obj_classname(copy));
+ return Qnil;
+}
+
+/* id_tv_{,u}sec are accessor methods on Ruby Time instances. */
+static ID id_tv_sec;
+static ID id_tv_nsec;
+
+/**
+ * grpc_rb_time_timeval creates a time_eval from a ruby time object.
+ *
+ * This func is copied from ruby source, MRI/source/time.c, which is published
+ * under the same license as the ruby.h, on which the entire extensions is
+ * based.
+ */
+gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
+ gpr_timespec t;
+ gpr_timespec *time_const;
+ const char *tstr = interval ? "time interval" : "time";
+ const char *want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>";
+
+ switch (TYPE(time)) {
+
+ case T_DATA:
+ if (CLASS_OF(time) == rb_cTimeVal) {
+ Data_Get_Struct(time, gpr_timespec, time_const);
+ t = *time_const;
+ } else if (CLASS_OF(time) == rb_cTime) {
+ t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0));
+ t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0));
+ } else {
+ rb_raise(rb_eTypeError,
+ "bad input: (%s)->c_timeval, got <%s>,%s",
+ tstr, rb_obj_classname(time), want);
+ }
+ break;
+
+ case T_FIXNUM:
+ t.tv_sec = FIX2LONG(time);
+ if (interval && t.tv_sec < 0)
+ rb_raise(rb_eArgError, "%s must be positive", tstr);
+ t.tv_nsec = 0;
+ break;
+
+ case T_FLOAT:
+ if (interval && RFLOAT(time)->float_value < 0.0)
+ rb_raise(rb_eArgError, "%s must be positive", tstr);
+ else {
+ double f, d;
+
+ d = modf(RFLOAT(time)->float_value, &f);
+ if (d < 0) {
+ d += 1;
+ f -= 1;
+ }
+ t.tv_sec = (time_t)f;
+ if (f != t.tv_sec) {
+ rb_raise(rb_eRangeError, "%f out of Time range",
+ RFLOAT(time)->float_value);
+ }
+ t.tv_nsec = (time_t)(d*1e9+0.5);
+ }
+ break;
+
+ case T_BIGNUM:
+ t.tv_sec = NUM2LONG(time);
+ if (interval && t.tv_sec < 0)
+ rb_raise(rb_eArgError, "%s must be positive", tstr);
+ t.tv_nsec = 0;
+ break;
+
+ default:
+ rb_raise(rb_eTypeError,
+ "bad input: (%s)->c_timeval, got <%s>,%s",
+ tstr, rb_obj_classname(time), want);
+ break;
+ }
+ return t;
+}
+
+/* id_at is the constructor method of the ruby standard Time class. */
+static ID id_at;
+
+/* id_inspect is the inspect method found on various ruby objects. */
+static ID id_inspect;
+
+/* id_to_s is the to_s method found on various ruby objects. */
+static ID id_to_s;
+
+/* Converts `a wrapped time constant to a standard time. */
+VALUE grpc_rb_time_val_to_time(VALUE self) {
+ gpr_timespec *time_const = NULL;
+ Data_Get_Struct(self, gpr_timespec, time_const);
+ return rb_funcall(rb_cTime, id_at, 2, INT2NUM(time_const->tv_sec),
+ INT2NUM(time_const->tv_nsec));
+}
+
+/* Invokes inspect on the ctime version of the time val. */
+VALUE grpc_rb_time_val_inspect(VALUE self) {
+ return rb_funcall(grpc_rb_time_val_to_time(self), id_inspect, 0);
+}
+
+/* Invokes to_s on the ctime version of the time val. */
+VALUE grpc_rb_time_val_to_s(VALUE self) {
+ return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0);
+}
+
+/* Adds a module with constants that map to gpr's static timeval structs. */
+void Init_google_time_consts() {
+ VALUE rb_mTimeConsts = rb_define_module_under(rb_mGoogleRPC, "TimeConsts");
+ rb_cTimeVal = rb_define_class_under(rb_mGoogleRPC, "TimeSpec", rb_cObject);
+ rb_define_const(rb_mTimeConsts, "ZERO",
+ Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
+ GC_DONT_FREE, (void *)&gpr_time_0));
+ rb_define_const(rb_mTimeConsts, "INFINITE_FUTURE",
+ Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
+ GC_DONT_FREE, (void *)&gpr_inf_future));
+ rb_define_const(rb_mTimeConsts, "INFINITE_PAST",
+ Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
+ GC_DONT_FREE, (void *)&gpr_inf_past));
+ rb_define_method(rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
+ rb_define_method(rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
+ rb_define_method(rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
+ id_at = rb_intern("at");
+ id_inspect = rb_intern("inspect");
+ id_to_s = rb_intern("to_s");
+ id_tv_sec = rb_intern("tv_sec");
+ id_tv_nsec = rb_intern("tv_nsec");
+}
+
+void grpc_rb_shutdown(void *vm) {
+ grpc_shutdown();
+}
+
+/* Initialize the Google RPC module. */
+VALUE rb_mGoogle = Qnil;
+VALUE rb_mGoogleRPC = Qnil;
+void Init_grpc() {
+ grpc_init();
+ ruby_vm_at_exit(grpc_rb_shutdown);
+ rb_mGoogle = rb_define_module("Google");
+ rb_mGoogleRPC = rb_define_module_under(rb_mGoogle, "RPC");
+
+ Init_google_rpc_byte_buffer();
+ Init_google_rpc_event();
+ Init_google_rpc_channel();
+ Init_google_rpc_completion_queue();
+ Init_google_rpc_call();
+ Init_google_rpc_metadata();
+ Init_google_rpc_server();
+ Init_google_rpc_status();
+ Init_google_time_consts();
+}
diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h
new file mode 100644
index 0000000000..fd43c3795f
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_grpc.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_H_
+#define GRPC_RB_H_
+
+#include <sys/time.h>
+#include <ruby.h>
+#include <grpc/support/time.h>
+
+/* rb_mGoogle is the top-level Google module. */
+extern VALUE rb_mGoogle;
+
+/* rb_mGoogleRPC is the module containing all the ruby wrapper GRPC classes. */
+extern VALUE rb_mGoogleRPC;
+
+/* Class used to wrap timeval structs. */
+extern VALUE rb_cTimeVal;
+
+/* GC_NOT_MARKED is used in calls to Data_Wrap_Struct to indicate that the
+ wrapped struct does not need to participate in ruby gc. */
+extern const RUBY_DATA_FUNC GC_NOT_MARKED;
+
+/* GC_DONT_FREED is used in calls to Data_Wrap_Struct to indicate that the
+ wrapped struct should not be freed the wrapped ruby object is released by
+ the garbage collector. */
+extern const RUBY_DATA_FUNC GC_DONT_FREE;
+
+/* A ruby object alloc func that fails by raising an exception. */
+VALUE grpc_rb_cannot_alloc(VALUE cls);
+
+/* A ruby object init func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init(VALUE self);
+
+/* A ruby object clone init func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self);
+
+/* grpc_rb_time_timeval creates a gpr_timespec from a ruby time object. */
+gpr_timespec grpc_rb_time_timeval(VALUE time, int interval);
+
+#endif /* GRPC_RB_H_ */
diff --git a/src/ruby/ext/grpc/rb_metadata.c b/src/ruby/ext/grpc/rb_metadata.c
new file mode 100644
index 0000000000..13d515a929
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_metadata.c
@@ -0,0 +1,215 @@
+/*
+ *
+ * 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 "rb_metadata.h"
+
+#include <ruby.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include "rb_grpc.h"
+
+/* grpc_rb_metadata wraps a grpc_metadata. It provides a peer ruby object,
+ * 'mark' to minimize copying when a metadata is created from ruby. */
+typedef struct grpc_rb_metadata {
+ /* Holder of ruby objects involved in constructing the metadata */
+ VALUE mark;
+ /* The actual metadata */
+ grpc_metadata *wrapped;
+} grpc_rb_metadata;
+
+
+/* Destroys Metadata instances. */
+static void grpc_rb_metadata_free(void *p) {
+ if (p == NULL) {
+ return;
+ };
+
+ /* Because metadata is only created during a call to grpc_call_add_metadata,
+ * and the call takes ownership of the metadata, this does not free the
+ * wrapped struct, only the wrapper */
+ xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_metadata_mark(void *p) {
+ grpc_rb_metadata *md = NULL;
+ if (p == NULL) {
+ return;
+ }
+
+ md = (grpc_rb_metadata *)p;
+ /* If it's not already cleaned up, mark the mark object */
+ if (md->mark != Qnil && BUILTIN_TYPE(md->mark) != T_NONE) {
+ rb_gc_mark(md->mark);
+ }
+}
+
+/* Allocates Metadata instances.
+
+ Provides safe default values for the Metadata fields. */
+static VALUE grpc_rb_metadata_alloc(VALUE cls) {
+ grpc_rb_metadata *wrapper = ALLOC(grpc_rb_metadata);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_metadata_mark, grpc_rb_metadata_free,
+ wrapper);
+}
+
+/* id_key and id_value are the names of the hidden ivars that preserve the
+ * original byte_buffer source string */
+static ID id_key;
+static ID id_value;
+
+/* Initializes Metadata instances. */
+static VALUE grpc_rb_metadata_init(VALUE self, VALUE key, VALUE value) {
+ grpc_rb_metadata *wrapper = NULL;
+ grpc_metadata *md = ALLOC(grpc_metadata);
+
+ /* Use direct pointers to the strings wrapped by the ruby object to avoid
+ * copying */
+ Data_Get_Struct(self, grpc_rb_metadata, wrapper);
+ wrapper->wrapped = md;
+ if (TYPE(key) == T_SYMBOL) {
+ md->key = (char *)rb_id2name(SYM2ID(key));
+ } else { /* StringValueCStr does all other type exclusions for us */
+ md->key = StringValueCStr(key);
+ }
+ md->value = RSTRING_PTR(value);
+ md->value_length = RSTRING_LEN(value);
+
+ /* Save references to the original values on the mark object so that the
+ * pointers used there are valid for the lifetime of the object. */
+ wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
+ rb_ivar_set(wrapper->mark, id_key, key);
+ rb_ivar_set(wrapper->mark, id_value, value);
+
+ return self;
+}
+
+/* Clones Metadata instances.
+
+ Gives Metadata a consistent implementation of Ruby's object copy/dup
+ protocol. */
+static VALUE grpc_rb_metadata_init_copy(VALUE copy, VALUE orig) {
+ grpc_rb_metadata *orig_md = NULL;
+ grpc_rb_metadata *copy_md = NULL;
+
+ if (copy == orig) {
+ return copy;
+ }
+
+ /* Raise an error if orig is not a metadata object or a subclass. */
+ if (TYPE(orig) != T_DATA ||
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_metadata_free) {
+ rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cMetadata));
+ }
+
+ Data_Get_Struct(orig, grpc_rb_metadata, orig_md);
+ Data_Get_Struct(copy, grpc_rb_metadata, copy_md);
+
+ /* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper
+ * object. */
+ MEMCPY(copy_md, orig_md, grpc_rb_metadata, 1);
+ return copy;
+}
+
+/* Gets the key from a metadata instance. */
+static VALUE grpc_rb_metadata_key(VALUE self) {
+ VALUE key = Qnil;
+ grpc_rb_metadata *wrapper = NULL;
+ grpc_metadata *md = NULL;
+
+ Data_Get_Struct(self, grpc_rb_metadata, wrapper);
+ if (wrapper->mark != Qnil) {
+ key = rb_ivar_get(wrapper->mark, id_key);
+ if (key != Qnil) {
+ return key;
+ }
+ }
+
+ md = wrapper->wrapped;
+ if (md == NULL || md->key == NULL) {
+ return Qnil;
+ }
+ return rb_str_new2(md->key);
+}
+
+/* Gets the value from a metadata instance. */
+static VALUE grpc_rb_metadata_value(VALUE self) {
+ VALUE val = Qnil;
+ grpc_rb_metadata *wrapper = NULL;
+ grpc_metadata *md = NULL;
+
+ Data_Get_Struct(self, grpc_rb_metadata, wrapper);
+ if (wrapper->mark != Qnil) {
+ val = rb_ivar_get(wrapper->mark, id_value);
+ if (val != Qnil) {
+ return val;
+ }
+ }
+
+ md = wrapper->wrapped;
+ if (md == NULL || md->value == NULL) {
+ return Qnil;
+ }
+ return rb_str_new2(md->value);
+}
+
+/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */
+VALUE rb_cMetadata = Qnil;
+void Init_google_rpc_metadata() {
+ rb_cMetadata = rb_define_class_under(rb_mGoogleRPC, "Metadata", rb_cObject);
+
+ /* Allocates an object managed by the ruby runtime */
+ rb_define_alloc_func(rb_cMetadata, grpc_rb_metadata_alloc);
+
+ /* Provides a ruby constructor and support for dup/clone. */
+ rb_define_method(rb_cMetadata, "initialize", grpc_rb_metadata_init, 2);
+ rb_define_method(rb_cMetadata, "initialize_copy", grpc_rb_metadata_init_copy,
+ 1);
+
+ /* Provides accessors for the code and details. */
+ rb_define_method(rb_cMetadata, "key", grpc_rb_metadata_key, 0);
+ rb_define_method(rb_cMetadata, "value", grpc_rb_metadata_value, 0);
+
+ id_key = rb_intern("__key");
+ id_value = rb_intern("__value");
+}
+
+/* Gets the wrapped metadata from the ruby wrapper */
+grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v) {
+ grpc_rb_metadata *wrapper = NULL;
+ Data_Get_Struct(v, grpc_rb_metadata, wrapper);
+ return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_metadata.h b/src/ruby/ext/grpc/rb_metadata.h
new file mode 100644
index 0000000000..6b705914d6
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_metadata.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_METADATA_H_
+#define GRPC_RB_METADATA_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */
+extern VALUE rb_cMetadata;
+
+/* grpc_rb_metadata_create_with_mark creates a grpc_rb_metadata with a ruby mark
+ * object that will be kept alive while the metadata is alive. */
+extern VALUE grpc_rb_metadata_create_with_mark(VALUE mark, grpc_metadata *md);
+
+/* Gets the wrapped metadata from the ruby wrapper */
+grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v);
+
+/* Initializes the Metadata class. */
+void Init_google_rpc_metadata();
+
+#endif /* GRPC_RB_METADATA_H_ */
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
new file mode 100644
index 0000000000..f4230bd471
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -0,0 +1,226 @@
+/*
+ *
+ * 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 "rb_server.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_call.h"
+#include "rb_channel_args.h"
+#include "rb_completion_queue.h"
+#include "rb_grpc.h"
+
+/* rb_cServer is the ruby class that proxies grpc_server. */
+VALUE rb_cServer = Qnil;
+
+/* grpc_rb_server wraps a grpc_server. It provides a peer ruby object,
+ * 'mark' to minimize copying when a server is created from ruby. */
+typedef struct grpc_rb_server {
+ /* Holder of ruby objects involved in constructing the server */
+ VALUE mark;
+ /* The actual server */
+ grpc_server *wrapped;
+} grpc_rb_server;
+
+/* Destroys server instances. */
+static void grpc_rb_server_free(void *p) {
+ grpc_rb_server *svr = NULL;
+ if (p == NULL) {
+ return;
+ };
+ svr = (grpc_rb_server *)p;
+
+ /* Deletes the wrapped object if the mark object is Qnil, which indicates
+ * that no other object is the actual owner. */
+ if (svr->wrapped != NULL && svr->mark == Qnil) {
+ grpc_server_shutdown(svr->wrapped);
+ grpc_server_destroy(svr->wrapped);
+ }
+
+ xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_server_mark(void *p) {
+ grpc_rb_server *server = NULL;
+ if (p == NULL) {
+ return;
+ }
+ server = (grpc_rb_server *)p;
+ if (server->mark != Qnil) {
+ rb_gc_mark(server->mark);
+ }
+}
+
+/* Allocates grpc_rb_server instances. */
+static VALUE grpc_rb_server_alloc(VALUE cls) {
+ grpc_rb_server *wrapper = ALLOC(grpc_rb_server);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_server_mark, grpc_rb_server_free,
+ wrapper);
+}
+
+/* Initializes Server instances. */
+static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
+ grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+ grpc_rb_server *wrapper = NULL;
+ grpc_server *srv = NULL;
+ grpc_channel_args args;
+ MEMZERO(&args, grpc_channel_args, 1);
+
+ Data_Get_Struct(self, grpc_rb_server, wrapper);
+ grpc_rb_hash_convert_to_channel_args(channel_args, &args);
+ srv = grpc_server_create(cq, &args);
+ if (args.args != NULL) {
+ xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
+ }
+ if (srv == NULL) {
+ rb_raise(rb_eRuntimeError, "could not create a gRPC server, not sure why");
+ }
+ wrapper->wrapped = srv;
+
+ /* Add the cq as the server's mark object. This ensures the ruby cq can't be
+ * GCed before the server */
+ wrapper->mark = cqueue;
+ return self;
+}
+
+/* Clones Server instances.
+
+ Gives Server a consistent implementation of Ruby's object copy/dup
+ protocol. */
+static VALUE grpc_rb_server_init_copy(VALUE copy, VALUE orig) {
+ grpc_rb_server *orig_srv = NULL;
+ grpc_rb_server *copy_srv = NULL;
+
+ if (copy == orig) {
+ return copy;
+ }
+
+ /* Raise an error if orig is not a server object or a subclass. */
+ if (TYPE(orig) != T_DATA ||
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_free) {
+ rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cServer));
+ }
+
+ Data_Get_Struct(orig, grpc_rb_server, orig_srv);
+ Data_Get_Struct(copy, grpc_rb_server, copy_srv);
+
+ /* use ruby's MEMCPY to make a byte-for-byte copy of the server wrapper
+ * object. */
+ MEMCPY(copy_srv, orig_srv, grpc_rb_server, 1);
+ return copy;
+}
+
+static VALUE grpc_rb_server_request_call(VALUE self, VALUE tag_new) {
+ grpc_call_error err;
+ grpc_rb_server *s = NULL;
+ Data_Get_Struct(self, grpc_rb_server, s);
+ if (s->wrapped == NULL) {
+ rb_raise(rb_eRuntimeError, "closed!");
+ } else {
+ err = grpc_server_request_call(s->wrapped, ROBJECT(tag_new));
+ if (err != GRPC_CALL_OK) {
+ rb_raise(rb_eCallError, "server request failed: %s (code=%d)",
+ grpc_call_error_detail_of(err), err);
+ }
+ }
+ return Qnil;
+}
+
+static VALUE grpc_rb_server_start(VALUE self) {
+ grpc_rb_server *s = NULL;
+ Data_Get_Struct(self, grpc_rb_server, s);
+ if (s->wrapped == NULL) {
+ rb_raise(rb_eRuntimeError, "closed!");
+ } else {
+ grpc_server_start(s->wrapped);
+ }
+ return Qnil;
+}
+
+static VALUE grpc_rb_server_destroy(VALUE self) {
+ grpc_rb_server *s = NULL;
+ Data_Get_Struct(self, grpc_rb_server, s);
+ if (s->wrapped != NULL) {
+ grpc_server_shutdown(s->wrapped);
+ grpc_server_destroy(s->wrapped);
+ s->wrapped = NULL;
+ s->mark = Qnil;
+ }
+ return Qnil;
+}
+
+static VALUE grpc_rb_server_add_http2_port(VALUE self, VALUE port) {
+ grpc_rb_server *s = NULL;
+ int added_ok = 0;
+ Data_Get_Struct(self, grpc_rb_server, s);
+ if (s->wrapped == NULL) {
+ rb_raise(rb_eRuntimeError, "closed!");
+ } else {
+ added_ok = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
+ if (added_ok == 0) {
+ rb_raise(rb_eRuntimeError, "could not add port %s to server, not sure why",
+ StringValueCStr(port));
+ }
+ }
+ return Qnil;
+}
+
+void Init_google_rpc_server() {
+ rb_cServer = rb_define_class_under(rb_mGoogleRPC, "Server", rb_cObject);
+
+ /* Allocates an object managed by the ruby runtime */
+ rb_define_alloc_func(rb_cServer, grpc_rb_server_alloc);
+
+ /* Provides a ruby constructor and support for dup/clone. */
+ rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, 2);
+ rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1);
+
+ /* Add the server methods. */
+ rb_define_method(rb_cServer, "request_call", grpc_rb_server_request_call, 1);
+ rb_define_method(rb_cServer, "start", grpc_rb_server_start, 0);
+ rb_define_method(rb_cServer, "destroy", grpc_rb_server_destroy, 0);
+ rb_define_alias(rb_cServer, "close", "destroy");
+ rb_define_method(rb_cServer, "add_http2_port", grpc_rb_server_add_http2_port,
+ 1);
+}
+
+/* Gets the wrapped server from the ruby wrapper */
+grpc_server* grpc_rb_get_wrapped_server(VALUE v) {
+ grpc_rb_server *wrapper = NULL;
+ Data_Get_Struct(v, grpc_rb_server, wrapper);
+ return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_server.h b/src/ruby/ext/grpc/rb_server.h
new file mode 100644
index 0000000000..4619203d60
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_BYTE_BUFFER_H_
+#define GRPC_RB_SERVER_H_
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+/* rb_cServer is the Server class whose instances proxy
+ grpc_byte_buffer. */
+extern VALUE rb_cServer;
+
+/* Initializes the Server class. */
+void Init_google_rpc_server();
+
+/* Gets the wrapped server from the ruby wrapper */
+grpc_server* grpc_rb_get_wrapped_server(VALUE v);
+
+#endif /* GRPC_RB_SERVER_H_ */
diff --git a/src/ruby/ext/grpc/rb_status.c b/src/ruby/ext/grpc/rb_status.c
new file mode 100644
index 0000000000..747c47c556
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_status.c
@@ -0,0 +1,243 @@
+/*
+ *
+ * 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 "rb_status.h"
+
+#include <ruby.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/status.h>
+#include "rb_grpc.h"
+
+/* grpc_rb_status wraps a grpc_status. It provides a peer ruby object, 'mark'
+ * to minimize copying when a status is created from ruby. */
+typedef struct grpc_rb_status {
+ /* Holder of ruby objects involved in constructing the status */
+ VALUE mark;
+ /* The actual status */
+ grpc_status *wrapped;
+} grpc_rb_status;
+
+/* Destroys Status instances. */
+static void grpc_rb_status_free(void *p) {
+ grpc_rb_status *status = NULL;
+ if (p == NULL) {
+ return;
+ };
+ status = (grpc_rb_status *)p;
+
+ /* Delete the wrapped object if the mark object is Qnil, which indicates that
+ * no other object is the actual owner. */
+ if (status->wrapped != NULL && status->mark == Qnil) {
+ status->mark = Qnil;
+ if (status->wrapped->details) {
+ xfree(status->wrapped->details);
+ }
+ xfree(status->wrapped);
+ }
+
+ xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_status_mark(void *p) {
+ grpc_rb_status *status = NULL;
+ if (p == NULL) {
+ return;
+ }
+ status = (grpc_rb_status *)p;
+
+ /* If it's not already cleaned up, mark the mark object */
+ if (status->mark != Qnil) {
+ rb_gc_mark(status->mark);
+ }
+}
+
+/* Allocates Status instances.
+
+ Provides safe initial defaults for the instance fields. */
+static VALUE grpc_rb_status_alloc(VALUE cls) {
+ grpc_rb_status *wrapper = ALLOC(grpc_rb_status);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_status_mark, grpc_rb_status_free,
+ wrapper);
+}
+
+/* The name of the attribute used on the mark object to hold the details. */
+static ID id_details;
+
+/* Initializes Status instances. */
+static VALUE grpc_rb_status_init(VALUE self, VALUE code, VALUE details) {
+ grpc_rb_status *wrapper = NULL;
+ grpc_status *status = NULL;
+ Data_Get_Struct(self, grpc_rb_status, wrapper);
+
+ /* Use a direct pointer to the original detail value to avoid copying. Assume
+ * that details is null-terminated. */
+ status = ALLOC(grpc_status);
+ status->details = StringValueCStr(details);
+ status->code = NUM2INT(code);
+ wrapper->wrapped = status;
+
+ /* Create the mark and add the original details object to it. */
+ wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
+ rb_ivar_set(wrapper->mark, id_details, details);
+ return self;
+}
+
+/* Clones Status instances.
+
+ Gives Status a consistent implementation of Ruby's object copy/dup
+ protocol. */
+static VALUE grpc_rb_status_init_copy(VALUE copy, VALUE orig) {
+ grpc_rb_status *orig_status = NULL;
+ grpc_rb_status *copy_status = NULL;
+
+ if (copy == orig) {
+ return copy;
+ }
+
+ /* Raise an error if orig is not a Status object or a subclass. */
+ if (TYPE(orig) != T_DATA ||
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_status_free) {
+ rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cStatus));
+ }
+
+ Data_Get_Struct(orig, grpc_rb_status, orig_status);
+ Data_Get_Struct(copy, grpc_rb_status, copy_status);
+ MEMCPY(copy_status, orig_status, grpc_rb_status, 1);
+ return copy;
+}
+
+/* Gets the Status code. */
+static VALUE grpc_rb_status_code(VALUE self) {
+ grpc_rb_status *status = NULL;
+ Data_Get_Struct(self, grpc_rb_status, status);
+ return INT2NUM(status->wrapped->code);
+}
+
+/* Gets the Status details. */
+static VALUE grpc_rb_status_details(VALUE self) {
+ VALUE from_ruby;
+ grpc_rb_status *wrapper = NULL;
+ grpc_status *status;
+
+ Data_Get_Struct(self, grpc_rb_status, wrapper);
+ if (wrapper->mark != Qnil) {
+ from_ruby = rb_ivar_get(wrapper->mark, id_details);
+ if (from_ruby != Qnil) {
+ return from_ruby;
+ }
+ }
+
+ status = wrapper->wrapped;
+ if (status == NULL || status->details == NULL) {
+ return Qnil;
+ }
+
+ return rb_str_new2(status->details);
+}
+
+void Init_google_status_codes() {
+ /* Constants representing the status codes or grpc_status_code in status.h */
+ VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRPC, "StatusCodes");
+ rb_define_const(rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
+ rb_define_const(rb_mStatusCodes, "CANCELLED", INT2NUM(GRPC_STATUS_CANCELLED));
+ rb_define_const(rb_mStatusCodes, "UNKNOWN", INT2NUM(GRPC_STATUS_UNKNOWN));
+ rb_define_const(rb_mStatusCodes, "INVALID_ARGUMENT",
+ INT2NUM(GRPC_STATUS_INVALID_ARGUMENT));
+ rb_define_const(rb_mStatusCodes, "DEADLINE_EXCEEDED",
+ INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED));
+ rb_define_const(rb_mStatusCodes, "NOT_FOUND", INT2NUM(GRPC_STATUS_NOT_FOUND));
+ rb_define_const(rb_mStatusCodes, "ALREADY_EXISTS",
+ INT2NUM(GRPC_STATUS_ALREADY_EXISTS));
+ rb_define_const(rb_mStatusCodes, "PERMISSION_DENIED",
+ INT2NUM(GRPC_STATUS_PERMISSION_DENIED));
+ rb_define_const(rb_mStatusCodes, "UNAUTHENTICATED",
+ INT2NUM(GRPC_STATUS_UNAUTHENTICATED));
+ rb_define_const(rb_mStatusCodes, "RESOURCE_EXHAUSTED",
+ INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED));
+ rb_define_const(rb_mStatusCodes, "FAILED_PRECONDITION",
+ INT2NUM(GRPC_STATUS_FAILED_PRECONDITION));
+ rb_define_const(rb_mStatusCodes, "ABORTED", INT2NUM(GRPC_STATUS_ABORTED));
+ rb_define_const(rb_mStatusCodes, "OUT_OF_RANGE",
+ INT2NUM(GRPC_STATUS_OUT_OF_RANGE));
+ rb_define_const(rb_mStatusCodes, "UNIMPLEMENTED",
+ INT2NUM(GRPC_STATUS_UNIMPLEMENTED));
+ rb_define_const(rb_mStatusCodes, "INTERNAL", INT2NUM(GRPC_STATUS_INTERNAL));
+ rb_define_const(rb_mStatusCodes, "UNAVAILABLE",
+ INT2NUM(GRPC_STATUS_UNAVAILABLE));
+ rb_define_const(rb_mStatusCodes, "DATA_LOSS", INT2NUM(GRPC_STATUS_DATA_LOSS));
+}
+
+/* rb_cStatus is the Status class whose instances proxy grpc_status. */
+VALUE rb_cStatus = Qnil;
+
+/* Initializes the Status class. */
+void Init_google_rpc_status() {
+ rb_cStatus = rb_define_class_under(rb_mGoogleRPC, "Status", rb_cObject);
+
+ /* Allocates an object whose memory is managed by the Ruby. */
+ rb_define_alloc_func(rb_cStatus, grpc_rb_status_alloc);
+
+ /* Provides a ruby constructor and support for dup/clone. */
+ rb_define_method(rb_cStatus, "initialize", grpc_rb_status_init, 2);
+ rb_define_method(rb_cStatus, "initialize_copy", grpc_rb_status_init_copy, 1);
+
+ /* Provides accessors for the code and details. */
+ rb_define_method(rb_cStatus, "code", grpc_rb_status_code, 0);
+ rb_define_method(rb_cStatus, "details", grpc_rb_status_details, 0);
+ id_details = rb_intern("__details");
+ Init_google_status_codes();
+}
+
+VALUE grpc_rb_status_create_with_mark(VALUE mark, grpc_status* s) {
+ grpc_rb_status *status = NULL;
+ if (s == NULL) {
+ return Qnil;
+ }
+ status = ALLOC(grpc_rb_status);
+ status->wrapped = s;
+ status->mark = mark;
+ return Data_Wrap_Struct(rb_cStatus, grpc_rb_status_mark, grpc_rb_status_free,
+ status);
+}
+
+/* Gets the wrapped status from the ruby wrapper */
+grpc_status* grpc_rb_get_wrapped_status(VALUE v) {
+ grpc_rb_status *wrapper = NULL;
+ Data_Get_Struct(v, grpc_rb_status, wrapper);
+ return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_status.h b/src/ruby/ext/grpc/rb_status.h
new file mode 100644
index 0000000000..ceb6f9f81e
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_status.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_RB_STATUS_H_
+#define GRPC_RB_STATUS_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* rb_cStatus is the Status class whose instances proxy grpc_status. */
+extern VALUE rb_cStatus;
+
+/* grpc_rb_status_create_with_mark creates a grpc_rb_status with a ruby mark
+ * object that will be kept alive while the status is alive. */
+extern VALUE grpc_rb_status_create_with_mark(VALUE mark, grpc_status *s);
+
+/* Gets the wrapped status from the ruby wrapper object */
+grpc_status* grpc_rb_get_wrapped_status(VALUE v);
+
+/* Initializes the Status class. */
+void Init_google_rpc_status();
+
+#endif /* GRPC_RB_STATUS_H_ */