Merge pull request #1295 from yugui/fix/typed-struct

Use TypedData_XXX instead of Data_XXX
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index b096341..6da7d3c 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -117,6 +117,37 @@
   }
 }
 
+static size_t md_ary_datasize(const void *p) {
+    const grpc_metadata_array* const ary = (grpc_metadata_array*)p;
+    size_t i, datasize = sizeof(grpc_metadata_array);
+    for (i = 0; i < ary->count; ++i) {
+        const grpc_metadata* const md = &ary->metadata[i];
+        datasize += strlen(md->key);
+        datasize += md->value_length;
+    }
+    datasize += ary->capacity * sizeof(grpc_metadata);
+    return datasize;
+}
+
+static const rb_data_type_t grpc_rb_md_ary_data_type = {
+    "grpc_metadata_array",
+    {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, md_ary_datasize},
+    NULL, NULL,
+    0
+};
+
+/* Describes grpc_call struct for RTypedData */
+static const rb_data_type_t grpc_call_data_type = {
+    "grpc_call",
+    {GRPC_RB_GC_NOT_MARKED, grpc_rb_call_destroy, GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL, NULL,
+    /* it is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because grpc_rb_call_destroy
+     * touches a hash object.
+     * TODO(yugui) Directly use st_table and call the free function earlier?
+     */
+    0
+};
+
 /* Error code details is a hash containing text strings describing errors */
 VALUE rb_error_code_details;
 
@@ -135,7 +166,7 @@
 static VALUE grpc_rb_call_cancel(VALUE self) {
   grpc_call *call = NULL;
   grpc_call_error err;
-  Data_Get_Struct(self, grpc_call, call);
+  TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call);
   err = grpc_call_cancel(call);
   if (err != GRPC_CALL_OK) {
     rb_raise(grpc_rb_eCallError, "cancel failed: %s (code=%d)",
@@ -205,7 +236,8 @@
   int i;
 
   /* Construct a metadata object from key and value and add it */
-  Data_Get_Struct(md_ary_obj, grpc_metadata_array, md_ary);
+  TypedData_Get_Struct(md_ary_obj, grpc_metadata_array,
+                       &grpc_rb_md_ary_data_type, md_ary);
 
   if (TYPE(val) == T_ARRAY) {
     /* If the value is an array, add capacity for each value in the array */
@@ -243,7 +275,8 @@
   grpc_metadata_array *md_ary = NULL;
 
   /* Construct a metadata object from key and value and add it */
-  Data_Get_Struct(md_ary_obj, grpc_metadata_array, md_ary);
+  TypedData_Get_Struct(md_ary_obj, grpc_metadata_array,
+                       &grpc_rb_md_ary_data_type, md_ary);
 
   if (TYPE(val) == T_ARRAY) {
     /* If the value is an array, add capacity for each value in the array */
@@ -270,8 +303,8 @@
 
   /* Initialize the array, compute it's capacity, then fill it. */
   grpc_metadata_array_init(md_ary);
-  md_ary_obj =
-      Data_Wrap_Struct(grpc_rb_cMdAry, GC_NOT_MARKED, GC_DONT_FREE, md_ary);
+  md_ary_obj = TypedData_Wrap_Struct(grpc_rb_cMdAry, &grpc_rb_md_ary_data_type,
+                                     md_ary);
   rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_capacity_hash_cb, md_ary_obj);
   md_ary->metadata = gpr_malloc(md_ary->capacity * sizeof(grpc_metadata));
   rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_fill_hash_cb, md_ary_obj);
@@ -556,7 +589,7 @@
   grpc_event *ev = NULL;
   grpc_call_error err;
   VALUE result = Qnil;
-  Data_Get_Struct(self, grpc_call, call);
+  TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call);
 
   /* Validate the ops args, adding them to a ruby array */
   if (TYPE(ops_hash) != T_HASH) {
@@ -736,7 +769,7 @@
 /* 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);
+  TypedData_Get_Struct(v, grpc_call, &grpc_call_data_type, c);
   return c;
 }
 
@@ -753,6 +786,5 @@
     rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c),
                  UINT2NUM(NUM2UINT(obj) + 1));
   }
-  return Data_Wrap_Struct(grpc_rb_cCall, GC_NOT_MARKED,
-                          grpc_rb_call_destroy, c);
+  return TypedData_Wrap_Struct(grpc_rb_cCall, &grpc_call_data_type, c);
 }
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index 9bd7c2e..214675a 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -105,13 +105,19 @@
   }
 }
 
+static rb_data_type_t grpc_channel_data_type = {
+    "grpc_channel",
+    {grpc_rb_channel_mark, grpc_rb_channel_free, GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL, NULL,
+    RUBY_TYPED_FREE_IMMEDIATELY
+};
+
 /* 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);
+  return TypedData_Wrap_Struct(cls, &grpc_channel_data_type, wrapper);
 }
 
 /*
@@ -135,7 +141,7 @@
   /* "21" == 2 mandatory args, 1 (credentials) is optional */
   rb_scan_args(argc, argv, "21", &target, &channel_args, &credentials);
 
-  Data_Get_Struct(self, grpc_rb_channel, wrapper);
+  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
   target_chars = StringValueCStr(target);
   grpc_rb_hash_convert_to_channel_args(channel_args, &args);
   if (credentials == Qnil) {
@@ -176,8 +182,8 @@
     return Qnil;
   }
 
-  Data_Get_Struct(orig, grpc_rb_channel, orig_ch);
-  Data_Get_Struct(copy, grpc_rb_channel, copy_ch);
+  TypedData_Get_Struct(orig, grpc_rb_channel, &grpc_channel_data_type, orig_ch);
+  TypedData_Get_Struct(copy, grpc_rb_channel, &grpc_channel_data_type, copy_ch);
 
   /* use ruby's MEMCPY to make a byte-for-byte copy of the channel wrapper
    * object. */
@@ -198,7 +204,7 @@
   char *host_chars = StringValueCStr(host);
 
   cq = grpc_rb_get_wrapped_completion_queue(cqueue);
-  Data_Get_Struct(self, grpc_rb_channel, wrapper);
+  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
   ch = wrapper->wrapped;
   if (ch == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
@@ -231,7 +237,7 @@
   grpc_rb_channel *wrapper = NULL;
   grpc_channel *ch = NULL;
 
-  Data_Get_Struct(self, grpc_rb_channel, wrapper);
+  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
   ch = wrapper->wrapped;
   if (ch != NULL) {
     grpc_channel_destroy(ch);
@@ -277,6 +283,6 @@
 /* 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);
+  TypedData_Get_Struct(v, grpc_rb_channel, &grpc_channel_data_type, wrapper);
   return wrapper->wrapped;
 }
diff --git a/src/ruby/ext/grpc/rb_channel_args.c b/src/ruby/ext/grpc/rb_channel_args.c
index 9b92ec1..acd545f 100644
--- a/src/ruby/ext/grpc/rb_channel_args.c
+++ b/src/ruby/ext/grpc/rb_channel_args.c
@@ -38,6 +38,13 @@
 
 #include "rb_grpc.h"
 
+static rb_data_type_t grpc_rb_channel_args_data_type = {
+    "grpc_channel_args",
+    {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL, NULL,
+    RUBY_TYPED_FREE_IMMEDIATELY
+};
+
 /* 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,
@@ -60,7 +67,8 @@
       return ST_STOP;
   }
 
-  Data_Get_Struct(args_obj, grpc_channel_args, args);
+  TypedData_Get_Struct(args_obj, grpc_channel_args,
+                       &grpc_rb_channel_args_data_type, 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));
@@ -126,8 +134,9 @@
     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(grpc_rb_cChannelArgs, GC_NOT_MARKED,
-                                     GC_DONT_FREE, params->dst));
+                    TypedData_Wrap_Struct(grpc_rb_cChannelArgs,
+                                          &grpc_rb_channel_args_data_type,
+                                          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;
diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
index a72f01f..e2c9b85 100644
--- a/src/ruby/ext/grpc/rb_completion_queue.c
+++ b/src/ruby/ext/grpc/rb_completion_queue.c
@@ -116,21 +116,31 @@
   grpc_completion_queue_destroy(cq);
 }
 
+static rb_data_type_t grpc_rb_completion_queue_data_type = {
+    "grpc_completion_queue",
+    {GRPC_RB_GC_NOT_MARKED, grpc_rb_completion_queue_destroy,
+     GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL, NULL,
+    /* cannot immediately free because grpc_rb_completion_queue_shutdown_drain
+     * calls rb_thread_call_without_gvl. */
+    0
+};
+
 /* 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);
+  return TypedData_Wrap_Struct(cls, &grpc_rb_completion_queue_data_type, 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);
+  TypedData_Get_Struct(self, grpc_completion_queue,
+                       &grpc_rb_completion_queue_data_type, 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,
@@ -158,7 +168,8 @@
                                                  VALUE timeout) {
   next_call_stack next_call;
   MEMZERO(&next_call, next_call_stack, 1);
-  Data_Get_Struct(self, grpc_completion_queue, next_call.cq);
+  TypedData_Get_Struct(self, grpc_completion_queue,
+                       &grpc_rb_completion_queue_data_type, next_call.cq);
   next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
   next_call.tag = ROBJECT(tag);
   next_call.event = NULL;
@@ -192,6 +203,7 @@
 /* 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);
+  TypedData_Get_Struct(v, grpc_completion_queue,
+                       &grpc_rb_completion_queue_data_type, cq);
   return cq;
 }
diff --git a/src/ruby/ext/grpc/rb_credentials.c b/src/ruby/ext/grpc/rb_credentials.c
index 122cffc..1ec8891 100644
--- a/src/ruby/ext/grpc/rb_credentials.c
+++ b/src/ruby/ext/grpc/rb_credentials.c
@@ -86,14 +86,21 @@
   }
 }
 
+static rb_data_type_t grpc_rb_credentials_data_type = {
+    "grpc_credentials",
+    {grpc_rb_credentials_mark, grpc_rb_credentials_free,
+     GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL,
+    NULL,
+    RUBY_TYPED_FREE_IMMEDIATELY};
+
 /* Allocates Credential instances.
    Provides safe initial defaults for the instance fields. */
 static VALUE grpc_rb_credentials_alloc(VALUE cls) {
   grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
   wrapper->wrapped = NULL;
   wrapper->mark = Qnil;
-  return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
-                          grpc_rb_credentials_free, wrapper);
+  return TypedData_Wrap_Struct(cls, &grpc_rb_credentials_data_type, wrapper);
 }
 
 /* Clones Credentials instances.
@@ -113,8 +120,10 @@
     rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(grpc_rb_cCredentials));
   }
 
-  Data_Get_Struct(orig, grpc_rb_credentials, orig_cred);
-  Data_Get_Struct(copy, grpc_rb_credentials, copy_cred);
+  TypedData_Get_Struct(orig, grpc_rb_credentials,
+                       &grpc_rb_credentials_data_type, orig_cred);
+  TypedData_Get_Struct(copy, grpc_rb_credentials,
+                       &grpc_rb_credentials_data_type, copy_cred);
 
   /* use ruby's MEMCPY to make a byte-for-byte copy of the credentials
    * wrapper object. */
@@ -136,8 +145,7 @@
   }
 
   wrapper->mark = Qnil;
-  return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
-                          grpc_rb_credentials_free, wrapper);
+  return TypedData_Wrap_Struct(cls, &grpc_rb_credentials_data_type, wrapper);
 }
 
 /*
@@ -154,8 +162,7 @@
   }
 
   wrapper->mark = Qnil;
-  return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
-                          grpc_rb_credentials_free, wrapper);
+  return TypedData_Wrap_Struct(cls, &grpc_rb_credentials_data_type, wrapper);
 }
 
 /*
@@ -169,8 +176,10 @@
   grpc_rb_credentials *other_wrapper = NULL;
   grpc_rb_credentials *wrapper = NULL;
 
-  Data_Get_Struct(self, grpc_rb_credentials, self_wrapper);
-  Data_Get_Struct(other, grpc_rb_credentials, other_wrapper);
+  TypedData_Get_Struct(self, grpc_rb_credentials,
+                       &grpc_rb_credentials_data_type, self_wrapper);
+  TypedData_Get_Struct(other, grpc_rb_credentials,
+                       &grpc_rb_credentials_data_type, other_wrapper);
   wrapper = ALLOC(grpc_rb_credentials);
   wrapper->wrapped = grpc_composite_credentials_create(self_wrapper->wrapped,
                                                        other_wrapper->wrapped);
@@ -181,8 +190,8 @@
   }
 
   wrapper->mark = Qnil;
-  return Data_Wrap_Struct(grpc_rb_cCredentials, grpc_rb_credentials_mark,
-                          grpc_rb_credentials_free, wrapper);
+  return TypedData_Wrap_Struct(grpc_rb_cCredentials,
+                               &grpc_rb_credentials_data_type, wrapper);
 }
 
 /* The attribute used on the mark object to hold the pem_root_certs. */
