Pro Yearly is on sale from $80 to $50! »

at_exit

 at_exit

C3b68d6841dcd343be9685f1cdaf8ac9?s=128

Atsushi Tanaka

July 20, 2019
Tweet

Transcript

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

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

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

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

  5. fluent-logger

  6. 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 ộ
  7. 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ͷॳظԽ
  8. 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 ộ ऴྃॲཧͷొ࿥
  9. 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 ộ ऴྃॲཧ
  10. 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 ộ ະॲཧͷϩάΛૹ৴͠ऴΘΔ·Ͱ଴ͭ
  11. Ruby on Rails (Active Record)

  12. 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

  13. 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

    τϥϯβΫγϣϯதʹऴྃͨ͠Β
 ϩʔϧόοΫ͢Δ
  14. at_exit{}Λ࢖͏ͱ  ϓϩάϥϜऴྃ࣌ͷॲཧΛ ొ࿥Ͱ͖Δ

  15. 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); }
  16. 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ͷఆٛ
  17. 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); } ϒϩοΫͷొ࿥
  18. 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; }
  19. 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; } Ϧετͷઌ಄ʹ௥Ճ
  20. ࣮͸ऴྃॲཧͷղઆ͕͋Δ

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

  22. 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)); } }
  23. 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)); } } εΫϦϓτΛ࣮ߦ
  24. 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)); }
  25. 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)); } ॲཧͷ࣮ମ
  26. 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)); } εΫϦϓτͷऴ୺ʹୡͨ͠
  27. 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; }
  28. 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; } ऴྃ࣌ͷϑοΫͷ࣮ߦ
  29. 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(); }
  30. 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(); }
  31. 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;
  32. 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Ͱొ࿥ͨ͠
 ϒϩοΫΛ࣮ߦ
  33. 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; } }
  34. 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Λݺͼग़ͨ͠ͷͱٯॱʹ࣮ߦʣ
  35. ྫ֎ൃੜͷ৔߹Λ௥ͬͯΈΔ

  36. 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);
  37. 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ͷఆٛ
  38. 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); ॲཧͷதஅ
  39. 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); }
  40. 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); }
  41. eval.c /*! Calls ruby_cleanup() and exits the process */ void

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

    ruby_stop(int ex) { exit(ruby_cleanup(ex)); } ͖ͬ͞ݟͨ
  43. ·ͱΊ w Rubyͷऴྃ࣌ʹԿ͔࣮ߦ͢ΔͳΒat_exit Λ࢖͏ w at_exit ʹ౉ͨ͠ϒϩοΫ͸ยํ޲ϦετʹೖΔ ઌ಄͔ΒೖΕͯઌ಄͔Βॱʹ࣮ߦ w Rubyͷιʔείʔυ͸ҙ֎ͱಡΊΔ

    ΄ͱΜͲ$ݴޠ͚ͩͲ؆୯
  44. ͓·͚ ͕࣌ؒ͋Ε͹

  45. END΋࢖͑Δ

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