Project

General

Profile

Bug #13239

Updated by nobu (Nobuyoshi Nakada) almost 8 years ago

I've stumbled upon a case when ruby is supposed to throw "IOError: stream closed"(https://github.com/ruby/ruby/blob/trunk/thread.c#L4823) because there was a retained FD lock by another thread, but I'm was getting this instead of it: 

 ``` 
 RuntimeError: can't modify frozen IOError 
   /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/puma-3.6.2/lib/puma/server.rb:877:in `write' 
   /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/puma-3.6.2/lib/puma/server.rb:877:in `<<' 
   /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/puma-3.6.2/lib/puma/server.rb:877:in `stop' 
 ``` 

 I've done some digging and it appeared to a be a ruby bug with how such exceptions(so called "special exceptions") are handled. This exception is being frozen right after creation: https://github.com/ruby/ruby/blob/trunk/vm.c#L2078 but later, when it's thrown, it's being handled exactly as regular exception that is not frozen, which leads to a problem here:  
 https://github.com/ruby/ruby/blob/trunk/eval.c#L511 - it is an attempt to assign the "#cause" attribute on it when it all happens inside a rescue clause, since the exception itself is frozen. 
 I've created a script to reproduce it:  

 ```ruby  
 ``` 
 rd, wr = IO.pipe 
 Thread.new do 
   IO.select [rd] 
   wr.close 
 end 

 begin 
   raise 'any exception' 
 rescue 
   wr << 'A'  
 end  
 ```  
 It works with this ruby fork where I've added sleep for couple of seconds to imitate slow system call response, to keep the FD locked for a while and produce initial exception: https://github.com/ruby/ruby/blob/trunk/eval.c#L511

Back