Slide 1

Slide 1 text

at_exit {} 2019-07-20 Nagano.rb #2 @bgpat

Slide 2

Slide 2 text

@bgpat / Atsushi Tanaka Infrastructure Engineer at Wantedly, Inc.

Slide 3

Slide 3 text

ऴྃॲཧɺ࢖ͬͯ·͔͢ʁ

Slide 4

Slide 4 text

ऴྃॲཧɺ࢖ͬͯ·͔͢ʁ ऴྃॲཧϓϩάϥϜ͕ऴྃ͢Δͱ͖ʹ࣮ߦ͞ΕΔॲཧ

Slide 5

Slide 5 text

fluent-logger

Slide 6

Slide 6 text

lib/fluent/logger/fluent_logger.rb class FluentLogger < LoggerBase ộ def initialize(tag_prefix = nil, *args) super() ộ at_exit { close } end ộ def close @mon.synchronize { if @pending begin send_data(@pending) rescue => e set_last_error(e) @logger.error("FluentLogger: Can't send logs to #{connection_string}: #{$!}") call_buffer_overflow_handler(@pending) end end @con.close if connect? @con = nil @pending = nil } self end ộ

Slide 7

Slide 7 text

lib/fluent/logger/fluent_logger.rb class FluentLogger < LoggerBase ộ def initialize(tag_prefix = nil, *args) super() ộ at_exit { close } end ộ def close @mon.synchronize { if @pending begin send_data(@pending) rescue => e set_last_error(e) @logger.error("FluentLogger: Can't send logs to #{connection_string}: #{$!}") call_buffer_overflow_handler(@pending) end end @con.close if connect? @con = nil @pending = nil } self end ộ MPHHFSͷॳظԽ

Slide 8

Slide 8 text

lib/fluent/logger/fluent_logger.rb class FluentLogger < LoggerBase ộ def initialize(tag_prefix = nil, *args) super() ộ at_exit { close } end ộ def close @mon.synchronize { if @pending begin send_data(@pending) rescue => e set_last_error(e) @logger.error("FluentLogger: Can't send logs to #{connection_string}: #{$!}") call_buffer_overflow_handler(@pending) end end @con.close if connect? @con = nil @pending = nil } self end ộ ऴྃॲཧͷొ࿥

Slide 9

Slide 9 text

lib/fluent/logger/fluent_logger.rb class FluentLogger < LoggerBase ộ def initialize(tag_prefix = nil, *args) super() ộ at_exit { close } end ộ def close @mon.synchronize { if @pending begin send_data(@pending) rescue => e set_last_error(e) @logger.error("FluentLogger: Can't send logs to #{connection_string}: #{$!}") call_buffer_overflow_handler(@pending) end end @con.close if connect? @con = nil @pending = nil } self end ộ ऴྃॲཧ

Slide 10

Slide 10 text

lib/fluent/logger/fluent_logger.rb class FluentLogger < LoggerBase ộ def initialize(tag_prefix = nil, *args) super() ộ at_exit { close } end ộ def close @mon.synchronize { if @pending begin send_data(@pending) rescue => e set_last_error(e) @logger.error("FluentLogger: Can't send logs to #{connection_string}: #{$!}") call_buffer_overflow_handler(@pending) end end @con.close if connect? @con = nil @pending = nil } self end ộ ະॲཧͷϩάΛૹ৴͠ऴΘΔ·Ͱ଴ͭ

Slide 11

Slide 11 text

Ruby on Rails (Active Record)

Slide 12

Slide 12 text

activerecord/lib/active_record/railties/console_sandbox.rb # frozen_string_literal: true ActiveRecord::Base.connection.begin_transaction(joinable: false) at_exit do ActiveRecord::Base.connection.rollback_transaction end

Slide 13

Slide 13 text

