Faraday: `middleware_mutex`λŠ” κ²½μŸμ μž…λ‹ˆλ‹€.

에 λ§Œλ“  2019λ…„ 10μ›” 23일  Β·  9μ½”λ©˜νŠΈ  Β·  좜처: lostisland/faraday

#!/usr/bin/env ruby

# Taken from faraday internals
def middleware_mutex(&block)
    <strong i="5">@middleware_mutex</strong> ||= begin
        require 'monitor'
        puts "I'm making a monitor!"
        Monitor.new
    end
    puts @middleware_mutex.inspect
    @middleware_mutex.synchronize(&block)
end


threads = 10.times.collect do
    Thread.new do
        middleware_mutex do
            puts "I got the power!"
        end
    end
end

threads.each(&:join)

λ‹€μŒμ€ 2.6의 좜λ ₯ μ˜ˆμž…λ‹ˆλ‹€.

koyoko% rvm use 2.6          
Using /home/samuel/.rvm/gems/ruby-2.6.3
^[[A%                                                                                                                                                         koyoko% ./middleware_mutex.rb
I'm making a monitor!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055fb2217ec60 @mon_mutex=#<Thread::Mutex:0x000055fb2217ec10>, @mon_mutex_owner_object_id=47268548572720, @mon_owner=nil, @mon_count=0>
I got the power!
koyoko% ./middleware_mutex.rb
I'm making a monitor!
I'm making a monitor!
#<Monitor:0x000055f197e82388 @mon_mutex=#<Thread::Mutex:0x000055f197e82338>, @mon_mutex_owner_object_id=47248062026180, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e81eb0 @mon_mutex=#<Thread::Mutex:0x000055f197e81e60>, @mon_mutex_owner_object_id=47248062025560, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e81b90 @mon_mutex=#<Thread::Mutex:0x000055f197e81b40>, @mon_mutex_owner_object_id=47248062025160, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e81938 @mon_mutex=#<Thread::Mutex:0x000055f197e818e8>, @mon_mutex_owner_object_id=47248062024860, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e816e0 @mon_mutex=#<Thread::Mutex:0x000055f197e81690>, @mon_mutex_owner_object_id=47248062024560, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x000055f197e827c0 @mon_mutex=#<Thread::Mutex:0x000055f197e82590>, @mon_mutex_owner_object_id=47248062026720, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e81438 @mon_mutex=#<Thread::Mutex:0x000055f197e813e8>, @mon_mutex_owner_object_id=47248062024220, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e811e0 @mon_mutex=#<Thread::Mutex:0x000055f197e81190>, @mon_mutex_owner_object_id=47248062023920, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e80f88 @mon_mutex=#<Thread::Mutex:0x000055f197e80f38>, @mon_mutex_owner_object_id=47248062023620, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!
#<Monitor:0x000055f197e80d30 @mon_mutex=#<Thread::Mutex:0x000055f197e80ce0>, @mon_mutex_owner_object_id=47248062023320, @mon_owner=nil, @mon_count=0>
I got the power!

λ‹€μŒμ€ jruby의 좜λ ₯ μ˜ˆμž…λ‹ˆλ‹€.

koyoko% rvm use jruby        
Using /home/samuel/.rvm/gems/jruby-9.2.6.0
koyoko% ./middleware_mutex.rb
I'm making a monitor!
I'm making a monitor!
I'm making a monitor!
#<Monitor:0x7ea668b0 @mon_mutex=#<Thread::Mutex:0x3027cbd3>, @mon_owner=nil, @mon_count=0>
I got the power!
I'm making a monitor!I'm making a monitor!#<Monitor:0x53ca91a5 @mon_mutex=#<Thread::Mutex:0x57f50f04>, @mon_owner=nil, @mon_count=0>I'm making a monitor!
#<Monitor:0x1b5e79ce @mon_mutex=#<Thread::Mutex:0x159994d5>, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x67589eb3 @mon_mutex=#<Thread::Mutex:0x63671680>, @mon_owner=nil, @mon_count=1>#<Monitor:0x1b5e79ce @mon_mutex=#<Thread::Mutex:0x159994d5>, @mon_owner=nil, @mon_count=0>
I got the power!
#<Monitor:0x67589eb3 @mon_mutex=#<Thread::Mutex:0x63671680>, @mon_owner=nil, @mon_count=0>
I got the power!

#<Monitor:0x49c2c95a @mon_mutex=#<Thread::Mutex:0xcc04e06>, @mon_owner=nil, @mon_count=0>
I got the power!

I'm making a monitor!#<Monitor:0x1b5e79ce @mon_mutex=#<Thread::Mutex:0x159994d5>, @mon_owner=nil, @mon_count=0>
#<Monitor:0x6fa38df1 @mon_mutex=#<Thread::Mutex:0x756e0d98>, @mon_owner=nil, @mon_count=0>
I got the power!
I got the power!

#<Monitor:0x4a9f0f52 @mon_mutex=#<Thread::Mutex:0x600dfde7>, @mon_owner=nil, @mon_count=0>

I got the power!

I got the power!
I got the power!

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

@ioquatix

$ xsel -b | ruby --dump=insns
...
== disasm: #<ISeq:middleware_mutex@-:1 (1,0)-(9,3)> (catch: FALSE)
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: 0, kw: -1@-1, kwrest: -1])
[ 1] block@0<Block>
0000 putnil                                                           (   2)[LiCa]
0001 defined                      instance-variable, :<strong i="7">@middleware_mutex</strong>, false
0005 branchunless                 14
0007 getinstancevariable          :<strong i="8">@middleware_mutex</strong>, <is:0>
0010 dup
0011 branchif                     42
0013 pop
0014 putself                                                          (   3)[Li]
0015 putstring                    "monitor"
0017 opt_send_without_block       <callinfo!mid:require, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0020 pop
0021 putself                                                          (   4)[Li]
0022 putstring                    "I'm making a monitor!"
0024 opt_send_without_block       <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0027 pop
0028 opt_getinlinecache           35, <is:1>                          (   5)[Li]
0031 getconstant                  :Monitor
0033 opt_setinlinecache           <is:1>
0035 opt_send_without_block       <callinfo!mid:new, argc:0, ARGS_SIMPLE>, <callcache>
0038 dup                                                              (   2)
0039 setinstancevariable          :<strong i="9">@middleware_mutex</strong>, <is:0>
0042 pop
0043 putself                                                          (   7)[Li]
0044 getinstancevariable          :<strong i="10">@middleware_mutex</strong>, <is:0>
0047 opt_send_without_block       <callinfo!mid:inspect, argc:0, ARGS_SIMPLE>, <callcache>
0050 opt_send_without_block       <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0053 pop
0054 getinstancevariable          :<strong i="11">@middleware_mutex</strong>, <is:0>          (   8)[Li]
0057 getblockparamproxy           block<strong i="12">@0</strong>, 0
0060 send                         <callinfo!mid:synchronize, argc:0, ARGS_BLOCKARG>, <callcache>, nil
0064 leave                                                            (   9)[Re]

