empathy 0.0.1.RC0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.rdoc +135 -0
  5. data/Rakefile +33 -0
  6. data/TESTING.rdoc +11 -0
  7. data/empathy.gemspec +23 -0
  8. data/empathy.mspec +34 -0
  9. data/lib/empathy.rb +162 -0
  10. data/lib/empathy/em/condition_variable.rb +57 -0
  11. data/lib/empathy/em/mutex.rb +70 -0
  12. data/lib/empathy/em/queue.rb +84 -0
  13. data/lib/empathy/em/thread.rb +363 -0
  14. data/lib/empathy/object.rb +19 -0
  15. data/lib/empathy/thread.rb +8 -0
  16. data/lib/empathy/version.rb +3 -0
  17. data/mspec/lib/mspec/empathy.rb +19 -0
  18. data/mspec/lib/mspec/guards/empathy.rb +10 -0
  19. data/rubyspec/core/kernel/fixtures/__method__.rb +25 -0
  20. data/rubyspec/core/kernel/fixtures/autoload_b.rb +5 -0
  21. data/rubyspec/core/kernel/fixtures/autoload_c.rb +5 -0
  22. data/rubyspec/core/kernel/fixtures/autoload_d.rb +5 -0
  23. data/rubyspec/core/kernel/fixtures/caller_fixture1.rb +42 -0
  24. data/rubyspec/core/kernel/fixtures/caller_fixture2.rb +26 -0
  25. data/rubyspec/core/kernel/fixtures/chomp.rb +4 -0
  26. data/rubyspec/core/kernel/fixtures/chomp_f.rb +4 -0
  27. data/rubyspec/core/kernel/fixtures/chop.rb +4 -0
  28. data/rubyspec/core/kernel/fixtures/chop_f.rb +4 -0
  29. data/rubyspec/core/kernel/fixtures/classes.rb +410 -0
  30. data/rubyspec/core/kernel/fixtures/eval_locals.rb +6 -0
  31. data/rubyspec/core/kernel/fixtures/eval_return_with_lambda.rb +12 -0
  32. data/rubyspec/core/kernel/fixtures/eval_return_without_lambda.rb +14 -0
  33. data/rubyspec/core/kernel/fixtures/test.rb +362 -0
  34. data/rubyspec/core/kernel/sleep_spec.rb +43 -0
  35. data/rubyspec/core/mutex/lock_spec.rb +8 -0
  36. data/rubyspec/core/mutex/locked_spec.rb +8 -0
  37. data/rubyspec/core/mutex/sleep_spec.rb +56 -0
  38. data/rubyspec/core/mutex/synchronize_spec.rb +8 -0
  39. data/rubyspec/core/mutex/try_lock_spec.rb +8 -0
  40. data/rubyspec/core/mutex/unlock_spec.rb +8 -0
  41. data/rubyspec/core/thread/abort_on_exception_spec.rb +126 -0
  42. data/rubyspec/core/thread/add_trace_func_spec.rb +7 -0
  43. data/rubyspec/core/thread/alive_spec.rb +60 -0
  44. data/rubyspec/core/thread/allocate_spec.rb +9 -0
  45. data/rubyspec/core/thread/backtrace_spec.rb +7 -0
  46. data/rubyspec/core/thread/critical_spec.rb +96 -0
  47. data/rubyspec/core/thread/current_spec.rb +15 -0
  48. data/rubyspec/core/thread/element_reference_spec.rb +53 -0
  49. data/rubyspec/core/thread/element_set_spec.rb +46 -0
  50. data/rubyspec/core/thread/exclusive_spec.rb +20 -0
  51. data/rubyspec/core/thread/exit_spec.rb +21 -0
  52. data/rubyspec/core/thread/fixtures/classes.rb +291 -0
  53. data/rubyspec/core/thread/fork_spec.rb +9 -0
  54. data/rubyspec/core/thread/group_spec.rb +5 -0
  55. data/rubyspec/core/thread/initialize_spec.rb +26 -0
  56. data/rubyspec/core/thread/inspect_spec.rb +48 -0
  57. data/rubyspec/core/thread/join_spec.rb +63 -0
  58. data/rubyspec/core/thread/key_spec.rb +64 -0
  59. data/rubyspec/core/thread/keys_spec.rb +47 -0
  60. data/rubyspec/core/thread/kill_spec.rb +21 -0
  61. data/rubyspec/core/thread/list_spec.rb +38 -0
  62. data/rubyspec/core/thread/main_spec.rb +10 -0
  63. data/rubyspec/core/thread/new_spec.rb +56 -0
  64. data/rubyspec/core/thread/pass_spec.rb +8 -0
  65. data/rubyspec/core/thread/priority_spec.rb +9 -0
  66. data/rubyspec/core/thread/raise_spec.rb +225 -0
  67. data/rubyspec/core/thread/run_spec.rb +9 -0
  68. data/rubyspec/core/thread/safe_level_spec.rb +6 -0
  69. data/rubyspec/core/thread/set_trace_func_spec.rb +7 -0
  70. data/rubyspec/core/thread/shared/exit.rb +173 -0
  71. data/rubyspec/core/thread/shared/start.rb +51 -0
  72. data/rubyspec/core/thread/shared/wakeup.rb +59 -0
  73. data/rubyspec/core/thread/start_spec.rb +9 -0
  74. data/rubyspec/core/thread/status_spec.rb +48 -0
  75. data/rubyspec/core/thread/stop_spec.rb +66 -0
  76. data/rubyspec/core/thread/terminate_spec.rb +11 -0
  77. data/rubyspec/core/thread/value_spec.rb +36 -0
  78. data/rubyspec/core/thread/wakeup_spec.rb +7 -0
  79. data/rubyspec/empathy_spec.rb +26 -0
  80. data/rubyspec/library/conditionvariable/broadcast_spec.rb +62 -0
  81. data/rubyspec/library/conditionvariable/signal_spec.rb +64 -0
  82. data/rubyspec/library/conditionvariable/wait_spec.rb +21 -0
  83. data/rubyspec/library/mutex/lock_spec.rb +10 -0
  84. data/rubyspec/library/mutex/locked_spec.rb +10 -0
  85. data/rubyspec/library/mutex/synchronize_spec.rb +10 -0
  86. data/rubyspec/library/mutex/try_lock_spec.rb +10 -0
  87. data/rubyspec/library/mutex/unlock_spec.rb +10 -0
  88. data/rubyspec/library/queue/append_spec.rb +7 -0
  89. data/rubyspec/library/queue/clear_spec.rb +15 -0
  90. data/rubyspec/library/queue/deq_spec.rb +7 -0
  91. data/rubyspec/library/queue/empty_spec.rb +15 -0
  92. data/rubyspec/library/queue/enq_spec.rb +7 -0
  93. data/rubyspec/library/queue/length_spec.rb +7 -0
  94. data/rubyspec/library/queue/num_waiting_spec.rb +19 -0
  95. data/rubyspec/library/queue/pop_spec.rb +7 -0
  96. data/rubyspec/library/queue/push_spec.rb +7 -0
  97. data/rubyspec/library/queue/shared/deque.rb +37 -0
  98. data/rubyspec/library/queue/shared/enque.rb +10 -0
  99. data/rubyspec/library/queue/shared/length.rb +9 -0
  100. data/rubyspec/library/queue/shift_spec.rb +7 -0
  101. data/rubyspec/library/queue/size_spec.rb +7 -0
  102. data/rubyspec/shared/kernel/raise.rb +68 -0
  103. data/rubyspec/shared/mutex/lock.rb +52 -0
  104. data/rubyspec/shared/mutex/locked.rb +31 -0
  105. data/rubyspec/shared/mutex/synchronize.rb +23 -0
  106. data/rubyspec/shared/mutex/try_lock.rb +30 -0
  107. data/rubyspec/shared/mutex/unlock.rb +35 -0
  108. data/rubyspec/spec_helper.rb +48 -0
  109. data/spec/empathy_spec.rb +129 -0
  110. data/spec/library_spec.rb +79 -0
  111. data/spec/spec_helper.rb +6 -0
  112. metadata +222 -0