activerecord/lib/active_record/railties/console_sandbox.rb # frozen_string_literal: true ActiveRecord::Base.connection.begin_transaction(joinable: false) at_exit do ActiveRecord::Base.connection.rollback_transaction end τϥϯβΫγϣϯதʹऴྃͨ͠Β
 ϩʔϧόοΫ͢Δ

Slide 14

Slide 14 text

at_exit{}Λ࢖͏ͱ ϓϩάϥϜऴྃ࣌ͷॲཧΛ ొ࿥Ͱ͖Δ

Slide 15

Slide 15 text

eval_jump.c static VALUE rb_f_at_exit(void) { VALUE proc; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "called without a block"); } proc = rb_block_proc(); rb_set_end_proc(rb_call_end_proc, proc); return proc; } void Init_jump(void) { rb_define_global_function("at_exit", rb_f_at_exit, 0); }

Slide 16

Slide 16 text

eval_jump.c static VALUE rb_f_at_exit(void) { VALUE proc; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "called without a block"); } proc = rb_block_proc(); rb_set_end_proc(rb_call_end_proc, proc); return proc; } void Init_jump(void) { rb_define_global_function("at_exit", rb_f_at_exit, 0); } at_exitͷఆٛ

Slide 17

Slide 17 text

eval_jump.c static VALUE rb_f_at_exit(void) { VALUE proc; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "called without a block"); } proc = rb_block_proc(); rb_set_end_proc(rb_call_end_proc, proc); return proc; } void Init_jump(void) { rb_define_global_function("at_exit", rb_f_at_exit, 0); } ϒϩοΫͷొ࿥

Slide 18

Slide 18 text

eval_jump.c void rb_set_end_proc(void (*func)(VALUE), VALUE data) { struct end_proc_data *link = ALLOC(struct end_proc_data); struct end_proc_data **list; rb_thread_t *th = GET_THREAD(); if (th->top_wrapper) { list = &ephemeral_end_procs; } else { list = &end_procs; } link->next = *list; link->func = func; link->data = data; *list = link; }

Slide 19

Slide 19 text

eval_jump.c void rb_set_end_proc(void (*func)(VALUE), VALUE data) { struct end_proc_data *link = ALLOC(struct end_proc_data); struct end_proc_data **list; rb_thread_t *th = GET_THREAD(); if (th->top_wrapper) { list = &ephemeral_end_procs; } else { list = &end_procs; } link->next = *list; link->func = func; link->data = data; *list = link; } Ϧετͷઌ಄ʹ௥Ճ

Slide 20

Slide 20 text

࣮͸ऴྃॲཧͷղઆ͕͋Δ

Slide 21

Slide 21 text

ਖ਼ৗऴྃͷ৔߹Λ௥ͬͯΈΔ

Slide 22

Slide 22 text

