diff --git a/include/dsn/utility/fail_point.h b/include/dsn/utility/fail_point.h index d2c100db87..7bf9f9c696 100644 --- a/include/dsn/utility/fail_point.h +++ b/include/dsn/utility/fail_point.h @@ -48,16 +48,16 @@ } \ } while (0) -/// The only entry to define a fail point with `void` function: lambda function must be +/// The only entry to define a fail point with `not return` function: lambda function usually /// return void type. When a fail point is defined, it's referenced via the name. -#define FAIL_POINT_INJECT_VOID_F(name, lambda) \ +#define FAIL_POINT_INJECT_NOT_RETURN_F(name, lambda) \ do { \ if (dsn_likely(!::dsn::fail::_S_FAIL_POINT_ENABLED)) \ break; \ auto __Func = lambda; \ auto __Res = ::dsn::fail::eval(name); \ - if (__Res == nullptr) { \ - __Func(); \ + if (__Res != nullptr) { \ + __Func(*__Res); \ } \ } while (0) diff --git a/src/aio/native_linux_aio_provider.cpp b/src/aio/native_linux_aio_provider.cpp index 184029a05b..6f7be7f2d7 100644 --- a/src/aio/native_linux_aio_provider.cpp +++ b/src/aio/native_linux_aio_provider.cpp @@ -93,7 +93,7 @@ error_code native_linux_aio_provider::write(const aio_context &aio_ctx, } // mock the `ret` to reproduce the `write incomplete` case in the first write - FAIL_POINT_INJECT_VOID_F("aio_pwrite_incomplete", [&]() -> void { + FAIL_POINT_INJECT_NOT_RETURN_F("aio_pwrite_incomplete", [&](string_view s) -> void { if (dsn_unlikely(buffer_offset == 0)) { --ret; } diff --git a/src/aio/test/aio.cpp b/src/aio/test/aio.cpp index 0b62352387..d739178d13 100644 --- a/src/aio/test/aio.cpp +++ b/src/aio/test/aio.cpp @@ -39,7 +39,7 @@ DEFINE_TASK_CODE_AIO(LPC_AIO_TEST, TASK_PRIORITY_COMMON, THREAD_POOL_TEST_SERVER TEST(core, aio) { fail::setup(); - fail::cfg("aio_pwrite_incomplete", "off()"); + fail::cfg("aio_pwrite_incomplete", "void()"); const char *buffer = "hello, world"; int len = (int)strlen(buffer); @@ -148,7 +148,7 @@ TEST(core, aio_share) TEST(core, operation_failed) { fail::setup(); - fail::cfg("aio_pwrite_incomplete", "off()"); + fail::cfg("aio_pwrite_incomplete", "void()"); auto fp = file::open("tmp_test_file", O_WRONLY, 0600); EXPECT_TRUE(fp == nullptr); diff --git a/src/replica/duplication/load_from_private_log.cpp b/src/replica/duplication/load_from_private_log.cpp index 51286ea897..28b0e22e62 100644 --- a/src/replica/duplication/load_from_private_log.cpp +++ b/src/replica/duplication/load_from_private_log.cpp @@ -83,7 +83,7 @@ void load_from_private_log::run() _duplicator->progress().confirmed_decree); repeat(1_s); - FAIL_POINT_INJECT_VOID_F("duplication_sync_complete", [&]() -> void { + FAIL_POINT_INJECT_NOT_RETURN_F("duplication_sync_complete", [&](string_view s) -> void { if (_duplicator->progress().confirmed_decree == invalid_decree) { // set_confirmed_decree(9), the value must be equal (decree_start of // `test_start_duplication` in `load_from_private_log_test.cpp`) -1 diff --git a/src/replica/duplication/test/load_from_private_log_test.cpp b/src/replica/duplication/test/load_from_private_log_test.cpp index 122d79e7b2..e6c471806a 100644 --- a/src/replica/duplication/test/load_from_private_log_test.cpp +++ b/src/replica/duplication/test/load_from_private_log_test.cpp @@ -175,7 +175,7 @@ class load_from_private_log_test : public duplication_test_base fail::setup(); fail::cfg("open_read", "25%1*return()"); fail::cfg("mutation_log_read_log_block", "25%1*return()"); - fail::cfg("duplication_sync_complete", "off()"); + fail::cfg("duplication_sync_complete", "void()"); duplicator->run_pipeline(); duplicator->wait_all(); fail::teardown(); diff --git a/src/utils/fail_point.cpp b/src/utils/fail_point.cpp index cc262ed69c..d71addbfa6 100644 --- a/src/utils/fail_point.cpp +++ b/src/utils/fail_point.cpp @@ -59,6 +59,8 @@ inline const char *task_type_to_string(fail_point::task_type t) return "Return"; case fail_point::Print: return "Print"; + case fail_point::Void: + return "Void"; default: dfatal("unexpected type: %d", t); __builtin_unreachable(); @@ -123,6 +125,8 @@ bool fail_point::parse_from_string(string_view action) _task = Return; } else if (task_type.compare("print") == 0) { _task = Print; + } else if (task_type.compare("void") == 0) { + _task = Void; } else { return false; } @@ -153,6 +157,7 @@ const std::string *fail_point::eval() switch (_task) { case Off: break; + case Void: case Return: return &_arg; case Print: diff --git a/src/utils/fail_point_impl.h b/src/utils/fail_point_impl.h index fbf3071758..2d82716d52 100644 --- a/src/utils/fail_point_impl.h +++ b/src/utils/fail_point_impl.h @@ -44,9 +44,19 @@ struct fail_point { enum task_type { + // `action` contain `off()`, which would `close` the fail_point whose `function` passed will + // not be executed; Off, + // `action` contain `return()`, which would `return` args passed and execute `return` type + // function passed. it's usually used for `FAIL_POINT_INJECT_F` Return, + // `action` contain `print()`, which would only just print `action` string value and ignore + // the `function` passed Print, + // `action` contain `void()`, which would return args and execute `function` passed that + // better mark as `void` type, it's usually used for `FAIL_POINT_INJECT_NOT_RETURN_F` to + // avoid `return` function + Void, }; void set_action(string_view action);