Feature #8961
openSynchronizable module to easily wrap methods in a mutex
Description
=begin
I propose a Synchronizable mixin to easily wrap methods in a mutex which works together with Ruby 2.1's method name symbols returned from '(({def}))'.
The Mixin adds a new '(({synchronized}))' class method which would alias the referenced method and redefines the original method wrapped in a '(({synchronize do .. end}))' block.
This is probably somewhat related and an alternative to #8556.
Proof of concept (I've used Monitor here so potential users won't have to worry about reentrancy):
require 'monitor'
module Synchronizable
module ClassMethods
def synchronized(method)
aliased = :"#{method}_without_synchronization"
alias_method aliased, method
define_method method do |*args, &block|
monitor.synchronize do
__send__(aliased, *args, &block)
end
end
end
end
def monitor
@monitor ||= Monitor.new
end
def self.included(base)
base.extend(ClassMethods)
end
end
class Foo
include Synchronizable
synchronized def bar
# ...
end
end
=end
Updated by headius (Charles Nutter) almost 11 years ago
I would like to see this in 2.1, as a standard Module method. The fact that "def" returns the method name now makes this really easy.
I think this would need to be implemented natively to work, however. The prototype above has a key flaw: there's no guarantee that only one Monitor will be created, so two threads could execute the same method at the same time, synchronizing against different monitors. Putting the synchronized wrapper into C code would prevent a potential context switch when first creating the Monitor instance (or it could simply use some other mechanism, such as normal Object monitor synchronization in JRuby).
This feature is similar to an extension in JRuby called JRuby::Synchronized that causes all method lookups to return synchronized equivalents.
Combined with https://bugs.ruby-lang.org/issues/8556 this could go a very long way toward giving Ruby users better tools to write thread-safe code.
Updated by tobiassvn (Tobias Svensson) almost 11 years ago
Having this as a method on Module directly would of course be ideal. However, I believe the mutex/monitor used should still be exposed as a private method so it can be used without the 'synchronized' method.
Updated by headius (Charles Nutter) almost 11 years ago
tobiassvn (Tobias Svensson) wrote:
Having this as a method on Module directly would of course be ideal. However, I believe the mutex/monitor used should still be exposed as a private method so it can be used without the 'synchronized' method.
Maybe. I don't like the idea of exposing this mutex/monitor, since it could be modified or locked and never released. I would be more in favor of a "tap" form that synchronizes against the same internal monitor, similar to Java's "synchronized" keyword.
obj.synchronized { thread-sensitive code here }
That would also open up the possibility of using a lighter-weight internal mutex/monitor rather than the rather heavy-weight Ruby-land version.
Updated by nobu (Nobuyoshi Nakada) almost 11 years ago
headius (Charles Nutter) wrote:
Maybe. I don't like the idea of exposing this mutex/monitor, since it could be modified or locked and never released. I would be more in favor of a "tap" form that synchronizes against the same internal monitor, similar to Java's "synchronized" keyword.
obj.synchronized { thread-sensitive code here }
Use MonitorMixin.
Updated by nobu (Nobuyoshi Nakada) almost 11 years ago
- Description updated (diff)
Updated by headius (Charles Nutter) almost 11 years ago
nobu (Nobuyoshi Nakada) wrote:
headius (Charles Nutter) wrote:
Maybe. I don't like the idea of exposing this mutex/monitor, since it could be modified or locked and never released. I would be more in favor of a "tap" form that synchronizes against the same internal monitor, similar to Java's "synchronized" keyword.
obj.synchronized { thread-sensitive code here }
Use MonitorMixin.
Yeah, that's not a bad option from a pure-Ruby perspective. We could add "synchronized" to classes that include MonitorMixin, perhaps?
- added to monitor.rb:
module MonitorMixin
module ClassMethods
def synchronized(method)
aliased = :"#{method}_without_synchronization"
alias_method aliased, method
define_method method do |*args, &block|
mon_enter
begin
__send__(aliased, *args, &block)
ensure
mon_exit
end
end
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
class Foo
include MonitorMixin
synchronized def bar
# ...
end
end
...
My suggestion to have it be native on Module opened up the possibility of implementing it in a faster, native way. MonitorMixin has a very large perf hit on all impls right now, but especially MRI. See my benchmarks in https://github.com/ruby/ruby/pull/405#issuecomment-25417666
Updated by tobiassvn (Tobias Svensson) almost 11 years ago
I suppose if this is being added to MonitorMixin it should probably be in Mutex_m as well?
Updated by headius (Charles Nutter) almost 11 years ago
tobiassvn (Tobias Svensson) wrote:
I suppose if this is being added to MonitorMixin it should probably be in Mutex_m as well?
I don't think so, since a Mutex is not reentrant and what we want is monitor semantics for #synchronized.
Updated by hsbt (Hiroshi SHIBATA) over 2 years ago
- Project changed from 14 to Ruby master