system.cpp 4.4 KB
Newer Older
1 2
#include "megbrain/system.h"
#include "megbrain/test/helper.h"
M
Megvii Engine Team 已提交
3
#include "megbrain/utils/timer.h"
4

M
Megvii Engine Team 已提交
5 6
#if MGB_BUILD_SLIM_SERVING || defined(ANDROID) || defined(WIN32) || defined(IOS) || \
        defined(__APPLE__)
7 8 9 10 11 12 13 14 15 16 17 18
#pragma message "sys test disabled on unsupported platforms"

#else

#include <unistd.h>

using namespace mgb;
using namespace sys;

using Result = TimedFuncInvoker::Result;
using Param = TimedFuncInvoker::Param;
namespace {
M
Megvii Engine Team 已提交
19 20 21 22
struct SleepParam {
    double init_time = 0;
    double sleep_time = 0;
};
23

M
Megvii Engine Team 已提交
24 25 26 27 28 29 30
//! double sleep(double secs); return secs * 2 for check
Result func_sleep(const Param& param) {
    auto sp = param.as_single_pod<SleepParam>();
    mgb_assert(sp.sleep_time >= 0);
    usleep(sp.sleep_time * 1e6);
    return Result::from_pod(sp.sleep_time * 2);
}
31

M
Megvii Engine Team 已提交
32 33 34 35 36
void func_sleep_init(const Param& param) {
    auto sp = param.as_single_pod<SleepParam>();
    mgb_assert(sp.init_time >= 0);
    if (sp.init_time > 0) {
        usleep(sp.init_time * 1e6);
37 38 39
    }
}

M
Megvii Engine Team 已提交
40 41
}  // namespace

42 43
namespace mgb {
namespace sys {
M
Megvii Engine Team 已提交
44 45 46 47 48 49 50
class TimedFuncInvokerTest {
    static auto do_make(bool has_init) {
        auto ins = TimedFuncInvoker::make_test_ins();
        if (has_init) {
            ins->register_func(0, func_sleep, func_sleep_init);
        } else {
            ins->register_func(0, func_sleep);
51
        }
M
Megvii Engine Team 已提交
52 53
        return ins;
    }
54

M
Megvii Engine Team 已提交
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
public:
    static auto make_ins(bool has_init = false) {
        auto ins = do_make(has_init);
        auto do_fork = [has_init](const std::string& arg) {
            auto pid = fork();
            if (pid)
                return pid;
            auto ins = do_make(has_init);
            ins->fork_exec_impl_mainloop(arg.c_str());
            mgb_assert(0);
        };
        ins->set_fork_exec_impl(do_fork);
        return ins;
    }
};
}  // namespace sys
}  // namespace mgb
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

TEST(TestSystem, TimedFuncInvokerBasic) {
    auto ins = TimedFuncInvokerTest::make_ins();
    double time = 0.1;
    SleepParam sleep_param{0., time};
    RealTimer timer;
    auto ret = ins->invoke(0, Param::from_pod(sleep_param), time * 2);
    auto tused = timer.get_secs();
    ASSERT_GT(tused, time);
    ASSERT_EQ(ret.val().as_single_pod<double>(), time * 2);

    // check max time in the second invocation
    timer.reset();
    ret = ins->invoke(0, Param::from_pod(sleep_param), time * 2);
    tused = timer.get_secs();
    ASSERT_GT(tused, time);
    ASSERT_LT(tused, time * 2);
    ASSERT_EQ(ret.val().as_single_pod<double>(), time * 2);
};

TEST(TestSystem, TimedFuncInvokerTimeout) {
    auto ins = TimedFuncInvokerTest::make_ins();
    double time = 0.1;
    SleepParam sleep_param{0., time};
    auto ret = ins->invoke(0, Param::from_pod(sleep_param), time / 2);
    ASSERT_FALSE(ret.valid());
}

TEST(TestSystem, TimedFuncInvokerThreadSafety) {
    // since TimedFuncInvoker uses a singleton, it is important to be
    // thread-safe
    auto ins = TimedFuncInvokerTest::make_ins();

    std::atomic_size_t nr_ready{0};

M
Megvii Engine Team 已提交
107 108
    auto worker = [&](double* ret, double sleep_time, double timeout) {
        ++nr_ready;
109 110 111
        while (nr_ready.load() != 2)
            std::this_thread::yield();
        SleepParam sleep_param{0., sleep_time};
M
Megvii Engine Team 已提交
112
        for (int i = 0; i < 5; ++i) {
113 114 115 116 117 118 119 120
            auto result = ins->invoke(0, Param::from_pod(sleep_param), timeout);
            if (!result.valid())
                *ret = -1;
            else
                *ret = result->as_single_pod<double>();
        }
    };
    double ret0, ret1;
M
Megvii Engine Team 已提交
121
    std::thread th0{worker, &ret0, 0.1, 0.15}, th1{worker, &ret1, 0.2, 0.15};
122 123 124 125 126 127 128 129 130 131 132
    th0.join();
    th1.join();

    ASSERT_EQ(0.2, ret0);
    ASSERT_EQ(-1., ret1);
}

TEST(TestSystem, TimedFuncInvokerException) {
    auto ins = TimedFuncInvokerTest::make_ins();
    double time = -1;
    SleepParam sleep_param{0., time};
M
Megvii Engine Team 已提交
133 134
    ASSERT_THROW(
            ins->invoke(0, Param::from_pod(sleep_param), 0.1),
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
            TimedFuncInvoker::RemoteError);
}

TEST(TestSystem, TimedFuncInvokerInitFunc) {
    auto ins = TimedFuncInvokerTest::make_ins(true);
    SleepParam sleep_param;
    sleep_param.init_time = 0.1;
    sleep_param.sleep_time = 0.1;
    RealTimer timer;
    auto ret = ins->invoke(0, Param::from_pod(sleep_param), 0.15);
    ASSERT_GT(timer.get_secs(), 0.2);
    ASSERT_EQ(ret.val().as_single_pod<double>(), 0.2);
    timer.reset();
    ret = ins->invoke(0, Param::from_pod(sleep_param), 0.05);
    ASSERT_FALSE(ret.valid());
}

M
Megvii Engine Team 已提交
152
#endif  // disable tests on some platforms
153 154

// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}