{"action":"created","comment":{"url":"https://api.github.com/repos/freenet/plugin-WebOfTrust/pulls/comments/23471529","id":23471529,"diff_hunk":"@@ -0,0 +1,363 @@\n+package plugins.WebOfTrust.util;\n+\n+import freenet.node.FastRunnable;\n+import freenet.node.PrioRunnable;\n+import freenet.support.Executor;\n+import freenet.support.Ticker;\n+import freenet.support.io.NativeThread;\n+\n+/**\n+ * A {@link DelayedBackgroundJob} implementation that uses a {@link Ticker} for scheduling.\n+ *\n+ * @author bertm\n+ * @see TickerDelayedBackgroundJobFactory\n+ */\n+public class TickerDelayedBackgroundJob implements DelayedBackgroundJob {\n+    static enum JobState {\n+        /** Waiting for a trigger, no running job thread or scheduled job. */\n+        IDLE,\n+        /** Waiting for the scheduled job to be executed, no running job thread. */\n+        WAITING,\n+        /** Running the job in the job thread. */\n+        RUNNING,\n+        /** Waiting for the running job thread to finish before we terminate. */\n+        TERMINATING,\n+        /** Terminated, no running job thread. */\n+        TERMINATED\n+    }\n+\n+    /** Constant for {@link #nextExecutionTime} meaning there is no next execution requested. */\n+    private final static long NO_EXECUTION = Long.MAX_VALUE;\n+\n+    /** Job wrapper for status tracking. */\n+    private final DelayedBackgroundRunnable realJob;\n+    /** Human-readable name of this job. */\n+    private final String name;\n+    /** Aggregation delay in milliseconds. */\n+    private final long defaultDelay;\n+    /** The ticker used to schedule this job. */\n+    private final Ticker ticker;\n+    /** The executor of this background job. */\n+    private final Executor executor;\n+\n+    /** Tiny job to run on the ticker, invoking the execution of the {@link #realJob} on the\n+     * executor (only set in {@code WAITING} state). */\n+    private Runnable waitingTickerJob = null;\n+    /** Running state of this job. */\n+    private JobState state = JobState.IDLE;\n+    /** Job execution thread, only if the job is running (only set in {@code RUNNING} state). */\n+    private Thread thread = null;\n+    /** Next absolute time we have a job execution scheduled, or {@link #NO_EXECUTION} if none. */\n+    private long nextExecutionTime = NO_EXECUTION;\n+\n+    /**\n+     * Constructs a delayed background job with the given default delay. Negative delays\n+     * are treated as zero delay.\n+     * The {@link Ticker} given and its {@link Executor} <b>must</b> have an asynchronous\n+     * implementation of respectively\n+     * {@link Ticker#queueTimedJob(Runnable, String, long, boolean, boolean) queueTimedJob} and\n+     * {@link Executor#execute(Runnable, String) execute}. When this background job is\n+     * {@link BackgroundJob#terminate() terminated}, the running job will be notified by means of\n+     * interruption of its thread. Hence, the job implementer must take care not to swallow\n+     * {@link InterruptedException}.\n+     * @param job the job to run in the background\n+     * @param name a human-readable name for the job\n+     * @param delay the default background job aggregation delay in milliseconds\n+     * @param ticker an asynchronous ticker with asynchronous executor\n+     *\n+     * @see TickerDelayedBackgroundJobFactory\n+     */\n+    public TickerDelayedBackgroundJob(Runnable job, String name, long delay, Ticker ticker) {\n+        if (job == null || name == null || ticker == null || ticker.getExecutor() == null) {\n+            throw new NullPointerException();\n+        }\n+        if (delay < 0) {\n+            delay = 0;\n+        }\n+        this.realJob = new DelayedBackgroundRunnable(job);\n+        this.name = name;\n+        this.defaultDelay = delay;\n+        this.ticker = ticker;\n+        this.executor = ticker.getExecutor();\n+    }\n+\n+    /**\n+     * Triggers scheduling of the job with the default delay if no job is scheduled. If a job\n+     * is already scheduled later than the default delay, it is rescheduled at the default delay.\n+     *\n+     * The first trigger received after the start of the last job execution leads to scheduling of\n+     * another execution of the job, either after the default delay or when the currently executing\n+     * job is finished, whichever comes last. A newly constructed delayed background job can be\n+     * assumed to have started its last job infinitely in the past.\n+     * @see #triggerExecution(long)\n+     */\n+    @Override\n+    public synchronized void triggerExecution() {\n+        tryEnqueue(defaultDelay);\n+    }\n+\n+    /**\n+     * Triggers scheduling of the job with the given delay if no job is scheduled. If a job\n+     * is already scheduled later than the given delay, it is rescheduled at the given delay.\n+     * Negative delays are treated as to zero delays.\n+     *\n+     * The first trigger received after the start of the last job execution leads to scheduling of\n+     * another execution of the job, either after the default delay or when the currently executing\n+     * job is finished, whichever comes last. A newly constructed delayed background job can be\n+     * assumed to have started its last job infinitely in the past.\n+     * @param delay the maximum trigger aggregation delay in milliseconds\n+     * @see #triggerExecution()\n+     */\n+    @Override\n+    public synchronized void triggerExecution(long delay) {\n+        tryEnqueue(delay);\n+    }\n+\n+    @Override\n+    public synchronized void terminate() {\n+        switch (state) {\n+            case TERMINATED:\n+                assert(waitingTickerJob == null) : \"having ticker job in TERMINATED state\";\n+                assert(thread == null) : \"having job thread in TERMINATED state\";\n+                return;\n+            case TERMINATING:\n+                assert(waitingTickerJob == null) : \"having ticker job in TERMINATING state\";\n+                assert(thread != null) : \"TERMINATING state but no thread\";","path":"src/plugins/WebOfTrust/util/TickerDelayedBackgroundJob.java","position":127,"original_position":125,"commit_id":"c0fe528b55d9f2ee6f2f2bf8af544b3b7d63c359","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":"ACK.\r\n-> Done.","created_at":"2015-01-23T19:05:52Z","updated_at":"2015-01-23T19:05:52Z","html_url":"https://github.com/freenet/plugin-WebOfTrust/pull/26#discussion_r23471529","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/23471529"},"html":{"href":"https://github.com/freenet/plugin-WebOfTrust/pull/26#discussion_r23471529"},"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-23T19:05:52Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bc46bd91b53b89846209637ac8543b84496adb7c","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/c0fe528b55d9f2ee6f2f2bf8af544b3b7d63c359","head":{"label":"bertm:background-jobs","ref":"background-jobs","sha":"c0fe528b55d9f2ee6f2f2bf8af544b3b7d63c359","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":"2015-01-12T14:01: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":7525,"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":"2015-01-20T23:53:18Z","pushed_at":"2015-01-23T18:54:48Z","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":9557,"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/c0fe528b55d9f2ee6f2f2bf8af544b3b7d63c359"}}}}