Class: Ffmprb::Util::Thread

Inherits:
Thread
  • Object
show all
Includes:
ProcVis::Node
Defined in:
lib/ffmprb/util/thread.rb

Direct Known Subclasses

Reader

Defined Under Namespace

Classes: Error, ParentError

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes included from ProcVis::Node

#_proc_vis

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ProcVis::Node

#proc_vis_edge, #proc_vis_name, #proc_vis_node

Constructor Details

#initialize(name = "some", main: false, &blk) ⇒ Thread

Returns a new instance of Thread.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/ffmprb/util/thread.rb', line 45

def initialize(name="some", main: false, &blk)
  orig_caller = caller
  @name = name
  @parent = Thread.current
  @live_children = []
  @children_mon = Monitor.new
  @dead_children_q = Queue.new
  @backtrace = (@parent.respond_to?(:backtrace)? @parent.backtrace : []) + caller
  Ffmprb.logger.debug{"about to launch #{'main ' if main}#{name}"}
  sync_q = Queue.new
  super() do
    self.report_on_exception = false
    @parent.proc_vis_node self  if @parent.respond_to? :proc_vis_node
    if @parent.respond_to? :child_lives
      @parent.child_lives self
      Ffmprb.logger.warn "Not the main: false thread run by a #{self.class.name} thread"  if main
    else
      Ffmprb.logger.warn "Not the main: true thread run by a not #{self.class.name} thread"  unless
        main
    end
    sync_q.enq :ok
    Ffmprb.logger.debug{"#{name} thread launched"}
    begin
      blk.call.tap do
        Ffmprb.logger.debug{"#{name} thread done"}
      end
    rescue Exception
      Ffmprb.logger.warn "#{$!.class.name} raised in #{name} thread: #{$!.message}\nBacktrace:\n\t#{($!.backtrace + backtrace ).join("\n\t")}"
      cause = $!
      Ffmprb.logger.warn "...caused by #{cause.class.name}: #{cause.message}\nBacktrace:\n\t#{cause.backtrace.join("\n\t")}" while
        cause = cause.cause
      raise $!  # TODO? I have no idea why I need to give it `$!` -- the docs say I need not
    ensure
      @parent.child_dies self  if @parent.respond_to? :child_dies
      @parent.proc_vis_node self, :remove  if @parent.respond_to? :proc_vis_node
    end
  end
  sync_q.deq
end

Class Attribute Details

.timeoutObject

Returns the value of attribute timeout.



13
14
15
# File 'lib/ffmprb/util/thread.rb', line 13

def timeout
  @timeout
end

Instance Attribute Details

#backtraceObject (readonly)

Returns the value of attribute backtrace.



43
44
45
# File 'lib/ffmprb/util/thread.rb', line 43

def backtrace
  @backtrace
end

#nameObject (readonly)

Returns the value of attribute name.



42
43
44
# File 'lib/ffmprb/util/thread.rb', line 42

def name
  @name
end

Class Method Details

.join_children!(limit = nil, timeout: self.timeout) ⇒ Object



36
37
38
# File 'lib/ffmprb/util/thread.rb', line 36

def join_children!(limit=nil, timeout: self.timeout)
  Thread.current.join_children! limit, timeout: timeout
end

.timeout_or_live(limit = nil, log: "while doing this", timeout: self.timeout, &blk) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/ffmprb/util/thread.rb', line 15

def timeout_or_live(limit=nil, log: "while doing this", timeout: self.timeout, &blk)
  started_at = Time.now
  timeouts = 0
  logged_timeouts = 1
  begin
    timeouts += 1
    time = Time.now - started_at
    fail TimeLimitError  if limit && time > limit
    Timeout.timeout timeout do
      blk.call time
    end
  rescue Timeout::Error
    if timeouts > 2 * logged_timeouts
      Ffmprb.logger.info "A little bit of timeout #{log.respond_to?(:call)? log.call : log} (##{timeouts}x#{timeout})"
      logged_timeouts = timeouts
    end
    current.live!
    retry
  end
end

Instance Method Details

#child_dies(thr) ⇒ Object



99
100
101
102
103
104
105
106
107
# File 'lib/ffmprb/util/thread.rb', line 99

def child_dies(thr)
  @children_mon.synchronize do
    Ffmprb.logger.debug{"releasing #{thr.name} thread"}
    @dead_children_q.enq thr
    fail "System Error"  unless
      @live_children.delete thr
  end
  proc_vis_edge self, thr, :remove
end

#child_lives(thr) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/ffmprb/util/thread.rb', line 91

def child_lives(thr)
  @children_mon.synchronize do
    Ffmprb.logger.debug{"picking up #{thr.name} thread"}
    @live_children << thr
  end
  proc_vis_edge self, thr
end

#join_children!(limit = nil, timeout: Thread.timeout) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/ffmprb/util/thread.rb', line 109

def join_children!(limit=nil, timeout: Thread.timeout)
  timeout = [timeout, limit].compact.min
  Ffmprb.logger.debug "joining threads: #{@live_children.size} live, #{@dead_children_q.size} dead"
  until @live_children.empty? && @dead_children_q.empty?
    thr = self.class.timeout_or_live limit, log: "joining threads: #{@live_children.size} live, #{@dead_children_q.size} dead", timeout: timeout do
      @dead_children_q.deq
    end
    Ffmprb.logger.debug "joining the late #{thr.name} thread"
    fail "System Error"  unless
      thr.join(timeout)  # NOTE should not block
  end
end

#live!Object

TODO protected: none of these methods should be called by a user code, the only public methods are above



87
88
89
# File 'lib/ffmprb/util/thread.rb', line 87

def live!
  fail ParentError  if @parent.status.nil?
end