@@ -0,0 +1,9 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+
4
+ require File.expand_path('../shared/wakeup', __FILE__)
5
+
6
+ describe "Thread#run" do
7
+ it_behaves_like :thread_wakeup, :run
8
+ end
9
+
@@ -0,0 +1,6 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+
4
+ describe "Thread#safe_level" do
5
+ it "needs to be reviewed for spec completeness"
6
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ ruby_version_is "1.9" do
4
+ describe "Thread#set_trace_func" do
5
+ it "needs to be reviewed for spec completeness"
6
+ end
7
+ end
@@ -0,0 +1,173 @@
1
+ describe :thread_exit, :shared => true do
2
+ before(:each) do
3
+ ScratchPad.clear
4
+ end
5
+
6
+ it "kills sleeping thread" do
7
+ sleeping_thread = Thread.new do
8
+ sleep
9
+ ScratchPad.record :after_sleep
10
+ end
11
+ Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep"
12
+ sleeping_thread.send(@method)
13
+ sleeping_thread.join
14
+ ScratchPad.recorded.should == nil
15
+ end
16
+
17
+ it "kills current thread" do
18
+ thread = Thread.new do
19
+ Thread.current.send(@method)
20
+ ScratchPad.record :after_sleep
21
+ end
22
+ thread.join
23
+ ScratchPad.recorded.should == nil
24
+ end
25
+
26
+ it "runs ensure clause" do
27
+ thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record :in_ensure_clause }
28
+ thread.join
29
+ ScratchPad.recorded.should == :in_ensure_clause
30
+ end
31
+
32
+ it "runs nested ensure clauses" do
33
+ ScratchPad.record []
34
+ outer = Thread.new do
35
+ begin
36
+ inner = Thread.new do
37
+ begin
38
+ sleep
39
+ ensure
40
+ ScratchPad << :inner_ensure_clause
41
+ end
42
+ end
43
+ sleep
44
+ ensure
45
+ ScratchPad << :outer_ensure_clause
46
+ Thread.pass while inner.status and inner.status != "sleep"
47
+ inner.send(@method)
48
+ inner.join
49
+ end
50
+ end
51
+ Thread.pass while outer.status and outer.status != "sleep"
52
+ outer.send(@method)
53
+ outer.join
54
+ ScratchPad.recorded.should include(:inner_ensure_clause)
55
+ ScratchPad.recorded.should include(:outer_ensure_clause)
56
+ end
57
+
58
+ it "does not set $!" do
59
+ thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record $! }
60
+ thread.join
61
+ ScratchPad.recorded.should == nil
62
+ end
63
+
64
+ it "cannot be rescued" do
65
+ thread = Thread.new do
66
+ begin
67
+ Thread.current.send(@method)
68
+ rescue Exception
69
+ ScratchPad.record :in_rescue
70
+ end
71
+ ScratchPad.record :end_of_thread_block
72
+ end
73
+
74
+ thread.join
75
+ ScratchPad.recorded.should == nil
76
+ end
77
+
78
+ ruby_version_is "" ... "1.9" do
79
+ it "killing dying sleeping thread wakes up thread" do
80
+ t = ThreadSpecs.dying_thread_ensures { Thread.stop; ScratchPad.record :after_stop }
81
+ Thread.pass while t.status and t.status != "sleep"
82
+ t.send(@method)
83
+ t.join
84
+ ScratchPad.recorded.should == :after_stop
85
+ end
86
+ end
87
+
88
+ # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed
89
+ quarantine! do
90
+ it "killing dying running does nothing" do
91
+ in_ensure_clause = false
92
+ exit_loop = true
93
+ t = ThreadSpecs.dying_thread_ensures do
94
+ in_ensure_clause = true
95
+ loop { if exit_loop then break end }
96
+ ScratchPad.record :after_stop
97
+ end
98
+
99
+ Thread.pass until in_ensure_clause == true
100
+ 10.times { t.send(@method); Thread.pass }
101
+ exit_loop = true
102
+ t.join
103
+ ScratchPad.recorded.should == :after_stop
104
+ end
105
+ end
106
+
107
+ quarantine! do
108
+
109
+ it "propogates inner exception to Thread.join if there is an outer ensure clause" do
110
+ thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { }
111
+ lambda { thread.join }.should raise_error(RuntimeError, "In dying thread")
112
+ end
113
+
114
+ it "runs all outer ensure clauses even if inner ensure clause raises exception" do
115
+ thread = ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record :in_outer_ensure_clause }
116
+ ScratchPad.recorded.should == :in_outer_ensure_clause
117
+ end
118
+
119
+ it "sets $! in outer ensure clause if inner ensure clause raises exception" do
120
+ thread = ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record $! }
121
+ ScratchPad.recorded.to_s.should == "In dying thread"
122
+ end
123
+ end
124
+
125
+ it "can be rescued by outer rescue clause when inner ensure clause raises exception" do
126
+ thread = Thread.new do
127
+ begin
128
+ begin
129
+ Thread.current.send(@method)
130
+ ensure
131
+ raise "In dying thread"
132
+ end
133
+ rescue Exception
134
+ ScratchPad.record $!
135
+ end
136
+ :end_of_thread_block
137
+ end
138
+
139
+ thread.value.should == :end_of_thread_block
140
+ ScratchPad.recorded.to_s.should == "In dying thread"
141
+ end
142
+
143
+ it "is deferred if ensure clause does Thread.stop" do
144
+ ThreadSpecs.wakeup_dying_sleeping_thread(@method) { Thread.stop; ScratchPad.record :after_sleep }
145
+ ScratchPad.recorded.should == :after_sleep
146
+ end
147
+
148
+ # Hangs on 1.8.6.114 OS X, possibly also on Linux
149
+ # FIX: There is no such thing as not_compliant_on(:ruby)!!!
150
+ quarantine! do
151
+ not_compliant_on(:ruby) do # Doing a sleep in the ensure block hangs the process
152
+ it "is deferred if ensure clause sleeps" do
153
+ ThreadSpecs.wakeup_dying_sleeping_thread(@method) { sleep; ScratchPad.record :after_sleep }
154
+ ScratchPad.recorded.should == :after_sleep
155
+ end
156
+ end
157
+ end
158
+
159
+ # This case occurred in JRuby where native threads are used to provide
160
+ # the same behavior as MRI green threads. Key to this issue was the fact
161
+ # that the thread which called #exit in its block was also being explicitly
162
+ # sent #join from outside the thread. The 100.times provides a certain
163
+ # probability that the deadlock will occur. It was sufficient to reliably
164
+ # reproduce the deadlock in JRuby.
165
+ it "does not deadlock when called from within the thread while being joined from without" do
166
+ 100.times do
167
+ t = Thread.new { Thread.stop; Thread.current.send(@method) }
168
+ Thread.pass while t.status and t.status != "sleep"
169
+ t.wakeup.should == t
170
+ t.join.should == t
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,51 @@
1
+ describe :thread_start, :shared => true do
2
+ before(:each) do
3
+ ScratchPad.clear
4
+ end
5
+
6
+ ruby_version_is '' ... '1.9' do
7
+ it "raises a ThreadError if not passed a block" do
8
+ lambda {
9
+ Thread.send(@method)
10
+ }.should raise_error(ThreadError)
11
+ end
12
+ end
13
+
14
+ ruby_version_is '1.9' do
15
+ it "raises a ArgumentError if not passed a block" do
16
+ lambda {
17
+ Thread.send(@method)
18
+ }.should raise_error(ArgumentError)
19
+ end
20
+ end
21
+
22
+ it "spawns a new Thread running the block" do
23
+ run = false
24
+ t = Thread.send(@method) { run = true }
25
+ t.should be_kind_of(Thread)
26
+ t.join
27
+
28
+ run.should be_true
29
+ end
30
+
31
+ it "respects Thread subclasses" do
32
+ c = Class.new(Thread)
33
+ t = c.send(@method) { }
34
+ t.should be_kind_of(c)
35
+
36
+ t.join
37
+ end
38
+
39
+ it "does not call #initialize" do
40
+ c = Class.new(Thread) do
41
+ def initialize
42
+ ScratchPad.record :bad
43
+ end
44
+ end
45
+
46
+ t = c.send(@method) { }
47
+ t.join
48
+
49
+ ScratchPad.recorded.should == nil
50
+ end
51
+ end
@@ -0,0 +1,59 @@
1
+ describe :thread_wakeup, :shared => true do
2
+ it "can interrupt Kernel#sleep" do
3
+ exit_loop = false
4
+ after_sleep1 = false
5
+ after_sleep2 = false
6
+
7
+ t = Thread.new do
8
+ while true
9
+ break if exit_loop == true
10
+ end
11
+
12
+ sleep
13
+ after_sleep1 = true
14
+
15
+ sleep
16
+ after_sleep2 = true
17
+ end
18
+
19
+ 10.times { t.send(@method); Thread.pass }
20
+ t.status.should_not == "sleep"
21
+
22
+ exit_loop = true
23
+
24
+ 10.times { sleep 0.1 if t.status and t.status != "sleep" }
25
+ after_sleep1.should == false # t should be blocked on the first sleep
26
+ t.send(@method)
27
+
28
+ 10.times { sleep 0.1 if after_sleep1 != true }
29
+ 10.times { sleep 0.1 if t.status and t.status != "sleep" }
30
+ after_sleep2.should == false # t should be blocked on the second sleep
31
+ t.send(@method)
32
+
33
+ t.join
34
+ end
35
+
36
+ it "does not result in a deadlock" do
37
+ t = Thread.new do
38
+ 1000.times {Thread.stop }
39
+ end
40
+
41
+ while(t.status != false) do
42
+ begin
43
+ t.send(@method)
44
+ rescue ThreadError
45
+ # The thread might die right after.
46
+ t.status.should == false
47
+ end
48
+ Thread.pass
49
+ end
50
+
51
+ 1.should == 1 # test succeeds if we reach here
52
+ end
53
+
54
+ it "raises a ThreadError when trying to wake up a dead thread" do
55
+ t = Thread.new { 1 }
56
+ t.join
57
+ lambda { t.wakeup }.should raise_error(ThreadError)
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+ require File.expand_path('../shared/start', __FILE__)
4
+
5
+ describe "Thread.start" do
6
+ describe "Thread.start" do
7
+ it_behaves_like :thread_start, :start
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+
4
+ describe "Thread#status" do
5
+ it "can check it's own status" do
6
+ ThreadSpecs.status_of_current_thread.status.should == 'run'
7
+ end
8
+
9
+ it "describes a running thread" do
10
+ ThreadSpecs.status_of_running_thread.status.should == 'run'
11
+ end
12
+
13
+ it "describes a sleeping thread" do
14
+ ThreadSpecs.status_of_sleeping_thread.status.should == 'sleep'
15
+ end
16
+
17
+ it "describes a blocked thread" do
18
+ ThreadSpecs.status_of_blocked_thread.status.should == 'sleep'
19
+ end
20
+
21
+ it "describes a completed thread" do
22
+ ThreadSpecs.status_of_completed_thread.status.should == false
23
+ end
24
+
25
+ it "describes a killed thread" do
26
+ ThreadSpecs.status_of_killed_thread.status.should == false
27
+ end
28
+
29
+ it "describes a thread with an uncaught exception" do
30
+ ThreadSpecs.status_of_thread_with_uncaught_exception.status.should == nil
31
+ end
32
+
33
+ ruby_version_is ""..."1.9" do
34
+ it "describes a dying running thread" do
35
+ ThreadSpecs.status_of_dying_running_thread.status.should == 'aborting'
36
+ end
37
+ end
38
+
39
+ it "describes a dying sleeping thread" do
40
+ ThreadSpecs.status_of_dying_sleeping_thread.status.should == 'sleep'
41
+ end
42
+
43
+ quarantine! do
44
+ it "reports aborting on a killed thread" do
45
+ ThreadSpecs.status_of_aborting_thread.status.should == 'aborting'
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+
4
+ describe "Thread.stop" do
5
+ it "causes the current thread to sleep indefinitely" do
6
+ t = Thread.new { Thread.stop; 5 }
7
+ Thread.pass while t.status and t.status != 'sleep'
8
+ t.status.should == 'sleep'
9
+ t.run
10
+ t.value.should == 5
11
+ end
12
+
13
+ ruby_version_is ""..."1.9" do
14
+ it "resets Thread.critical to false" do
15
+ t = Thread.new { Thread.critical = true; Thread.stop }
16
+ Thread.pass while t.status and t.status != 'sleep'
17
+ Thread.critical.should == false
18
+ t.run
19
+ t.join
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "Thread#stop?" do
25
+ it "can check it's own status" do
26
+ ThreadSpecs.status_of_current_thread.stop?.should == false
27
+ end
28
+
29
+ it "describes a running thread" do
30
+ ThreadSpecs.status_of_running_thread.stop?.should == false
31
+ end
32
+
33
+ it "describes a sleeping thread" do
34
+ ThreadSpecs.status_of_sleeping_thread.stop?.should == true
35
+ end
36
+
37
+ it "describes a blocked thread" do
38
+ ThreadSpecs.status_of_blocked_thread.stop?.should == true
39
+ end
40
+
41
+ it "describes a completed thread" do
42
+ ThreadSpecs.status_of_completed_thread.stop?.should == true
43
+ end
44
+
45
+ it "describes a killed thread" do
46
+ ThreadSpecs.status_of_killed_thread.stop?.should == true
47
+ end
48
+
49
+ it "describes a thread with an uncaught exception" do
50
+ ThreadSpecs.status_of_thread_with_uncaught_exception.stop?.should == true
51
+ end
52
+
53
+ it "describes a dying running thread" do
54
+ ThreadSpecs.status_of_dying_running_thread.stop?.should == false
55
+ end
56
+
57
+ it "describes a dying sleeping thread" do
58
+ ThreadSpecs.status_of_dying_sleeping_thread.stop?.should == true
59
+ end
60
+
61
+ quarantine! do
62
+ it "reports aborting on a killed thread" do
63
+ ThreadSpecs.status_of_aborting_thread.stop?.should == false
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+ require File.expand_path('../shared/exit', __FILE__)
4
+
5
+ describe "Thread#terminate" do
6
+ it_behaves_like :thread_exit, :terminate
7
+ end
8
+
9
+ describe "Thread#terminate!" do
10
+ it "needs to be reviewed for spec completeness"
11
+ end