diff --git a/ext/digest/digest.c b/ext/digest/digest.c
index 7d285cfe74..773efbde6a 100644
--- a/ext/digest/digest.c
+++ b/ext/digest/digest.c
@@ -668,6 +668,7 @@ rb_digest_base_update(VALUE self, VALUE str)
     TypedData_Get_Struct(self, void, &digest_type, pctx);
 
     StringValue(str);
+    str = rb_str_new_frozen(str);
     algo->update_func(pctx, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str));
     RB_GC_GUARD(str);
 
diff --git a/ext/digest/md5/md5init.c b/ext/digest/md5/md5init.c
index fdd8b36c5d..951e421c26 100644
--- a/ext/digest/md5/md5init.c
+++ b/ext/digest/md5/md5init.c
@@ -2,6 +2,7 @@
 /* $Id$ */
 
 #include <ruby/ruby.h>
+#include <ruby/thread.h>
 #include "../digest.h"
 #if defined(MD5_USE_OPENSSL)
 #include "md5ossl.h"
@@ -11,14 +12,63 @@
 #include "md5.h"
 #endif
 
+struct unpack_nogvl {
+  MD5_CTX *context;
+  const uint8_t *data;
+  size_t len;
+};
+
+void * MD5_UpdateNoGVLUnpack(void * ptr)
+{
+  struct unpack_nogvl *args;
+  args = ptr;
+  MD5_Update(args->context, args->data, args->len);
+  return NULL;
+}
+
+void MD5_UpdateNoGVL(MD5_CTX *context, const uint8_t *data, size_t nbytes)
+{
+  struct unpack_nogvl args;
+  args.context = context;
+  args.data = data;
+  args.len = nbytes;
+
+  rb_thread_call_without_gvl(MD5_UpdateNoGVLUnpack, &args, NULL, NULL);
+
+  return;
+}
+
+struct finish_nogvl {
+  MD5_CTX *context;
+  uint8_t *digest;
+};
+
+void * MD5_FinishNoGvlUnpack(void *ptr)
+{
+  struct finish_nogvl *args;
+  args = ptr;
+  MD5_Finish(args->context, args->digest);
+  return NULL;
+}
+
+int MD5_FinishNoGVL(MD5_CTX *pms, uint8_t *digest)
+{
+    struct finish_nogvl args;
+    args.context = pms;
+    args.digest = digest;
+
+    rb_thread_call_without_gvl(MD5_FinishNoGvlUnpack, &args, NULL, NULL);
+    return 1;
+}
+
 static const rb_digest_metadata_t md5 = {
     RUBY_DIGEST_API_VERSION,
     MD5_DIGEST_LENGTH,
     MD5_BLOCK_LENGTH,
     sizeof(MD5_CTX),
     (rb_digest_hash_init_func_t)MD5_Init,
-    (rb_digest_hash_update_func_t)MD5_Update,
-    (rb_digest_hash_finish_func_t)MD5_Finish,
+    (rb_digest_hash_update_func_t)MD5_UpdateNoGVL,
+    (rb_digest_hash_finish_func_t)MD5_FinishNoGVL,
 };
 
 /*
diff --git a/ext/digest/rmd160/rmd160init.c b/ext/digest/rmd160/rmd160init.c
index 10d9370163..1db91c4d74 100644
--- a/ext/digest/rmd160/rmd160init.c
+++ b/ext/digest/rmd160/rmd160init.c
@@ -2,6 +2,7 @@
 /* $Id$ */
 
 #include <ruby/ruby.h>
+#include <ruby/thread.h>
 #include "../digest.h"
 #if defined(RMD160_USE_OPENSSL)
 #include "rmd160ossl.h"
@@ -9,14 +10,64 @@
 #include "rmd160.h"
 #endif
 
