Threadsafing a mixin module without using self.new or initialize.

Discussion in 'Ruby' started by Mike Bethany, Jun 17, 2011.

  1. Mike Bethany

    Mike Bethany Guest

    [Note: parts of this message were removed to make it a legal post.]

    Threadsafing a mixin module without using self.new or initialize.

    Project: Eventable - https://github.com/mikbe/eventable

    Background: https://github.com/mikbe/eventable/issues/4

    Problem Description:
    I need to threadsafe access to an instance variable, an array. To do so I've
    wrapped all methods that access the array in a mutex.synchronize block. The
    problem is I'm creating the mutex in each function using @eventable_mutex
    ||= Mutex.new which could cause collisions if two threads try to use a
    method that creates the mutex simultaneously.

    I've verified this could happen: https://gist.github.com/1031308

    Since this is a mixin module I can't use initialize to create the mutex
    instance variable and I don't want to use trapself.new because if the class
    uses this method itself then the instance variable will never be created. My
    aversion to using self.new to create the instance variable is this: if I
    redefine a method to fix an issue it's not unreasonable for someone else to
    do the same thing. If their doing exactly what I did breaks what I did then
    it's a bad fix. (see https://github.com/mikbe/eventable/pull/3)

    So, my current idea for a solution is to create a class level mutex for the
    inheriting class that locks on the creation of the mutex, for instance:
    https://gist.github.com/1031419. This seems ugly though and I'm fairly new
    to Ruby so I'm wondering if I've missed, or simply don't know about,
    something that would be a better solution.
     
    Mike Bethany, Jun 17, 2011
    #1
    1. Advertisements

  2. You do not want to be creating the mutex from different threads -
    because then mutex creation itself is not thread safe.
    Why not?

    module Anything
    def initialize(*a,&b)
    super
    @lock = Mutex.new
    @data = []
    end
    end

    class Other
    include Anything

    def initialize
    super # important!
    @another_var = 15
    end
    end
    That approach does not scale well if you have lots of instances created
    concurrently. If anything fails you can add a method which creates the
    mutex and which is called before the instance is used by multiple
    threads - as simple as that. But I'd prefer the approach with #initialize.

    Kind regards

    robert
     
    Robert Klemme, Jun 17, 2011
    #2
    1. Advertisements

  3. Mike Bethany

    Mike Bethany Guest

    [Note: parts of this message were removed to make it a legal post.]

    [smacks forehead] Doh! I'm still learning Ruby, didn't even think about
    that. I like the initialize method MUCH better. If they forget to include
    the super it'll raise an exception when they go to use it so I just need to
    make sure I document and emphasize that fact really well.


    Yeah, I thought about that and it was why I didn't really like my solution.
    Limiting creation at the class level could be a major bottleneck. Plus it
    just felt hackey; but not in a good way.


    Thanks for the help!
     
    Mike Bethany, Jun 18, 2011
    #3
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.