Skip to content

Commit

Permalink
Replicate thread deadlock behavior in Node loader
Browse files Browse the repository at this point in the history
  • Loading branch information
devraymondsh committed May 13, 2024
1 parent 799ddb8 commit 924d5ac
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 0 deletions.
1 change: 1 addition & 0 deletions source/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ add_subdirectory(metacall_node_event_loop_signal_test)
#add_subdirectory(metacall_node_default_export_test) # TODO: This is a feature in order to export by default all functions if there is no module.exports (bootstrap.js)
add_subdirectory(metacall_node_call_test)
add_subdirectory(metacall_node_inline_test)
add_subdirectory(metacall_node_multithread_deadlock_test)
add_subdirectory(metacall_node_async_test)
add_subdirectory(metacall_node_async_multiple_test)
add_subdirectory(metacall_node_reentrant_test)
Expand Down
147 changes: 147 additions & 0 deletions source/tests/metacall_node_multithread_deadlock_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Check if this loader is enabled
if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE)
return()
endif()

#
# Executable name and options
#

# Target name
set(target metacall-node-multithread-deadlock-test)
message(STATUS "Test ${target}")

#
# Compiler warnings
#

include(Warnings)

#
# Compiler security
#

include(SecurityFlags)

#
# Sources
#

set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}")
set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source")

set(sources
${source_path}/main.cpp
${source_path}/metacall_node_multithread_deadlock_test.cpp
)

# Group source files
set(header_group "Header Files (API)")
set(source_group "Source Files")
source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$"
${header_group} ${headers})
source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$"
${source_group} ${sources})

#
# Create executable
#

# Build executable
add_executable(${target}
${sources}
)

# Create namespaced alias
add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target})

#
# Project options
#

set_target_properties(${target}
PROPERTIES
${DEFAULT_PROJECT_OPTIONS}
FOLDER "${IDE_FOLDER}"
)

#
# Include directories
#

target_include_directories(${target}
PRIVATE
${DEFAULT_INCLUDE_DIRECTORIES}
${PROJECT_BINARY_DIR}/source/include
)

#
# Libraries
#

target_link_libraries(${target}
PRIVATE
${DEFAULT_LIBRARIES}

GTest

${META_PROJECT_NAME}::metacall
)

#
# Compile definitions
#

target_compile_definitions(${target}
PRIVATE
${DEFAULT_COMPILE_DEFINITIONS}
)

#
# Compile options
#

target_compile_options(${target}
PRIVATE
${DEFAULT_COMPILE_OPTIONS}
)

#
# Linker options
#

target_link_libraries(${target}
PRIVATE
${DEFAULT_LINKER_OPTIONS}
)

#
# Define test
#

add_test(NAME ${target}
COMMAND $<TARGET_FILE:${target}>
)

#
# Define dependencies
#

add_dependencies(${target}
node_loader
)

#
# Define test properties
#

set_property(TEST ${target}
PROPERTY LABELS ${target}
)

include(TestEnvironmentVariables)

test_environment_variables(${target}
""
${TESTS_ENVIRONMENT_VARIABLES}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* MetaCall Library by Parra Studios
* A library for providing a foreign function interface calls.
*
* Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <gtest/gtest.h>

int main(int argc, char *argv[])
{
::testing::InitGoogleTest(&argc, argv);

return RUN_ALL_TESTS();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* MetaCall Library by Parra Studios
* A library for providing a foreign function interface calls.
*
* Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <gtest/gtest.h>

#include <metacall/metacall.h>
#include <metacall/metacall_loaders.h>
#include <metacall/metacall_value.h>

#include <thread>

class metacall_node_multithread_deadlock_test : public testing::Test
{
public:
};

TEST_F(metacall_node_multithread_deadlock_test, DefaultConstructor)
{
metacall_print_info();

ASSERT_EQ((int)0, (int)metacall_initialize());

std::atomic<unsigned int> callbacks_executed(0);

/* NodeJS */
#if defined(OPTION_BUILD_LOADERS_NODE)
{
/* Test if the loader can cause a data race */
auto runner = []()
{
/* Running it over and over again making sure data race is attempted */
for (size_t i = 0; i < 2000000; i++)
{
void *future = metacall("j");

EXPECT_NE((void *)NULL, (void *)future);

EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE);
void *ret = metacall_await_future(
metacall_value_to_future(future),
[](void *result, void *) -> void * {
EXPECT_NE((void *)NULL, (void *)result);

EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE);

EXPECT_EQ((double)34.0, (double)metacall_value_to_double(result));

// ++success_callbacks;

return metacall_value_create_double(155.0);
},
[](void *, void *) -> void * {
int this_should_never_be_executed = 0;

EXPECT_EQ((int)1, (int)this_should_never_be_executed);

return NULL;
},
NULL);

metacall_value_destroy(future);
metacall_value_destroy(ret);
}
};

std::thread t1 (runner);
std::thread t2 (runner);
std::thread t3 (runner);
std::thread t4 (runner);
std::thread t5 (runner);
std::thread t6 (runner);
std::thread t7 (runner);
std::thread t8 (runner);

t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
}
#endif /* OPTION_BUILD_LOADERS_NODE */

EXPECT_EQ((int)0, (int)metacall_destroy());

EXPECT_EQ((unsigned int)3, (unsigned int)callbacks_executed);
}

0 comments on commit 924d5ac

Please sign in to comment.