{"action":"created","comment":{"url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/comments/22519360","id":22519360,"diff_hunk":"@@ -0,0 +1,231 @@\n+package plugins.WebOfTrust.util;\n+\n+/**\n+ * Implementation of delayed background jobs where {@link #triggerExecution} blocks until the\n+ * background job has finished, as if the background job was performed synchronously. This\n+ * class is intended to be used in unit tests, and is likely not very useful for general purposes.\n+ *\n+ * @author bertm\n+ */\n+public class SynchronousDelayedBackgroundJob implements DelayedBackgroundJob {\n+    /** Constant deadline value indicating no set {@link #nextRunDeadline deadline}, greater than\n+     * all deadlines. */\n+    private static final long NO_DEADLINE = Long.MAX_VALUE;\n+\n+    /** The actual background job to run. */\n+    private final Runnable job;\n+    /** The name of the background job, used to set the Thread's name. */\n+    private final String name;\n+    /** The default trigger aggregation delay for {@link #triggerExecution()}. */\n+    private final long defaultDelay;\n+\n+    /** Flag for termination, set after first call to {@link #terminate()}, never unset. */\n+    private boolean isTerminating = false;\n+    /** The currently running job thread, if any. */\n+    private Thread runningJobThread = null;\n+    /** The number of completed runs, assumed to never overflow. */\n+    private long runCount = 0;\n+    /** Whether the next run has already been scheduled. */\n+    private boolean nextRunScheduled = false;\n+    /** The deadline for the next run, if any (always {@link #NO_DEADLINE} if\n+     * {@link #nextRunScheduled} is {@code false}. */\n+    private long nextRunDeadline = NO_DEADLINE;\n+\n+    /**\n+     * Constructs a delayed background job with the given default delay. Negative delays\n+     * are treated as zero delay.\n+     * When this background job is {@link BackgroundJob#terminate() terminated}, the running job\n+     * will be notified by means of interruption of its thread. Hence, the job implementer must take\n+     * care not to swallow {@link InterruptedException}.\n+     * @param job the job to run\n+     * @param name the human-readable name of the job\n+     * @param delay the default background job aggregation delay in milliseconds\n+     */\n+    public SynchronousDelayedBackgroundJob(Runnable job, String name, long delay) {\n+        this.job = job;\n+        this.name = name;\n+        this.defaultDelay = delay;\n+    }\n+\n+    /**\n+     * Triggers the execution of the job after either aggregating triggers for at most the default\n+     * delay, or as soon as the currently running job finishes, whichever comes last.\n+     * If this background job was terminating or has terminated prior to the invocation of this\n+     * method, this method has no effect and returns immediately.\n+     * This method waits for the first job to start after the trigger to finish before returning,\n+     * except when this method is called from the background job itself, in that case this method\n+     * does not wait for the job to finish (otherwise a deadlock would be the result).\n+     * If the calling thread is interrupted while waiting for the job to finish execution, this\n+     * method will return as soon as possible, without waiting for the job to finish, and sets the\n+     * thread interruption flag.\n+     */\n+    @Override\n+    public synchronized void triggerExecution() {\n+        triggerExecution(defaultDelay);\n+    }\n+\n+    /**\n+     * Triggers the execution of the job after either aggregating triggers for at most the given\n+     * delay, or as soon as the currently running job finishes, whichever comes last. Negative\n+     * delays are treated as to zero delays.\n+     * If this background job was terminating or has terminated prior to the invocation of this\n+     * method, this method has no effect and returns immediately.\n+     * This method waits for the first job to start after the trigger to finish before returning,\n+     * except when this method is called from the background job itself, in that case this method\n+     * does not wait for the job to finish (otherwise a deadlock would be the result).\n+     * If the calling thread is interrupted while waiting for the job to finish execution, this\n+     * method will return as soon as possible, without waiting for the job to finish, and sets the\n+     * thread interruption flag.\n+     * @param delay the maximum trigger aggregation delay in milliseconds\n+     */\n+    @Override\n+    public synchronized void triggerExecution(long delay) {\n+        if (isTerminating) {\n+            return;\n+        }\n+        triggerExecutionAsynchronously(delay);\n+        // When called from the job thread, don't wait for the job to finish: this would cause a\n+        // deadlock.\n+        if (runningJobThread == Thread.currentThread()) {\n+            return;\n+        }\n+        // Wait for the relevant execution to complete.\n+        long waitForRunCount;\n+        if (runningJobThread == null) {\n+            // Wait for next run to finish\n+            waitForRunCount = runCount + 1;\n+        } else {\n+            // Wait for next run after current run to finish\n+            waitForRunCount = runCount + 2;\n+        }\n+        while (runCount < waitForRunCount) {\n+            try {\n+                wait();","path":"src/plugins/WebOfTrust/util/SynchronousDelayedBackgroundJob.java","position":103,"original_position":103,"commit_id":"cfbe9813161cec66a10b6deb92083376060ce89f","original_commit_id":"cfbe9813161cec66a10b6deb92083376060ce89f","user":{"login":"xor-freenet","id":78502,"avatar_url":"https://avatars.githubusercontent.com/u/78502?v=3","gravatar_id":"","url":"https://api.github.com/users/xor-freenet","html_url":"https://github.com/xor-freenet","followers_url":"https://api.github.com/users/xor-freenet/followers","following_url":"https://api.github.com/users/xor-freenet/following{/other_user}","gists_url":"https://api.github.com/users/xor-freenet/gists{/gist_id}","starred_url":"https://api.github.com/users/xor-freenet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xor-freenet/subscriptions","organizations_url":"https://api.github.com/users/xor-freenet/orgs","repos_url":"https://api.github.com/users/xor-freenet/repos","events_url":"https://api.github.com/users/xor-freenet/events{/privacy}","received_events_url":"https://api.github.com/users/xor-freenet/received_events","type":"User","site_admin":false},"body":"Not a blocker, just a suggestion. Also, this is probably best read AFTER all comments upon this class because then you'll already know the current shortcomings.\r\nTo simplify the whole of this class, how about that:\r\n- Make it internally use a TickerDelayedBackgroundJob for its whole affairs instead of the current homebrew code.\r\n- To allow waiting for the job execution to finish here - which is the main difference to the TickerDelayedBackgroundJob -  wrap the Runnable which is fed to the TickerDelayedBackgroundJob inside of another Runnable which does the job of notifying the wait() here.\r\n- Make sure to also adapt terminate() to notify() the wait() here.\r\n\r\n","created_at":"2015-01-06T11:40:12Z","updated_at":"2015-01-06T11:40:12Z","html_url":"https://github.com/freenet/plugin-WebOfTrust/pull/26#discussion_r22519360","pull_request_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26","_links":{"self":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/comments/22519360"},"html":{"href":"https://github.com/freenet/plugin-WebOfTrust/pull/26#discussion_r22519360"},"pull_request":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26"}}},"pull_request":{"url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26","id":25122908,"html_url":"https://github.com/freenet/plugin-WebOfTrust/pull/26","diff_url":"https://github.com/freenet/plugin-WebOfTrust/pull/26.diff","patch_url":"https://github.com/freenet/plugin-WebOfTrust/pull/26.patch","issue_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/issues/26","number":26,"state":"open","locked":false,"title":"Background jobs","user":{"login":"bertm","id":323319,"avatar_url":"https://avatars.githubusercontent.com/u/323319?v=3","gravatar_id":"","url":"https://api.github.com/users/bertm","html_url":"https://github.com/bertm","followers_url":"https://api.github.com/users/bertm/followers","following_url":"https://api.github.com/users/bertm/following{/other_user}","gists_url":"https://api.github.com/users/bertm/gists{/gist_id}","starred_url":"https://api.github.com/users/bertm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bertm/subscriptions","organizations_url":"https://api.github.com/users/bertm/orgs","repos_url":"https://api.github.com/users/bertm/repos","events_url":"https://api.github.com/users/bertm/events{/privacy}","received_events_url":"https://api.github.com/users/bertm/received_events","type":"User","site_admin":false},"body":"This changeset introduces an abstraction for (maintenance) jobs running in the background, of which execution may be triggered from other threads. SubscriptionManager could very well use this, but I haven't made it do so yet.\r\n\r\n~~@xor-freenet Unit tests are not complete, I'll be finishing those tomorrow. I'm filing this pull request early so you can have a look at it already.~~","created_at":"2014-11-27T00:28:15Z","updated_at":"2015-01-06T11:40:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"72dc4b9d01acad76ba0e627ddab03dc7979a1701","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26/commits","review_comments_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26/comments","review_comment_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/comments/{number}","comments_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/issues/26/comments","statuses_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/statuses/cfbe9813161cec66a10b6deb92083376060ce89f","head":{"label":"bertm:background-jobs","ref":"background-jobs","sha":"cfbe9813161cec66a10b6deb92083376060ce89f","user":{"login":"bertm","id":323319,"avatar_url":"https://avatars.githubusercontent.com/u/323319?v=3","gravatar_id":"","url":"https://api.github.com/users/bertm","html_url":"https://github.com/bertm","followers_url":"https://api.github.com/users/bertm/followers","following_url":"https://api.github.com/users/bertm/following{/other_user}","gists_url":"https://api.github.com/users/bertm/gists{/gist_id}","starred_url":"https://api.github.com/users/bertm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bertm/subscriptions","organizations_url":"https://api.github.com/users/bertm/orgs","repos_url":"https://api.github.com/users/bertm/repos","events_url":"https://api.github.com/users/bertm/events{/privacy}","received_events_url":"https://api.github.com/users/bertm/received_events","type":"User","site_admin":false},"repo":{"id":26762111,"name":"plugin-WebOfTrust","full_name":"bertm/plugin-WebOfTrust","owner":{"login":"bertm","id":323319,"avatar_url":"https://avatars.githubusercontent.com/u/323319?v=3","gravatar_id":"","url":"https://api.github.com/users/bertm","html_url":"https://github.com/bertm","followers_url":"https://api.github.com/users/bertm/followers","following_url":"https://api.github.com/users/bertm/following{/other_user}","gists_url":"https://api.github.com/users/bertm/gists{/gist_id}","starred_url":"https://api.github.com/users/bertm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bertm/subscriptions","organizations_url":"https://api.github.com/users/bertm/orgs","repos_url":"https://api.github.com/users/bertm/repos","events_url":"https://api.github.com/users/bertm/events{/privacy}","received_events_url":"https://api.github.com/users/bertm/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bertm/plugin-WebOfTrust","description":"WoT plugin","fork":true,"url":"https://api.github.com/repos/bertm/plugin-WebOfTrust","forks_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/forks","keys_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/teams","hooks_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/hooks","issue_events_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/issues/events{/number}","events_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/events","assignees_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/assignees{/user}","branches_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/branches{/branch}","tags_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/tags","blobs_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/git/refs{/sha}","trees_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/statuses/{sha}","languages_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/languages","stargazers_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/stargazers","contributors_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/contributors","subscribers_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/subscribers","subscription_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/subscription","commits_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/commits{/sha}","git_commits_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/git/commits{/sha}","comments_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/comments{/number}","issue_comment_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/issues/comments/{number}","contents_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/contents/{+path}","compare_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/merges","archive_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/downloads","issues_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/issues{/number}","pulls_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/pulls{/number}","milestones_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/milestones{/number}","notifications_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/labels{/name}","releases_url":"https://api.github.com/repos/bertm/plugin-WebOfTrust/releases{/id}","created_at":"2014-11-17T15:10:31Z","updated_at":"2014-11-17T15:10:32Z","pushed_at":"2014-11-30T21:12:06Z","git_url":"git://github.com/bertm/plugin-WebOfTrust.git","ssh_url":"git@github.com:bertm/plugin-WebOfTrust.git","clone_url":"https://github.com/bertm/plugin-WebOfTrust.git","svn_url":"https://github.com/bertm/plugin-WebOfTrust","homepage":"https://freenetproject.org/","size":7484,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"freenet:master","ref":"master","sha":"edf1e4bd51abd10b13a73a2d6545ec4b524b4319","user":{"login":"freenet","id":77513,"avatar_url":"https://avatars.githubusercontent.com/u/77513?v=3","gravatar_id":"","url":"https://api.github.com/users/freenet","html_url":"https://github.com/freenet","followers_url":"https://api.github.com/users/freenet/followers","following_url":"https://api.github.com/users/freenet/following{/other_user}","gists_url":"https://api.github.com/users/freenet/gists{/gist_id}","starred_url":"https://api.github.com/users/freenet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/freenet/subscriptions","organizations_url":"https://api.github.com/users/freenet/orgs","repos_url":"https://api.github.com/users/freenet/repos","events_url":"https://api.github.com/users/freenet/events{/privacy}","received_events_url":"https://api.github.com/users/freenet/received_events","type":"Organization","site_admin":false},"repo":{"id":185075,"name":"plugin-WebOfTrust","full_name":"freenet/plugin-WebOfTrust","owner":{"login":"freenet","id":77513,"avatar_url":"https://avatars.githubusercontent.com/u/77513?v=3","gravatar_id":"","url":"https://api.github.com/users/freenet","html_url":"https://github.com/freenet","followers_url":"https://api.github.com/users/freenet/followers","following_url":"https://api.github.com/users/freenet/following{/other_user}","gists_url":"https://api.github.com/users/freenet/gists{/gist_id}","starred_url":"https://api.github.com/users/freenet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/freenet/subscriptions","organizations_url":"https://api.github.com/users/freenet/orgs","repos_url":"https://api.github.com/users/freenet/repos","events_url":"https://api.github.com/users/freenet/events{/privacy}","received_events_url":"https://api.github.com/users/freenet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/freenet/plugin-WebOfTrust","description":"WoT plugin","fork":false,"url":"https://api.github.com/repos/freenet/plugin-WebOfTrust","forks_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/forks","keys_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/keys{/key_id}","collaborators_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/teams","hooks_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/hooks","issue_events_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/issues/events{/number}","events_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/events","assignees_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/assignees{/user}","branches_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/branches{/branch}","tags_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/tags","blobs_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/git/refs{/sha}","trees_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/git/trees{/sha}","statuses_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/statuses/{sha}","languages_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/languages","stargazers_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/stargazers","contributors_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/contributors","subscribers_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/subscribers","subscription_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/subscription","commits_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/commits{/sha}","git_commits_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/git/commits{/sha}","comments_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/comments{/number}","issue_comment_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/issues/comments/{number}","contents_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/contents/{+path}","compare_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/compare/{base}...{head}","merges_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/merges","archive_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/downloads","issues_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/issues{/number}","pulls_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls{/number}","milestones_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/milestones{/number}","notifications_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/labels{/name}","releases_url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/releases{/id}","created_at":"2009-04-25T03:07:10Z","updated_at":"2014-12-30T10:08:13Z","pushed_at":"2014-12-30T10:08:12Z","git_url":"git://github.com/freenet/plugin-WebOfTrust.git","ssh_url":"git@github.com:freenet/plugin-WebOfTrust.git","clone_url":"https://github.com/freenet/plugin-WebOfTrust.git","svn_url":"https://github.com/freenet/plugin-WebOfTrust","homepage":"https://freenetproject.org/","size":9409,"stargazers_count":25,"watchers_count":25,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":15,"mirror_url":null,"open_issues_count":4,"forks":15,"open_issues":4,"watchers":25,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26"},"html":{"href":"https://github.com/freenet/plugin-WebOfTrust/pull/26"},"issue":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/issues/26"},"comments":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/issues/26/comments"},"review_comments":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26/comments"},"review_comment":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/26/commits"},"statuses":{"href":"https://api.github.com/repos/freenet/plugin-WebOfTrust/statuses/cfbe9813161cec66a10b6deb92083376060ce89f"}}}}