{"action":"created","comment":{"url":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/comments/39791132","id":39791132,"diff_hunk":"@@ -9,30 +12,87 @@ module Stud\n   def self.interval(time, opts = {}, &block)\n     start = Time.now\n     while true\n-      break if Task.interrupted?\n       if opts[:sleep_then_run]\n         start = sleep_for_interval(time, start)\n+        break if stop?\n         block.call\n       else\n         block.call\n         start = sleep_for_interval(time, start)\n+        break if stop?\n       end\n     end # loop forever\n   end # def interval\n \n+  def interval(time, opts = {}, &block)\n+    Stud.interval(time, opts, &block)\n+  end # def interval\n+\n+  # stop! instructs interval to stop and exit its execution loop before going to\n+  # sleep between block executions.\n+  # NOW the tricky part is: this is typically an operation that will be called\n+  # from another thread than the thread running the interval loop in which case\n+  # the target parameter must be set to the Thread object which is running the\n+  # interval loop.\n+  # Note that the stop logic is compatible with Stud::Task so if interval is run\n+  # inside a Stud::Task, calling Stud::Task#stop! will stop the interval the same\n+  # way as calling stop! on the interval itself.\n+  # @param target [Thread] the target thread to stop, defaut to Thread.current\n+  def self.stop!(target = Thread.current)\n+    # setting/getting threalocal var is thread safe in JRuby\n+    target[STUD_STOP_REQUESTED] = true\n+    target.wakeup\n+    nil\n+  end\n+\n+  # stop? returns true if stop! has been called\n+  # @param target [Thread] the target thread to check for stop, defaut to Thread.current\n+  # @return [Boolean] true if the stop! has been called\n+  def self.stop?(target = Thread.current)\n+    # setting/getting threalocal var is thread safe in JRuby\n+    target[STUD_STOP_REQUESTED]\n+  end\n+\n+  class << Stud\n+    # also support Stud.interrupted? for backward compatibility.\n+    alias_method :interrupted?, :stop?\n+  end\n+\n+  # stoppable_sleep will try to sleep for the given duration seconds (which may be any number,\n+  # including a Float with fractional seconds). an optional stop_condition_block can be supplied\n+  # to verify for sleep interruption if the block returns a truthy value. if not block is supplied\n+  # it will check for the Stud.stop? condition. this check will be performed at 1s interval\n+  # by default or you can supply a different stop_condition_interval.\n+  # @param duration [Numeric] sleep time in (fractional) seconds\n+  # @param stop_condition_interval [Numeric] optional interval in (fractional) seconds to perform the sleep interruption verification, default is 1s\n+  # @param stop_condition_block [Proc] optional sleep interruption code block that must evaluate to a truthy value, default is to use Stud.stop?\n+  def self.stoppable_sleep(duration, stop_condition_interval = 1.0, &stop_condition_block)\n+    sleep_start = Time.now\n+\n+    while (remaining_duration = (duration - (Time.now - sleep_start))) >= stop_condition_interval\n+      # sleep X if there is more than X remaining to sleep in relation to the loop start time\n+      sleep(stop_condition_interval)","path":"lib/stud/interval.rb","position":74,"original_position":74,"commit_id":"7b54013304c174b32d6eb349fb8171420b3112db","original_commit_id":"7b54013304c174b32d6eb349fb8171420b3112db","user":{"login":"jordansissel","id":131818,"avatar_url":"https://avatars.githubusercontent.com/u/131818?v=3","gravatar_id":"","url":"https://api.github.com/users/jordansissel","html_url":"https://github.com/jordansissel","followers_url":"https://api.github.com/users/jordansissel/followers","following_url":"https://api.github.com/users/jordansissel/following{/other_user}","gists_url":"https://api.github.com/users/jordansissel/gists{/gist_id}","starred_url":"https://api.github.com/users/jordansissel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jordansissel/subscriptions","organizations_url":"https://api.github.com/users/jordansissel/orgs","repos_url":"https://api.github.com/users/jordansissel/repos","events_url":"https://api.github.com/users/jordansissel/events{/privacy}","received_events_url":"https://api.github.com/users/jordansissel/received_events","type":"User","site_admin":false},"body":"did you mean `sleep([stop_condition_interval, remaining_duration].min)` ?","created_at":"2015-09-17T19:56:19Z","updated_at":"2015-09-17T19:56:19Z","html_url":"https://github.com/jordansissel/ruby-stud/pull/27#discussion_r39791132","pull_request_url":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27","_links":{"self":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/comments/39791132"},"html":{"href":"https://github.com/jordansissel/ruby-stud/pull/27#discussion_r39791132"},"pull_request":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27"}}},"pull_request":{"url":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27","id":44837573,"html_url":"https://github.com/jordansissel/ruby-stud/pull/27","diff_url":"https://github.com/jordansissel/ruby-stud/pull/27.diff","patch_url":"https://github.com/jordansissel/ruby-stud/pull/27.patch","issue_url":"https://api.github.com/repos/jordansissel/ruby-stud/issues/27","number":27,"state":"open","locked":false,"title":"rework interruption logic","user":{"login":"colinsurprenant","id":2010,"avatar_url":"https://avatars.githubusercontent.com/u/2010?v=3","gravatar_id":"","url":"https://api.github.com/users/colinsurprenant","html_url":"https://github.com/colinsurprenant","followers_url":"https://api.github.com/users/colinsurprenant/followers","following_url":"https://api.github.com/users/colinsurprenant/following{/other_user}","gists_url":"https://api.github.com/users/colinsurprenant/gists{/gist_id}","starred_url":"https://api.github.com/users/colinsurprenant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/colinsurprenant/subscriptions","organizations_url":"https://api.github.com/users/colinsurprenant/orgs","repos_url":"https://api.github.com/users/colinsurprenant/repos","events_url":"https://api.github.com/users/colinsurprenant/events{/privacy}","received_events_url":"https://api.github.com/users/colinsurprenant/received_events","type":"User","site_admin":false},"body":"rework interruption logic for Stud.interval and Stud::Task\r\nfixed #23 \r\nfixes #25 \r\nfixes #26  ","created_at":"2015-09-11T22:52:06Z","updated_at":"2015-09-17T19:56:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a48502fe8f3b25e767850a1a9f7e747af25901bb","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27/commits","review_comments_url":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27/comments","review_comment_url":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/comments{/number}","comments_url":"https://api.github.com/repos/jordansissel/ruby-stud/issues/27/comments","statuses_url":"https://api.github.com/repos/jordansissel/ruby-stud/statuses/7b54013304c174b32d6eb349fb8171420b3112db","head":{"label":"colinsurprenant:fix/interrupt","ref":"fix/interrupt","sha":"7b54013304c174b32d6eb349fb8171420b3112db","user":{"login":"colinsurprenant","id":2010,"avatar_url":"https://avatars.githubusercontent.com/u/2010?v=3","gravatar_id":"","url":"https://api.github.com/users/colinsurprenant","html_url":"https://github.com/colinsurprenant","followers_url":"https://api.github.com/users/colinsurprenant/followers","following_url":"https://api.github.com/users/colinsurprenant/following{/other_user}","gists_url":"https://api.github.com/users/colinsurprenant/gists{/gist_id}","starred_url":"https://api.github.com/users/colinsurprenant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/colinsurprenant/subscriptions","organizations_url":"https://api.github.com/users/colinsurprenant/orgs","repos_url":"https://api.github.com/users/colinsurprenant/repos","events_url":"https://api.github.com/users/colinsurprenant/events{/privacy}","received_events_url":"https://api.github.com/users/colinsurprenant/received_events","type":"User","site_admin":false},"repo":{"id":18068431,"name":"ruby-stud","full_name":"colinsurprenant/ruby-stud","owner":{"login":"colinsurprenant","id":2010,"avatar_url":"https://avatars.githubusercontent.com/u/2010?v=3","gravatar_id":"","url":"https://api.github.com/users/colinsurprenant","html_url":"https://github.com/colinsurprenant","followers_url":"https://api.github.com/users/colinsurprenant/followers","following_url":"https://api.github.com/users/colinsurprenant/following{/other_user}","gists_url":"https://api.github.com/users/colinsurprenant/gists{/gist_id}","starred_url":"https://api.github.com/users/colinsurprenant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/colinsurprenant/subscriptions","organizations_url":"https://api.github.com/users/colinsurprenant/orgs","repos_url":"https://api.github.com/users/colinsurprenant/repos","events_url":"https://api.github.com/users/colinsurprenant/events{/privacy}","received_events_url":"https://api.github.com/users/colinsurprenant/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/colinsurprenant/ruby-stud","description":"Common software patterns I use frequently.","fork":true,"url":"https://api.github.com/repos/colinsurprenant/ruby-stud","forks_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/forks","keys_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/keys{/key_id}","collaborators_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/teams","hooks_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/hooks","issue_events_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/issues/events{/number}","events_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/events","assignees_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/assignees{/user}","branches_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/branches{/branch}","tags_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/tags","blobs_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/git/refs{/sha}","trees_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/git/trees{/sha}","statuses_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/statuses/{sha}","languages_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/languages","stargazers_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/stargazers","contributors_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/contributors","subscribers_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/subscribers","subscription_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/subscription","commits_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/commits{/sha}","git_commits_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/git/commits{/sha}","comments_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/comments{/number}","issue_comment_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/issues/comments{/number}","contents_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/contents/{+path}","compare_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/compare/{base}...{head}","merges_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/merges","archive_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/downloads","issues_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/issues{/number}","pulls_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/pulls{/number}","milestones_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/milestones{/number}","notifications_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/labels{/name}","releases_url":"https://api.github.com/repos/colinsurprenant/ruby-stud/releases{/id}","created_at":"2014-03-24T16:01:06Z","updated_at":"2015-09-11T15:10:25Z","pushed_at":"2015-09-17T19:53:05Z","git_url":"git://github.com/colinsurprenant/ruby-stud.git","ssh_url":"git@github.com:colinsurprenant/ruby-stud.git","clone_url":"https://github.com/colinsurprenant/ruby-stud.git","svn_url":"https://github.com/colinsurprenant/ruby-stud","homepage":"","size":210,"stargazers_count":0,"watchers_count":0,"language":"Ruby","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":"jordansissel:master","ref":"master","sha":"97f9ac41b754d8b4a2469c3a08ce86702e5fee40","user":{"login":"jordansissel","id":131818,"avatar_url":"https://avatars.githubusercontent.com/u/131818?v=3","gravatar_id":"","url":"https://api.github.com/users/jordansissel","html_url":"https://github.com/jordansissel","followers_url":"https://api.github.com/users/jordansissel/followers","following_url":"https://api.github.com/users/jordansissel/following{/other_user}","gists_url":"https://api.github.com/users/jordansissel/gists{/gist_id}","starred_url":"https://api.github.com/users/jordansissel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jordansissel/subscriptions","organizations_url":"https://api.github.com/users/jordansissel/orgs","repos_url":"https://api.github.com/users/jordansissel/repos","events_url":"https://api.github.com/users/jordansissel/events{/privacy}","received_events_url":"https://api.github.com/users/jordansissel/received_events","type":"User","site_admin":false},"repo":{"id":4613328,"name":"ruby-stud","full_name":"jordansissel/ruby-stud","owner":{"login":"jordansissel","id":131818,"avatar_url":"https://avatars.githubusercontent.com/u/131818?v=3","gravatar_id":"","url":"https://api.github.com/users/jordansissel","html_url":"https://github.com/jordansissel","followers_url":"https://api.github.com/users/jordansissel/followers","following_url":"https://api.github.com/users/jordansissel/following{/other_user}","gists_url":"https://api.github.com/users/jordansissel/gists{/gist_id}","starred_url":"https://api.github.com/users/jordansissel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jordansissel/subscriptions","organizations_url":"https://api.github.com/users/jordansissel/orgs","repos_url":"https://api.github.com/users/jordansissel/repos","events_url":"https://api.github.com/users/jordansissel/events{/privacy}","received_events_url":"https://api.github.com/users/jordansissel/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jordansissel/ruby-stud","description":"Common software patterns I use frequently.","fork":false,"url":"https://api.github.com/repos/jordansissel/ruby-stud","forks_url":"https://api.github.com/repos/jordansissel/ruby-stud/forks","keys_url":"https://api.github.com/repos/jordansissel/ruby-stud/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jordansissel/ruby-stud/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jordansissel/ruby-stud/teams","hooks_url":"https://api.github.com/repos/jordansissel/ruby-stud/hooks","issue_events_url":"https://api.github.com/repos/jordansissel/ruby-stud/issues/events{/number}","events_url":"https://api.github.com/repos/jordansissel/ruby-stud/events","assignees_url":"https://api.github.com/repos/jordansissel/ruby-stud/assignees{/user}","branches_url":"https://api.github.com/repos/jordansissel/ruby-stud/branches{/branch}","tags_url":"https://api.github.com/repos/jordansissel/ruby-stud/tags","blobs_url":"https://api.github.com/repos/jordansissel/ruby-stud/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jordansissel/ruby-stud/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jordansissel/ruby-stud/git/refs{/sha}","trees_url":"https://api.github.com/repos/jordansissel/ruby-stud/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jordansissel/ruby-stud/statuses/{sha}","languages_url":"https://api.github.com/repos/jordansissel/ruby-stud/languages","stargazers_url":"https://api.github.com/repos/jordansissel/ruby-stud/stargazers","contributors_url":"https://api.github.com/repos/jordansissel/ruby-stud/contributors","subscribers_url":"https://api.github.com/repos/jordansissel/ruby-stud/subscribers","subscription_url":"https://api.github.com/repos/jordansissel/ruby-stud/subscription","commits_url":"https://api.github.com/repos/jordansissel/ruby-stud/commits{/sha}","git_commits_url":"https://api.github.com/repos/jordansissel/ruby-stud/git/commits{/sha}","comments_url":"https://api.github.com/repos/jordansissel/ruby-stud/comments{/number}","issue_comment_url":"https://api.github.com/repos/jordansissel/ruby-stud/issues/comments{/number}","contents_url":"https://api.github.com/repos/jordansissel/ruby-stud/contents/{+path}","compare_url":"https://api.github.com/repos/jordansissel/ruby-stud/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jordansissel/ruby-stud/merges","archive_url":"https://api.github.com/repos/jordansissel/ruby-stud/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jordansissel/ruby-stud/downloads","issues_url":"https://api.github.com/repos/jordansissel/ruby-stud/issues{/number}","pulls_url":"https://api.github.com/repos/jordansissel/ruby-stud/pulls{/number}","milestones_url":"https://api.github.com/repos/jordansissel/ruby-stud/milestones{/number}","notifications_url":"https://api.github.com/repos/jordansissel/ruby-stud/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jordansissel/ruby-stud/labels{/name}","releases_url":"https://api.github.com/repos/jordansissel/ruby-stud/releases{/id}","created_at":"2012-06-10T07:12:24Z","updated_at":"2015-05-20T05:13:37Z","pushed_at":"2015-09-17T19:53:05Z","git_url":"git://github.com/jordansissel/ruby-stud.git","ssh_url":"git@github.com:jordansissel/ruby-stud.git","clone_url":"https://github.com/jordansissel/ruby-stud.git","svn_url":"https://github.com/jordansissel/ruby-stud","homepage":"","size":371,"stargazers_count":17,"watchers_count":17,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":14,"forks":11,"open_issues":14,"watchers":17,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27"},"html":{"href":"https://github.com/jordansissel/ruby-stud/pull/27"},"issue":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/issues/27"},"comments":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/issues/27/comments"},"review_comments":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27/comments"},"review_comment":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/pulls/27/commits"},"statuses":{"href":"https://api.github.com/repos/jordansissel/ruby-stud/statuses/7b54013304c174b32d6eb349fb8171420b3112db"}}}}