Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5a97aec

Browse files
committedJun 11, 2015
Reworked signal handling. Closes #3418.
1 parent 0bc749b commit 5a97aec

File tree

9 files changed

+206
-196
lines changed

9 files changed

+206
-196
lines changed
 

‎kernel/loader.rb

+29-26
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,34 @@ def done
790790
Process.exit! @exit_code
791791
end
792792

793+
def handle_exception(e)
794+
case e
795+
when SystemExit
796+
@exit_code = e.status
797+
when SyntaxError
798+
@exit_code = 1
799+
800+
show_syntax_error(e)
801+
802+
STDERR.puts "\nBacktrace:"
803+
STDERR.puts
804+
STDERR.puts e.awesome_backtrace.show
805+
when Interrupt
806+
@exit_code = 1
807+
808+
Rubinius::Logger.log_exception "An exception occurred #{@stage}:", e
809+
when SignalException
810+
Signal.trap(e.signo, "SIG_DFL")
811+
Process.kill e.signo, Process.pid
812+
end
813+
rescue Object => e
814+
@exit_code = 1
815+
816+
Rubinius::Logger.log_exception "An exception occurred #{@stage}:", e
817+
ensure
818+
epilogue
819+
end
820+
793821
# Orchestrate everything.
794822
def main
795823
preamble
@@ -810,33 +838,8 @@ def main
810838
script
811839
repl
812840

813-
rescue SystemExit => e
814-
@exit_code = e.status
815-
816-
epilogue
817-
rescue SyntaxError => e
818-
@exit_code = 1
819-
820-
show_syntax_error(e)
821-
822-
STDERR.puts "\nBacktrace:"
823-
STDERR.puts
824-
STDERR.puts e.awesome_backtrace.show
825-
epilogue
826-
rescue Interrupt => e
827-
@exit_code = 1
828-
829-
Rubinius::Logger.log_exception "An exception occurred #{@stage}:", e
830-
epilogue
831-
rescue SignalException => e
832-
Signal.trap(e.signo, "SIG_DFL")
833-
Process.kill e.signo, Process.pid
834-
epilogue
835841
rescue Object => e
836-
@exit_code = 1
837-
838-
Rubinius::Logger.log_exception "An exception occurred #{@stage}:", e
839-
epilogue
842+
handle_exception e
840843
else
841844
# We do this, run epilogue both in the rescue blocks and also here,
842845
# so that at_exit{} hooks can read $!.

‎vm/builtin/system.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -1019,13 +1019,15 @@ namespace rubinius {
10191019
}
10201020

