RMagick, migrate to ImageMagick 7 #RubyKaigi #RubyKaigi2019

6e8aca910e7ee095397d3b90acb25f6c?s=47 Watson
April 18, 2019

RMagick, migrate to ImageMagick 7 #RubyKaigi #RubyKaigi2019

6e8aca910e7ee095397d3b90acb25f6c?s=128

Watson

April 18, 2019
Tweet

Transcript

  1. RMagick, migrate to ImageMagick 7 Shizuo Fujita

  2. self.inspect • @watson1978 • Ubiregi, Inc • Ruby committer •

    RMagick maintainer
  3. What's RMagick

  4. "RMagick is an interface between the Ruby programming language and

    the ImageMagick image processing library."
  5. Active members ˏmockdeep ˏwatson1978 ˏdlemstra

  6. My Motivation

  7. I want to use Ruby ... •Web programming

  8. I want to use Ruby ... •Web programming •iOS programming

  9. I want to use Ruby ... •Web programming •iOS programming

    •Image processing
  10. I want to use Ruby ... •Web programming •iOS programming

    •Image processing •Machine learning, etc
  11. I want to use Ruby ... •Web programming •iOS programming

    •Image processing •Machine learning, etc To meet crazy great people at Ruby World
  12. Current Status of RMagick

  13. None
  14. Fixed major problems since v2.16.0 •Set up •Memory leaks •SEGVs

  15. Set up problems

  16. Set up problems •macOS (Homebrew) •Windows

  17. macOS (Homebrew)

  18. macOS (Homebrew)

  19. Windows •Ruby has some environments • MingW • MSWin •

    Cygwin • Windows Subsystem for Linux
  20. Windows •Ruby has some environments • MingW • MSWin •

    Cygwin • Windows Subsystem for Linux
  21. MingW...?

  22. Set up : MingW

  23. Solution https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers

  24. MingW works

  25. MSWin...?

  26. Set up : MSWin

  27. MSWin supported...? Looks like MSWin supporting code...

  28. Not supported... Why !? For MSWin

  29. MSWin works well

  30. Memory leak

  31. Fixed many memory leaks 55 Closed

  32. Case 1 VALUE Image_shear(VALUE self, VALUE x_shear, VALUE y_shear) {

    Image *image, *new_image; ExceptionInfo *exception; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception); rm_check_exception(exception, new_image, DestroyOnError); (void) DestroyExceptionInfo(exception); rm_ensure_result(new_image); return rm_image_new(new_image); }
  33. VALUE Image_shear(VALUE self, VALUE x_shear, VALUE y_shear) { Image *image,

    *new_image; ExceptionInfo *exception; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception); rm_check_exception(exception, new_image, DestroyOnError); (void) DestroyExceptionInfo(exception); rm_ensure_result(new_image); return rm_image_new(new_image); } Case 1 Allocate memory
  34. VALUE Image_shear(VALUE self, VALUE x_shear, VALUE y_shear) { Image *image,

    *new_image; ExceptionInfo *exception; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception); rm_check_exception(exception, new_image, DestroyOnError); (void) DestroyExceptionInfo(exception); rm_ensure_result(new_image); return rm_image_new(new_image); } Case 1 Allocate memory Destroy memory
  35. VALUE Image_shear(VALUE self, VALUE x_shear, VALUE y_shear) { Image *image,

    *new_image; ExceptionInfo *exception; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception); rm_check_exception(exception, new_image, DestroyOnError); (void) DestroyExceptionInfo(exception); rm_ensure_result(new_image); return rm_image_new(new_image); } Case 1 Allocate memory Destroy memory Raise exception with unexpected value
  36. VALUE Image_shear(VALUE self, VALUE x_shear, VALUE y_shear) { Image *image,

    *new_image; ExceptionInfo *exception; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception); rm_check_exception(exception, new_image, DestroyOnError); (void) DestroyExceptionInfo(exception); rm_ensure_result(new_image); return rm_image_new(new_image); } Case 1 Allocate memory Destroy memory Raise exception with unexpected value Unreachable by exception
  37. Case 1: Exception causes Memory Leak image = Magick::Image.read('sample.png').first begin

    image.shear('x', 'y') rescue end
  38. Case 2 static VALUE has_attribute(VALUE self, MagickBooleanType....[snip]) { Image *image;

    ExceptionInfo *exception; MagickBooleanType r; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); r = (attr_test)(image, exception); CHECK_EXCEPTION() return r ? Qtrue : Qfalse; }
  39. static VALUE has_attribute(VALUE self, MagickBooleanType....[snip]) { Image *image; ExceptionInfo *exception;

    MagickBooleanType r; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); r = (attr_test)(image, exception); CHECK_EXCEPTION() return r ? Qtrue : Qfalse; } Case 2 Allocate memory
  40. static VALUE has_attribute(VALUE self, MagickBooleanType....[snip]) { Image *image; ExceptionInfo *exception;

    MagickBooleanType r; image = rm_check_destroyed(self); exception = AcquireExceptionInfo(); r = (attr_test)(image, exception); CHECK_EXCEPTION() return r ? Qtrue : Qfalse; } Case 2 Allocate memory Missing destroy
  41. Case 2: Missing destroy lead to Memory Leak image =

    Magick::Image.read('sample.png').first image.gray?
  42. Case 3 Unnecessary clone API returns NEW image in allocated

    memory
  43. Case 3: Memory Leak by incorrect API usage image =

    Magick::Image.read('sample.png').first image.sharpen_channel
  44. Case 4

  45. Case 4 Found memory leak in ImageMagick

  46. Case 4

  47. Case 4: Huge software has Memory Leak

  48. How to detect memory leak

  49. Profiler tool

  50. Profiler tool Memory leak detected

  51. Profiler tool

  52. Profiler tool... •Detects at executed code only •unit-test •RSpec •Needs

    better coverage...
  53. Code reading... •To find same pattern that profiler detects

  54. SEGV

  55. Closed 5 SEGVs 4 Fixed 1 Workaround

  56. Workaround Case Help wanted

  57. Workaround Case •Call back image processing progress to Ruby User's

    Ruby code RMagick ImageMagick Callback
  58. ruby_stack_length() shows 51119378542 = 47 GB • Invalid stack length

    • GC running →SEGV
  59. RMagick is better than ever.

  60. Future...

  61. ImageMagick 7 support

  62. Preparing draft code... now

  63. Plan 1: Split RMagick ver 4.x RMagick ver 3.x ver

    3.0 ver 3.1 ver 2.16.1 ImageMagick 7 ImageMagick 6
  64. Plan 2 : Mix both ImageMagick 7 ImageMagick 7 ImageMagick

    6 Using many #if defined ...
  65. Plan 3: Bundle both ImageMagick 7 ImageMagick 6

  66. We welcome good ideas for the future

  67. Announcement •RubyData Workshop (Tomorrow, 14:20 ~) •RubyKaigi 2019 Code Party

    (Tomorrow, 19:00 ~)
  68. Thank you