@@ -217,7 +226,8 @@
   rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key,
                &pem_cert_chain);
 
-  Data_Get_Struct(self, grpc_rb_credentials, wrapper);
+  TypedData_Get_Struct(self, grpc_rb_credentials,
+                       &grpc_rb_credentials_data_type, wrapper);
   if (pem_root_certs == Qnil) {
     rb_raise(rb_eRuntimeError,
              "could not create a credential: nil pem_root_certs");
@@ -228,8 +238,8 @@
   } else {
     key_cert_pair.private_key = RSTRING_PTR(pem_private_key);
     key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain);
-    creds = grpc_ssl_credentials_create(
-        RSTRING_PTR(pem_root_certs), &key_cert_pair);
+    creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs),
+                                        &key_cert_pair);
   }
   if (creds == NULL) {
     rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
@@ -253,8 +263,8 @@
   rb_define_alloc_func(grpc_rb_cCredentials, grpc_rb_credentials_alloc);
 
   /* Provides a ruby constructor and support for dup/clone. */
-  rb_define_method(grpc_rb_cCredentials, "initialize",
-                   grpc_rb_credentials_init, -1);
+  rb_define_method(grpc_rb_cCredentials, "initialize", grpc_rb_credentials_init,
+                   -1);
   rb_define_method(grpc_rb_cCredentials, "initialize_copy",
                    grpc_rb_credentials_init_copy, 1);
 
@@ -277,6 +287,7 @@
 /* Gets the wrapped grpc_credentials from the ruby wrapper */
 grpc_credentials *grpc_rb_get_wrapped_credentials(VALUE v) {
   grpc_rb_credentials *wrapper = NULL;
-  Data_Get_Struct(v, grpc_rb_credentials, wrapper);
+  TypedData_Get_Struct(v, grpc_rb_credentials, &grpc_rb_credentials_data_type,
+                       wrapper);
   return wrapper->wrapped;
 }
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
index 1cbd1aa..70a28dd 100644
--- a/src/ruby/ext/grpc/rb_grpc.c
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -46,12 +46,15 @@
 #include "rb_credentials.h"
 #include "rb_server_credentials.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;
-
 static VALUE grpc_rb_cTimeVal = Qnil;
 
+static rb_data_type_t grpc_rb_timespec_data_type = {
+    "gpr_timespec",
+    {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL,
+    NULL,
+    RUBY_TYPED_FREE_IMMEDIATELY};
+
 /* Alloc func that blocks allocation of a given object by raising an
  * exception. */
 VALUE grpc_rb_cannot_alloc(VALUE cls) {
@@ -97,7 +100,8 @@
   switch (TYPE(time)) {
     case T_DATA:
       if (CLASS_OF(time) == grpc_rb_cTimeVal) {
-        Data_Get_Struct(time, gpr_timespec, time_const);
+        TypedData_Get_Struct(time, gpr_timespec, &grpc_rb_timespec_data_type,
+                             time_const);
         t = *time_const;
       } else if (CLASS_OF(time) == rb_cTime) {
         t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0));
@@ -201,7 +205,8 @@
 /* Converts a wrapped time constant to a standard time. */
 static VALUE grpc_rb_time_val_to_time(VALUE self) {
   gpr_timespec *time_const = NULL;
-  Data_Get_Struct(self, gpr_timespec, time_const);
+  TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type,
+                       time_const);
   return rb_funcall(rb_cTime, id_at, 2, INT2NUM(time_const->tv_sec),
                     INT2NUM(time_const->tv_nsec));
 }
@@ -222,18 +227,18 @@
       rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts");
   grpc_rb_cTimeVal =
       rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject);
-  rb_define_const(grpc_rb_mTimeConsts, "ZERO",
-                  Data_Wrap_Struct(grpc_rb_cTimeVal,
-                                   GC_NOT_MARKED, GC_DONT_FREE,
-                                   (void *)&gpr_time_0));
-  rb_define_const(grpc_rb_mTimeConsts, "INFINITE_FUTURE",
-                  Data_Wrap_Struct(grpc_rb_cTimeVal,
-                                   GC_NOT_MARKED, GC_DONT_FREE,
-                                   (void *)&gpr_inf_future));
-  rb_define_const(grpc_rb_mTimeConsts, "INFINITE_PAST",
-                  Data_Wrap_Struct(grpc_rb_cTimeVal,
-                                   GC_NOT_MARKED, GC_DONT_FREE,
-                                   (void *)&gpr_inf_past));
+  rb_define_const(
+      grpc_rb_mTimeConsts, "ZERO",
+      TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
+                            (void *)&gpr_time_0));
+  rb_define_const(
+      grpc_rb_mTimeConsts, "INFINITE_FUTURE",
+      TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
+                            (void *)&gpr_inf_future));
+  rb_define_const(
+      grpc_rb_mTimeConsts, "INFINITE_PAST",
+      TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
+                            (void *)&gpr_inf_past));
   rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
   rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
   rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h