+struct unpack_nogvl {
+  RMD160_CTX *context;
+  const uint8_t *data;
+  size_t len;
+};
+
+void * RMD160_UpdateNoGVLUnpack(void * ptr)
+{
+  struct unpack_nogvl *args;
+  args = ptr;
+  RMD160_Update(args->context, args->data, args->len);
+  return NULL;
+}
+
+void RMD160_UpdateNoGVL(RMD160_CTX *context, const uint8_t *data, size_t nbytes)
+{
+  struct unpack_nogvl args;
+  args.context = context;
+  args.data = data;
+  args.len = nbytes;
+
+  rb_thread_call_without_gvl(RMD160_UpdateNoGVLUnpack, &args, NULL, NULL);
+
+  return;
+}
+
+struct finish_nogvl {
+  RMD160_CTX *context;
+  uint8_t *digest;
+};
+
+void * RMD160_FinishNoGvlUnpack(void *ptr)
+{
+  struct finish_nogvl *args;
+  args = ptr;
+  RMD160_Finish(args->context, args->digest);
+  return NULL;
+}
+
+int RMD160_FinishNoGVL(RMD160_CTX *pms, uint8_t *digest)
+{
+    struct finish_nogvl args;
+    args.context = pms;
+    args.digest = digest;
+
+    rb_thread_call_without_gvl(RMD160_FinishNoGvlUnpack, &args, NULL, NULL);
+    return 1;
+}
+
+
 static const rb_digest_metadata_t rmd160 = {
     RUBY_DIGEST_API_VERSION,
     RMD160_DIGEST_LENGTH,
     RMD160_BLOCK_LENGTH,
     sizeof(RMD160_CTX),
     (rb_digest_hash_init_func_t)RMD160_Init,
-    (rb_digest_hash_update_func_t)RMD160_Update,
-    (rb_digest_hash_finish_func_t)RMD160_Finish,
+    (rb_digest_hash_update_func_t)RMD160_UpdateNoGVL,
+    (rb_digest_hash_finish_func_t)RMD160_FinishNoGVL,
 };
 
 /*
diff --git a/ext/digest/sha1/sha1init.c b/ext/digest/sha1/sha1init.c
index e12d297699..3d3e39127f 100644
--- a/ext/digest/sha1/sha1init.c
+++ b/ext/digest/sha1/sha1init.c
@@ -2,6 +2,7 @@
 /* $Id$ */
 
 #include <ruby/ruby.h>
+#include <ruby/thread.h>
 #include "../digest.h"
 #if defined(SHA1_USE_OPENSSL)
 #include "sha1ossl.h"
@@ -11,14 +12,62 @@
 #include "sha1.h"
 #endif
 
+struct unpack_nogvl {
+  SHA1_CTX *context;
+  const uint8_t *data;
+  size_t len;
+};
+
+void * SHA1_UpdateNoGVLUnpack(void * ptr)
+{
+  struct unpack_nogvl *args;
+  args = ptr;
+  SHA1_Update(args->context, args->data, args->len);
+  return NULL;
+}
+
+void SHA1_UpdateNoGVL(SHA1_CTX *context, const uint8_t *data, size_t nbytes)
+{
+  struct unpack_nogvl args;
+  args.context = context;
+  args.data = data;
+  args.len = nbytes;
+
+  rb_thread_call_without_gvl(SHA1_UpdateNoGVLUnpack, &args, NULL, NULL);
+
+  return;
+}
+
+struct finish_nogvl {
+  SHA1_CTX *context;
+  uint8_t *digest;
+};
+
+void * SHA1_FinishNoGvlUnpack(void *ptr)
+{
+  struct finish_nogvl *args;
+  args = ptr;
+  SHA1_Finish(args->context, args->digest);
+  return NULL;
+}
+
+int SHA1_FinishNoGVL(SHA1_CTX *pms, uint8_t *digest)
+{
+    struct finish_nogvl args;
+    args.context = pms;
+    args.digest = digest;
+
+    rb_thread_call_without_gvl(SHA1_FinishNoGvlUnpack, &args, NULL, NULL);
+    return 1;
+}
 static const rb_digest_metadata_t sha1 = {
     RUBY_DIGEST_API_VERSION,
     SHA1_DIGEST_LENGTH,
     SHA1_BLOCK_LENGTH,
     sizeof(SHA1_CTX),
     (rb_digest_hash_init_func_t)SHA1_Init,
-    (rb_digest_hash_update_func_t)SHA1_Update,
-    (rb_digest_hash_finish_func_t)SHA1_Finish,
+    (rb_digest_hash_update_func_t)SHA1_UpdateNoGVL,
+    (rb_digest_hash_finish_func_t)SHA1_FinishNoGVL,
 };
 
 /*
diff --git a/ext/digest/sha2/sha2init.c b/ext/digest/sha2/sha2init.c
index 9fd8ece7fe..60e0b53400 100644
--- a/ext/digest/sha2/sha2init.c
+++ b/ext/digest/sha2/sha2init.c
@@ -2,6 +2,7 @@
 /* $Id$ */
 
 #include <ruby/ruby.h>
