summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Williams <[email protected]>2024-11-20 21:27:16 +1300
committerGitHub <[email protected]>2024-11-20 21:27:16 +1300
commit3c0b09ac9e9afe5a8a536ac3c27c9202bcebc25c (patch)
tree6b9c38a1eaf8109a57ee736d910d87468afc80fe
parent682ff52e8f4d495eb7e1d32ebb7819ac01bab1ed (diff)
Allow `io_buffer_memmove` to release the GVL for large buffers. (#12021)
[Feature #20902]
Notes
Notes: Merged-By: ioquatix <[email protected]>
-rw-r--r--NEWS.md5
-rw-r--r--common.mk1
-rw-r--r--io_buffer.c39
3 files changed, 43 insertions, 2 deletions
diff --git a/NEWS.md b/NEWS.md
index e9dfb92ff6..5ce746ca62 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -66,6 +66,10 @@ Note: We're only listing outstanding class updates.
* An optional `Fiber::Scheduler#blocking_operation_wait` hook allows blocking operations to be moved out of the
event loop in order to reduce latency and improve multi-core processor utilization. [[Feature #20876]]
+* IO::Buffer
+
+ * `IO::Buffer#copy` can release the GVL, allowing other threads to run while copying data. [[Feature #20902]]
+
## Stdlib updates
* Tempfile
@@ -242,3 +246,4 @@ details of the default gems or bundled gems.
[Feature #20624]: https://bugs.ruby-lang.org/issues/20624
[Feature #20775]: https://bugs.ruby-lang.org/issues/20775
[Feature #20876]: https://bugs.ruby-lang.org/issues/20876
+[Feature #20902]: https://bugs.ruby-lang.org/issues/20902
diff --git a/common.mk b/common.mk
index ad8b83312e..60435e6e8d 100644
--- a/common.mk
+++ b/common.mk
@@ -8816,6 +8816,7 @@ io_buffer.$(OBJEXT): {$(VPATH)}onigmo.h
io_buffer.$(OBJEXT): {$(VPATH)}oniguruma.h
io_buffer.$(OBJEXT): {$(VPATH)}st.h
io_buffer.$(OBJEXT): {$(VPATH)}subst.h
+io_buffer.$(OBJEXT): {$(VPATH)}thread.h
io_buffer.$(OBJEXT): {$(VPATH)}thread_native.h
iseq.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
iseq.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
diff --git a/io_buffer.c b/io_buffer.c
index 53733c8ff4..64715cbc6c 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -9,6 +9,9 @@
#include "ruby/io/buffer.h"
#include "ruby/fiber/scheduler.h"
+// For `rb_nogvl`.
+#include "ruby/thread.h"
+
#include "internal.h"
#include "internal/array.h"
#include "internal/bits.h"
@@ -2327,6 +2330,30 @@ io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values
return SIZET2NUM(offset);
}
+static size_t IO_BUFFER_BLOCKING_SIZE = 1024*1024;
+
+struct io_buffer_memmove_arguments {
+ unsigned char * destination;
+ const unsigned char * source;
+ size_t length;
+};
+
+static void *
+io_buffer_memmove_blocking(void *data)
+{
+ struct io_buffer_memmove_arguments *arguments = (struct io_buffer_memmove_arguments *)data;
+
+ memmove(arguments->destination, arguments->source, arguments->length);
+
+ return NULL;
+}
+
+static void
+io_buffer_memmove_unblock(void *data)
+{
+ // No safe way to interrupt.
+}
+
static void
io_buffer_memmove(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
{
@@ -2340,8 +2367,16 @@ io_buffer_memmove(struct rb_io_buffer *buffer, size_t offset, const void *source
rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
}
- if (length != 0) {
- memmove((unsigned char*)base+offset, (const unsigned char*)source_base+source_offset, length);
+ struct io_buffer_memmove_arguments arguments = {
+ .destination = (unsigned char*)base+offset,
+ .source = (unsigned char*)source_base+source_offset,
+ .length = length
+ };
+
+ if (arguments.length >= IO_BUFFER_BLOCKING_SIZE) {
+ rb_nogvl(io_buffer_memmove_blocking, &arguments, io_buffer_memmove_unblock, &arguments, RB_NOGVL_OFFLOAD_SAFE);
+ } else if (arguments.length != 0) {
+ memmove(arguments.destination, arguments.source, arguments.length);
}
}