{"action":"created","comment":{"url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/comments/36122058","id":36122058,"diff_hunk":"@@ -0,0 +1,175 @@\n+/*\r\n+ * Copyright (c) 2007-2014 Sonatype, Inc. All rights reserved.\r\n+ *\r\n+ * This program is licensed to you under the Apache License Version 2.0,\r\n+ * and you may not use this file except in compliance with the Apache License Version 2.0.\r\n+ * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.\r\n+ *\r\n+ * Unless required by applicable law or agreed to in writing,\r\n+ * software distributed under the Apache License Version 2.0 is distributed on an\r\n+ * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.\r\n+ */\r\n+package org.sonatype.sisu.litmus.testsupport.concurrent;\r\n+\r\n+import java.util.ArrayList;\r\n+import java.util.List;\r\n+import java.util.concurrent.CyclicBarrier;\r\n+import java.util.concurrent.ExecutorService;\r\n+import java.util.concurrent.Executors;\r\n+import java.util.concurrent.Future;\r\n+import java.util.concurrent.TimeUnit;\r\n+import java.util.concurrent.atomic.AtomicBoolean;\r\n+import java.util.concurrent.atomic.AtomicInteger;\r\n+\r\n+import com.google.common.base.Preconditions;\r\n+import com.google.common.base.Throwables;\r\n+import org.slf4j.Logger;\r\n+import org.slf4j.LoggerFactory;\r\n+\r\n+/**\r\n+ * Executes a batch of {@link ConcurrentTask}s concurrently for testing purposes.\r\n+ *\r\n+ * @since 1.15\r\n+ */\r\n+public class ConcurrentRunner\r\n+{\r\n+  private List<ConcurrentTask> tasks = new ArrayList<>();\r\n+\r\n+  // A cyclic barrier ensures that the threads align after each unit of work, to avoid some completing much before\r\n+  // the others, thereby increasing contention\r\n+  private CyclicBarrier startingGun;\r\n+\r\n+  // A signal to get test workers to abort if a peer finds a failure\r\n+  private final AtomicBoolean failureHasOccurred = new AtomicBoolean(false);\r\n+\r\n+  private final int iterations;\r\n+\r\n+  private final int iterationTimeoutSeconds;\r\n+\r\n+  private AtomicInteger runInvocations = new AtomicInteger(0);\r\n+\r\n+  private final Logger log = Preconditions.checkNotNull(LoggerFactory.getLogger(getClass()));\r\n+\r\n+  /**\r\n+   * @param iterations              How many times each task should be invoked\r\n+   * @param iterationTimeoutSeconds How long each iteration is given for all tasks to complete\r\n+   */\r\n+  public ConcurrentRunner(int iterations, int iterationTimeoutSeconds) {\r\n+    this.iterations = iterations;\r\n+    this.iterationTimeoutSeconds = iterationTimeoutSeconds;\r\n+  }\r\n+\r\n+  /**\r\n+   * Adds a task to the runner, which will be executed 'iterations' times by a single thread.\r\n+   */\r\n+  public void addTask(final ConcurrentTask task) {\r\n+    addTask(1, task);\r\n+  }\r\n+\r\n+  /**\r\n+   * Adds one or more instances of a task to the runner. The task isn't cloned but used by multiple threads, and so\r\n+   * tasks provided to this method should be stateless and thread-safe.\r\n+   */\r\n+  public void addTask(final int instances, final ConcurrentTask task)\r\n+  {\r\n+    Preconditions.checkArgument(instances >= 1);\r\n+    for (int i = 0; i < instances; i++) {\r\n+      tasks.add(task);\r\n+    }\r\n+  }\r\n+\r\n+  /**\r\n+   * Invokes the tasks 'iteration' times.\r\n+   *\r\n+   * @throws Exception an exception thrown by one of the tasks\r\n+   */\r\n+  public void go() throws Exception {\r\n+    runInvocations.set(0);\r\n+\r\n+    startingGun = new CyclicBarrier(tasks.size(), new Runnable()\r\n+    {\r\n+      int iterationCount = 0;\r\n+\r\n+      @Override\r\n+      public void run() {\r\n+        log.debug(\"Iteration starting: {}\", iterationCount++);\r\n+      }\r\n+    });\r\n+\r\n+    List<ConcurrentTestWorker> testWorkers = buildTaskWorkers();\r\n+\r\n+    final List<Future<Void>> taskResults = executeTasksConcurrently(testWorkers);\r\n+\r\n+    failIfTasksWereCancelled(taskResults);\r\n+\r\n+    failIfTasksCaughtExceptions(testWorkers);\r\n+  }\r\n+\r\n+  private List<ConcurrentTestWorker> buildTaskWorkers() {\r\n+    List<ConcurrentTestWorker> testWorkers = new ArrayList<>();\r\n+\r\n+    final ConcurrentTestContext context = new ConcurrentTestContext(startingGun, failureHasOccurred,\r\n+        iterations, runInvocations, iterationTimeoutSeconds);\r\n+\r\n+    for (int workerNumber = 0; workerNumber < tasks.size(); workerNumber++) {\r\n+      final ConcurrentTestWorker worker = new ConcurrentTestWorker(workerNumber, tasks.get(workerNumber), context);\r\n+      testWorkers.add(worker);\r\n+    }\r\n+    return testWorkers;\r\n+  }\r\n+\r\n+  private List<Future<Void>> executeTasksConcurrently(final List<ConcurrentTestWorker> testWorkers)\r\n+      throws InterruptedException\r\n+  {\r\n+    ExecutorService service = Executors.newFixedThreadPool(tasks.size());\r\n+    final List<Future<Void>> futures = service\r","path":"testsupport/src/main/java/org/sonatype/sisu/litmus/testsupport/concurrent/ConcurrentRunner.java","position":126,"original_position":126,"commit_id":"5dd4b3feaec9b277b2f0f09f21ca389e49e9127d","original_commit_id":"412f49f015440f082130d9cd8aaaccd00e45ea1d","user":{"login":"mrprescott","id":2325642,"avatar_url":"https://avatars.githubusercontent.com/u/2325642?v=3","gravatar_id":"","url":"https://api.github.com/users/mrprescott","html_url":"https://github.com/mrprescott","followers_url":"https://api.github.com/users/mrprescott/followers","following_url":"https://api.github.com/users/mrprescott/following{/other_user}","gists_url":"https://api.github.com/users/mrprescott/gists{/gist_id}","starred_url":"https://api.github.com/users/mrprescott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrprescott/subscriptions","organizations_url":"https://api.github.com/users/mrprescott/orgs","repos_url":"https://api.github.com/users/mrprescott/repos","events_url":"https://api.github.com/users/mrprescott/events{/privacy}","received_events_url":"https://api.github.com/users/mrprescott/received_events","type":"User","site_admin":false},"body":"fixed","created_at":"2015-08-03T19:35:28Z","updated_at":"2015-08-03T19:35:28Z","html_url":"https://github.com/sonatype/sisu-goodies/pull/40#discussion_r36122058","pull_request_url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40","_links":{"self":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/comments/36122058"},"html":{"href":"https://github.com/sonatype/sisu-goodies/pull/40#discussion_r36122058"},"pull_request":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40"}}},"pull_request":{"url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40","id":41475545,"html_url":"https://github.com/sonatype/sisu-goodies/pull/40","diff_url":"https://github.com/sonatype/sisu-goodies/pull/40.diff","patch_url":"https://github.com/sonatype/sisu-goodies/pull/40.patch","issue_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues/40","number":40,"state":"open","locked":false,"title":"Concurrency support","user":{"login":"mrprescott","id":2325642,"avatar_url":"https://avatars.githubusercontent.com/u/2325642?v=3","gravatar_id":"","url":"https://api.github.com/users/mrprescott","html_url":"https://github.com/mrprescott","followers_url":"https://api.github.com/users/mrprescott/followers","following_url":"https://api.github.com/users/mrprescott/following{/other_user}","gists_url":"https://api.github.com/users/mrprescott/gists{/gist_id}","starred_url":"https://api.github.com/users/mrprescott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrprescott/subscriptions","organizations_url":"https://api.github.com/users/mrprescott/orgs","repos_url":"https://api.github.com/users/mrprescott/repos","events_url":"https://api.github.com/users/mrprescott/events{/privacy}","received_events_url":"https://api.github.com/users/mrprescott/received_events","type":"User","site_admin":false},"body":"This the concurrency test-support code extracted from https://github.com/sonatype/nexus-oss/pull/1622","created_at":"2015-08-03T14:58:05Z","updated_at":"2015-08-03T19:35:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4500ee38f57b5f4b378f61a2f44dc6aabcd375c5","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40/commits","review_comments_url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40/comments","review_comment_url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues/40/comments","statuses_url":"https://api.github.com/repos/sonatype/sisu-goodies/statuses/5dd4b3feaec9b277b2f0f09f21ca389e49e9127d","head":{"label":"sonatype:concurrency-support","ref":"concurrency-support","sha":"5dd4b3feaec9b277b2f0f09f21ca389e49e9127d","user":{"login":"sonatype","id":44938,"avatar_url":"https://avatars.githubusercontent.com/u/44938?v=3","gravatar_id":"","url":"https://api.github.com/users/sonatype","html_url":"https://github.com/sonatype","followers_url":"https://api.github.com/users/sonatype/followers","following_url":"https://api.github.com/users/sonatype/following{/other_user}","gists_url":"https://api.github.com/users/sonatype/gists{/gist_id}","starred_url":"https://api.github.com/users/sonatype/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonatype/subscriptions","organizations_url":"https://api.github.com/users/sonatype/orgs","repos_url":"https://api.github.com/users/sonatype/repos","events_url":"https://api.github.com/users/sonatype/events{/privacy}","received_events_url":"https://api.github.com/users/sonatype/received_events","type":"Organization","site_admin":false},"repo":{"id":2698192,"name":"sisu-goodies","full_name":"sonatype/sisu-goodies","owner":{"login":"sonatype","id":44938,"avatar_url":"https://avatars.githubusercontent.com/u/44938?v=3","gravatar_id":"","url":"https://api.github.com/users/sonatype","html_url":"https://github.com/sonatype","followers_url":"https://api.github.com/users/sonatype/followers","following_url":"https://api.github.com/users/sonatype/following{/other_user}","gists_url":"https://api.github.com/users/sonatype/gists{/gist_id}","starred_url":"https://api.github.com/users/sonatype/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonatype/subscriptions","organizations_url":"https://api.github.com/users/sonatype/orgs","repos_url":"https://api.github.com/users/sonatype/repos","events_url":"https://api.github.com/users/sonatype/events{/privacy}","received_events_url":"https://api.github.com/users/sonatype/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sonatype/sisu-goodies","description":"","fork":false,"url":"https://api.github.com/repos/sonatype/sisu-goodies","forks_url":"https://api.github.com/repos/sonatype/sisu-goodies/forks","keys_url":"https://api.github.com/repos/sonatype/sisu-goodies/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sonatype/sisu-goodies/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sonatype/sisu-goodies/teams","hooks_url":"https://api.github.com/repos/sonatype/sisu-goodies/hooks","issue_events_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues/events{/number}","events_url":"https://api.github.com/repos/sonatype/sisu-goodies/events","assignees_url":"https://api.github.com/repos/sonatype/sisu-goodies/assignees{/user}","branches_url":"https://api.github.com/repos/sonatype/sisu-goodies/branches{/branch}","tags_url":"https://api.github.com/repos/sonatype/sisu-goodies/tags","blobs_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/refs{/sha}","trees_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sonatype/sisu-goodies/statuses/{sha}","languages_url":"https://api.github.com/repos/sonatype/sisu-goodies/languages","stargazers_url":"https://api.github.com/repos/sonatype/sisu-goodies/stargazers","contributors_url":"https://api.github.com/repos/sonatype/sisu-goodies/contributors","subscribers_url":"https://api.github.com/repos/sonatype/sisu-goodies/subscribers","subscription_url":"https://api.github.com/repos/sonatype/sisu-goodies/subscription","commits_url":"https://api.github.com/repos/sonatype/sisu-goodies/commits{/sha}","git_commits_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/commits{/sha}","comments_url":"https://api.github.com/repos/sonatype/sisu-goodies/comments{/number}","issue_comment_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues/comments{/number}","contents_url":"https://api.github.com/repos/sonatype/sisu-goodies/contents/{+path}","compare_url":"https://api.github.com/repos/sonatype/sisu-goodies/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sonatype/sisu-goodies/merges","archive_url":"https://api.github.com/repos/sonatype/sisu-goodies/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sonatype/sisu-goodies/downloads","issues_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues{/number}","pulls_url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls{/number}","milestones_url":"https://api.github.com/repos/sonatype/sisu-goodies/milestones{/number}","notifications_url":"https://api.github.com/repos/sonatype/sisu-goodies/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sonatype/sisu-goodies/labels{/name}","releases_url":"https://api.github.com/repos/sonatype/sisu-goodies/releases{/id}","created_at":"2011-11-02T21:23:34Z","updated_at":"2015-04-13T10:52:18Z","pushed_at":"2015-08-03T19:17:26Z","git_url":"git://github.com/sonatype/sisu-goodies.git","ssh_url":"git@github.com:sonatype/sisu-goodies.git","clone_url":"https://github.com/sonatype/sisu-goodies.git","svn_url":"https://github.com/sonatype/sisu-goodies","homepage":"","size":1965,"stargazers_count":3,"watchers_count":3,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":3,"default_branch":"master"}},"base":{"label":"sonatype:master","ref":"master","sha":"13b3fea645fec693c8b180fbbd3f365a472d9bc7","user":{"login":"sonatype","id":44938,"avatar_url":"https://avatars.githubusercontent.com/u/44938?v=3","gravatar_id":"","url":"https://api.github.com/users/sonatype","html_url":"https://github.com/sonatype","followers_url":"https://api.github.com/users/sonatype/followers","following_url":"https://api.github.com/users/sonatype/following{/other_user}","gists_url":"https://api.github.com/users/sonatype/gists{/gist_id}","starred_url":"https://api.github.com/users/sonatype/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonatype/subscriptions","organizations_url":"https://api.github.com/users/sonatype/orgs","repos_url":"https://api.github.com/users/sonatype/repos","events_url":"https://api.github.com/users/sonatype/events{/privacy}","received_events_url":"https://api.github.com/users/sonatype/received_events","type":"Organization","site_admin":false},"repo":{"id":2698192,"name":"sisu-goodies","full_name":"sonatype/sisu-goodies","owner":{"login":"sonatype","id":44938,"avatar_url":"https://avatars.githubusercontent.com/u/44938?v=3","gravatar_id":"","url":"https://api.github.com/users/sonatype","html_url":"https://github.com/sonatype","followers_url":"https://api.github.com/users/sonatype/followers","following_url":"https://api.github.com/users/sonatype/following{/other_user}","gists_url":"https://api.github.com/users/sonatype/gists{/gist_id}","starred_url":"https://api.github.com/users/sonatype/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonatype/subscriptions","organizations_url":"https://api.github.com/users/sonatype/orgs","repos_url":"https://api.github.com/users/sonatype/repos","events_url":"https://api.github.com/users/sonatype/events{/privacy}","received_events_url":"https://api.github.com/users/sonatype/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sonatype/sisu-goodies","description":"","fork":false,"url":"https://api.github.com/repos/sonatype/sisu-goodies","forks_url":"https://api.github.com/repos/sonatype/sisu-goodies/forks","keys_url":"https://api.github.com/repos/sonatype/sisu-goodies/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sonatype/sisu-goodies/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sonatype/sisu-goodies/teams","hooks_url":"https://api.github.com/repos/sonatype/sisu-goodies/hooks","issue_events_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues/events{/number}","events_url":"https://api.github.com/repos/sonatype/sisu-goodies/events","assignees_url":"https://api.github.com/repos/sonatype/sisu-goodies/assignees{/user}","branches_url":"https://api.github.com/repos/sonatype/sisu-goodies/branches{/branch}","tags_url":"https://api.github.com/repos/sonatype/sisu-goodies/tags","blobs_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/refs{/sha}","trees_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sonatype/sisu-goodies/statuses/{sha}","languages_url":"https://api.github.com/repos/sonatype/sisu-goodies/languages","stargazers_url":"https://api.github.com/repos/sonatype/sisu-goodies/stargazers","contributors_url":"https://api.github.com/repos/sonatype/sisu-goodies/contributors","subscribers_url":"https://api.github.com/repos/sonatype/sisu-goodies/subscribers","subscription_url":"https://api.github.com/repos/sonatype/sisu-goodies/subscription","commits_url":"https://api.github.com/repos/sonatype/sisu-goodies/commits{/sha}","git_commits_url":"https://api.github.com/repos/sonatype/sisu-goodies/git/commits{/sha}","comments_url":"https://api.github.com/repos/sonatype/sisu-goodies/comments{/number}","issue_comment_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues/comments{/number}","contents_url":"https://api.github.com/repos/sonatype/sisu-goodies/contents/{+path}","compare_url":"https://api.github.com/repos/sonatype/sisu-goodies/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sonatype/sisu-goodies/merges","archive_url":"https://api.github.com/repos/sonatype/sisu-goodies/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sonatype/sisu-goodies/downloads","issues_url":"https://api.github.com/repos/sonatype/sisu-goodies/issues{/number}","pulls_url":"https://api.github.com/repos/sonatype/sisu-goodies/pulls{/number}","milestones_url":"https://api.github.com/repos/sonatype/sisu-goodies/milestones{/number}","notifications_url":"https://api.github.com/repos/sonatype/sisu-goodies/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sonatype/sisu-goodies/labels{/name}","releases_url":"https://api.github.com/repos/sonatype/sisu-goodies/releases{/id}","created_at":"2011-11-02T21:23:34Z","updated_at":"2015-04-13T10:52:18Z","pushed_at":"2015-08-03T19:17:26Z","git_url":"git://github.com/sonatype/sisu-goodies.git","ssh_url":"git@github.com:sonatype/sisu-goodies.git","clone_url":"https://github.com/sonatype/sisu-goodies.git","svn_url":"https://github.com/sonatype/sisu-goodies","homepage":"","size":1965,"stargazers_count":3,"watchers_count":3,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40"},"html":{"href":"https://github.com/sonatype/sisu-goodies/pull/40"},"issue":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/issues/40"},"comments":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/issues/40/comments"},"review_comments":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40/comments"},"review_comment":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/pulls/40/commits"},"statuses":{"href":"https://api.github.com/repos/sonatype/sisu-goodies/statuses/5dd4b3feaec9b277b2f0f09f21ca389e49e9127d"}}}}