Skip to content

Instantly share code, notes, and snippets.

@zsombor
Last active December 31, 2015 06:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zsombor/a6bd7b0440602e5333e1 to your computer and use it in GitHub Desktop.
Save zsombor/a6bd7b0440602e5333e1 to your computer and use it in GitHub Desktop.
# Some examples for why the
#
# @obj ||= "some value"
#
# expression is threadsafe on MRI (should one be dumb enough to write
# such code)
#
# MRI will only preempt a Ruby thread when it is returning from a
# method call. If a code block contains no ruby method calls then
# then it will not be preempted. If it contains a single method
# call then the preemption point is predictable.
#
# This code:
#
# @obj ||= "some value"
#
# expands to:
#
# if @obj
# @obj = "some value"
# end
#
# and for this to be unsafe it should contain two method calls. There
# are none or one depending on how you count it.
#
# Now if I were to change the code to something like this
#
# @obj ||= Whatever.new
#
# then now there is a function call between the CHECK and SET
# operations and it is no longer thread safe.
#
#
# Regardless of how safe the original code was, the best thing to do
# is avoiding shared state when working with threads:
#
# Don't communicate by sharing memory; share memory by communicating
# (Go Programming Language).
class DoesNotPreemptWithoutMethodCalls
def run
@whatever = nil
@non_nil_count = 0
@thread_count = 1_000
@thread_count.times.map do
Thread.new do
if @whatever != nil
@non_nil_count += 1
end
@whatever ||= 1 # ??? Thread safe ???
end
end.each(&:join)
if @thread_count == @non_nil_count + 1
puts "OK"
else
puts "BUM: Preempted without a method call!"
end
end
end
DoesNotPreemptWithoutMethodCalls.new.run
class HaveSomeMethodcallsForPreemtion
def count_non_nils
if @whatever != nil
@non_nil_count += 1
end
# may preempt here
end
def run
@whatever = nil
@non_nil_count = 0
@thread_count = 1_000
@thread_count.times.map do
Thread.new do
count_non_nils # may be preempted here
@whatever ||= 1 # but not here
end
end.each(&:join)
if @thread_count == @non_nil_count + 1
puts "2 - OK"
else
puts "2 - BUM: Preempted without a method call!"
end
end
end
HaveSomeMethodcallsForPreemtion.new.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment