-
Notifications
You must be signed in to change notification settings - Fork 609
/
thread18.rb
136 lines (118 loc) · 3.18 KB
/
thread18.rb
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# -*- encoding: us-ascii -*-
class Thread
def self.set_critical(obj)
Rubinius.primitive :thread_set_critical
Kernel.raise PrimitiveFailure, "Thread.set_critical primitive failed"
end
def self.start(*args)
thr = Rubinius.invoke_primitive :thread_allocate, self
Rubinius.asm(args, thr) do |args, obj|
run obj
dup
send :setup, 0, true
pop
run args
push_block
send_with_splat :__thread_initialize__, 0, true
# no pop here, as .asm blocks imply a pop as they're not
# allowed to leak a stack value
end
return thr
end
class << self
alias_method :fork, :start
end
def self.stop
# Make sure that if we're stopping the current Thread,
# others can run, so reset critical.
Thread.critical = false
sleep
nil
end
def self.critical
@critical
end
def self.critical=(value)
set_critical value
@critical = !!value
end
# Called by Thread#fork in the new thread
#
def __run__()
begin
begin
Rubinius.unlock(self)
@result = @block.call(*@args)
ensure
begin
# We must lock self in a careful way.
#
# At this point, it's possible that an other thread does Thread#raise
# and then our execution is interrupted AT ANY GIVEN TIME. We
# absolutely must make sure to lock self as soon as possible to lock
# out interrupts from other threads.
#
# Rubinius.uninterrupted_lock(self) just does that.
#
# Notice that this can't be moved to other methods and there should be
# no preceding code before it in the enclosing ensure clause.
# These are to prevent any interrupted lock failures.
Rubinius.uninterrupted_lock(self)
# Now, we locked self. No other thread can interrupt this thread
# anymore.
# If there is any not-triggered interrupt, check and process it. In
# either case, we jump to the following ensure clause.
Rubinius.check_interrupts
ensure
@joins.each { |join| join.send self }
end
end
rescue Exception => e
# I don't really get this, but this is MRI's behavior. If we're dying
# by request, ignore any raised exception.
@exception = e # unless @dying
ensure
@alive = false
Rubinius.unlock(self)
unlock_locks
end
if @exception
if abort_on_exception or Thread.abort_on_exception
Thread.main.raise @exception
elsif $DEBUG
STDERR.puts "Exception in thread: #{@exception.message} (#{@exception.class})"
end
end
end
def setup
@group = nil
@alive = true
@result = false
@exception = nil
@critical = false
@dying = false
@joins = []
@killed = false
end
def kill
@dying = true
Rubinius.synchronize(self) do
if @sleep and @killed
@sleep = false
wakeup
else
@sleep = false
@killed = true
kill_prim
end
end
end
alias_method :exit, :kill
alias_method :terminate, :kill
def value
join_inner { @result }
end
def active_exception
current_exception
end
end