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,46 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+
4
+ describe "Thread#[]=" do
5
+ ruby_version_is ""..."1.9" do
6
+ it "raises exceptions on the wrong type of keys" do
7
+ lambda { Thread.current[nil] = true }.should raise_error(TypeError)
8
+ lambda { Thread.current[5] = true }.should raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ ruby_version_is "1.9" do
13
+ it "raises exceptions on the wrong type of keys" do
14
+ lambda { Thread.current[nil] = true }.should raise_error(TypeError)
15
+ lambda { Thread.current[5] = true }.should raise_error(TypeError)
16
+ end
17
+
18
+ it "is not shared across fibers" do
19
+ fib = Fiber.new do
20
+ Thread.current[:value] = 1
21
+ Fiber.yield
22
+ Thread.current[:value].should == 1
23
+ end
24
+ fib.resume
25
+ Thread.current[:value].should be_nil
26
+ Thread.current[:value] = 2
27
+ fib.resume
28
+ Thread.current[:value] = 2
29
+ end
30
+
31
+ it "stores a local in another thread when in a fiber" do
32
+ fib = Fiber.new do
33
+ t = Thread.new do
34
+ sleep
35
+ Thread.current[:value].should == 1
36
+ end
37
+
38
+ Thread.pass while t.status and t.status != "sleep"
39
+ t[:value] = 1
40
+ t.wakeup
41
+ t.join
42
+ end
43
+ fib.resume
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ ruby_version_is "1.9" do
4
+ describe "Thread.exclusive" do
5
+ before :each do
6
+ ScratchPad.clear
7
+ end
8
+
9
+ it "yields to the block" do
10
+ Thread.exclusive { ScratchPad.record true }
11
+ ScratchPad.recorded.should == true
12
+ end
13
+
14
+ it "returns the result of yielding" do
15
+ Thread.exclusive { :result }.should == :result
16
+ end
17
+
18
+ it "needs to be reviewed for spec completeness"
19
+ end
20
+ end
@@ -0,0 +1,21 @@
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
+ ruby_version_is ""..."1.9.1" do
6
+ describe "Thread#exit" do
7
+ it_behaves_like :thread_exit, :exit
8
+ end
9
+ end
10
+
11
+ describe "Thread#exit!" do
12
+ it "needs to be reviewed for spec completeness"
13
+ end
14
+
15
+ describe "Thread.exit" do
16
+ it "causes the current thread to exit" do
17
+ thread = Thread.new { Thread.exit; sleep }
18
+ thread.join
19
+ thread.status.should be_false
20
+ end
21
+ end
@@ -0,0 +1,291 @@
1
+ unless defined? Channel
2
+ require 'thread'
3
+ class Channel < Queue
4
+ alias receive shift
5
+ end
6
+ end
7
+
8
+ module ThreadSpecs
9
+
10
+ class SubThread < Thread
11
+ def initialize(*args)
12
+ super { args.first << 1 }
13
+ end
14
+ end
15
+
16
+ class Status
17
+ attr_reader :thread, :inspect, :status
18
+ def initialize(thread)
19
+ @thread = thread
20
+ @alive = thread.alive?
21
+ @inspect = thread.inspect
22
+ @status = thread.status
23
+ @stop = thread.stop?
24
+ end
25
+
26
+ def alive?
27
+ @alive
28
+ end
29
+
30
+ def stop?
31
+ @stop
32
+ end
33
+ end
34
+
35
+ # TODO: In the great Thread spec rewrite, abstract this
36
+ class << self
37
+ attr_accessor :state
38
+ end
39
+
40
+ def self.clear_state
41
+ @state = nil
42
+ end
43
+
44
+ def self.spin_until_sleeping(t)
45
+ Thread.pass while t.status and t.status != "sleep"
46
+ end
47
+
48
+ def self.sleeping_thread
49
+ Thread.new do
50
+ begin
51
+ sleep
52
+ ScratchPad.record :woken
53
+ rescue Object => e
54
+ ScratchPad.record e
55
+ end
56
+ end
57
+ end
58
+
59
+ def self.running_thread
60
+ Thread.new do
61
+ begin
62
+ ThreadSpecs.state = :running
63
+ loop { }
64
+ ScratchPad.record :woken
65
+ rescue Object => e
66
+ ScratchPad.record e
67
+ end
68
+ end
69
+ end
70
+
71
+ def self.completed_thread
72
+ Thread.new {}
73
+ end
74
+
75
+ def self.status_of_current_thread
76
+ Thread.new { Status.new(Thread.current) }.value
77
+ end
78
+
79
+ def self.status_of_running_thread
80
+ t = running_thread
81
+ Thread.pass while t.status and t.status != "run"
82
+ status = Status.new t
83
+ t.kill
84
+ t.join
85
+ status
86
+ end
87
+
88
+ def self.status_of_completed_thread
89
+ t = completed_thread
90
+ t.join
91
+ Status.new t
92
+ end
93
+
94
+ def self.status_of_sleeping_thread
95
+ t = sleeping_thread
96
+ Thread.pass while t.status and t.status != 'sleep'
97
+ status = Status.new t
98
+ t.run
99
+ t.join
100
+ status
101
+ end
102
+
103
+ def self.status_of_blocked_thread
104
+ m = Mutex.new
105
+ m.lock
106
+ t = Thread.new { m.lock }
107
+ Thread.pass while t.status and t.status != 'sleep'
108
+ status = Status.new t
109
+ m.unlock
110
+ t.join
111
+ status
112
+ end
113
+
114
+ def self.status_of_aborting_thread
115
+ end
116
+
117
+ def self.status_of_killed_thread
118
+ t = Thread.new { sleep }
119
+ Thread.pass while t.status and t.status != 'sleep'
120
+ t.kill
121
+ t.join
122
+ Status.new t
123
+ end
124
+
125
+ def self.status_of_thread_with_uncaught_exception
126
+ t = Thread.new { raise "error" }
127
+ begin
128
+ t.join
129
+ rescue RuntimeError
130
+ end
131
+ Status.new t
132
+ end
133
+
134
+ def self.status_of_dying_running_thread
135
+ status = nil
136
+ t = dying_thread_ensures { status = Status.new Thread.current }
137
+ t.join
138
+ status
139
+ end
140
+
141
+ def self.status_of_dying_sleeping_thread
142
+ t = dying_thread_ensures { Thread.stop; }
143
+ Thread.pass while t.status and t.status != 'sleep'
144
+ status = Status.new t
145
+ t.wakeup
146
+ t.join
147
+ status
148
+ end
149
+
150
+ def self.dying_thread_ensures(kill_method_name=:kill)
151
+ t = Thread.new do
152
+ begin
153
+ Thread.current.send(kill_method_name)
154
+ ensure
155
+ yield
156
+ end
157
+ end
158
+ end
159
+
160
+ def self.dying_thread_with_outer_ensure(kill_method_name=:kill)
161
+ t = Thread.new do
162
+ begin
163
+ begin
164
+ Thread.current.send(kill_method_name)
165
+ ensure
166
+ raise "In dying thread"
167
+ end
168
+ ensure
169
+ yield
170
+ end
171
+ end
172
+ end
173
+
174
+ def self.join_dying_thread_with_outer_ensure(kill_method_name=:kill)
175
+ t = dying_thread_with_outer_ensure(kill_method_name) { yield }
176
+ lambda { t.join }.should raise_error(RuntimeError, "In dying thread")
177
+ return t
178
+ end
179
+
180
+ def self.wakeup_dying_sleeping_thread(kill_method_name=:kill)
181
+ t = ThreadSpecs.dying_thread_ensures(kill_method_name) { yield }
182
+ Thread.pass while t.status and t.status != 'sleep'
183
+ t.wakeup
184
+ t.join
185
+ end
186
+
187
+ def self.critical_is_reset
188
+ # Create another thread to verify that it can call Thread.critical=
189
+ t = Thread.new do
190
+ initial_critical = Thread.critical
191
+ Thread.critical = true
192
+ Thread.critical = false
193
+ initial_critical == false && Thread.critical == false
194
+ end
195
+ v = t.value
196
+ t.join
197
+ v
198
+ end
199
+
200
+ def self.counter
201
+ @@counter
202
+ end
203
+
204
+ def self.counter= c
205
+ @@counter = c
206
+ end
207
+
208
+ def self.increment_counter(incr)
209
+ incr.times do
210
+ begin
211
+ Thread.critical = true
212
+ @@counter += 1
213
+ ensure
214
+ Thread.critical = false
215
+ end
216
+ end
217
+ end
218
+
219
+ def self.critical_thread1()
220
+ Thread.critical = true
221
+ Thread.current.key?(:thread_specs).should == false
222
+ end
223
+
224
+ def self.critical_thread2(isThreadStop)
225
+ Thread.current[:thread_specs].should == 101
226
+ Thread.critical.should == !isThreadStop
227
+ if not isThreadStop
228
+ Thread.critical = false
229
+ end
230
+ end
231
+
232
+ def self.main_thread1(critical_thread, isThreadSleep, isThreadStop)
233
+ # Thread.stop resets Thread.critical. Also, with native threads, the Thread.Stop may not have executed yet
234
+ # since the main thread will race with the critical thread
235
+ if not isThreadStop
236
+ Thread.critical.should == true
237
+ end
238
+ critical_thread[:thread_specs] = 101
239
+ if isThreadSleep or isThreadStop
240
+ # Thread#wakeup calls are not queued up. So we need to ensure that the thread is sleeping before calling wakeup
241
+ Thread.pass while critical_thread.status and critical_thread.status != "sleep"
242
+ critical_thread.wakeup
243
+ end
244
+ end
245
+
246
+ def self.main_thread2(critical_thread)
247
+ Thread.pass # The join below seems to cause a deadlock with CRuby unless Thread.pass is called first
248
+ critical_thread.join
249
+ Thread.critical.should == false
250
+ end
251
+
252
+ def self.critical_thread_yields_to_main_thread(isThreadSleep=false, isThreadStop=false)
253
+ @@after_first_sleep = false
254
+
255
+ critical_thread = Thread.new do
256
+ Thread.pass while Thread.main.status and Thread.main.status != "sleep"
257
+ critical_thread1()
258
+ Thread.main.wakeup
259
+ yield
260
+ Thread.pass while @@after_first_sleep != true # Need to ensure that the next statement does not see the first sleep itself
261
+ Thread.pass while Thread.main.status and Thread.main.status != "sleep"
262
+ critical_thread2(isThreadStop)
263
+ Thread.main.wakeup
264
+ end
265
+
266
+ sleep 5
267
+ @@after_first_sleep = true
268
+ main_thread1(critical_thread, isThreadSleep, isThreadStop)
269
+ sleep 5
270
+ main_thread2(critical_thread)
271
+ end
272
+
273
+ def self.create_critical_thread()
274
+ critical_thread = Thread.new do
275
+ Thread.critical = true
276
+ yield
277
+ Thread.critical = false
278
+ end
279
+ return critical_thread
280
+ end
281
+
282
+ def self.create_and_kill_critical_thread(passAfterKill=false)
283
+ critical_thread = ThreadSpecs.create_critical_thread do
284
+ Thread.current.kill
285
+ if passAfterKill
286
+ Thread.pass
287
+ end
288
+ ScratchPad.record("status=" + Thread.current.status)
289
+ end
290
+ end
291
+ 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.fork" do
6
+ describe "Thread.start" do
7
+ it_behaves_like :thread_start, :fork
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+ describe "Thread#group" do
4
+ it "needs to be reviewed for spec completeness"
5
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+
4
+ describe "Thread#initialize" do
5
+
6
+ describe "already initialized" do
7
+
8
+ before do
9
+ @t = Thread.new { sleep }
10
+ end
11
+
12
+ after do
13
+ @t.kill
14
+ end
15
+
16
+ it "raises a ThreadError" do
17
+ lambda {
18
+ @t.instance_eval do
19
+ initialize {}
20
+ end
21
+ }.should raise_error(ThreadError)
22
+ end
23
+
24
+ end
25
+
26
+ 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#inspect" do
5
+ it "can check it's own status" do
6
+ ThreadSpecs.status_of_current_thread.inspect.should include('run')
7
+ end
8
+
9
+ it "describes a running thread" do
10
+ ThreadSpecs.status_of_running_thread.inspect.should include('run')
11
+ end
12
+
13
+ it "describes a sleeping thread" do
14
+ ThreadSpecs.status_of_sleeping_thread.inspect.should include('sleep')
15
+ end
16
+
17
+ it "describes a blocked thread" do
18
+ ThreadSpecs.status_of_blocked_thread.inspect.should include('sleep')
19
+ end
20
+
21
+ it "describes a completed thread" do
22
+ ThreadSpecs.status_of_completed_thread.inspect.should include('dead')
23
+ end
24
+
25
+ it "describes a killed thread" do
26
+ ThreadSpecs.status_of_killed_thread.inspect.should include('dead')
27
+ end
28
+
29
+ it "describes a thread with an uncaught exception" do
30
+ ThreadSpecs.status_of_thread_with_uncaught_exception.inspect.should include('dead')
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.inspect.should include('aborting')
36
+ end
37
+ end
38
+
39
+ it "describes a dying sleeping thread" do
40
+ ThreadSpecs.status_of_dying_sleeping_thread.status.should include('sleep')
41
+ end
42
+
43
+ quarantine! do
44
+ it "reports aborting on a killed thread" do
45
+ ThreadSpecs.status_of_aborting_thread.inspect.should include('aborting')
46
+ end
47
+ end
48
+ end