Commit 5b45b016 authored by tqcq's avatar tqcq
Browse files

feat add hot_reloader

parent ebea797d
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ target_sources(
          src/sled/synchronization/sequence_checker.cc
          src/sled/synchronization/thread_local.cc
          src/sled/system/location.cc
          src/sled/system/hot_reloader.cc
          src/sled/system/pid.cc
          src/sled/system/thread.cc
          src/sled/system/thread_pool.cc
@@ -211,6 +212,21 @@ if(SLED_BUILD_TESTS)
  sled_add_test(
    NAME sled_cache_test SRCS src/sled/cache/lru_cache_test.cc
    src/sled/cache/fifo_cache_test.cc src/sled/cache/expire_cache_test.cc)

  add_library(hot_reloader_test_dynamic SHARED
              src/sled/system/hot_reloader_test_dynamic.cc)
  target_link_libraries(hot_reloader_test_dynamic PRIVATE sled)
  message(
    STATUS "HOT_RELOADER_TEST_DYNAMIC_LOCATION ${hot_reloader_test_dynamic}")
  get_target_property(HOT_RELOADER_TEST_DYNAMIC_LOCATION
                      hot_reloader_test_dynamic LINK_LIBRARIES)

  sled_add_test(
    NAME sled_hot_reloader_test SRCS src/sled/system/hot_reloader_test.cc LIBS
    hot_reloader_test_dynamic)
  target_compile_definitions(
    sled_hot_reloader_test
    PRIVATE "TEST_BIN_PATH=\"${HOT_RELOADER_TEST_DYNAMIC_LOCATION}\"")
endif(SLED_BUILD_TESTS)

if(SLED_BUILD_FUZZ)
+30 −0
Original line number Diff line number Diff line
@@ -7,6 +7,36 @@
#else
#define SLED_THREAD_ANNOTATION_ATTRIBUTE__(x)// no-op
#endif
//
// Global compiler specific defines/customizations
//
#if defined(_MSC_VER)
#if defined(__cplusplus)
#define SLED_EXPORT extern "C" __declspec(dllexport)
#define SLED_IMPORT extern "C" __declspec(dllimport)
#else
#define SLED_EXPORT __declspec(dllexport)
#define SLED_IMPORT __declspec(dllimport)
#endif
#endif// defined(_MSC_VER)

#if defined(__GNUC__)// clang & gcc
#if defined(__cplusplus)
#define SLED_EXPORT extern "C" __attribute__((visibility("default")))
#else
#define SLED_EXPORT __attribute__((visibility("default")))
#endif
#define SLED_IMPORT
#endif// defined(__GNUC__)

#if defined(__MINGW32__)
#undef SLED_EXPORT
#if defined(__cplusplus)
#define SLED_EXPORT extern "C" __declspec(dllexport)
#else
#define SLED_EXPORT __declspec(dllexport)
#endif
#endif

#define SLED_NODISCARD SLED_THREAD_ANNOTATION_ATTRIBUTE__(__warn_unused_result__)
#define SLED_DEPRECATED SLED_THREAD_ANNOTATION_ATTRIBUTE__(deprecated)
+1954 −0

File added.

Preview size limit exceeded, changes collapsed.

+136 −0
Original line number Diff line number Diff line
#include "sled/system/hot_reloader.h"
#include "sled/log/log.h"

#define CR_HOST CR_SAFEST
#define CR_MAIN_FUNC "sled_hot_loader_main"
#include "sled/system/detail/cr.h"

namespace sled {
namespace detail {
HotReloader::Failure
TranslateFailure(cr_failure failure)
{
    switch (failure) {
    case CR_NONE:
        return HotReloader::Failure::kOk;
    case CR_SEGFAULT:
        return HotReloader::Failure::kSegmentationFault;
    case CR_ILLEGAL:
        return HotReloader::Failure::kIllegal;
    case CR_ABORT:
        return HotReloader::Failure::kAbort;
    case CR_MISALIGN:
        return HotReloader::Failure::kMisalign;
    case CR_BOUNDS:
        return HotReloader::Failure::kBounds;
    case CR_STACKOVERFLOW:
        return HotReloader::Failure::kStackOverflow;
    case CR_STATE_INVALIDATED:
        return HotReloader::Failure::kStateInvalidated;
    case CR_BAD_IMAGE:
        return HotReloader::Failure::kBadImage;
    case CR_INITIAL_FAILURE:
        return HotReloader::Failure::kInitialFailure;
    case CR_OTHER:
        return HotReloader::Failure::kOther;
    default:
        return HotReloader::Failure::kOther;
    }
}
}// namespace detail

std::string
DynamicLibraryName(sled::string_view base_name)
{
#if defined(_WIN32)
    return sled::to_string(base_name) + ".dll";
#elif defined(__APPLE__)
    return "lib" + sled::to_string(base_name) + ".dylib";
#elif defined(__linux__)
    return "lib" + sled::to_string(base_name) + ".so";
#else
#error "Unsupported platform"
#endif
}

class HotReloader::Impl {
public:
    Impl(sled::string_view bin_path) : bin_path_(sled::to_string(bin_path)) {}

    ~Impl() { cr_plugin_close(ctx_); }

    bool Initialize()
    {
        if (cr_plugin_open(ctx_, bin_path_.c_str())) { return true; }
        LOGV("HotReloader", "Failed to load plugin: {}", bin_path_);
        return false;
    }

    sled::optional<Failure> Update(bool force_reload)
    {
        if (cr_plugin_update(ctx_, force_reload) == 0) { return sled::nullopt; }
        return detail::TranslateFailure(ctx_.failure);
    }

    void set_userdata(void *data) { ctx_.userdata = data; }

    sled::string_view name() const { return bin_path_; }

    VersionType version() const { return ctx_.version; }

    VersionType last_working_version() const { return ctx_.last_working_version; }

    VersionType next_version() const { return ctx_.next_version; }

private:
    std::string bin_path_;
    cr_plugin ctx_;
};

HotReloader::HotReloader(sled::string_view bin_path) : impl_(new Impl(bin_path)) {}

HotReloader::~HotReloader() {}

bool
HotReloader::Initialize()
{
    return impl_->Initialize();
}

sled::optional<HotReloader::Failure>
HotReloader::UpdateOrError(bool force_reload)
{
    return impl_->Update(force_reload);
}

void
HotReloader::set_userdata(void *data)
{
    impl_->set_userdata(data);
}

sled::string_view
HotReloader::name() const
{
    return impl_->name();
}

HotReloader::VersionType
HotReloader::version() const
{
    return impl_->version();
}

HotReloader::VersionType
HotReloader::last_working_version() const
{
    return impl_->last_working_version();
}

HotReloader::VersionType
HotReloader::next_version() const
{
    return impl_->next_version();
}

}// namespace sled
+94 −0
Original line number Diff line number Diff line
#ifndef SLED_SYSTEM_HOT_LOADER_H
#define SLED_SYSTEM_HOT_LOADER_H

#pragma once
#include "sled/nonstd/string_view.h"
#include "sled/optional.h"
#include <memory>

// hot reload library should implement
// sled_hot_loader_main function
// if success return 0,
// otherwise return negative value (reversed -1, -2)
// int sled_hot_loader_main(void* ctx, int op) {
//     switch(op) {
//         case 0:  on_load(...);
//         case 2:  on_unload(...);
//         case 3:  on_close(...);
//     }
//     if (failure) {
//         return -3;
//     }
//     return 0;
// }
namespace sled {
// Windows return <base_name>.dll
// linux   return lib<base_name>.so
// macos   return lib<base_name>.dylib
std::string DynamicLibraryName(sled::string_view base_name);

class HotReloader {
public:
    using VersionType = unsigned int;

    // cr_mode defines how much we validate global state transfer between
    // instances. The default is CR_UNSAFE, you can choose another mode by
    // defining CR_HOST, ie.: #define CR_HOST CR_SAFEST

    enum class LoadMode {
        kSafest = 0, // validate address and size of the state section,
                     // if anything changes the load will rollback
        kSafe = 1,   // validate only the size of the state section,
                     // if it changes the load will rollback
        kUnsafe = 2, // don't validate anything but that the size of section fits
                     // may not be identical though
        kDisable = 3,// disable the auto state transfer
    };

    enum class Operation {
        kLoad   = 0,
        kStep   = 1,
        kUnload = 2,
        kClose  = 3,
    };

    enum class Failure {
        kOk                = 0,
        kSegmentationFault = 1,// SIGSEGV
        kIllegal           = 2,// SIGILL
        kAbort             = 3,// SIGBRT
        kMisalign          = 4,// SIGBUS
        kBounds            = 5,// ARRAY BOUNDS EXEEDED
        kStackOverflow     = 6,
        kStateInvalidated  = 7,
        kBadImage          = 8,// invalid  binary
        kInitialFailure    = 9,
        kOther             = 10,
        kUser              = 0x100,
    };

    HotReloader(sled::string_view bin_path);
    ~HotReloader();

    bool Initialize();
    /**
     * @brief 如果 Update失败,再次Update将会回滚
     * @force_reload: 如果当前库已经加载,是否reload当前库
     * @ status: 如果不为nullptr,返回当前加载状态
    **/
    sled::optional<Failure> UpdateOrError(bool force_reload = true);
    // bool Rollback();

    void set_userdata(void *data);
    sled::string_view name() const;
    VersionType version() const;
    VersionType last_working_version() const;
    VersionType next_version() const;

private:
    class Impl;
    std::unique_ptr<Impl> impl_;
};
}// namespace sled

#endif// SLED_SYSTEM_HOT_LOADER_H
Loading