summaryrefslogtreecommitdiff
path: root/test/-ext-/thread/test_instrumentation_api.rb
blob: dd620e738033904d3d9c1c5ed3040ff163588547 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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
84
85
86
87
88
89
90
91
# frozen_string_literal: false
require 'envutil'

class TestThreadInstrumentation < Test::Unit::TestCase
  def setup
    pend("No windows support") if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM

    require '-test-/thread/instrumentation'

    Thread.list.each do |thread|
      if thread != Thread.current
        thread.kill
        thread.join rescue nil
      end
    end
    assert_equal [Thread.current], Thread.list

    Bug::ThreadInstrumentation.reset_counters
    Bug::ThreadInstrumentation::register_callback
  end

  def teardown
    return if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
    Bug::ThreadInstrumentation::unregister_callback
  end

  THREADS_COUNT = 3

  def test_thread_instrumentation
    threads = threaded_cpu_work
    assert_equal [false] * THREADS_COUNT, threads.map(&:status)
    counters = Bug::ThreadInstrumentation.counters
    assert_join_counters(counters)
    assert_global_join_counters(counters)
  end

  def test_join_counters # Bug #18900
    thr = Thread.new { fib(30) }
    Bug::ThreadInstrumentation.reset_counters
    thr.join
    assert_join_counters(Bug::ThreadInstrumentation.local_counters)
  end

  def test_thread_instrumentation_fork_safe
    skip "No fork()" unless Process.respond_to?(:fork)

    thread_statuses = counters = nil
    IO.popen("-") do |read_pipe|
      if read_pipe
        thread_statuses = Marshal.load(read_pipe)
        counters = Marshal.load(read_pipe)
      else
        Bug::ThreadInstrumentation.reset_counters
        threads = threaded_cpu_work
        Marshal.dump(threads.map(&:status), STDOUT)
        Marshal.dump(Bug::ThreadInstrumentation.counters, STDOUT)
      end
    end
    assert_predicate $?, :success?

    assert_equal [false] * THREADS_COUNT, thread_statuses
    assert_join_counters(counters)
    assert_global_join_counters(counters)
  end

  def test_thread_instrumentation_unregister
    Bug::ThreadInstrumentation::unregister_callback
    assert Bug::ThreadInstrumentation::register_and_unregister_callbacks
  end

  private

  def fib(n = 20)
    return n if n <= 1
    fib(n-1) + fib(n-2)
  end

  def threaded_cpu_work(size = 20)
    THREADS_COUNT.times.map { Thread.new { fib(size) } }.each(&:join)
  end

  def assert_join_counters(counters)
    counters.each_with_index do |c, i|
      assert_operator c, :>, 0, "Call counters[#{i}]: #{counters.inspect}"
    end
  end

  def assert_global_join_counters(counters)
    assert_equal THREADS_COUNT, counters.first
  end
end