{"action":"created","comment":{"url":"https://api.github.com/repos/rspec/rspec-core/pulls/comments/25533481","id":25533481,"diff_hunk":"@@ -0,0 +1,93 @@\n+class ThreadOrderSupport\n+  def initialize\n+    @bodies  = {}\n+    @threads = []\n+    @queue   = [] # we may not have thread stdlib required, so may not have Queue class","path":"spec/support/thread_order_support.rb","position":5,"original_position":5,"commit_id":"1ed82dbabd6c5175c95b6b49a0b2899e5a5ca60d","original_commit_id":"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},"body":"I'm going to have to look into this in more detail this weekend, I'm clearly confused about how it works, as I'd assume this would cause library wide checks to fail.","created_at":"2015-02-27T19:48:44Z","updated_at":"2015-02-27T19:48:44Z","html_url":"https://github.com/rspec/rspec-core/pull/1858#discussion_r25533481","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/25533481"},"html":{"href":"https://github.com/rspec/rspec-core/pull/1858#discussion_r25533481"},"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-27T19:48:44Z","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-27T01:16:03Z","pushed_at":"2015-02-27T07:50:58Z","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":654,"watchers_count":654,"language":"Ruby","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":452,"mirror_url":null,"open_issues_count":22,"forks":452,"open_issues":22,"watchers":654,"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"}}}}