λ”°λΌμ„œ ||= λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

if defined?(@middleware_mutex)
  <strong i="17">@middleware_mutex</strong>
else
  <strong i="18">@middleware_mutex</strong> = expression
end

MRIμ—μ„œ μž¬μ •μ˜ν•˜μ§€ μ•Šλ„λ‘ ν•˜λ €λ©΄ λ‹€μŒμ΄ ν•„μš”ν•©λ‹ˆλ‹€.

if defined?(@middleware_mutex)
  <strong i="22">@middleware_mutex</strong>
else
  result = expression
  # special bytecode to do `<strong i="23">@middleware_mutex</strong> = result unless defined?(@middleware_mutex)` atomically
end

λ¬Όλ‘  ν• λ‹Ήν•˜κΈ° 전에 μΆ”κ°€ 확인을 ν•˜κΈ° λ•Œλ¬Έμ— λΉ„μš©μ΄ λ“­λ‹ˆλ‹€.

그리고 블둝은 μ—¬μ „νžˆ β€‹β€‹μ—¬λŸ¬ 번 μ‹€ν–‰λ˜μ–΄ μžλ™μœΌλ‘œ μˆ˜μ •ν•˜λ €κ³  ν•˜λ©΄ ꡐ착 μƒνƒœμ™€ 높은 μ˜€λ²„ν—€λ“œκ°€ λ°œμƒν•  수 μžˆμœΌλ―€λ‘œ ν•΄λ‹Ή 뢀뢄은 μ‚¬μš©μž μ½”λ“œμ— λ§‘κΈ°λŠ” 것이 κ°€μž₯ μ’‹μŠ΅λ‹ˆλ‹€.

λͺ¨λ“  9 λŒ“κΈ€

