[#48745] [ruby-trunk - Bug #7267][Open] Dir.glob on Mac OS X returns unexpected string encodings for unicode file names — "kennygrant (Kenny Grant)" <kennygrant@...>

17 messages 2012/11/02

[#48773] [ruby-trunk - Bug #7269][Open] Refinement doesn't work if using locate after method — "ko1 (Koichi Sasada)" <redmine@...>

12 messages 2012/11/03

[#48847] [ruby-trunk - Bug #7274][Open] UnboundMethods should be bindable to any object that is_a?(owner of the UnboundMethod) — "rits (First Last)" <redmine@...>

21 messages 2012/11/04

[#48854] [ruby-trunk - Bug #7276][Open] TestFile#test_utime failure — "jonforums (Jon Forums)" <redmine@...>

14 messages 2012/11/04

[#48988] [ruby-trunk - Feature #7292][Open] Enumerable#to_h — "marcandre (Marc-Andre Lafortune)" <ruby-core@...>

40 messages 2012/11/06

[#48997] [ruby-trunk - Feature #7297][Open] map_to alias for each_with_object — "nathan.f77 (Nathan Broadbent)" <nathan.f77@...>

19 messages 2012/11/06

[#49001] [ruby-trunk - Bug #7298][Open] Behavior of Enumerator.new different between 1.9.3 and 2.0.0 — "ayumin (Ayumu AIZAWA)" <ayumu.aizawa@...>

12 messages 2012/11/06

[#49018] [ruby-trunk - Feature #7299][Open] Ruby should not completely ignore blocks. — "marcandre (Marc-Andre Lafortune)" <ruby-core@...>

13 messages 2012/11/07

[#49044] [ruby-trunk - Bug #7304][Open] Random test failures around test_autoclose_true_closed_by_finalizer — "luislavena (Luis Lavena)" <luislavena@...>

11 messages 2012/11/07

[#49196] [ruby-trunk - Feature #7322][Open] Add a new operator name #>< for bit-wise "exclusive or" — "alexeymuranov (Alexey Muranov)" <redmine@...>

18 messages 2012/11/10

[#49211] [ruby-trunk - Feature #7328][Open] Move ** operator precedence under unary + and - — "boris_stitnicky (Boris Stitnicky)" <boris@...>

20 messages 2012/11/11

[#49229] [ruby-trunk - Bug #7331][Open] Set the precedence of unary `-` equal to the precedence `-`, same for `+` — "alexeymuranov (Alexey Muranov)" <redmine@...>

17 messages 2012/11/11

[#49256] [ruby-trunk - Feature #7336][Open] Flexiable OPerator Precedence — "trans (Thomas Sawyer)" <transfire@...>

18 messages 2012/11/12

[#49354] review open pull requests on github — Zachary Scott <zachary@...>

Could we get a review on any open pull requests on github before the

12 messages 2012/11/15
[#49355] Re: review open pull requests on github — "NARUSE, Yui" <naruse@...> 2012/11/15

2012/11/15 Zachary Scott <[email protected]>:

[#49356] Re: review open pull requests on github — Zachary Scott <zachary@...> 2012/11/15

Ok, I was hoping one of the maintainers might want to.

[#49451] [ruby-trunk - Bug #7374][Open] File.expand_path resolving to first file/dir instead of absolute path — mdube@... (Martin Dubé) <mdube@...>

12 messages 2012/11/16

[#49463] [ruby-trunk - Feature #7375][Open] embedding libyaml in psych for Ruby 2.0 — "tenderlovemaking (Aaron Patterson)" <aaron@...>

21 messages 2012/11/16
[#49494] [ruby-trunk - Feature #7375] embedding libyaml in psych for Ruby 2.0 — "vo.x (Vit Ondruch)" <v.ondruch@...> 2012/11/17

[#49467] [ruby-trunk - Feature #7377][Open] #indetical? as an alias for #equal? — "aef (Alexander E. Fischer)" <aef@...>

13 messages 2012/11/17

[#49558] [ruby-trunk - Bug #7395][Open] Negative numbers can't be primes by definition — "zzak (Zachary Scott)" <zachary@...>

10 messages 2012/11/19

[#49566] [ruby-trunk - Feature #7400][Open] Incorporate OpenSSL tests from JRuby. — "zzak (Zachary Scott)" <zachary@...>

11 messages 2012/11/19

[#49770] [ruby-trunk - Feature #7414][Open] Now that const_get supports "Foo::Bar" syntax, so should const_defined?. — "robertgleeson (Robert Gleeson)" <rob@...>

9 messages 2012/11/20

[#49950] [ruby-trunk - Feature #7427][Assigned] Update Rubygems — "mame (Yusuke Endoh)" <mame@...>

17 messages 2012/11/24

[#50043] [ruby-trunk - Bug #7429][Open] Provide options for core collections to customize behavior — "headius (Charles Nutter)" <headius@...>

10 messages 2012/11/24

[#50092] [ruby-trunk - Feature #7434][Open] Allow caller_locations and backtrace_locations to receive negative params — "sam.saffron (Sam Saffron)" <sam.saffron@...>

21 messages 2012/11/25

[#50094] [ruby-trunk - Bug #7436][Open] Allow for a "granularity" flag for backtrace_locations — "sam.saffron (Sam Saffron)" <sam.saffron@...>

11 messages 2012/11/25

[#50207] [ruby-trunk - Bug #7445][Open] strptime('%s %z') doesn't work — "felipec (Felipe Contreras)" <felipe.contreras@...>

19 messages 2012/11/27

[#50424] [ruby-trunk - Bug #7485][Open] ruby cannot build on mingw32 due to missing __sync_val_compare_and_swap — "drbrain (Eric Hodel)" <[email protected]>

15 messages 2012/11/30

[#50429] [ruby-trunk - Feature #7487][Open] Cutting through the issues with Refinements — "trans (Thomas Sawyer)" <transfire@...>

13 messages 2012/11/30

[ruby-core:49989] [ruby-trunk - Feature #6762][Assigned] Control interrupt timing

From: "mame (Yusuke Endoh)" <mame@...>
Date: 2012-11-24 03:15:19 UTC
List: ruby-core #49989
Issue #6762 has been updated by mame (Yusuke Endoh).

Status changed from Open to Assigned
Priority changed from Normal to High

Ko1 said, this issue is requiring just the name.
Please decide the name and commit it before preview2 (1 Dec.).
Otherwise, I'll postpone this ticket to next minor.

-- 
Yusuke Endoh <[email protected]>
----------------------------------------
Feature #6762: Control interrupt timing
https://bugs.ruby-lang.org/issues/6762#change-33758

Author: ko1 (Koichi Sasada)
Status: Assigned
Priority: High
Assignee: ko1 (Koichi Sasada)
Category: core
Target version: 2.0.0


=begin
= Abstract

Add asynchronous interrupt timing control feature.  Control with the following three modes:

* immediate: process interrupt immediately
* never: never process interrupt
* on_blocking: delay interrupt until blocking operation

  # example
  th = Thread.new do
    Thread.control_interrupt(RuntimeError => :never) do
      # in this block, thrown RuntimeError doesn't occur 
    end
    ... # raise thrown RuntimeError
  end
  ...
  th.raise "foo" 

= Background

== Terminology

* Interrupt: asynchronous interrupt and corresponding procedures
  * Thread#raise and occurring exception
  * signal and corresponding trap 
  * Thread#kill and thread termination
  * Main thread termination and thread termination
    (after main thread termination, all threads exit themselves)
* Interrupt checking: check interrupt
* Blocking operation: Possible to block the current thread such as IO read/write.  In CRuby implementation, it is nearly equals to tasks without GVL

== Current use-cases of Interrupt

There are several `Interrupt' in Ruby.

  # Example 1
  th = Thread.new{
    begin
      ...
    rescue FooError
      ...
    end
  }
  th.raise(FooError) #=> Raise FooError on thread `th'

  # Example 2
  q = Queue.new
  th1 = Thread.new{
    q << calc_in_algorithm1
  }
  th2 = Thread.new{
    q << calc_in_algorithm2
  }
  result = q.pop
  th1.raise(TerminateCalcError)
  th2.raise(TerminateCalcError)
  # Run two algorithms simultaneously.
  # If we get an answer from one algorithm,
  # kill them with TerminateCalcError
  # In this case, it is also okay with Thread#kill

  # Example 3
  trap(SIGINT){
    # do something
    # maybe termination process
  }
  trap(SIGHUP){
    # do something
    # maybe reloading configuration process
  }
  server_exec # server main process

In such interrupts are checked at several points such as:

* method invocation timing
* method returning timing
* move program counter
* before and after block operation

== Problem

Interrupt causes the following problems because we can't control occurring timing. 

* Un-safe ensure clause: Generally, ensure clause should not interrupt because it contains important tasks such as freeing resources.
* Un-safe resource allocation: If interrupt occurs between resource allocation and assign it to the variable, we can't free this object (however, this problem not too big because we have a gc and appropriate finalizer can free it).
* (other problems? please complement me)

I show an example below.

  # Example 4
  # this method is similar implementation of timeout()
  def timeout(sec)
    timer_thread = Thread.new(Thread.current){|parent|
      sleep(sec)
      parent.raise(TimeoutError)
    }
    begin
      yield
    ensure
      timer_thread.stop # close thread
    end
  end
  timeout(3){
    begin
      f = # point (a)
      open(...)  # of course, there are no problem with open(...){|f| ...} 
                 # but it is an example to show the problem
      ...
    ensure
      ... # point (b)
      f.close
    end
  }


On example 4, there are two problems.

Point (b) is easy to understand.  If interrupt was thrown at point (b), then `f.close()' isn't called.  It is problem.

On the point (a), it is a position between resource allocation (open()) and assignment `f = '.  It is very rare, but it is possible.  If we get interrupt before assignment, then we can't free resources (can't call f.close()) in ensure clause.  It is also problem.

The problem is we can't control interrupt timing.

= Proposal

Adding interrupt timing control feature to Thread.  Introduce two methods to Thread class.

* Thread.control_interrupt
* Thread.check_interrupt

Rdoc documents are:

Thread.control_interrupt():

  call-seq:
    Thread.control_interrupt(hash) { ... } -> result of the block
 
  Thread.control_interrupt controls interrupt timing.
 
  _interrupt_ means asynchronous event and corresponding procedure
  by Thread#raise, Thread#kill, signal trap (not supported yet)
  and main thread termination (if main thread terminates, then all
  other thread will be killed).
 
  _hash_ has pairs of ExceptionClass and TimingSymbol.  TimingSymbol
  is one of them:
  - :immediate   Invoke interrupt immediately.
  - :on_blocking Invoke interrupt while _BlockingOperation_.
  - :never       Never invoke interrupt.
 
  _BlockingOperation_ means that the operation will block the calling thread,
  such as read and write.  On CRuby implementation, _BlockingOperation_ is
  operation executed without GVL.
 
  Masked interrupts are delayed until they are enabled.
  This method is similar to sigprocmask(3).
 
  TODO (DOC): control_interrupt is stacked.
  TODO (DOC): check ancestors.
  TODO (DOC): to prevent all interrupt, {Object => :never} works.
 
  NOTE: Asynchronous interrupts are difficult to use.
        If you need to communicate between threads,
        please consider to use another way such as Queue.
        Or use them with deep understanding about this method.
 
 
    # example: Guard from Thread#raise
    th = Thread.new do
      Thead.control_interrupt(RuntimeError => :never) {
        begin
          # Thread#raise doesn't interrupt here.
          # You can write resource allocation code safely.
          Thread.control_interrupt(RuntimeError => :immediate) {
            # ...
            # It is possible to be interrupted by Thread#raise.
          }
        ensure
          # Thread#raise doesn't interrupt here.
          # You can write resource dealocation code safely.
        end
      }
    end
    Thread.pass
    # ...
    th.raise "stop"
 
    # example: Guard from TimeoutError
    require 'timeout'
    Thread.control_interrupt(TimeoutError => :never) {
      timeout(10){
        # TimeoutError doesn't occur here
        Thread.control_interrupt(TimeoutError => :on_blocking) {
          # possible to be killed by TimeoutError
          # while blocking operation
        }
        # TimeoutError doesn't occur here
      }
    }
 
    # example: Stack control settings
    Thread.control_interrupt(FooError => :never) {
      Thread.control_interrupt(BarError => :never) {
         # FooError and BarError are prohibited.
      }
    }
 
    # example: check ancestors
    Thread.control_interrupt(Exception => :never) {
      # all exceptions inherited from Exception are prohibited.
    }

Thread.check_interrupt():

  call-seq:
    Thread.check_interrupt() -> nil
 
  Check queued interrupts.
 
  If there are queued interrupts, process respective procedures.
 
  This method can be defined as the following Ruby code:
 
    def Thread.check_interrupt
      Thread.control_interrupt(Object => :immediate) {
        Thread.pass
      }
    end
 
  Examples:
 
    th = Thread.new{
      Thread.control_interrupt(RuntimeError => :on_blocking){
        while true
          ...
          # reach safe point to invoke interrupt
          Thread.check_interrupt
          ...
        end
      }
    }
    ...
    th.raise # stop thread
 
  NOTE: This example can be described by the another code.
        You need to keep to avoid asynchronous interrupts.
 
    flag = true
    th = Thread.new{
      Thread.control_interrupt(RuntimeError => :on_blocking){
        while true
          ...
          # reach safe point to invoke interrupt
          break if flag == false
          ...
        end
      }
    }
    ...
    flag = false # stop thread

I have already commit-ed these methods into trunk.
Please try it and discuss.

This commit is easy to revert :)

Naming is also problem as usual.  Good naming is also welcome.


= Acknowledgment

The base of this proposal is a discussion[1].

[1] Akira Tanaka "Re: Thread#raise, Thread#kill, and timeout.rb are
unsafe" ruty-talk (2008.3) <http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/294917>

Many dev-people help me to make up this proposal.

=end



-- 
http://bugs.ruby-lang.org/

In This Thread