+#include <ruby/thread.h>
 #include "../digest.h"
 #if defined(SHA2_USE_OPENSSL)
 #include "sha2ossl.h"
@@ -13,6 +14,58 @@
 
 #define FOREACH_BITLEN(func)	func(256) func(384) func(512)
 
+#define DECLARE_NO_GVL_METHODS(bitlen) \
+struct unpack_##bitlen##_nogvl { \
+  SHA##bitlen##_CTX *context; \
+  const uint8_t *data; \
+  size_t len; \
+}; \
+\
+void * SHA##bitlen##_UpdateNoGVLUnpack(void * ptr) \
+{ \
+  struct unpack_##bitlen##_nogvl *args; \
+  args = ptr; \
+  SHA##bitlen##_Update(args->context, args->data, args->len); \
+  return NULL; \
+} \
+\
+void SHA##bitlen##_UpdateNoGVL(SHA##bitlen##_CTX *context, const uint8_t *data, size_t nbytes) \
+{ \
+  struct unpack_##bitlen##_nogvl args; \
+  args.context = context; \
+  args.data = data; \
+  args.len = nbytes; \
+\
+  rb_thread_call_without_gvl(SHA##bitlen##_UpdateNoGVLUnpack, &args, NULL, NULL); \
+\
+  return; \
+} \
+\
+struct finish_##bitlen##_nogvl { \
+  SHA##bitlen##_CTX *context; \
+  uint8_t *digest; \
+}; \
+\
+void * SHA##bitlen##_FinishNoGvlUnpack(void *ptr) \
+{ \
+  struct finish_##bitlen##_nogvl *args; \
+  args = ptr; \
+  SHA##bitlen##_Finish(args->context, args->digest); \
+  return NULL; \
+} \
+\
+int SHA##bitlen##_FinishNoGVL(SHA##bitlen##_CTX *pms, uint8_t *digest) \
+{ \
+    struct finish_##bitlen##_nogvl args; \
+    args.context = pms; \
+    args.digest = digest; \
+\
+    rb_thread_call_without_gvl(SHA##bitlen##_FinishNoGvlUnpack, &args, NULL, NULL); \
+    return 1; \
+}
+
+FOREACH_BITLEN(DECLARE_NO_GVL_METHODS)
+
 #define DEFINE_ALGO_METADATA(bitlen) \
 static const rb_digest_metadata_t sha##bitlen = { \
     RUBY_DIGEST_API_VERSION, \
@@ -20,8 +73,8 @@ static const rb_digest_metadata_t sha##bitlen = { \
     SHA##bitlen##_BLOCK_LENGTH, \
     sizeof(SHA##bitlen##_CTX), \
     (rb_digest_hash_init_func_t)SHA##bitlen##_Init, \
-    (rb_digest_hash_update_func_t)SHA##bitlen##_Update, \
-    (rb_digest_hash_finish_func_t)SHA##bitlen##_Finish, \
+    (rb_digest_hash_update_func_t)SHA##bitlen##_UpdateNoGVL, \
+    (rb_digest_hash_finish_func_t)SHA##bitlen##_FinishNoGVL, \
 };
 
 FOREACH_BITLEN(DEFINE_ALGO_METADATA)
