{"action":"created","comment":{"url":"https://api.github.com/repos/threadly/threadly/pulls/comments/42814622","id":42814622,"diff_hunk":"@@ -812,101 +731,204 @@ public int getCurrentRunningCount() {\n      * Ensures all threads have been started.  This will make new idle workers to accept tasks.\n      */\n     public void prestartAllThreads() {\n-      synchronized (workersLock) {\n-        boolean startedThreads = false;\n-        while (currentPoolSize < maxPoolSize) {\n-          availableWorkers.addFirst(makeNewWorker());\n-          startedThreads = true;\n-        }\n-        \n-        if (startedThreads) {\n-          workersLock.notifyAll();\n-        }\n-      }\n-    }\n-    \n-    /**\n-     * Call to get (or create, if able to) an idle worker.  If there are no idle workers available \n-     * then this call will block until one does become available.\n-     * \n-     * @return A worker ready to accept a task or {@code null} if the pool was shutdown\n-     * @throws InterruptedException Thrown if this thread is interrupted while waiting to acquire a worker\n-     */\n-    public Worker getWorker() throws InterruptedException {\n-      synchronized (workersLock) {\n-        while (availableWorkers.isEmpty() && ! shutdownFinishing) {\n-          if (currentPoolSize < maxPoolSize) {\n-            return makeNewWorker();\n-          } else {\n-            waitingForWorker = true;\n-            try {\n-              workersLock.wait();\n-            } finally {\n-              waitingForWorker = false;\n-            }\n-          }\n+      int casPoolSize;\n+      while ((casPoolSize = currentPoolSize.get()) < maxPoolSize) {\n+        if (currentPoolSize.compareAndSet(casPoolSize, casPoolSize + 1)) {\n+          makeNewWorker();\n         }\n-        \n-        return availableWorkers.poll();\n       }\n     }\n     \n     /**\n-     * This function REQUIRES that workersLock is synchronized before calling.  This call creates \n-     * a new worker, starts it, but does NOT add it as an available worker (so you can immediately \n-     * use it).  If you want this worker to be available for other tasks, it must be added to the \n-     * {@code availableWorkers} queue.\n-     * \n-     * @return Newly created worker, started and ready to accept work\n+     * This call creates and starts a new worker.  It does not modify {@link currentPoolSize} so \n+     * that MUST be updated in a thread safe way before this is invoked.  As soon as the worker \n+     * starts it will attempt to start taking tasks, no further action is needed.\n      */\n-    protected Worker makeNewWorker() {\n+    protected void makeNewWorker() {\n       Worker w = new Worker(this, threadFactory);\n-      currentPoolSize++;\n       w.start();\n-      \n-      // will be added to available workers when done with first task\n-      return w;\n     }\n     \n     /**\n-     * Shuts down the worker and ensures this now dead worker wont be used.\n+     * Adds a worker to the head of the idle worker chain.\n      * \n-     * @param w worker to shutdown\n+     * @param worker Worker that is ready to become idle\n      */\n-    protected void killWorker(Worker w) {\n-      // IMPORTANT** if this lock is removed, it is important to read the comment bellow\n-      synchronized (workersLock) {\n-        /* this will throw an exception if the worker has already stopped, \n-         * we want to ensure the pool size is not decremented more than once for a given worker.\n-         * \n-         * We are able to stop first, because we are holding the workers lock.  In the future if we \n-         * try to reduce locking around here, we need to ensure that the worker is removed from the \n-         * available workers BEFORE stopping.\n-         */\n-        w.stopIfRunning();\n-        currentPoolSize--;\n-        // it may not always be here, but it sometimes can (for example when a worker is interrupted)\n-        availableWorkers.remove(w);\n+    protected void addWorkerToIdleChain(Worker worker) {\n+      while (true) {\n+        Worker casWorker = idleWorker.get();\n+        // we can freely set this value until we get into the idle linked queue\n+        worker.nextIdleWorker = casWorker;\n+        if (idleWorker.compareAndSet(casWorker, worker)) {\n+          break;\n+        }\n       }\n     }\n-    \n+\n     /**\n-     * Called by the worker after it completes a task.  This is so that we can run any after task \n-     * cleanup, and make sure that the worker is now available for future tasks.\n+     * Invoked when a worker becomes idle.  This will provide another task for that worker, or \n+     * block until a task is either ready, or the worker should be shutdown (either because pool \n+     * was shut down, or max pool size changed).\n      * \n-     * @param worker worker that is now idle and ready for more tasks\n+     * @param worker Worker which is now idle and ready for a task\n+     * @return Task that is ready for immediate execution\n      */\n-    protected void workerDone(Worker worker) {\n-      synchronized (workersLock) {\n-        if (shutdownFinishing || currentPoolSize > maxPoolSize) {\n-          killWorker(worker);\n-        } else {\n-          // always add to the front so older workers are at the back\n-          availableWorkers.addFirst(worker);\n-          \n-          if (waitingForWorker) {\n-            workersLock.notify();\n+    public TaskWrapper workerIdle(Worker worker) {\n+      boolean interruptedChecked = false;\n+      boolean queued = false;\n+      try {\n+        idle: while (true) {\n+          int casPoolSize;\n+          if (shutdownFinishing) {\n+            currentPoolSize.decrementAndGet();\n+            worker.stopIfRunning();\n+            return null;\n+          } else if ((casPoolSize = currentPoolSize.get()) > maxPoolSize) {\n+            if (currentPoolSize.compareAndSet(casPoolSize, casPoolSize - 1)) {\n+              worker.stopIfRunning();\n+              return null;\n+            }\n+          } else {\n+            // ready to poll task, we loop here to avoid pool state recheck logic unless we have to block\n+            while (true) {\n+              TaskWrapper nextTask = queueManager.getNextTask();\n+              if (nextTask == null) {\n+                if (queued) {\n+                  // we can only park after we have queued, then checked again for a result\n+                  LockSupport.park();\n+                  continue idle;\n+                } else {\n+                  addWorkerToIdleChain(worker);\n+                  queued = true;\n+                }\n+              } else {\n+                /* TODO - right now this has a a deficiency where a recurring period task can cut in ","path":"src/main/java/org/threadly/concurrent/PriorityScheduler.java","position":611,"original_position":611,"commit_id":"10b6d473e4a5f831976dc766b0f6939da74e7371","original_commit_id":"10b6d473e4a5f831976dc766b0f6939da74e7371","user":{"login":"jentfoo","id":2165757,"avatar_url":"https://avatars.githubusercontent.com/u/2165757?v=3","gravatar_id":"","url":"https://api.github.com/users/jentfoo","html_url":"https://github.com/jentfoo","followers_url":"https://api.github.com/users/jentfoo/followers","following_url":"https://api.github.com/users/jentfoo/following{/other_user}","gists_url":"https://api.github.com/users/jentfoo/gists{/gist_id}","starred_url":"https://api.github.com/users/jentfoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jentfoo/subscriptions","organizations_url":"https://api.github.com/users/jentfoo/orgs","repos_url":"https://api.github.com/users/jentfoo/repos","events_url":"https://api.github.com/users/jentfoo/events{/privacy}","received_events_url":"https://api.github.com/users/jentfoo/received_events","type":"User","site_admin":false},"body":"This is a small deficiency, but one worth talking about.  Read the comment to see the details.  It's very unlikely because thread 2 would have to go so long without any time, but it is in theory fully possible.\r\n\r\nA worse version would be it's possible with fixedDelay tasks too, if thread 2 did not get CPU time for the entire period of the delay.  But the same failure would be possible in that way too.","created_at":"2015-10-22T22:28:25Z","updated_at":"2015-10-22T22:28:25Z","html_url":"https://github.com/threadly/threadly/pull/184#discussion_r42814622","pull_request_url":"https://api.github.com/repos/threadly/threadly/pulls/184","_links":{"self":{"href":"https://api.github.com/repos/threadly/threadly/pulls/comments/42814622"},"html":{"href":"https://github.com/threadly/threadly/pull/184#discussion_r42814622"},"pull_request":{"href":"https://api.github.com/repos/threadly/threadly/pulls/184"}}},"pull_request":{"url":"https://api.github.com/repos/threadly/threadly/pulls/184","id":48518974,"html_url":"https://github.com/threadly/threadly/pull/184","diff_url":"https://github.com/threadly/threadly/pull/184.diff","patch_url":"https://github.com/threadly/threadly/pull/184.patch","issue_url":"https://api.github.com/repos/threadly/threadly/issues/184","number":184,"state":"open","locked":false,"title":"PriorityScheduler: remove the queue manager thread","user":{"login":"jentfoo","id":2165757,"avatar_url":"https://avatars.githubusercontent.com/u/2165757?v=3","gravatar_id":"","url":"https://api.github.com/users/jentfoo","html_url":"https://github.com/jentfoo","followers_url":"https://api.github.com/users/jentfoo/followers","following_url":"https://api.github.com/users/jentfoo/following{/other_user}","gists_url":"https://api.github.com/users/jentfoo/gists{/gist_id}","starred_url":"https://api.github.com/users/jentfoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jentfoo/subscriptions","organizations_url":"https://api.github.com/users/jentfoo/orgs","repos_url":"https://api.github.com/users/jentfoo/repos","events_url":"https://api.github.com/users/jentfoo/events{/privacy}","received_events_url":"https://api.github.com/users/jentfoo/received_events","type":"User","site_admin":false},"body":"This is a large change that is really the sum of months of work.\r\nThis removes the queue manager thread.  Workers never move into an idle structure.  Instead each worker tries to aggressively take tasks.  A lot more similar to how java.util's schedulers work, but with quite a few complexities due to how threadly works.\r\nThis provides a massive performance improvement.  But also still has some problems I am yet to figure out solutions for (documented as TODO).\r\n\r\nBecause of the complexity and the risk of this change I will describe things in-line (sorry for the spam to all the watchers of the project).\r\n\r\nDon't merge this until we know we are all okay with everything.","created_at":"2015-10-22T22:00:19Z","updated_at":"2015-10-22T22:28:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a23c93c5b1a59458029c44c0bd78748e30f0d75c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/threadly/threadly/pulls/184/commits","review_comments_url":"https://api.github.com/repos/threadly/threadly/pulls/184/comments","review_comment_url":"https://api.github.com/repos/threadly/threadly/pulls/comments{/number}","comments_url":"https://api.github.com/repos/threadly/threadly/issues/184/comments","statuses_url":"https://api.github.com/repos/threadly/threadly/statuses/10b6d473e4a5f831976dc766b0f6939da74e7371","head":{"label":"jentfoo:PriorityScheduler-no_queue_manager_thread","ref":"PriorityScheduler-no_queue_manager_thread","sha":"10b6d473e4a5f831976dc766b0f6939da74e7371","user":{"login":"jentfoo","id":2165757,"avatar_url":"https://avatars.githubusercontent.com/u/2165757?v=3","gravatar_id":"","url":"https://api.github.com/users/jentfoo","html_url":"https://github.com/jentfoo","followers_url":"https://api.github.com/users/jentfoo/followers","following_url":"https://api.github.com/users/jentfoo/following{/other_user}","gists_url":"https://api.github.com/users/jentfoo/gists{/gist_id}","starred_url":"https://api.github.com/users/jentfoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jentfoo/subscriptions","organizations_url":"https://api.github.com/users/jentfoo/orgs","repos_url":"https://api.github.com/users/jentfoo/repos","events_url":"https://api.github.com/users/jentfoo/events{/privacy}","received_events_url":"https://api.github.com/users/jentfoo/received_events","type":"User","site_admin":false},"repo":{"id":11236509,"name":"threadly","full_name":"jentfoo/threadly","owner":{"login":"jentfoo","id":2165757,"avatar_url":"https://avatars.githubusercontent.com/u/2165757?v=3","gravatar_id":"","url":"https://api.github.com/users/jentfoo","html_url":"https://github.com/jentfoo","followers_url":"https://api.github.com/users/jentfoo/followers","following_url":"https://api.github.com/users/jentfoo/following{/other_user}","gists_url":"https://api.github.com/users/jentfoo/gists{/gist_id}","starred_url":"https://api.github.com/users/jentfoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jentfoo/subscriptions","organizations_url":"https://api.github.com/users/jentfoo/orgs","repos_url":"https://api.github.com/users/jentfoo/repos","events_url":"https://api.github.com/users/jentfoo/events{/privacy}","received_events_url":"https://api.github.com/users/jentfoo/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jentfoo/threadly","description":"A library of tools to assist with concurrent java development, with a large focus on testability.","fork":true,"url":"https://api.github.com/repos/jentfoo/threadly","forks_url":"https://api.github.com/repos/jentfoo/threadly/forks","keys_url":"https://api.github.com/repos/jentfoo/threadly/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jentfoo/threadly/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jentfoo/threadly/teams","hooks_url":"https://api.github.com/repos/jentfoo/threadly/hooks","issue_events_url":"https://api.github.com/repos/jentfoo/threadly/issues/events{/number}","events_url":"https://api.github.com/repos/jentfoo/threadly/events","assignees_url":"https://api.github.com/repos/jentfoo/threadly/assignees{/user}","branches_url":"https://api.github.com/repos/jentfoo/threadly/branches{/branch}","tags_url":"https://api.github.com/repos/jentfoo/threadly/tags","blobs_url":"https://api.github.com/repos/jentfoo/threadly/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jentfoo/threadly/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jentfoo/threadly/git/refs{/sha}","trees_url":"https://api.github.com/repos/jentfoo/threadly/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jentfoo/threadly/statuses/{sha}","languages_url":"https://api.github.com/repos/jentfoo/threadly/languages","stargazers_url":"https://api.github.com/repos/jentfoo/threadly/stargazers","contributors_url":"https://api.github.com/repos/jentfoo/threadly/contributors","subscribers_url":"https://api.github.com/repos/jentfoo/threadly/subscribers","subscription_url":"https://api.github.com/repos/jentfoo/threadly/subscription","commits_url":"https://api.github.com/repos/jentfoo/threadly/commits{/sha}","git_commits_url":"https://api.github.com/repos/jentfoo/threadly/git/commits{/sha}","comments_url":"https://api.github.com/repos/jentfoo/threadly/comments{/number}","issue_comment_url":"https://api.github.com/repos/jentfoo/threadly/issues/comments{/number}","contents_url":"https://api.github.com/repos/jentfoo/threadly/contents/{+path}","compare_url":"https://api.github.com/repos/jentfoo/threadly/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jentfoo/threadly/merges","archive_url":"https://api.github.com/repos/jentfoo/threadly/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jentfoo/threadly/downloads","issues_url":"https://api.github.com/repos/jentfoo/threadly/issues{/number}","pulls_url":"https://api.github.com/repos/jentfoo/threadly/pulls{/number}","milestones_url":"https://api.github.com/repos/jentfoo/threadly/milestones{/number}","notifications_url":"https://api.github.com/repos/jentfoo/threadly/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jentfoo/threadly/labels{/name}","releases_url":"https://api.github.com/repos/jentfoo/threadly/releases{/id}","created_at":"2013-07-07T16:52:34Z","updated_at":"2015-05-04T00:53:24Z","pushed_at":"2015-10-22T21:57:10Z","git_url":"git://github.com/jentfoo/threadly.git","ssh_url":"git@github.com:jentfoo/threadly.git","clone_url":"https://github.com/jentfoo/threadly.git","svn_url":"https://github.com/jentfoo/threadly","homepage":"threadly.org","size":6336,"stargazers_count":1,"watchers_count":1,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"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":"threadly:unstable_staging","ref":"unstable_staging","sha":"2b9efdfe0dd99595e0df2466f47420022c7bd78f","user":{"login":"threadly","id":4627301,"avatar_url":"https://avatars.githubusercontent.com/u/4627301?v=3","gravatar_id":"","url":"https://api.github.com/users/threadly","html_url":"https://github.com/threadly","followers_url":"https://api.github.com/users/threadly/followers","following_url":"https://api.github.com/users/threadly/following{/other_user}","gists_url":"https://api.github.com/users/threadly/gists{/gist_id}","starred_url":"https://api.github.com/users/threadly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/threadly/subscriptions","organizations_url":"https://api.github.com/users/threadly/orgs","repos_url":"https://api.github.com/users/threadly/repos","events_url":"https://api.github.com/users/threadly/events{/privacy}","received_events_url":"https://api.github.com/users/threadly/received_events","type":"Organization","site_admin":false},"repo":{"id":9874976,"name":"threadly","full_name":"threadly/threadly","owner":{"login":"threadly","id":4627301,"avatar_url":"https://avatars.githubusercontent.com/u/4627301?v=3","gravatar_id":"","url":"https://api.github.com/users/threadly","html_url":"https://github.com/threadly","followers_url":"https://api.github.com/users/threadly/followers","following_url":"https://api.github.com/users/threadly/following{/other_user}","gists_url":"https://api.github.com/users/threadly/gists{/gist_id}","starred_url":"https://api.github.com/users/threadly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/threadly/subscriptions","organizations_url":"https://api.github.com/users/threadly/orgs","repos_url":"https://api.github.com/users/threadly/repos","events_url":"https://api.github.com/users/threadly/events{/privacy}","received_events_url":"https://api.github.com/users/threadly/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/threadly/threadly","description":"A library of tools to assist with safe concurrent java development.  Providing a unique priority based thread pool, and ways to distrbute threaded work safely.","fork":false,"url":"https://api.github.com/repos/threadly/threadly","forks_url":"https://api.github.com/repos/threadly/threadly/forks","keys_url":"https://api.github.com/repos/threadly/threadly/keys{/key_id}","collaborators_url":"https://api.github.com/repos/threadly/threadly/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/threadly/threadly/teams","hooks_url":"https://api.github.com/repos/threadly/threadly/hooks","issue_events_url":"https://api.github.com/repos/threadly/threadly/issues/events{/number}","events_url":"https://api.github.com/repos/threadly/threadly/events","assignees_url":"https://api.github.com/repos/threadly/threadly/assignees{/user}","branches_url":"https://api.github.com/repos/threadly/threadly/branches{/branch}","tags_url":"https://api.github.com/repos/threadly/threadly/tags","blobs_url":"https://api.github.com/repos/threadly/threadly/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/threadly/threadly/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/threadly/threadly/git/refs{/sha}","trees_url":"https://api.github.com/repos/threadly/threadly/git/trees{/sha}","statuses_url":"https://api.github.com/repos/threadly/threadly/statuses/{sha}","languages_url":"https://api.github.com/repos/threadly/threadly/languages","stargazers_url":"https://api.github.com/repos/threadly/threadly/stargazers","contributors_url":"https://api.github.com/repos/threadly/threadly/contributors","subscribers_url":"https://api.github.com/repos/threadly/threadly/subscribers","subscription_url":"https://api.github.com/repos/threadly/threadly/subscription","commits_url":"https://api.github.com/repos/threadly/threadly/commits{/sha}","git_commits_url":"https://api.github.com/repos/threadly/threadly/git/commits{/sha}","comments_url":"https://api.github.com/repos/threadly/threadly/comments{/number}","issue_comment_url":"https://api.github.com/repos/threadly/threadly/issues/comments{/number}","contents_url":"https://api.github.com/repos/threadly/threadly/contents/{+path}","compare_url":"https://api.github.com/repos/threadly/threadly/compare/{base}...{head}","merges_url":"https://api.github.com/repos/threadly/threadly/merges","archive_url":"https://api.github.com/repos/threadly/threadly/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/threadly/threadly/downloads","issues_url":"https://api.github.com/repos/threadly/threadly/issues{/number}","pulls_url":"https://api.github.com/repos/threadly/threadly/pulls{/number}","milestones_url":"https://api.github.com/repos/threadly/threadly/milestones{/number}","notifications_url":"https://api.github.com/repos/threadly/threadly/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/threadly/threadly/labels{/name}","releases_url":"https://api.github.com/repos/threadly/threadly/releases{/id}","created_at":"2013-05-05T21:38:11Z","updated_at":"2015-10-10T15:08:06Z","pushed_at":"2015-10-22T22:00:20Z","git_url":"git://github.com/threadly/threadly.git","ssh_url":"git@github.com:threadly/threadly.git","clone_url":"https://github.com/threadly/threadly.git","svn_url":"https://github.com/threadly/threadly","homepage":"threadly.org","size":15061,"stargazers_count":42,"watchers_count":42,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":12,"mirror_url":null,"open_issues_count":9,"forks":12,"open_issues":9,"watchers":42,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/threadly/threadly/pulls/184"},"html":{"href":"https://github.com/threadly/threadly/pull/184"},"issue":{"href":"https://api.github.com/repos/threadly/threadly/issues/184"},"comments":{"href":"https://api.github.com/repos/threadly/threadly/issues/184/comments"},"review_comments":{"href":"https://api.github.com/repos/threadly/threadly/pulls/184/comments"},"review_comment":{"href":"https://api.github.com/repos/threadly/threadly/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/threadly/threadly/pulls/184/commits"},"statuses":{"href":"https://api.github.com/repos/threadly/threadly/statuses/10b6d473e4a5f831976dc766b0f6939da74e7371"}}}}