Skip to content
Snippets Groups Projects
Commit 18e5facd authored by Lars Kanis's avatar Lars Kanis
Browse files

Release Ruby's GVL while calls to FXImage#savePixels and #loadPixels.

parent 54a11707
No related branches found
No related tags found
No related merge requests found
......@@ -351,6 +351,7 @@ def do_rake_compiler_setup
else
FileUtils.move('scintilla_wrap.cpp', 'scintilla_wrap.cpp.bak') if FileTest.exist?('scintilla_wrap.cpp')
end
have_func 'rb_thread_call_without_gvl'
end
# This directive processes the "--with-fox-include" and "--with-fox-lib"
......@@ -383,4 +384,5 @@ $CFLAGS += " -DRUBY_1_9" if RUBY_VERSION =~ /1\.9\./
$CFLAGS += " -DRUBY_2_0" if RUBY_VERSION =~ /2\.0\./
# Last step: build the makefile
create_header
create_makefile("fox16_c")
/*
* gvl_wrappers.c - Wrapper functions for locking/unlocking the Ruby GVL
*
*/
#include "FXRbCommon.h"
FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_WRAPPER_STRUCT );
FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_SKELETON );
FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_STUB );
......@@ -27,6 +27,8 @@
extern "C" {
#include "ruby.h"
#include "extconf.h"
#ifdef HAVE_RUBY_ENCODING_H
#include "ruby/encoding.h"
#endif
......@@ -97,3 +99,4 @@ extern "C" {
#include "FXScintilla.h"
#endif
#include "FXRuby.h"
#include "gvl_wrappers.h"
......@@ -73,10 +73,10 @@ inline void klass ## _gradient(klass* self,FXColor topleft,FXColor topright,FXCo
inline void klass ## _blend(klass* self,FXColor color){ \
self->klass::blend(color); \
} \
inline bool klass ## _savePixels(const klass* self,FXStream& store){ \
inline bool klass ## _savePixels_gvl(const klass* self,FXStream& store){ \
return self->klass::savePixels(store); \
} \
inline bool klass ## _loadPixels(klass* self,FXStream& store){ \
inline bool klass ## _loadPixels_gvl(klass* self,FXStream& store){ \
return self->klass::loadPixels(store); \
}
......
/*
* gvl_wrappers.h - Wrapper functions for locking/unlocking the Ruby GVL
*
* These are some obscure preprocessor directives that allow to generate
* drop-in replacement wrapper functions in a declarative manner.
* These wrapper functions ensure that ruby's GVL is released on each
* function call and reacquired at the end of the call or in callbacks.
* This way blocking functions calls don't block concurrent ruby threads.
*
* The wrapper of each function is prefixed by "gvl_".
*
* Use "gcc -E" to retrieve the generated code.
*/
#ifndef __gvl_wrappers_h
#define __gvl_wrappers_h
#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
extern "C" void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
rb_unblock_function_t *ubf, void *data2);
#endif
#define DEFINE_PARAM_LIST1(type, name) \
name,
#define DEFINE_PARAM_LIST2(type, name) \
p->params.name,
#define DEFINE_PARAM_LIST3(type, name) \
type name,
#define DEFINE_PARAM_DECL(type, name) \
type name;
#define DEFINE_GVL_WRAPPER_STRUCT(name, when_non_void, rettype, lastparamtype, lastparamname) \
struct gvl_wrapper_##name##_params { \
struct { \
FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_DECL) \
lastparamtype lastparamname; \
} params; \
when_non_void( rettype retval; ) \
};
#define DEFINE_GVL_SKELETON(name, when_non_void, rettype, lastparamtype, lastparamname) \
static void * gvl_##name##_skeleton( void *data ){ \
struct gvl_wrapper_##name##_params *p = (struct gvl_wrapper_##name##_params*)data; \
when_non_void( p->retval = ) \
name##_gvl( FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST2) p->params.lastparamname ); \
return NULL; \
}
#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
#define DEFINE_GVL_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
rettype name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
struct gvl_wrapper_##name##_params params = { \
{FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname}, when_non_void((rettype)0) \
}; \
rb_thread_call_without_gvl(gvl_##name##_skeleton, &params, RUBY_UBF_IO, 0); \
when_non_void( return params.retval; ) \
}
#else
#define DEFINE_GVL_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
rettype name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
return name##_gvl( FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname ); \
}
#endif
#define DEFINE_GVL_STUB_DECL(name, when_non_void, rettype, lastparamtype, lastparamname) \
rettype name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname);
#define DEFINE_GVL_TARGET_DECL(name, when_non_void, rettype, lastparamtype, lastparamname) \
rettype name##_gvl(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname);
#define GVL_TYPE_VOID(string)
#define GVL_TYPE_NONVOID(string) string
/*
* Definitions of blocking functions and their parameters
*/
#define FOR_EACH_PARAM_OF_FXImage_loadPixels(param) \
param(FXImage *, self)
#define FOR_EACH_PARAM_OF_FXImage_savePixels(param) \
param(const FXImage *, self)
#define FOR_EACH_PARAM_OF_FXBMPImage_loadPixels(param) \
param(FXBMPImage *, self)
#define FOR_EACH_PARAM_OF_FXBMPImage_savePixels(param) \
param(const FXBMPImage *, self)
#define FOR_EACH_PARAM_OF_FXJPGImage_loadPixels(param) \
param(FXJPGImage *, self)
#define FOR_EACH_PARAM_OF_FXJPGImage_savePixels(param) \
param(const FXJPGImage *, self)
#define FOR_EACH_PARAM_OF_FXGIFImage_loadPixels(param) \
param(FXGIFImage *, self)
#define FOR_EACH_PARAM_OF_FXGIFImage_savePixels(param) \
param(const FXGIFImage *, self)
#define FOR_EACH_PARAM_OF_FXICOImage_loadPixels(param) \
param(FXICOImage *, self)
#define FOR_EACH_PARAM_OF_FXICOImage_savePixels(param) \
param(const FXICOImage *, self)
#define FOR_EACH_PARAM_OF_FXPNGImage_loadPixels(param) \
param(FXPNGImage *, self)
#define FOR_EACH_PARAM_OF_FXPNGImage_savePixels(param) \
param(const FXPNGImage *, self)
#define FOR_EACH_PARAM_OF_FXPPMImage_loadPixels(param) \
param(FXPPMImage *, self)
#define FOR_EACH_PARAM_OF_FXPPMImage_savePixels(param) \
param(const FXPPMImage *, self)
#define FOR_EACH_PARAM_OF_FXPCXImage_loadPixels(param) \
param(FXPCXImage *, self)
#define FOR_EACH_PARAM_OF_FXPCXImage_savePixels(param) \
param(const FXPCXImage *, self)
#define FOR_EACH_PARAM_OF_FXRGBImage_loadPixels(param) \
param(FXRGBImage *, self)
#define FOR_EACH_PARAM_OF_FXRGBImage_savePixels(param) \
param(const FXRGBImage *, self)
#define FOR_EACH_PARAM_OF_FXTGAImage_loadPixels(param) \
param(FXTGAImage *, self)
#define FOR_EACH_PARAM_OF_FXTGAImage_savePixels(param) \
param(const FXTGAImage *, self)
#define FOR_EACH_PARAM_OF_FXTIFImage_loadPixels(param) \
param(FXTIFImage *, self)
#define FOR_EACH_PARAM_OF_FXTIFImage_savePixels(param) \
param(const FXTIFImage *, self)
#define FOR_EACH_PARAM_OF_FXXBMImage_loadPixels(param) \
param(FXXBMImage *, self)
#define FOR_EACH_PARAM_OF_FXXBMImage_savePixels(param) \
param(const FXXBMImage *, self)
#define FOR_EACH_PARAM_OF_FXXPMImage_loadPixels(param) \
param(FXXPMImage *, self)
#define FOR_EACH_PARAM_OF_FXXPMImage_savePixels(param) \
param(const FXXPMImage *, self)
/* function( name, void_or_nonvoid, returntype, lastparamtype, lastparamname ) */
#define FOR_EACH_BLOCKING_FUNCTION(function) \
function(FXImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXBMPImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXBMPImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXJPGImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXJPGImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXGIFImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXGIFImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXICOImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXICOImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXPNGImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXPNGImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXPPMImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXPPMImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXPCXImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXPCXImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXRGBImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXRGBImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXTGAImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXTGAImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXTIFImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXTIFImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXXBMImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXXBMImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXXPMImage_loadPixels, GVL_TYPE_NONVOID, bool, FXStream&, store) \
function(FXXPMImage_savePixels, GVL_TYPE_NONVOID, bool, FXStream&, store)
FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_STUB_DECL )
FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_TARGET_DECL )
#endif /* end __gvl_wrappers_h */
require 'fox16'
require 'test/unit'
require 'testcase'
require 'openssl'
class TC_FXJPGImage < Fox::TestCase
include Fox
def setup
super(self.class.name)
end
def test_save_with_thread
w, h = 4000, 3000
img_data = OpenSSL::Random.random_bytes(w) * h * 4
count = 0
th = Thread.new do
loop do
sleep 0.01
count += 1
end
end
img = FXJPGImage.new(app)
img.setPixels( img_data, 0, w, h )
jpeg_data = FXMemoryStream.open(FXStreamSave, nil) do |outfile|
img.savePixels(outfile)
outfile.takeBuffer
end
th.kill
assert_operator(count, :>=, 10)
assert_operator(jpeg_data.bytesize, :>=, 1000)
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment