Upgrade to Pro — share decks privately, control downloads, hide ads and more …

at_exit

 at_exit

Atsushi Tanaka

July 20, 2019
Tweet

More Decks by Atsushi Tanaka

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. fluent-logger

    View Slide

  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

    View Slide

  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ͷॳظԽ

    View Slide

  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

    ऴྃॲཧͷొ࿥

    View Slide

  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

    ऴྃॲཧ

    View Slide

  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

    ະॲཧͷϩάΛૹ৴͠ऴΘΔ·Ͱ଴ͭ

    View Slide

  11. Ruby on Rails
    (Active Record)

    View Slide

  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

    View Slide

  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
    τϥϯβΫγϣϯதʹऴྃͨ͠Β

    ϩʔϧόοΫ͢Δ

    View Slide

  14. at_exit{}Λ࢖͏ͱ

    ϓϩάϥϜऴྃ࣌ͷॲཧΛ
    ొ࿥Ͱ͖Δ

    View Slide

  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);
    }

    View Slide

  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ͷఆٛ

    View Slide

  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);
    }
    ϒϩοΫͷొ࿥

    View Slide

  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;
    }

    View Slide

  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;
    }
    Ϧετͷઌ಄ʹ௥Ճ

    View Slide

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

    View Slide

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

    View Slide

  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));
    }
    }

    View Slide

  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));
    }
    }
    εΫϦϓτΛ࣮ߦ

    View Slide

  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));
    }

    View Slide

  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));
    }
    ॲཧͷ࣮ମ

    View Slide

  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));
    }
    εΫϦϓτͷऴ୺ʹୡͨ͠

    View Slide

  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;
    }

    View Slide

  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;
    }
    ऴྃ࣌ͷϑοΫͷ࣮ߦ

    View Slide

  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();
    }

    View Slide

  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();
    }

    View Slide

  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;

    View Slide

  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Ͱొ࿥ͨ͠

    ϒϩοΫΛ࣮ߦ

    View Slide

  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;
    }
    }

    View Slide

  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Λݺͼग़ͨ͠ͷͱٯॱʹ࣮ߦʣ

    View Slide

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

    View Slide

  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);

    View Slide

  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ͷఆٛ

    View Slide

  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);
    ॲཧͷதஅ

    View Slide

  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);
    }

    View Slide

  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);
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  44. ͓·͚
    ͕࣌ؒ͋Ε͹

    View Slide

  45. END΋࢖͑Δ

    View Slide

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

    View Slide