Skip to content

Commit

Permalink
dylib functionality + namespace wrapper and other review comments + s…
Browse files Browse the repository at this point in the history
…uccessful mac & linux compilation
  • Loading branch information
soumiiow committed Feb 1, 2025
1 parent 9dede0e commit ad51fb2
Show file tree
Hide file tree
Showing 14 changed files with 753 additions and 0 deletions.
1 change: 1 addition & 0 deletions velox/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_subdirectory(base)
add_subdirectory(caching)
add_subdirectory(compression)
add_subdirectory(config)
add_subdirectory(dynamic_registry)
add_subdirectory(encode)
add_subdirectory(file)
add_subdirectory(hyperloglog)
Expand Down
21 changes: 21 additions & 0 deletions velox/common/dynamic_registry/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# 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.

velox_add_library(velox_dynamic_function_loader DynamicLibraryLoader.cpp)

velox_link_libraries(velox_dynamic_function_loader PRIVATE velox_exception)

if(${VELOX_BUILD_TESTING})
add_subdirectory(tests)
endif()
46 changes: 46 additions & 0 deletions velox/common/dynamic_registry/DynamicLibraryLoader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 <dlfcn.h>
#include <iostream>
#include "velox/common/base/Exceptions.h"

namespace facebook::velox {

static constexpr const char* kSymbolName = "registry";

void loadDynamicLibrary(const char* fileName) {
// Try to dynamically load the shared library.
void* handler = dlopen(fileName, RTLD_NOW);

if (handler == nullptr) {
VELOX_USER_FAIL("Error while loading shared library: {}", dlerror());
}

// Lookup the symbol.
void* registrySymbol = dlsym(handler, kSymbolName);
auto loadUserLibrary = reinterpret_cast<void (*)()>(registrySymbol);
const char* error = dlerror();

if (error != nullptr) {
VELOX_USER_FAIL("Couldn't find Velox registry symbol: {}", error);
}
// Invoke the registry function.
loadUserLibrary();
LOG(INFO) << "Loaded: " << fileName;
}

} // namespace facebook::velox
42 changes: 42 additions & 0 deletions velox/common/dynamic_registry/DynamicLibraryLoader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/

#pragma once

namespace facebook::velox {

/// Dynamically opens and registers functions defined in a shared library.
///
/// The library being linked needs to provide a function with the following
/// signature:
///
/// void registry();
///
/// The registration function needs to be defined in the root-level namespace,
/// and be enclosed by a extern "C" directive to prevent the compiler from
/// mangling the symbol name.

/// The function uses dlopen to load the shared library.
/// It then searches for the "void registry()" C function which typically
/// contains all the registration code for the UDFs defined in library. After
/// locating the function it executes the registration bringing the UDFs in the
/// scope of the Velox runtime.
///
/// If the same library is loaded twice then a no-op scenerio will happen.

void loadDynamicLibrary(const char* fileName);

} // namespace facebook::velox
32 changes: 32 additions & 0 deletions velox/common/dynamic_registry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Dynamic Loading of Velox Extensions

This generic utility adds extensibility features to load User Defined Functions (UDFs), connectors, or types without having to fork and build Velox, through the use of shared libraries.

## Getting started
1. Create a cpp file for your dynamic library
For dynamically loaded function registration, the format followed is mirrored of that of built-in function registration with some noted differences. Using [MyDynamicTestFunction.cpp](tests/MyDynamicTestFunction.cpp) as an example, the function uses the extern "C" keyword to protect against name mangling. A registry() function call is also necessary here.
Make sure to also include the necessary header file:
```
#include "velox/functions/DynamicUdf.h"
```

2. Register functions dynamically by creating .dylib (MacOS) or .so (Linux) shared libraries.
These shared libraries may be made using CMakeLists like the following:

* Linux

```
add_library(name_of_dynamic_fn SHARED TestFunction.cpp)
target_link_libraries(name_of_dynamic_fn PRIVATE xsimd fmt::fmt)
```
Above, the xsimd and fmt::fmt libraries are required for all necessary symbols to be defined when loading the TestFunction.cpp dynamically
* MacOS:
```
add_library(name_of_dynamic_fn SHARED TestFunction.cpp)
target_link_libraries(name_of_dynamic_fn PRIVATE Folly::folly xsimd)
```
Additionally, the below flag allows symbols to be resolved at runtime:
```
target_link_options(name_of_dynamic_fn PRIVATE "-Wl,-undefined,dynamic_lookup")
```
108 changes: 108 additions & 0 deletions velox/common/dynamic_registry/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# 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.

# To test functions being added by dynamically linked libraries, we compile
# `MyDynamicFunction.cpp` as a small .so library, and use the
# VELOX_TEST_DYNAMIC_LIBRARY_PATH macro to locate the .so binary.

add_library(velox_function_my_dynamic SHARED MyDynamicFunction.cpp)
add_library(velox_overwrite_int_function_my_dynamic SHARED
MyDynamicIntFunctionOverwrite.cpp)
add_library(velox_overwrite_varchar_function_my_dynamic SHARED
MyDynamicVarcharFunctionOverwrite.cpp)
add_library(velox_function_err_my_dynamic SHARED MyDynamicErrFunction.cpp)
add_library(velox_overload_int_function_my_dynamic SHARED
MyDynamicIntFunctionOverload.cpp)
add_library(velox_overload_varchar_function_my_dynamic SHARED
MyDynamicVarcharFunctionOverload.cpp)

if(APPLE)
set(CMAKE_DYLIB_TEST_LINK_LIBRARIES Folly::folly xsimd)
target_link_libraries(velox_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overwrite_int_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overwrite_varchar_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overload_int_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overload_varchar_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_function_err_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_options(velox_function_my_dynamic PRIVATE
"-Wl,-undefined,dynamic_lookup")
target_link_options(velox_overwrite_int_function_my_dynamic PRIVATE
"-Wl,-undefined,dynamic_lookup")
target_link_options(velox_overwrite_varchar_function_my_dynamic PRIVATE
"-Wl,-undefined,dynamic_lookup")
target_link_options(velox_function_err_my_dynamic PRIVATE
"-Wl,-undefined,dynamic_lookup")
target_link_options(velox_overload_int_function_my_dynamic PRIVATE
"-Wl,-undefined,dynamic_lookup")
target_link_options(velox_overload_varchar_function_my_dynamic PRIVATE
"-Wl,-undefined,dynamic_lookup")

else()
set(CMAKE_DYLIB_TEST_LINK_LIBRARIES fmt::fmt xsimd)

target_link_libraries(velox_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overwrite_int_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overwrite_varchar_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_function_err_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overload_int_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

target_link_libraries(velox_overload_varchar_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

endif()

# Here's the actual test which will dynamically load the library defined above.
add_executable(velox_function_dynamic_link_test DynamicLinkTest.cpp)

target_link_libraries(
velox_function_dynamic_link_test
velox_functions_test_lib
velox_dynamic_function_loader
velox_function_registry
xsimd
GTest::gmock
GTest::gtest
GTest::gtest_main)

target_compile_definitions(
velox_function_dynamic_link_test
PRIVATE VELOX_TEST_DYNAMIC_LIBRARY_PATH="${CMAKE_CURRENT_BINARY_DIR}")
target_compile_definitions(
velox_function_dynamic_link_test
PRIVATE
VELOX_TEST_DYNAMIC_LIBRARY_PATH_SUFFIX="${CMAKE_SHARED_LIBRARY_SUFFIX}")

add_test(NAME velox_function_dynamic_link_test
COMMAND velox_function_dynamic_link_test)
Loading

0 comments on commit ad51fb2

Please sign in to comment.