10211021
Object* System::vm_watch_signal(STATE, Fixnum* sig, Object* ignored) {
1022-
SignalThread* st = state->shared().signal_handler();
1022+
SignalThread* st = state->shared().signals();
1023+
10231024
if(st) {
10241025
native_int i = sig->to_native();
10251026
if(i < 0) {
1026-
st->add_signal(state, -i, SignalThread::eDefault);
1027+
st->add_signal_handler(state, -i, SignalThread::eDefault);
10271028
} else if(i > 0) {
1028-
st->add_signal(state, i, CBOOL(ignored) ? SignalThread::eIgnore : SignalThread::eCustom);
1029+
st->add_signal_handler(state, i,
1030+
CBOOL(ignored) ? SignalThread::eIgnore : SignalThread::eCustom);
10291031
}
10301032

10311033
return cTrue;

‎vm/environment.cpp

+15-20
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace rubinius {
7979
, signature_(0)
8080
, signal_thread_(NULL)
8181
, finalizer_thread_(NULL)
82+
, loader_(NULL)
8283
{
8384
#ifdef ENABLE_LLVM
8485
#if RBX_LLVM_API_VER < 305
@@ -107,6 +108,8 @@ namespace rubinius {
107108
root_vm->metrics().init(metrics::eRubyMetrics);
108109
state = new State(root_vm);
109110

111+
loader_ = new TypedRoot<Object*>(state);
112+
110113
NativeMethod::init_thread(state);
111114

112115
start_logging(state);
@@ -198,12 +201,6 @@ namespace rubinius {
198201
#endif
199202
}
200203

201-
void Environment::start_signals(STATE) {
202-
state->vm()->set_run_signals(true);
203-
signal_thread_ = new SignalThread(state, config);
204-
signal_thread_->start(state);
205-
}
206-
207204
void Environment::stop_signals(STATE) {
208205
signal_thread_->stop(state);
209206
}
@@ -597,7 +594,7 @@ namespace rubinius {
597594
}
598595

599596
stop_jit(state);
600-
stop_signals(state);
597+
state->shared().signals()->stop(state);
601598

602599
root_vm->set_call_frame(0);
603600

@@ -846,7 +843,7 @@ namespace rubinius {
846843

847844
load_argv(argc_, argv_);
848845

849-
start_signals(state);
846+
state->shared().start_signals(state);
850847
state->vm()->initialize_config();
851848

852849
load_tool();
@@ -870,23 +867,21 @@ namespace rubinius {
870867
state->shared().start_console(state);
871868
state->shared().start_metrics(state);
872869

873-
Object* loader = G(rubinius)->get_const(state, state->symbol("Loader"));
874-
if(loader->nil_p()) {
875-
rubinius::bug("Unable to find loader");
870+
Object* klass = G(rubinius)->get_const(state, state->symbol("Loader"));
871+
if(klass->nil_p()) {
872+
rubinius::bug("unable to find class Rubinius::Loader");
876873
}
877874

878-
OnStack<1> os(state, loader);
875+
Object* instance = klass->send(state, 0, state->symbol("new"));
876+
if(instance) {
877+
loader_->set(instance);
878+
} else {
879+
rubinius::bug("unable to instantiate Rubinius::Loader");
880+
}
879881

880882
// Enable the JIT after the core library has loaded
881883
G(jit)->enable(state);
882884

883-
Object* inst = loader->send(state, 0, state->symbol("new"));
884-
if(inst) {
885-
OnStack<1> os2(state, inst);
886-
887-
inst->send(state, 0, state->symbol("main"));
888-
} else {
889-
rubinius::bug("Unable to instantiate loader");
890-
}
885+
loader_->get()->send(state, 0, state->symbol("main"));
891886
}
892887
}

‎vm/environment.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "config_parser.hpp"
99
#include "configuration.hpp"
1010

11+
#include "gc/root.hpp"
12+
1113
namespace rubinius {
1214

1315
class ConfigParser;
@@ -61,6 +63,8 @@ namespace rubinius {
6163

6264
utilities::thread::Mutex halt_lock_;
6365

66+
TypedRoot<Object*>* loader_;
67+
6468
public:
6569
SharedState* shared;
6670
VM* root_vm;
@@ -81,6 +85,10 @@ namespace rubinius {
8185
return argv_;
8286
}
8387

88+
Object* loader() {
89+
return loader_->get();
90+
}
91+
8492
void set_root_vm(VM* vm) {
8593
root_vm = vm;
8694
state->set_vm(vm);

‎vm/shared_state.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "console.hpp"
1616
#include "metrics.hpp"
17+
#include "signal.hpp"
1718
#include "world_state.hpp"
1819
#include "builtin/randomizer.hpp"
1920
#include "builtin/array.hpp"
@@ -31,7 +32,7 @@ namespace rubinius {
3132

3233
SharedState::SharedState(Environment* env, Configuration& config, ConfigParser& cp)
3334
: internal_threads_(0)
34-
, signal_thread_(0)
35+
, signals_(0)
3536
, finalizer_thread_(0)
3637
, console_(0)
3738
, metrics_(0)
@@ -142,6 +143,15 @@ namespace rubinius {
142143
return threads;
143144
}
144145

146+
SignalThread* SharedState::start_signals(STATE) {
147+
SYNC(state);
148+
149+
signals_ = new SignalThread(state);
150+
signals_->start(state);
151+
152+
return signals_;
153+
}
154+
145155
console::Console* SharedState::start_console(STATE) {
146156
SYNC(state);
147157

‎vm/shared_state.hpp

+7-9
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ namespace rubinius {
8787
class SharedState : public Lockable {
8888
private:
8989
InternalThreads* internal_threads_;
90-
SignalThread* signal_thread_;
90+
SignalThread* signals_;
9191
FinalizerThread* finalizer_thread_;
9292
console::Console* console_;
9393
metrics::Metrics* metrics_;
@@ -160,14 +160,6 @@ namespace rubinius {
160160
return internal_threads_;
161161
}
162162

163-
SignalThread* signal_handler() const {
164-
return signal_thread_;
165-
}
166-
167-
void set_signal_handler(SignalThread* thr) {
168-
signal_thread_ = thr;
169-
}
170-
171163
FinalizerThread* finalizer_handler() const {
172164
return finalizer_thread_;
173165
}
@@ -215,6 +207,12 @@ namespace rubinius {
215207
return primitive_hits_[primitive];
216208
}
217209

210+
SignalThread* signals() const {
211+
return signals_;
212+
}
213+
214+
SignalThread* start_signals(STATE);
215+
218216
console::Console* console() const {
219217
return console_;
220218
}

‎vm/signal.cpp

+114-113
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,31 @@
33
#include "vm.hpp"
44
#include "call_frame.hpp"
55
#include "environment.hpp"
6+
#include "on_stack.hpp"
67
#include "signal.hpp"
7-
#include "configuration.hpp"
8-
9-
#include "builtin/module.hpp"
10-
#include "builtin/array.hpp"
118

129
#include "builtin/array.hpp"
1310
#include "builtin/class.hpp"
1411
#include "builtin/constant_scope.hpp"
1512
#include "builtin/jit.hpp"
1613
#include "builtin/module.hpp"
17-
#include "builtin/module.hpp"
1814
#include "builtin/native_method.hpp"
1915
#include "builtin/string.hpp"
2016
#include "builtin/thread.hpp"
2117

22-
#include <string>
23-
#include <iostream>
24-
#include <fcntl.h>
25-
2618
#include "util/logger.hpp"
2719

2820
#include "dtrace/dtrace.h"
2921

22+
#include <fcntl.h>
3023
#include <signal.h>
3124
#include <stdio.h>
3225
#include <stdlib.h>
3326
#include <sys/stat.h>
3427

28+
#include <iostream>
29+
#include <string>
30+
3531
#ifdef USE_EXECINFO
3632
#include <execinfo.h>
3733
#endif
@@ -53,33 +49,62 @@ namespace rubinius {
5349
// crashing inside the crash handler.
5450
static struct utsname machine_info;
5551

56-
SignalThread::SignalThread(STATE, Configuration& config)
57-
: InternalThread(state, "rbx.signal", InternalThread::eSmall)
52+
SignalThread::SignalThread(STATE)
53+
: InternalThread(state, "rbx.signals", InternalThread::eSmall)
5854
, shared_(state->shared())
59-
, target_(state->vm())
60-
, queued_signals_(0)
55+
, queue_index_(0)
56+
, process_index_(0)
6157
{
6258
signal_thread_ = this;
63-
shared_.set_signal_handler(this);
59+
install_default_handlers();
60+
}
6461

65-
setup_default_handlers();
62+
void SignalThread::signal_handler(int signal) {
63+
signal_thread_->queue_signal(signal);
64+
}
65+
66+
void SignalThread::queue_signal(int signal) {
67+
if(thread_exit_) return;
68+
69+
metrics().system_metrics.os_signals_received++;
70+
71+
{
72+
thread::Mutex::LockGuard guard(lock_);
73+
74+
pending_signals_[queue_index_] = signal;
75+
/* GCC 4.8.2 can't tell that this code is equivalent.
76+
* queue_index_ = ++queue_index_ % pending_signal_size_;
77+
*/
78+
++queue_index_;
79+
queue_index_ %= pending_signal_size_;
80+
81+
condition_.signal();
82+
}
6683
}
6784

6885
void SignalThread::initialize(STATE) {
6986
InternalThread::initialize(state);
7087

71-
for(int i = 0; i < NSIG; i++) {
88+
Thread::create(state, vm());
89+
90+
for(int i = 0; i < pending_signal_size_; i++) {
7291
pending_signals_[i] = 0;
7392
}
7493

75-
worker_lock_.init();
76-
worker_cond_.init();
94+
queue_index_ = process_index_ = 0;
95+
96+
watch_lock_.init();
97+
lock_.init();
98+
condition_.init();
7799
}
78100

79101
void SignalThread::wakeup(STATE) {
80102
InternalThread::wakeup(state);
81103

82-
worker_cond_.signal();
104+
{
105+
thread::Mutex::LockGuard guard(lock_);
106+
condition_.signal();
107+
}
83108
}
84109

85110
void SignalThread::stop(STATE) {
@@ -92,11 +117,73 @@ namespace rubinius {
92117
InternalThread::stop(state);
93118
}
94119

120+
void SignalThread::run(STATE) {
121+
GCTokenImpl gct;
122+
state->gc_independent(gct, 0);
123+
124+
#ifndef RBX_WINDOWS
125+
sigset_t set;
126+
sigfillset(&set);
127+
pthread_sigmask(SIG_BLOCK, &set, NULL);
128+
#endif
129+
130+
metrics().init(metrics::eRubyMetrics);
131+
132+
while(!thread_exit_) {
133+
int signal = pending_signals_[process_index_];
134+
pending_signals_[process_index_] = 0;
135+
136+
/* GCC 4.8.2 can't tell that this code is equivalent.
137+
* process_index_ = ++process_index_ % pending_signal_size_;
138+
*/
139+
++process_index_;
140+
process_index_ %= pending_signal_size_;
141+
142+
if(signal > 0) {
143+
GCDependent guard(state, 0);
144+
145+
metrics().system_metrics.os_signals_processed++;
146+
147+
Array* args = 0;
148+
OnStack<1> os(state, args);
149+
150+
args = Array::create(state, 1);
151+
args->set(state, 0, Fixnum::from(signal));
152+
153+
if(!G(rubinius)->send(state, 0, state->symbol("received_signal"), args, cNil)) {
154+
if(state->thread_state()->raise_reason() == cException ||
155+
state->thread_state()->raise_reason() == cExit) {
156+
Array* args = 0;
157+
Exception* exc = 0;
158+
OnStack<2> os(state, args, exc);
159+
160+
exc = state->thread_state()->current_exception();
161+
state->thread_state()->clear_raise();
162+
163+
args = Array::create(state, 1);
164+
args->set(state, 0, exc);
165+
166+
state->shared().env()->loader()->send(
167+
state, 0, state->symbol("handle_exception"), args, cNil);
168+
169+
state->shared().env()->halt_and_exit(state);
170+
171+
break;
172+
}
173+
}
174+
} else {
175+
thread::Mutex::LockGuard guard(lock_);
176+
177+
if(queue_index_ != process_index_) continue;
178+
condition_.wait(lock_);
179+
}
180+
}
181+
}
182+
95183
void SignalThread::print_machine_info(PrintFunction function) {
96184
function("node info: %s %s", machine_info.nodename, machine_info.version);
97185
}
98186

99-
100187
#define RBX_PROCESS_INFO_LEN 256
101188

102189
void SignalThread::print_process_info(PrintFunction function) {
@@ -126,116 +213,30 @@ namespace rubinius {
126213
function("process info: %s", process_info);
127214
}
128215

129-
void SignalThread::run(STATE) {
130-
#ifndef RBX_WINDOWS
131-
sigset_t set;
132-
sigfillset(&set);
133-
pthread_sigmask(SIG_BLOCK, &set, NULL);
134-
#endif
135-
136-
GCTokenImpl gct;
137-
138-
metrics().init(metrics::eRubyMetrics);
139-
140-
while(!thread_exit_) {
141-
{
142-
utilities::thread::Mutex::LockGuard lg(worker_lock_);
143-
if(thread_exit_) break;
144-
state->gc_independent(gct, 0);
145-
worker_cond_.wait(worker_lock_);
146-
// If we should exit now, don't try to become
147-
// dependent first but break and exit the thread
148-
if(thread_exit_) break;
149-
}
150-
state->gc_dependent(gct, 0);
151-
{
152-
utilities::thread::Mutex::LockGuard lg(worker_lock_);
153-
if(thread_exit_) break;
154-
}
155-
156-
target_->wakeup(state, gct, 0);
157-
}
158-
}
159-
160-
void SignalThread::signal_handler(int sig) {
161-
signal_thread_->handle_signal(sig);
162-
}
163-
164-
void SignalThread::handle_signal(int sig) {
165-
if(thread_exit_) return;
166-
167-
target_->metrics().system_metrics.os_signals_received++;
168-
169-
queued_signals_ = 1;
170-
pending_signals_[sig] = 1;
171-
172-
target_->set_check_local_interrupts();
173-
174-
if(target_->should_interrupt_with_signal()) {
175-
if(!pthread_equal(pthread_self(), target_->os_thread())) {
176-
#ifdef RBX_WINDOWS
177-
// TODO: Windows
178-
#else
179-
pthread_kill(target_->os_thread(), SIGVTALRM);
180-
#endif
181-
}
182-
}
183-
184-
worker_cond_.signal();
185-
}
186-
187-
void SignalThread::add_signal(STATE, int sig, HandlerType type) {
188-
SYNC(state);
189-
utilities::thread::Mutex::LockGuard lg(worker_lock_);
216+
void SignalThread::add_signal_handler(STATE, int signal, HandlerType type) {
217+
thread::SpinLock::LockGuard guard(watch_lock_);
190218

191219
#ifndef RBX_WINDOWS
192220
struct sigaction action;
193221

194222
if(type == eDefault) {
195223
action.sa_handler = SIG_DFL;
196-
watched_signals_.remove(sig);
224+
watched_signals_.remove(signal);
197225
} else if(type == eIgnore) {
198226
action.sa_handler = SIG_IGN;
199-
watched_signals_.push_back(sig);
227+
watched_signals_.push_back(signal);
200228
} else {
201229
action.sa_handler = signal_handler;
202-
watched_signals_.push_back(sig);
230+
watched_signals_.push_back(signal);
203231
}
204232

205233
action.sa_flags = 0;
206234
sigfillset(&action.sa_mask);
207235

208-
sigaction(sig, &action, NULL);
236+
sigaction(signal, &action, NULL);
209237
#endif
210238
}
211239

212-
bool SignalThread::deliver_signals(STATE, CallFrame* call_frame) {
213-
queued_signals_ = 0;
214-
215-
for(int i = 0; i < NSIG; i++) {
216-
if(pending_signals_[i] > 0) {
217-
pending_signals_[i] = 0;
218-
219-
target_->metrics().system_metrics.os_signals_processed++;
220-
221-
Array* args = Array::create(state, 1);
222-
args->set(state, 0, Fixnum::from(i));
223-
224-
// Check whether the send raised an exception and
225-
// stop running the handlers if that happens
226-
if(!G(rubinius)->send(state, call_frame,
227-
state->symbol("received_signal"), args, cNil)) {
228-
if(state->thread_state()->raise_reason() == cException ||
229-
state->thread_state()->raise_reason() == cExit) {
230-
return false;
231-
}
232-
}
233-
}
234-
}
235-
236-
return true;
237-
}
238-
239240
void SignalThread::print_backtraces() {
240241
STATE = shared_.env()->state;
241242
ThreadList* threads = shared_.threads();
@@ -398,7 +399,7 @@ namespace rubinius {
398399
}
399400
#endif
400401

401-
void SignalThread::setup_default_handlers() {
402+
void SignalThread::install_default_handlers() {
402403
#ifndef RBX_WINDOWS
403404
// Get the machine info.
404405
uname(&machine_info);

‎vm/signal.hpp

+17-18
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ namespace rubinius {
1616
struct CallFrame;
1717

1818
class SignalThread : public InternalThread, public Lockable {
19+
const static int pending_signal_size_ = 256;
1920
SharedState& shared_;
20-
VM* target_;
2121

22-
int pending_signals_[NSIG];
23-
int queued_signals_;
22+
int pending_signals_[pending_signal_size_];
23+
int queue_index_;
24+
int process_index_;
2425

2526
std::list<int> watched_signals_;
27+
utilities::thread::SpinLock watch_lock_;
2628

27-
utilities::thread::Condition worker_cond_;
28-
utilities::thread::Mutex worker_lock_;
29+
utilities::thread::Condition condition_;
30+
utilities::thread::Mutex lock_;
2931

3032
public:
3133
enum HandlerType {
@@ -34,28 +36,25 @@ namespace rubinius {
3436
eCustom
3537
};
3638

37-
SignalThread(STATE, Configuration& config);
39+
SignalThread(STATE);
3840

3941
SharedState& shared() {
4042
return shared_;
4143
}
4244

43-
void initialize(STATE);
44-
void setup_default_handlers();
45-
46-
void add_signal(State*, int sig, HandlerType type = eCustom);
47-
void handle_signal(int sig);
48-
static void signal_handler(int sig);
49-
50-
bool deliver_signals(STATE, CallFrame* call_frame);
45+
static void signal_handler(int signal);
5146

52-
void print_backtraces();
53-
54-
void open_pipes();
47+
void install_default_handlers();
5548

49+
void initialize(STATE);
5650
void run(STATE);
57-
void stop(STATE);
5851
void wakeup(STATE);
52+
void stop(STATE);
53+
54+
void queue_signal(int signal);
55+
void add_signal_handler(State*, int signal, HandlerType type = eCustom);
56+
57+
void print_backtraces();
5958

6059
typedef void (*PrintFunction)(const char* message, ...);
6160

‎vm/state.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ namespace rubinius {
2222
set_call_frame(call_frame);
2323
vm_->clear_check_local_interrupts();
2424

25-
if(vm_->run_signals_) {
26-
if(!vm_->shared.signal_handler()->deliver_signals(this, call_frame)) {
27-
return false;
28-
}
29-
}
30-
3125
Exception* exc = vm_->interrupted_exception_.get();
3226
if(!exc->nil_p()) {
3327
vm_->interrupted_exception_.set(nil<Exception>());

0 commit comments

Comments
 (0)
Please sign in to comment.