{"action":"created","comment":{"url":"https://api.github.com/repos/rspec/rspec-core/pulls/comments/25412186","id":25412186,"diff_hunk":"@@ -0,0 +1,37 @@\n+require 'rspec/core/reentrant_mutex'\n+\n+# There are no assertions specifically\n+# They are pass if they don't deadlock\n+RSpec.describe RSpec::Core::ReentrantMutex do\n+  let!(:mutex) { described_class.new }\n+  let!(:order) { ThreadOrderSupport.new }\n+  after { order.apocalypse! }\n+\n+  it 'can repeatedly synchronize within the same thread' do\n+    mutex.synchronize { mutex.synchronize { } }\n+  end\n+\n+  it 'locks other threads out while in the synchronize block' do\n+    order.declare(:before) { mutex.synchronize { } }\n+    order.declare(:within) { mutex.synchronize { } }\n+    order.declare(:after)  { mutex.synchronize { } }\n+\n+    order.pass_to :before, :resume_on => :exit\n+    mutex.synchronize { order.pass_to :before, :resume_on => :sleep }\n+    order.pass_to :before, :resume_on => :exit\n+  end\n+\n+  it 'resumes the next thread once all its synchronize blocks have completed' do\n+    order.declare(:thread) { mutex.synchronize { } }\n+    mutex.synchronize { order.pass_to :thread, :resume_on => :sleep }\n+    order.apocalypse! :join\n+  end\n+\n+  it 'is implemented without depending on the stdlib' do\n+    loaded_filenames = $LOADED_FEATURES.map { |filepath| File.basename filepath }\n+    pending 'thread seems to be required from core, and something is still requiring monitor'\n+    expect(loaded_filenames).to_not include 'monitor.rb'\n+    expect(loaded_filenames).to_not include 'thread.rb'\n+    expect(loaded_filenames).to_not include 'thread.bundle'\n+  end","path":"spec/rspec/core/reentrant_mutex_spec.rb","position":36,"original_position":36,"commit_id":"1ed82dbabd6c5175c95b6b49a0b2899e5a5ca60d","original_commit_id":"1ed82dbabd6c5175c95b6b49a0b2899e5a5ca60d","user":{"login":"myronmarston","id":49391,"avatar_url":"https://avatars.githubusercontent.com/u/49391?v=3","gravatar_id":"","url":"https://api.github.com/users/myronmarston","html_url":"https://github.com/myronmarston","followers_url":"https://api.github.com/users/myronmarston/followers","following_url":"https://api.github.com/users/myronmarston/following{/other_user}","gists_url":"https://api.github.com/users/myronmarston/gists{/gist_id}","starred_url":"https://api.github.com/users/myronmarston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/myronmarston/subscriptions","organizations_url":"https://api.github.com/users/myronmarston/orgs","repos_url":"https://api.github.com/users/myronmarston/repos","events_url":"https://api.github.com/users/myronmarston/events{/privacy}","received_events_url":"https://api.github.com/users/myronmarston/received_events","type":"User","site_admin":false},"body":"So I looked into what's loading monitor and thread...\r\n\r\n* Rubygems loads monitor.  Using `--disable=gem` bypasses that (which, BTW, our \"minimizes stdlibs\" spec does to prevent rubygems pollution).\r\n* Rake loads thread and monitor.  rspec-core doesn't generally load rake -- you have to specifically require `rspec/core/rake_task` -- but in the spec suite it's loaded because it tests the rake task stuff.\r\n* The DRB support likewise loads DRB (but again, it's not loaded if you're not using the drb option) and DRB loads thread. ","created_at":"2015-02-26T08:52:12Z","updated_at":"2015-02-26T08:52:12Z","html_url":"https://github.com/rspec/rspec-core/pull/1858#discussion_r25412186","pull_request_url":"https://api.github.com/repos/rspec/rspec-core/pulls/1858","_links":{"self":{"href":"https://api.github.com/repos/rspec/rspec-core/pulls/comments/25412186"},"html":{"href":"https://github.com/rspec/rspec-core/pull/1858#discussion_r25412186"},"pull_request":{"href":"https://api.github.com/repos/rspec/rspec-core/pulls/1858"}}},"pull_request":{"url":"https://api.github.com/repos/rspec/rspec-core/pulls/1858","id":28448532,"html_url":"https://github.com/rspec/rspec-core/pull/1858","diff_url":"https://github.com/rspec/rspec-core/pull/1858.diff","patch_url":"https://github.com/rspec/rspec-core/pull/1858.patch","issue_url":"https://api.github.com/repos/rspec/rspec-core/issues/1858","number":1858,"state":"open","locked":false,"title":"Make memoized helpers threadsafe","user":{"login":"JoshCheek","id":77495,"avatar_url":"https://avatars.githubusercontent.com/u/77495?v=3","gravatar_id":"","url":"https://api.github.com/users/JoshCheek","html_url":"https://github.com/JoshCheek","followers_url":"https://api.github.com/users/JoshCheek/followers","following_url":"https://api.github.com/users/JoshCheek/following{/other_user}","gists_url":"https://api.github.com/users/JoshCheek/gists{/gist_id}","starred_url":"https://api.github.com/users/JoshCheek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JoshCheek/subscriptions","organizations_url":"https://api.github.com/users/JoshCheek/orgs","repos_url":"https://api.github.com/users/JoshCheek/repos","events_url":"https://api.github.com/users/JoshCheek/events{/privacy}","received_events_url":"https://api.github.com/users/JoshCheek/received_events","type":"User","site_admin":false},"body":"Make memoized is a threadsafe object instead of a hash\r\n\r\nAccordingly, rename RSpec::Core::MemoizedHelpers::ContextHookMemoizedHash -> ContextHookMemoized\r\n\r\nMotivation\r\n----------\r\n\r\nWhen working in a truly threaded environment (e.g. Rbx and JRuby),\r\nyou can write a test like the one below, which should pass.\r\nBut, it will fail sometimes, because two threads request the uninitialized counter\r\nconcurrently. The first one to receive the value will then be\r\nincrementing a counter, that is later overwritten when the second\r\nthread's let block returns.\r\n\r\nYou can verify this by running the code sample below in Rubinius.\r\nAfter some number of attempts, the value will be less than 10k.\r\nIf you then make the `let` block a `let!` block, it will memoize\r\nbefore the threads are created, and the values will be correct again.\r\nWhile this is a reasonable solution, it is incredibly confusing\r\nwhen it arises (I spent a lot of hours trying to find the bug in my code),\r\nand requires that the user understand it's something they need to\r\nbe aware of and guard against.\r\n\r\n```ruby\r\nclass Counter\r\n  def initialize\r\n    @mutex = Mutex.new\r\n    @count = 0\r\n  end\r\n\r\n  attr_reader :count\r\n\r\n  def increment\r\n    @mutex.synchronize { @count += 1 }\r\n  end\r\nend\r\n\r\nRSpec.describe Counter do\r\n  let(:counter) { Counter.new }\r\n\r\n  it 'increments the count in a threadsafe manner' do\r\n    threads = 10.times.map do\r\n      Thread.new { 1000.times { counter.increment } }\r\n    end\r\n    threads.each &:join\r\n    expect(counter.count).to eq 10_000\r\n  end\r\nend\r\n```\r\n\r\nDecisions\r\n---------\r\n\r\nCreate an object because only #fetch was being called on the hash,\r\nand to make that threadsafe required adding a new method.\r\nAt that point, there is no value in having it subclass Hash,\r\nhaving to support an entire API that nothing else uses.\r\nSo just make it an object which wraps a hash and presents a memoized\r\ngetter/setter. Also named the method in such a way as to make it\r\nvery clear that it's not a hash, so a dev won't mistakenly think\r\nthey are working with one when they aren't.\r\n\r\nI had initially tried writing the test with Fibers. It was much shorter.\r\nBut Fibers don't work, because they execute within the same thread,\r\nso you can't identify the difference between another thread setting\r\nthe memoized value that we need to respect, and a call to super()\r\nsetting the value, which we need to override.\r\n(eg https://github.com/rspec/rspec-core/blob/655a52b03fd00c3d64554c4268e8da3ac0bd587c/spec/rspec/core/memoized_helpers_spec.rb#L169)","created_at":"2015-02-02T06:54:54Z","updated_at":"2015-02-26T08:52:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"925d86412bc9014a180ffaa2302e7f30496bfda5","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/rspec/rspec-core/pulls/1858/commits","review_comments_url":"https://api.github.com/repos/rspec/rspec-core/pulls/1858/comments","review_comment_url":"https://api.github.com/repos/rspec/rspec-core/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rspec/rspec-core/issues/1858/comments","statuses_url":"https://api.github.com/repos/rspec/rspec-core/statuses/1ed82dbabd6c5175c95b6b49a0b2899e5a5ca60d","head":{"label":"JoshCheek:threadsafe-let-block","ref":"threadsafe-let-block","sha":"1ed82dbabd6c5175c95b6b49a0b2899e5a5ca60d","user":{"login":"JoshCheek","id":77495,"avatar_url":"https://avatars.githubusercontent.com/u/77495?v=3","gravatar_id":"","url":"https://api.github.com/users/JoshCheek","html_url":"https://github.com/JoshCheek","followers_url":"https://api.github.com/users/JoshCheek/followers","following_url":"https://api.github.com/users/JoshCheek/following{/other_user}","gists_url":"https://api.github.com/users/JoshCheek/gists{/gist_id}","starred_url":"https://api.github.com/users/JoshCheek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JoshCheek/subscriptions","organizations_url":"https://api.github.com/users/JoshCheek/orgs","repos_url":"https://api.github.com/users/JoshCheek/repos","events_url":"https://api.github.com/users/JoshCheek/events{/privacy}","received_events_url":"https://api.github.com/users/JoshCheek/received_events","type":"User","site_admin":false},"repo":{"id":2401696,"name":"rspec-core","full_name":"JoshCheek/rspec-core","owner":{"login":"JoshCheek","id":77495,"avatar_url":"https://avatars.githubusercontent.com/u/77495?v=3","gravatar_id":"","url":"https://api.github.com/users/JoshCheek","html_url":"https://github.com/JoshCheek","followers_url":"https://api.github.com/users/JoshCheek/followers","following_url":"https://api.github.com/users/JoshCheek/following{/other_user}","gists_url":"https://api.github.com/users/JoshCheek/gists{/gist_id}","starred_url":"https://api.github.com/users/JoshCheek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JoshCheek/subscriptions","organizations_url":"https://api.github.com/users/JoshCheek/orgs","repos_url":"https://api.github.com/users/JoshCheek/repos","events_url":"https://api.github.com/users/JoshCheek/events{/privacy}","received_events_url":"https://api.github.com/users/JoshCheek/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/JoshCheek/rspec-core","description":"Rspec-2 runner and formatters","fork":true,"url":"https://api.github.com/repos/JoshCheek/rspec-core","forks_url":"https://api.github.com/repos/JoshCheek/rspec-core/forks","keys_url":"https://api.github.com/repos/JoshCheek/rspec-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/JoshCheek/rspec-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/JoshCheek/rspec-core/teams","hooks_url":"https://api.github.com/repos/JoshCheek/rspec-core/hooks","issue_events_url":"https://api.github.com/repos/JoshCheek/rspec-core/issues/events{/number}","events_url":"https://api.github.com/repos/JoshCheek/rspec-core/events","assignees_url":"https://api.github.com/repos/JoshCheek/rspec-core/assignees{/user}","branches_url":"https://api.github.com/repos/JoshCheek/rspec-core/branches{/branch}","tags_url":"https://api.github.com/repos/JoshCheek/rspec-core/tags","blobs_url":"https://api.github.com/repos/JoshCheek/rspec-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/JoshCheek/rspec-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/JoshCheek/rspec-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/JoshCheek/rspec-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/JoshCheek/rspec-core/statuses/{sha}","languages_url":"https://api.github.com/repos/JoshCheek/rspec-core/languages","stargazers_url":"https://api.github.com/repos/JoshCheek/rspec-core/stargazers","contributors_url":"https://api.github.com/repos/JoshCheek/rspec-core/contributors","subscribers_url":"https://api.github.com/repos/JoshCheek/rspec-core/subscribers","subscription_url":"https://api.github.com/repos/JoshCheek/rspec-core/subscription","commits_url":"https://api.github.com/repos/JoshCheek/rspec-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/JoshCheek/rspec-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/JoshCheek/rspec-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/JoshCheek/rspec-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/JoshCheek/rspec-core/contents/{+path}","compare_url":"https://api.github.com/repos/JoshCheek/rspec-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/JoshCheek/rspec-core/merges","archive_url":"https://api.github.com/repos/JoshCheek/rspec-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/JoshCheek/rspec-core/downloads","issues_url":"https://api.github.com/repos/JoshCheek/rspec-core/issues{/number}","pulls_url":"https://api.github.com/repos/JoshCheek/rspec-core/pulls{/number}","milestones_url":"https://api.github.com/repos/JoshCheek/rspec-core/milestones{/number}","notifications_url":"https://api.github.com/repos/JoshCheek/rspec-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/JoshCheek/rspec-core/labels{/name}","releases_url":"https://api.github.com/repos/JoshCheek/rspec-core/releases{/id}","created_at":"2011-09-16T20:14:39Z","updated_at":"2013-01-04T06:16:10Z","pushed_at":"2015-02-25T01:49:31Z","git_url":"git://github.com/JoshCheek/rspec-core.git","ssh_url":"git@github.com:JoshCheek/rspec-core.git","clone_url":"https://github.com/JoshCheek/rspec-core.git","svn_url":"https://github.com/JoshCheek/rspec-core","homepage":"http://relishapp.com/rspec/rspec-core","size":9116,"stargazers_count":1,"watchers_count":1,"language":"Ruby","has_issues":false,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"rspec:master","ref":"master","sha":"26331829661c5f5cf2fcbad4c988970929ccf0c6","user":{"login":"rspec","id":22388,"avatar_url":"https://avatars.githubusercontent.com/u/22388?v=3","gravatar_id":"","url":"https://api.github.com/users/rspec","html_url":"https://github.com/rspec","followers_url":"https://api.github.com/users/rspec/followers","following_url":"https://api.github.com/users/rspec/following{/other_user}","gists_url":"https://api.github.com/users/rspec/gists{/gist_id}","starred_url":"https://api.github.com/users/rspec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rspec/subscriptions","organizations_url":"https://api.github.com/users/rspec/orgs","repos_url":"https://api.github.com/users/rspec/repos","events_url":"https://api.github.com/users/rspec/events{/privacy}","received_events_url":"https://api.github.com/users/rspec/received_events","type":"Organization","site_admin":false},"repo":{"id":238972,"name":"rspec-core","full_name":"rspec/rspec-core","owner":{"login":"rspec","id":22388,"avatar_url":"https://avatars.githubusercontent.com/u/22388?v=3","gravatar_id":"","url":"https://api.github.com/users/rspec","html_url":"https://github.com/rspec","followers_url":"https://api.github.com/users/rspec/followers","following_url":"https://api.github.com/users/rspec/following{/other_user}","gists_url":"https://api.github.com/users/rspec/gists{/gist_id}","starred_url":"https://api.github.com/users/rspec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rspec/subscriptions","organizations_url":"https://api.github.com/users/rspec/orgs","repos_url":"https://api.github.com/users/rspec/repos","events_url":"https://api.github.com/users/rspec/events{/privacy}","received_events_url":"https://api.github.com/users/rspec/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rspec/rspec-core","description":"RSpec runner and formatters","fork":false,"url":"https://api.github.com/repos/rspec/rspec-core","forks_url":"https://api.github.com/repos/rspec/rspec-core/forks","keys_url":"https://api.github.com/repos/rspec/rspec-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rspec/rspec-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rspec/rspec-core/teams","hooks_url":"https://api.github.com/repos/rspec/rspec-core/hooks","issue_events_url":"https://api.github.com/repos/rspec/rspec-core/issues/events{/number}","events_url":"https://api.github.com/repos/rspec/rspec-core/events","assignees_url":"https://api.github.com/repos/rspec/rspec-core/assignees{/user}","branches_url":"https://api.github.com/repos/rspec/rspec-core/branches{/branch}","tags_url":"https://api.github.com/repos/rspec/rspec-core/tags","blobs_url":"https://api.github.com/repos/rspec/rspec-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rspec/rspec-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rspec/rspec-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/rspec/rspec-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rspec/rspec-core/statuses/{sha}","languages_url":"https://api.github.com/repos/rspec/rspec-core/languages","stargazers_url":"https://api.github.com/repos/rspec/rspec-core/stargazers","contributors_url":"https://api.github.com/repos/rspec/rspec-core/contributors","subscribers_url":"https://api.github.com/repos/rspec/rspec-core/subscribers","subscription_url":"https://api.github.com/repos/rspec/rspec-core/subscription","commits_url":"https://api.github.com/repos/rspec/rspec-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/rspec/rspec-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/rspec/rspec-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/rspec/rspec-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/rspec/rspec-core/contents/{+path}","compare_url":"https://api.github.com/repos/rspec/rspec-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rspec/rspec-core/merges","archive_url":"https://api.github.com/repos/rspec/rspec-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rspec/rspec-core/downloads","issues_url":"https://api.github.com/repos/rspec/rspec-core/issues{/number}","pulls_url":"https://api.github.com/repos/rspec/rspec-core/pulls{/number}","milestones_url":"https://api.github.com/repos/rspec/rspec-core/milestones{/number}","notifications_url":"https://api.github.com/repos/rspec/rspec-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rspec/rspec-core/labels{/name}","releases_url":"https://api.github.com/repos/rspec/rspec-core/releases{/id}","created_at":"2009-06-29T15:41:05Z","updated_at":"2015-02-26T06:45:47Z","pushed_at":"2015-02-26T06:45:47Z","git_url":"git://github.com/rspec/rspec-core.git","ssh_url":"git@github.com:rspec/rspec-core.git","clone_url":"https://github.com/rspec/rspec-core.git","svn_url":"https://github.com/rspec/rspec-core","homepage":"http://relishapp.com/rspec/rspec-core","size":20631,"stargazers_count":653,"watchers_count":653,"language":"Ruby","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":452,"mirror_url":null,"open_issues_count":21,"forks":452,"open_issues":21,"watchers":653,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rspec/rspec-core/pulls/1858"},"html":{"href":"https://github.com/rspec/rspec-core/pull/1858"},"issue":{"href":"https://api.github.com/repos/rspec/rspec-core/issues/1858"},"comments":{"href":"https://api.github.com/repos/rspec/rspec-core/issues/1858/comments"},"review_comments":{"href":"https://api.github.com/repos/rspec/rspec-core/pulls/1858/comments"},"review_comment":{"href":"https://api.github.com/repos/rspec/rspec-core/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rspec/rspec-core/pulls/1858/commits"},"statuses":{"href":"https://api.github.com/repos/rspec/rspec-core/statuses/1ed82dbabd6c5175c95b6b49a0b2899e5a5ca60d"}}}}