index 1d411ba..a502273 100644
--- a/src/ruby/ext/grpc/rb_grpc.h
+++ b/src/ruby/ext/grpc/rb_grpc.h
@@ -58,12 +58,16 @@
 
 /* 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;
+#define GRPC_RB_GC_NOT_MARKED (RUBY_DATA_FUNC)(NULL)
 
 /* 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;
+#define GRPC_RB_GC_DONT_FREE (RUBY_DATA_FUNC)(NULL)
+
+/* GRPC_RB_MEMSIZE_UNAVAILABLE is used in rb_data_type_t to indicate that the
+ * number of bytes used by the wrapped struct is not available. */
+#define GRPC_RB_MEMSIZE_UNAVAILABLE (size_t (*)(const void*))(NULL)
 
 /* A ruby object alloc func that fails by raising an exception. */
 VALUE grpc_rb_cannot_alloc(VALUE cls);
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index 80f7760..bc0878a 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -88,13 +88,23 @@
   }
 }
 
+static const rb_data_type_t grpc_rb_server_data_type = {
+    "grpc_server",
+    {grpc_rb_server_mark, grpc_rb_server_free, GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL,
+    NULL,
+    /* It is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because the free function would block
+     * and we might want to unlock GVL
+     * TODO(yugui) Unlock GVL?
+     */
+    0};
+
 /* 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);
+  return TypedData_Wrap_Struct(cls, &grpc_rb_server_data_type, wrapper);
 }
 
 /*
@@ -110,7 +120,8 @@
   grpc_channel_args args;
   MEMZERO(&args, grpc_channel_args, 1);
   cq = grpc_rb_get_wrapped_completion_queue(cqueue);
-  Data_Get_Struct(self, grpc_rb_server, wrapper);
+  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type,
+                       wrapper);
   grpc_rb_hash_convert_to_channel_args(channel_args, &args);
   srv = grpc_server_create(cq, &args);
 
@@ -146,8 +157,10 @@
     rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(grpc_rb_cServer));
   }
 
-  Data_Get_Struct(orig, grpc_rb_server, orig_srv);
-  Data_Get_Struct(copy, grpc_rb_server, copy_srv);
+  TypedData_Get_Struct(orig, grpc_rb_server, &grpc_rb_server_data_type,
+                       orig_srv);
+  TypedData_Get_Struct(copy, grpc_rb_server, &grpc_rb_server_data_type,
+                       copy_srv);
 
   /* use ruby's MEMCPY to make a byte-for-byte copy of the server wrapper
      object. */
@@ -194,7 +207,7 @@
   grpc_call_error err;
   request_call_stack st;
   VALUE result;
-  Data_Get_Struct(self, grpc_rb_server, s);
+  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
     return Qnil;
@@ -245,7 +258,7 @@
 
 static VALUE grpc_rb_server_start(VALUE self) {
   grpc_rb_server *s = NULL;
-  Data_Get_Struct(self, grpc_rb_server, s);
+  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
   } else {
@@ -256,7 +269,7 @@
 
 static VALUE grpc_rb_server_destroy(VALUE self) {
   grpc_rb_server *s = NULL;
-  Data_Get_Struct(self, grpc_rb_server, s);
+  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped != NULL) {
     grpc_server_shutdown(s->wrapped);
     grpc_server_destroy(s->wrapped);
@@ -288,7 +301,7 @@
   /* "11" == 1 mandatory args, 1 (rb_creds) is optional */
   rb_scan_args(argc, argv, "11", &port, &rb_creds);
 
-  Data_Get_Struct(self, grpc_rb_server, s);
+  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "closed!");
     return Qnil;
@@ -340,6 +353,6 @@
 /* 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);
+  TypedData_Get_Struct(v, grpc_rb_server, &grpc_rb_server_data_type, wrapper);
   return wrapper->wrapped;
 }
diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c
index 5109b96..a863894 100644
--- a/src/ruby/ext/grpc/rb_server_credentials.c
+++ b/src/ruby/ext/grpc/rb_server_credentials.c
@@ -86,6 +86,14 @@
   }
 }
 
+static const rb_data_type_t grpc_rb_server_credentials_data_type = {
+    "grpc_server_credentials",
+    {grpc_rb_server_credentials_mark, grpc_rb_server_credentials_free,
+     GRPC_RB_MEMSIZE_UNAVAILABLE},
+    NULL, NULL,
+    RUBY_TYPED_FREE_IMMEDIATELY
+};
+
 /* Allocates ServerCredential instances.
 
    Provides safe initial defaults for the instance fields. */
@@ -93,8 +101,8 @@
   grpc_rb_server_credentials *wrapper = ALLOC(grpc_rb_server_credentials);
   wrapper->wrapped = NULL;
   wrapper->mark = Qnil;
-  return Data_Wrap_Struct(cls, grpc_rb_server_credentials_mark,
-                          grpc_rb_server_credentials_free, wrapper);
+  return TypedData_Wrap_Struct(cls, &grpc_rb_server_credentials_data_type,
+                               wrapper);
 }
 
 /* Clones ServerCredentials instances.
@@ -116,8 +124,10 @@
              rb_obj_classname(grpc_rb_cServerCredentials));
   }
 
-  Data_Get_Struct(orig, grpc_rb_server_credentials, orig_ch);
-  Data_Get_Struct(copy, grpc_rb_server_credentials, copy_ch);
+  TypedData_Get_Struct(orig, grpc_rb_server_credentials,
+                       &grpc_rb_server_credentials_data_type, orig_ch);
+  TypedData_Get_Struct(copy, grpc_rb_server_credentials,
+                       &grpc_rb_server_credentials_data_type, copy_ch);
 
   /* use ruby's MEMCPY to make a byte-for-byte copy of the server_credentials
      wrapper object. */
@@ -153,7 +163,8 @@
   grpc_rb_server_credentials *wrapper = NULL;
   grpc_server_credentials *creds = NULL;
   grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
-  Data_Get_Struct(self, grpc_rb_server_credentials, wrapper);
+  TypedData_Get_Struct(self, grpc_rb_server_credentials,
+                       &grpc_rb_server_credentials_data_type, wrapper);
   if (pem_cert_chain == Qnil) {
     rb_raise(rb_eRuntimeError,
              "could not create a server credential: nil pem_cert_chain");
@@ -206,6 +217,7 @@
 /* Gets the wrapped grpc_server_credentials from the ruby wrapper */
 grpc_server_credentials *grpc_rb_get_wrapped_server_credentials(VALUE v) {
   grpc_rb_server_credentials *wrapper = NULL;
-  Data_Get_Struct(v, grpc_rb_server_credentials, wrapper);
+  TypedData_Get_Struct(v, grpc_rb_server_credentials,
+                       &grpc_rb_server_credentials_data_type, wrapper);
   return wrapper->wrapped;
 }