main.c int main(int argc, char **argv) { #ifdef RUBY_DEBUG_ENV ruby_set_debug_option(getenv("RUBY_DEBUG")); #endif #ifdef HAVE_LOCALE_H setlocale(LC_CTYPE, ""); #endif ruby_sysinit(&argc, &argv); { RUBY_INIT_STACK; ruby_init(); return ruby_run_node(ruby_options(argc, argv)); } }

Slide 23

Slide 23 text

main.c int main(int argc, char **argv) { #ifdef RUBY_DEBUG_ENV ruby_set_debug_option(getenv("RUBY_DEBUG")); #endif #ifdef HAVE_LOCALE_H setlocale(LC_CTYPE, ""); #endif ruby_sysinit(&argc, &argv); { RUBY_INIT_STACK; ruby_init(); return ruby_run_node(ruby_options(argc, argv)); } } εΫϦϓτΛ࣮ߦ

Slide 24

Slide 24 text

eval.c /*! Runs the given compiled source and exits this process. * @retval 0 if successfully run the source * @retval non-zero if an error occurred. */ int ruby_run_node(void *n) { int status; if (!ruby_executable_node(n, &status)) { ruby_cleanup(0); return status; } return ruby_cleanup(ruby_exec_node(n)); }

Slide 25

Slide 25 text

eval.c /*! Runs the given compiled source and exits this process. * @retval 0 if successfully run the source * @retval non-zero if an error occurred. */ int ruby_run_node(void *n) { int status; if (!ruby_executable_node(n, &status)) { ruby_cleanup(0); return status; } return ruby_cleanup(ruby_exec_node(n)); } ॲཧͷ࣮ମ

Slide 26

Slide 26 text

eval.c /*! Runs the given compiled source and exits this process. * @retval 0 if successfully run the source * @retval non-zero if an error occurred. */ int ruby_run_node(void *n) { int status; if (!ruby_executable_node(n, &status)) { ruby_cleanup(0); return status; } return ruby_cleanup(ruby_exec_node(n)); } εΫϦϓτͷऴ୺ʹୡͨ͠

Slide 27

Slide 27 text

eval.c /** Destructs the VM. * * Runs the VM finalization processes as well as ruby_finalize(), and * resources used by the VM. * * @param ex Default value to the return value. * @return If an error occurred returns a non-zero. If otherwise, retur * given ex. * @note This function does not raise any exception. */ int ruby_cleanup(volatile int ex) { int state; ộ SAVE_ROOT_JMPBUF(th, ruby_finalize_0()); ộ ruby_finalize_1(); ộ return sysex; }

Slide 28

Slide 28 text

eval.c /** Destructs the VM. * * Runs the VM finalization processes as well as ruby_finalize(), and * resources used by the VM. * * @param ex Default value to the return value. * @return If an error occurred returns a non-zero. If otherwise, retur * given ex. * @note This function does not raise any exception. */ int ruby_cleanup(volatile int ex) { int state; ộ SAVE_ROOT_JMPBUF(th, ruby_finalize_0()); ộ ruby_finalize_1(); ộ return sysex; } ऴྃ࣌ͷϑοΫͷ࣮ߦ

Slide 29

Slide 29 text

eval.c static void ruby_finalize_0(void) { EC_PUSH_TAG(GET_EC()); if (EC_EXEC_TAG() == TAG_NONE) { rb_trap_exit(); } EC_POP_TAG(); rb_exec_end_proc(); rb_clear_trace_func(); }

Slide 30

Slide 30 text

eval.c static void ruby_finalize_0(void) { EC_PUSH_TAG(GET_EC()); if (EC_EXEC_TAG() == TAG_NONE) { rb_trap_exit(); } EC_POP_TAG(); rb_exec_end_proc(); rb_clear_trace_func(); }

Slide 31

Slide 31 text

eval_jump.c void rb_exec_end_proc(void) { enum ruby_tag_type state; rb_execution_context_t * volatile ec = GET_EC(); volatile VALUE errinfo = ec->errinfo; EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { again: exec_end_procs_chain(&ephemeral_end_procs, &ec->errinfo); exec_end_procs_chain(&end_procs, &ec->errinfo); } else { EC_TMPPOP_TAG(); error_handle(state); if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo; EC_REPUSH_TAG(); goto again;

Slide 32

Slide 32 text

eval_jump.c void rb_exec_end_proc(void) { enum ruby_tag_type state; rb_execution_context_t * volatile ec = GET_EC(); volatile VALUE errinfo = ec->errinfo; EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { again: exec_end_procs_chain(&ephemeral_end_procs, &ec->errinfo); exec_end_procs_chain(&end_procs, &ec->errinfo); } else { EC_TMPPOP_TAG(); error_handle(state); if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo; EC_REPUSH_TAG(); goto again; BU@FYJUͰొ࿥ͨ͠
 ϒϩοΫΛ࣮ߦ

Slide 33

Slide 33 text

eval_jump.c static void exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp) { struct end_proc_data volatile endproc; struct end_proc_data *link; VALUE errinfo = *errp; while ((link = *procs) != 0) { *procs = link->next; endproc = *link; xfree(link); (*endproc.func) (endproc.data); *errp = errinfo; } }

Slide 34

Slide 34 text

eval_jump.c static void exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp) { struct end_proc_data volatile endproc; struct end_proc_data *link; VALUE errinfo = *errp; while ((link = *procs) != 0) { *procs = link->next; endproc = *link; xfree(link); (*endproc.func) (endproc.data); *errp = errinfo; } } ϦετΛઌ಄͔Βॱ൪ʹ࣮ߦ
 ʢat_exitΛݺͼग़ͨ͠ͷͱٯॱʹ࣮ߦʣ

Slide 35

Slide 35 text

ྫ֎ൃੜͷ৔߹Λ௥ͬͯΈΔ

Slide 36

Slide 36 text

process.c VALUE rb_f_exit(int argc, const VALUE *argv) { int istatus; if (rb_check_arity(argc, 0, 1) == 1) { istatus = exit_status_code(argv[0]); } else { istatus = EXIT_SUCCESS; } rb_exit(istatus); UNREACHABLE_RETURN(Qnil); }
 
 rb_define_global_function("exit", rb_f_exit, -1);

Slide 37

Slide 37 text

process.c VALUE rb_f_exit(int argc, const VALUE *argv) { int istatus; if (rb_check_arity(argc, 0, 1) == 1) { istatus = exit_status_code(argv[0]); } else { istatus = EXIT_SUCCESS; } rb_exit(istatus); UNREACHABLE_RETURN(Qnil); }
 
 rb_define_global_function("exit", rb_f_exit, -1); Kernel.exitͷఆٛ

Slide 38

Slide 38 text

process.c VALUE rb_f_exit(int argc, const VALUE *argv) { int istatus; if (rb_check_arity(argc, 0, 1) == 1) { istatus = exit_status_code(argv[0]); } else { istatus = EXIT_SUCCESS; } rb_exit(istatus); UNREACHABLE_RETURN(Qnil); }
 
 rb_define_global_function("exit", rb_f_exit, -1); ॲཧͷதஅ

Slide 39

Slide 39 text

process.c void rb_exit(int status) { if (GET_EC()->tag) { VALUE args[2]; args[0] = INT2NUM(status); args[1] = rb_str_new2("exit"); rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit)); } ruby_stop(status); }

Slide 40

Slide 40 text

process.c void rb_exit(int status) { if (GET_EC()->tag) { VALUE args[2]; args[0] = INT2NUM(status); args[1] = rb_str_new2("exit"); rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit)); } ruby_stop(status); }