JRubyμ—μ„œ 이것을 μ‹€ν–‰ν•˜λ©΄ <strong i="5">@middleware_mutex</strong> ||= begin λŠ” μ›μžμ μ΄μ§€ μ•Šκ³  블둝을 μ—¬λŸ¬ 번 μ‹€ν–‰ν•˜κ²Œ 될 뿐만 μ•„λ‹ˆλΌ @middleware_mutex κ°€ μ‹€μ œλ‘œ μ—¬λŸ¬ λͺ¨λ‹ˆν„°μ— μ—¬λŸ¬ 번 μ„€μ •λ©λ‹ˆλ‹€. puts κ°€ 차단/μŠ€λ ˆλ“œ μ»¨ν…μŠ€νŠΈ μ „ν™˜μ„ λ„μž…ν•  수 있기 λ•Œλ¬Έμ— 2.6μ—μ„œλ„ 이런 일이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

_뢄석 κ°μ‚¬ν•©λ‹ˆλ‹€!_

||= κ°€ MRIμ—μ„œλ„ 단일 할당을 보μž₯ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것은 맀우 ν₯λ―Έλ‘­μŠ΅λ‹ˆλ‹€.

@eregon λ„€, 저도 λ†€λžμŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ 무슨 일이 μΌμ–΄λ‚˜κ³  μžˆλŠ”μ§€ 보기 μœ„ν•΄ 지침을 버릴 수 μžˆμŠ΅λ‹ˆκΉŒ?

@ioquatix

$ xsel -b | ruby --dump=insns
...
== disasm: #<ISeq:middleware_mutex@-:1 (1,0)-(9,3)> (catch: FALSE)
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: 0, kw: -1@-1, kwrest: -1])
[ 1] block@0<Block>
0000 putnil                                                           (   2)[LiCa]
0001 defined                      instance-variable, :<strong i="7">@middleware_mutex</strong>, false
0005 branchunless                 14
0007 getinstancevariable          :<strong i="8">@middleware_mutex</strong>, <is:0>
0010 dup
0011 branchif                     42
0013 pop
0014 putself                                                          (   3)[Li]
0015 putstring                    "monitor"
0017 opt_send_without_block       <callinfo!mid:require, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0020 pop
0021 putself                                                          (   4)[Li]
0022 putstring                    "I'm making a monitor!"
0024 opt_send_without_block       <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0027 pop
0028 opt_getinlinecache           35, <is:1>                          (   5)[Li]
0031 getconstant                  :Monitor
0033 opt_setinlinecache           <is:1>
0035 opt_send_without_block       <callinfo!mid:new, argc:0, ARGS_SIMPLE>, <callcache>
0038 dup                                                              (   2)
0039 setinstancevariable          :<strong i="9">@middleware_mutex</strong>, <is:0>
0042 pop
0043 putself                                                          (   7)[Li]
0044 getinstancevariable          :<strong i="10">@middleware_mutex</strong>, <is:0>
0047 opt_send_without_block       <callinfo!mid:inspect, argc:0, ARGS_SIMPLE>, <callcache>
0050 opt_send_without_block       <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0053 pop
0054 getinstancevariable          :<strong i="11">@middleware_mutex</strong>, <is:0>          (   8)[Li]
0057 getblockparamproxy           block<strong i="12">@0</strong>, 0
0060 send                         <callinfo!mid:synchronize, argc:0, ARGS_BLOCKARG>, <callcache>, nil
0064 leave                                                            (   9)[Re]

λ”°λΌμ„œ ||= λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

if defined?(@middleware_mutex)
  <strong i="17">@middleware_mutex</strong>
else
  <strong i="18">@middleware_mutex</strong> = expression
end

MRIμ—μ„œ μž¬μ •μ˜ν•˜μ§€ μ•Šλ„λ‘ ν•˜λ €λ©΄ λ‹€μŒμ΄ ν•„μš”ν•©λ‹ˆλ‹€.

if defined?(@middleware_mutex)
  <strong i="22">@middleware_mutex</strong>
else
  result = expression
  # special bytecode to do `<strong i="23">@middleware_mutex</strong> = result unless defined?(@middleware_mutex)` atomically
end

λ¬Όλ‘  ν• λ‹Ήν•˜κΈ° 전에 μΆ”κ°€ 확인을 ν•˜κΈ° λ•Œλ¬Έμ— λΉ„μš©μ΄ λ“­λ‹ˆλ‹€.

