From 7f87cbc2e5289f328c3278a991519068d8747374 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 5 Apr 2013 21:21:19 +0000 Subject: add optional thread-safety module This can make Mahoro easier-to-use in multi-threaded apps where magic concurrency is not required. For real concurrency, it is recommended users create per-thread Mahoro objects. --- doc.mk | 5 +++-- lib/mahoro/thread_safe.rb | 45 +++++++++++++++++++++++++++++++++++++++++++++ mahoro.c | 3 +++ test.rb | 9 +++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 lib/mahoro/thread_safe.rb diff --git a/doc.mk b/doc.mk index 9d9af9b..2480585 100644 --- a/doc.mk +++ b/doc.mk @@ -1,7 +1,7 @@ all:: -.ri/created.rid: mahoro.c - rdoc --ri -o $(@D) $< +.ri/created.rid: mahoro.c lib/mahoro/thread_safe.rb + rdoc --ri -o $(@D) $^ order = order += Mahoro @@ -12,6 +12,7 @@ order += Mahoro\#flags= order += Mahoro\#valid? order += Mahoro\#compile order += Mahoro.compile +order += Mahoro::ThreadSafe mahoro.txt: .ri/created.rid ( \ diff --git a/lib/mahoro/thread_safe.rb b/lib/mahoro/thread_safe.rb new file mode 100644 index 0000000..4e77c35 --- /dev/null +++ b/lib/mahoro/thread_safe.rb @@ -0,0 +1,45 @@ +require 'thread' + +# Adds thread-safety to an existing Mahoro object. +# +# magic_t cookies of libmagic are not thread-safe by default, +# thus Mahoro is not thread-safe by default, either. +# +# For applications using persistent thread pools, this module is NOT +# recommended. Instead, it is best to create thread-local instances +# of (non-thread-safe) Mahoro for best performance. +# +# This is only intended for applications using Mahoro which meet the +# following requirements: +# 1) uses short-lived threads +# 2) does not need high concurrency for Mahoro operations +# +# Usage example: +# require "mahoro/thread_safe" +# +# # create a Mahoro object as usual +# mahoro_obj = Mahoro.new(...) +# +# # enable thread-safety +# mahoro_obj.extend Mahoro::ThreadSafe +# +# # mahoro_obj may now be used by multiple threads with automatic +# # mutual exclusion (the following example is safe, but not concurrent). +# Thread.new { mahoro_obj.file("/path/to/some_file") } +# Thread.new { mahoro_obj.file("/path/to/some_other_file") } +# +# # Real concurrency must be achieved by using different Mahoro objects +# # in different threads. As of Mahoro v0.4, Mahoro releases the GVL +# # on (Matz) Ruby 1.9 and later. +module Mahoro::ThreadSafe + + def self.extended(obj) # :nodoc: + obj.instance_variable_set(:@lock, Mutex.new) + end + + eval( + %w(file buffer flags= valid? compile load).map do |meth| + "\ndef #{meth}(*args)\n @lock.synchronize { super }\nend\n" + end.join("") + ) +end diff --git a/mahoro.c b/mahoro.c index a30946c..01d1cf8 100644 --- a/mahoro.c +++ b/mahoro.c @@ -384,6 +384,9 @@ void Init_mahoro(void) * str = File.read('/path/to/file.c') * mahoro_obj.buffer(str) -> 'ASCII C program text' * + * Mahoro is not thread-safe by default, see Mahoro::ThreadSafe for + * making this module thread-safe. + * * More information about libmagic: * https://en.wikipedia.org/wiki/Libmagic * diff --git a/test.rb b/test.rb index 75e46d6..5c2c23d 100755 --- a/test.rb +++ b/test.rb @@ -2,6 +2,7 @@ require 'test/unit' require 'mahoro' +require 'mahoro/thread_safe' require 'pathname' class MahoroTestCase < Test::Unit::TestCase @@ -90,6 +91,14 @@ class MahoroTestCase < Test::Unit::TestCase Mahoro.compile "magic.sample\0" end end + + def test_thread_safe + before = @m.method(:file) + @m.extend(Mahoro::ThreadSafe) + @m.flags = Mahoro::NONE + assert_c_text(@m.file('mahoro.c')) + assert_not_equal(before.object_id, @m.method(:file).object_id) + end end # arch-tag: test -- cgit v1.2.3-24-ge0c7