Slide 41

Slide 41 text

eval.c /*! Calls ruby_cleanup() and exits the process */ void ruby_stop(int ex) { exit(ruby_cleanup(ex)); }

Slide 42

Slide 42 text

eval.c /*! Calls ruby_cleanup() and exits the process */ void ruby_stop(int ex) { exit(ruby_cleanup(ex)); } ͖ͬ͞ݟͨ

Slide 43

Slide 43 text

·ͱΊ w Rubyͷऴྃ࣌ʹԿ͔࣮ߦ͢ΔͳΒat_exit Λ࢖͏ w at_exit ʹ౉ͨ͠ϒϩοΫ͸ยํ޲ϦετʹೖΔ ઌ಄͔ΒೖΕͯઌ಄͔Βॱʹ࣮ߦ w Rubyͷιʔείʔυ͸ҙ֎ͱಡΊΔ ΄ͱΜͲ$ݴޠ͚ͩͲ؆୯

Slide 44

Slide 44 text

͓·͚ ͕࣌ؒ͋Ε͹

Slide 45

Slide 45 text

END΋࢖͑Δ

Slide 46

Slide 46 text

https://docs.google.com/document/d/ 1CGHMFHkqww2pAnbVJo8p9TtGv3Ng15EKYGvRIcnwMA o/edit?usp=sharing