그리고 블둝은 μ—¬μ „νžˆ β€‹β€‹μ—¬λŸ¬ 번 μ‹€ν–‰λ˜μ–΄ μžλ™μœΌλ‘œ μˆ˜μ •ν•˜λ €κ³  ν•˜λ©΄ ꡐ착 μƒνƒœμ™€ 높은 μ˜€λ²„ν—€λ“œκ°€ λ°œμƒν•  수 μžˆμœΌλ―€λ‘œ ν•΄λ‹Ή 뢀뢄은 μ‚¬μš©μž μ½”λ“œμ— λ§‘κΈ°λŠ” 것이 κ°€μž₯ μ’‹μŠ΅λ‹ˆλ‹€.

였, λ‚˜λŠ” 이것이 μš°λ¦¬κ°€ μžλ™μœΌλ‘œ κ³ μΉ  수 μžˆλŠ” 것이 μ•„λ‹ˆλΌλŠ” 것에 μ „μ μœΌλ‘œ λ™μ˜ν•©λ‹ˆλ‹€. μ½”λ“œλŠ” μŠ€λ ˆλ“œ μ•ˆμ „μ„±μ˜ POVμ—μ„œ λ”μ°ν•˜κ²Œ κΉ¨μ Έ 있고 ||= μ›μžκ°€ 그것을 κ³ μΉ  수 μ—†μŠ΅λ‹ˆλ‹€.

μ™€μš° @ioquatix 뢄석에 κ°μ‚¬λ“œλ¦¬λ©° 이 μ£Όμ œμ— λŒ€ν•œ κ·€ν•˜μ˜ μ „λ¬Έ 지식을 λ‹€μ‹œ ν•œ 번 λͺ…ν™•ν•˜κ²Œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.
즉, λ‚΄κ°€ μ•„λŠ” ν•œ JRuby 및 기타 λ™μ‹œ 루비 κ΅¬ν˜„μ„ μ§€μ›ν•˜κΈ° μœ„ν•΄ νŠΉλ³„νžˆ λ„μž…λœ κ½€ 였래된 μ½”λ“œμž…λ‹ˆλ‹€.

μ˜€λŠ˜λ‚  μš°λ¦¬λŠ” κ³΅μ‹μ μœΌλ‘œ JRuby 및 기타 λΉ„ MRI 루비λ₯Ό 더 이상 μ§€μ›ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€λΆ€λΆ„ κ·Έλ ‡κ²Œ ν•˜λŠ” 데 ν•„μš”ν•œ 지식이 λΆ€μ‘±ν•˜λ‹€κ³  μƒκ°ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. ν•˜μ§€λ§Œ μ΅œμ†Œν•œ 도움이 되기λ₯Ό λ°”λΌλŠ” λ§ˆμŒμ—μ„œ κ·ΈλŒ€λ‘œ λ‘μ—ˆμŠ΅λ‹ˆλ‹€.

이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법을 잘 λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€. κ·€ν•˜ λ˜λŠ” @eregon 이 μ˜¬λ°”λ₯Έ λ°©ν–₯을 μ•Œλ €μ€„ 수 μžˆλ‹€λ©΄ 기꺼이 지식을 μ–»κ³  μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€. πŸ€“

이것은 CRuby의 버그이기도 ν•©λ‹ˆλ‹€. PR을 μ œμΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.

# 1074μ—μ„œ ν•˜λ‚˜μ˜ μ†”λ£¨μ…˜μ„ μ‹œλ„ν–ˆμŠ΅λ‹ˆλ‹€. MiddlewareRegistry λͺ¨λ“ˆμ„ 클래슀둜 λ³€ν™˜ν•˜λŠ” 것과 κ΄€λ ¨λœ 또 λ‹€λ₯Έ 아이디어가 μžˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ 이것 λŒ€μ‹ :

https://github.com/lostisland/faraday/blob/d51a6c6979363c820d81a9166847ab09d30fae2b/lib/faraday/response.rb#L26 -L28

λ‹€μŒκ³Ό 같을 수 μžˆμŠ΅λ‹ˆλ‹€.

module Faraday
  class Response
    self.middleware_registry = MiddlewareRegistry.new(File.expand_path('response', __dir__),
      raise_error: [:RaiseError, 'raise_error'],
      logger: [:Logger, 'logger'],
    )

이것은 클래슀 수λͺ… μ£ΌκΈ° 후크에 μ˜μ‘΄ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 루비가 해석될 λ•Œ MiddlewareRegistry κ°€ μ—¬κΈ°μ—μ„œ μΈμŠ€ν„΄μŠ€ν™”λ˜κ³  있음이 맀우 λΆ„λͺ…ν•©λ‹ˆλ‹€.

class MiddlewareRegistry
  def initialize
    <strong i="14">@mutex</strong> = Monitor.new
  end
end
이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