├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── lib ├── base │ ├── asm.h │ ├── common.h │ ├── init.cpp │ ├── init.h │ ├── logging.cpp │ ├── logging.h │ ├── macro.h │ ├── thread.cpp │ └── thread.h ├── common │ ├── config.cpp │ ├── config.h │ ├── http_status.cpp │ ├── http_status.h │ ├── protocol.h │ ├── stat.h │ ├── subprocess.cpp │ ├── subprocess.h │ ├── time.h │ ├── uv.cpp │ └── uv.h ├── ipc │ ├── base.cpp │ ├── base.h │ ├── fifo.cpp │ ├── fifo.h │ ├── shm_region.cpp │ ├── shm_region.h │ ├── spsc_queue-inl.h │ └── spsc_queue.h ├── rdma │ ├── bit_map.cpp │ ├── bit_map.h │ ├── infinity.cpp │ ├── infinity.h │ ├── queue_pair.cpp │ ├── queue_pair.h │ ├── rdma_macro.h │ ├── shared_memory.cpp │ └── shared_memory.h ├── runtime │ ├── event_driven_worker.cpp │ ├── event_driven_worker.h │ ├── worker_lib.cpp │ └── worker_lib.h ├── server │ ├── connection_base.h │ ├── io_worker.cpp │ ├── io_worker.h │ ├── poller.cpp │ ├── poller.h │ ├── server_base.cpp │ └── server_base.h └── utils │ ├── appendable_buffer.h │ ├── bench.cpp │ ├── bench.h │ ├── bst.h │ ├── buffer_pool.h │ ├── docker.cpp │ ├── docker.h │ ├── dynamic_library.h │ ├── env_variables.h │ ├── exp_moving_avg.h │ ├── fs.cpp │ ├── fs.h │ ├── http_parser.c │ ├── http_parser.h │ ├── io.h │ ├── object_pool.h │ ├── perf_event.cpp │ ├── perf_event.h │ ├── procfs.cpp │ ├── procfs.h │ ├── random.cpp │ ├── random.h │ ├── socket.cpp │ └── socket.h └── src ├── dpu ├── agent │ ├── CMakeLists.txt │ ├── agent.cpp │ ├── agent.h │ ├── agent_main.cpp │ ├── engine_connection.cpp │ ├── engine_connection.h │ ├── gateway_connection.cpp │ └── gateway_connection.h └── gateway │ ├── CMakeLists.txt │ ├── engine_connection.cpp │ ├── engine_connection.h │ ├── func_call_context.h │ ├── gateway_main.cpp │ ├── grpc_connection.cpp │ ├── grpc_connection.h │ ├── http_connection.cpp │ ├── http_connection.h │ ├── server.cpp │ └── server.h └── host ├── engine ├── CMakeLists.txt ├── agent_connection.cpp ├── agent_connection.h ├── dispatcher.cpp ├── dispatcher.h ├── engine.cpp ├── engine.h ├── engine_main.cpp ├── gateway_connection.cpp ├── gateway_connection.h ├── message_connection.cpp ├── message_connection.h ├── monitor.cpp ├── monitor.h ├── tracer.cpp ├── tracer.h ├── worker_manager.cpp └── worker_manager.h ├── launcher ├── CMakeLists.txt ├── engine_connection.cpp ├── engine_connection.h ├── func_process.cpp ├── func_process.h ├── launcher.cpp ├── launcher.h └── launcher_main.cpp └── worker ├── cpp ├── CMakeLists.txt ├── func_worker.cpp ├── func_worker.h ├── func_worker_interface.h └── func_worker_main.cpp └── python ├── CMakeLists.txt ├── faas └── __init__.py └── module.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug/ 2 | .idea/ 3 | src/host/worker/python/faas/_faas_native.so -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/libuv"] 2 | path = deps/libuv 3 | url = https://github.com/libuv/libuv.git 4 | [submodule "deps/http-parser"] 5 | path = deps/http-parser 6 | url = https://github.com/nodejs/http-parser.git 7 | [submodule "deps/abseil-cpp"] 8 | path = deps/abseil-cpp 9 | url = https://github.com/abseil/abseil-cpp.git 10 | [submodule "deps/json"] 11 | path = deps/json 12 | url = https://github.com/nlohmann/json.git 13 | [submodule "deps/nghttp2"] 14 | path = deps/nghttp2 15 | url = https://github.com/nghttp2/nghttp2.git 16 | [submodule "deps/GSL"] 17 | path = deps/GSL 18 | url = https://github.com/microsoft/GSL.git 19 | [submodule "deps/fmt"] 20 | path = deps/fmt 21 | url = https://github.com/fmtlib/fmt.git 22 | [submodule "deps/pybind11"] 23 | path = deps/pybind11 24 | url = https://github.com/pybind/pybind11.git 25 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Fuyao) 2 | cmake_minimum_required(VERSION 3.16) 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | # enable build library 7 | set(PYTHON_LIBRARY_BUILD OFF) 8 | set(CPP_LIBRARY_BUILD ON) 9 | 10 | # header 11 | include_directories(${BASE_DIR}/lib) 12 | include_directories(${BASE_DIR}/src) 13 | 14 | add_compile_options(-fPIC) 15 | add_compile_options(-fexceptions) 16 | 17 | # deps 18 | set(ENABLE_LIB_ONLY 1) # used by nghttp2 to disable unnecessary component builds 19 | 20 | add_subdirectory(${BASE_DIR}/deps/abseil-cpp abseil) 21 | add_subdirectory(${BASE_DIR}/deps/fmt fmt) 22 | add_subdirectory(${BASE_DIR}/deps/GSL gsl) 23 | add_subdirectory(${BASE_DIR}/deps/json json) 24 | add_subdirectory(${BASE_DIR}/deps/libuv libuv) 25 | add_subdirectory(${BASE_DIR}/deps/nghttp2 nghttp2) 26 | 27 | # rdma verbs 28 | set(ENV{PKG_CONFIG_PATH} /usr/lib/x86_64-linux-gnu/pkgconfig) 29 | set(ENV{PKG_CONFIG_EXECUTABLE} /usr/bin/pkg-config) 30 | find_package(PkgConfig REQUIRED) 31 | pkg_check_modules(LIBIBVERBS REQUIRED IMPORTED_TARGET libibverbs) 32 | 33 | # dpu submodule 34 | add_subdirectory(${BASE_DIR}/src/dpu/gateway gateway) 35 | add_subdirectory(${BASE_DIR}/src/dpu/agent agent) 36 | 37 | # host submodule 38 | add_subdirectory(${BASE_DIR}/src/host/engine engine) 39 | add_subdirectory(${BASE_DIR}/src/host/launcher launcher) 40 | 41 | # cpp runtime library 42 | if (CPP_LIBRARY_BUILD) 43 | set(FUNC_WORKER_CPP_LIBRARIES_NAME worker_cpp) 44 | add_subdirectory(${BASE_DIR}/src/host/worker/cpp/ worker_cpp) 45 | endif() 46 | 47 | # python runtime library 48 | if (PYTHON_LIBRARY_BUILD) 49 | add_subdirectory(${BASE_DIR}/deps/pybind11 pybind11) 50 | 51 | # Replace it based on the actual situation 52 | set(PYTHON_INCLUDE_DIR /usr/include/python3.8) 53 | set(LIBRARY_OUTPUT_PATH ${BASE_DIR}/src/host/worker/python/faas) 54 | set(FUNC_WORKER_PYTHON_OUTPUT_NAME _faas_native) 55 | add_subdirectory(${BASE_DIR}/src/host/worker/python/ worker_python) 56 | endif () 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fuyao 2 | --- 3 | 4 | ## structure 5 | 6 |

 7 | Fuyao
 8 | |---deps
 9 | |---lib
10 |     |---base
11 |     |---common
12 |     |---ipc
13 |     |---rdma
14 |     |---runtime
15 |     |---server
16 |     |---utils
17 | |---src
18 |     |---dpu
19 |         |---agent
20 |         |---gateway
21 |     |---host
22 |         |---engine
23 |         |---launcher
24 |         |---worker
25 |             |---cpp
26 | 
27 | 28 | ## How to use it? 29 | 30 | [Fuyao-benchmarks](https://github.com/guoweiu/Fuyao-benchmarks) -------------------------------------------------------------------------------- /lib/base/asm.h: -------------------------------------------------------------------------------- 1 | //#pragma once 2 | #ifndef LUMINE_ASM_H 3 | #define LUMINE_ASM_H 4 | 5 | namespace faas { 6 | 7 | inline void asm_volatile_memory() { 8 | asm volatile("" : : : "memory"); 9 | } 10 | 11 | inline void asm_volatile_pause() { 12 | asm volatile("pause"); 13 | } 14 | 15 | } // namespace faas 16 | 17 | #endif //LUMINE_ASM_H -------------------------------------------------------------------------------- /lib/base/common.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_COMMON_H 2 | #define LUMINE_COMMON_H 3 | 4 | // C includes 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // C++ includes 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // STL containers 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | // Linux /usr/include/ 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | // Fmtlib 54 | #include 55 | #include 56 | 57 | // Guidelines Support Library (GSL) 58 | #include 59 | #include 60 | 61 | // Nghttp2 62 | #include 63 | 64 | // Absl 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | 86 | // Libuv 87 | #include 88 | 89 | namespace std { 90 | using gsl::span; 91 | } // namespace std 92 | 93 | #endif //LUMINE_COMMON_H 94 | -------------------------------------------------------------------------------- /lib/base/init.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/logging.h" 3 | #include "base/thread.h" 4 | #include "base/common.h" 5 | 6 | ABSL_FLAG(int, v, 0, "Show all VLOG(m) messages for m <= this."); 7 | 8 | namespace faas::base { 9 | 10 | void InitMain(int argc, char *argv[], 11 | std::vector *positional_args) { 12 | absl::InitializeSymbolizer(argv[0]); 13 | absl::FailureSignalHandlerOptions options; 14 | absl::InstallFailureSignalHandler(options); 15 | 16 | std::vector unparsed_args = absl::ParseCommandLine(argc, argv); 17 | logging::Init(absl::GetFlag(FLAGS_v)); 18 | 19 | if (positional_args == nullptr && unparsed_args.size() > 1) { 20 | LOG(FATAL) << "This program does not accept positional arguments"; 21 | } 22 | 23 | if (positional_args != nullptr) { 24 | positional_args->clear(); 25 | for (size_t i = 1; i < unparsed_args.size(); i++) { 26 | positional_args->push_back(unparsed_args[i]); 27 | } 28 | } 29 | 30 | Thread::RegisterMainThread(); 31 | } 32 | 33 | } // namespace faas 34 | -------------------------------------------------------------------------------- /lib/base/init.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_INIT_H 2 | #define LUMINE_INIT_H 3 | 4 | #include "common.h" 5 | 6 | namespace faas::base { 7 | 8 | void InitMain(int argc, char *argv[], std::vector *positional_args = nullptr); 9 | 10 | } // namespace faas 11 | 12 | #endif //LUMINE_INIT_H -------------------------------------------------------------------------------- /lib/base/macro.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_MACRO_H 2 | #define LUMINE_MACRO_H 3 | 4 | #include // for PIPE_BUF 5 | 6 | // Put this in the declarations for a class to be uncopyable. 7 | #define DISALLOW_COPY(TypeName) \ 8 | TypeName(const TypeName&) = delete 9 | 10 | // Put this in the declarations for a class to be unassignable. 11 | #define DISALLOW_ASSIGN(TypeName) \ 12 | TypeName& operator=(const TypeName&) = delete 13 | 14 | // Put this in the declarations for a class to be uncopyable and unassignable. 15 | #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ 16 | DISALLOW_COPY(TypeName); \ 17 | DISALLOW_ASSIGN(TypeName) 18 | 19 | // A macro to disallow all the implicit constructors, namely the 20 | // default constructor, copy constructor and operator= functions. 21 | // This is especially useful for classes containing only static methods. 22 | #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ 23 | TypeName() = delete; \ 24 | DISALLOW_COPY_AND_ASSIGN(TypeName) 25 | 26 | #define __FAAS_PREDICT_FALSE(x) __builtin_expect(x, 0) 27 | #define __FAAS_PREDICT_TRUE(x) __builtin_expect(false || (x), true) 28 | 29 | // We're always on x86_64 30 | #define __FAAS_CACHE_LINE_SIZE 64 31 | #define __FAAS_PAGE_SIZE 4096 32 | 33 | #ifndef __FAAS_FILE_CREAT_MODE 34 | #define __FAAS_FILE_CREAT_MODE 0664 35 | #endif 36 | 37 | #ifndef __FAAS_DIR_CREAT_MODE 38 | #define __FAAS_DIR_CREAT_MODE 0775 39 | #endif 40 | 41 | #ifndef __FAAS_MESSAGE_SIZE 42 | #define __FAAS_MESSAGE_SIZE 1024 43 | #endif 44 | static_assert(__FAAS_MESSAGE_SIZE <= PIPE_BUF, 45 | "__FAAS_MESSAGE_SIZE cannot exceed PIPE_BUF"); 46 | static_assert(__FAAS_MESSAGE_SIZE >= __FAAS_CACHE_LINE_SIZE * 2, 47 | "__FAAS_MESSAGE_SIZE is too small"); 48 | 49 | #endif //LUMINE_MACRO_H -------------------------------------------------------------------------------- /lib/base/thread.cpp: -------------------------------------------------------------------------------- 1 | #include "base/common.h" 2 | #include "base/thread.h" 3 | #include "utils/env_variables.h" 4 | 5 | namespace faas::base { 6 | 7 | thread_local Thread *Thread::current_ = nullptr; 8 | 9 | namespace { 10 | pid_t gettid() { 11 | return syscall(SYS_gettid); 12 | } 13 | } 14 | 15 | void Thread::Start(void *arg, bool non_blocking) { 16 | state_.store(kStarting); 17 | thread_tuple_ = new ThreadTuple{this, arg, non_blocking}; 18 | CHECK_EQ(pthread_create(&pthread_, nullptr, &Thread::StartRoutine, thread_tuple_), 0); 19 | started_.WaitForNotification(); 20 | DCHECK(state_.load() == kRunning || state_.load() == kFinished); 21 | } 22 | 23 | void Thread::Join() { 24 | State state = state_.load(); 25 | if (state == kFinished) { 26 | return; 27 | } 28 | DCHECK(state == kRunning); 29 | CHECK_EQ(pthread_join(pthread_, nullptr), 0); 30 | } 31 | 32 | void Thread::Run(void *arg, bool non_blocking) { 33 | tid_ = gettid(); 34 | non_blocking ? state_.store(kFinished) : state_.store(kRunning); 35 | started_.Notify(); 36 | DLOG(INFO) << "Start thread: " << name_; 37 | arg ? fn_with_arg_(arg) : fn_(); 38 | state_.store(kFinished); 39 | } 40 | 41 | void Thread::MarkThreadCategory(absl::string_view category) { 42 | CHECK(current_ == this); 43 | // Set cpuset 44 | std::string cpuset_var_name(fmt::format("FAAS_{}_THREAD_CPUSET", category)); 45 | std::string cpuset_str(utils::GetEnvVariable(cpuset_var_name)); 46 | if (!cpuset_str.empty()) { 47 | cpu_set_t set; 48 | CPU_ZERO(&set); 49 | for (const std::string_view &cpu_str: absl::StrSplit(cpuset_str, ",")) { 50 | int cpu; 51 | CHECK(absl::SimpleAtoi(cpu_str, &cpu)); 52 | CPU_SET(cpu, &set); 53 | } 54 | if (sched_setaffinity(0, sizeof(set), &set) != 0) { 55 | PLOG(FATAL) << "Failed to set CPU affinity to " << cpuset_str; 56 | } else { 57 | LOG(INFO) << "Successfully set CPU affinity of current thread to " << cpuset_str; 58 | } 59 | } else { 60 | LOG(INFO) << "Does not find cpuset setting for " << category 61 | << " threads (can be set by " << cpuset_var_name << ")"; 62 | } 63 | // Set nice 64 | std::string nice_var_name(fmt::format("FAAS_{}_THREAD_NICE", category)); 65 | std::string nice_str(utils::GetEnvVariable(nice_var_name)); 66 | if (!nice_str.empty()) { 67 | int nice_value; 68 | CHECK(absl::SimpleAtoi(nice_str, &nice_value)); 69 | int current_nice = nice(0); 70 | errno = 0; 71 | if (nice(nice_value - current_nice) == -1 && errno != 0) { 72 | PLOG(FATAL) << "Failed to set nice to " << nice_value; 73 | } else { 74 | CHECK_EQ(nice(0), nice_value); 75 | LOG(INFO) << "Successfully set nice of current thread to " << nice_value; 76 | } 77 | } else { 78 | LOG(INFO) << "Does not find nice setting for " << category 79 | << " threads (can be set by " << nice_var_name << ")"; 80 | } 81 | } 82 | 83 | void *Thread::StartRoutine(void *arg) { 84 | auto tuple = reinterpret_cast(arg); 85 | current_ = tuple->thread; 86 | current_->Run(tuple->arg, tuple->non_blocking); 87 | return nullptr; 88 | } 89 | 90 | void Thread::RegisterMainThread() { 91 | auto thread = new Thread("Main", std::function()); 92 | thread->state_.store(kRunning); 93 | thread->tid_ = gettid(); 94 | thread->pthread_ = pthread_self(); 95 | current_ = thread; 96 | LOG(INFO) << "Register main thread: tid=" << thread->tid_; 97 | } 98 | 99 | } // namespace faas 100 | -------------------------------------------------------------------------------- /lib/base/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_THREAD_H 2 | #define LUMINE_THREAD_H 3 | 4 | #include 5 | 6 | #include "base/common.h" 7 | #include "macro.h" 8 | #include "logging.h" 9 | 10 | namespace faas::base { 11 | 12 | // forward declaration 13 | class Thread; 14 | 15 | struct ThreadTuple{ 16 | Thread *thread; 17 | void *arg; 18 | bool non_blocking; 19 | }; 20 | 21 | class Thread { 22 | public: 23 | Thread(std::string_view name, std::function fn) 24 | : state_(kCreated), name_(std::string(name)), fn_(std::move(fn)), tid_(-1) {} 25 | 26 | Thread(std::string_view name, std::function fn) 27 | : state_(kCreated), name_(std::string(name)), fn_with_arg_(std::move(fn)), tid_(-1) {} 28 | 29 | virtual ~Thread() { 30 | State state = state_.load(); 31 | DCHECK(state == kCreated || state == kFinished); 32 | 33 | delete thread_tuple_; 34 | } 35 | 36 | void Start(void *arg = nullptr, bool non_blocking = false); 37 | 38 | void Join(); 39 | 40 | void MarkThreadCategory(absl::string_view category); 41 | 42 | const char *name() const { return name_.c_str(); } 43 | 44 | int tid() { return tid_; } 45 | 46 | static Thread *current() { 47 | DCHECK(current_ != nullptr); 48 | return current_; 49 | } 50 | 51 | static void RegisterMainThread(); 52 | 53 | protected: 54 | enum State { 55 | kCreated, kStarting, kRunning, kFinished 56 | }; 57 | 58 | std::atomic state_; 59 | std::string name_; 60 | std::function fn_; 61 | std::function fn_with_arg_; 62 | int tid_; 63 | ThreadTuple *thread_tuple_; 64 | 65 | absl::Notification started_; 66 | pthread_t pthread_; 67 | 68 | static thread_local Thread *current_; 69 | 70 | virtual void Run(void *arg, bool non_blocking); 71 | 72 | static void *StartRoutine(void *arg); 73 | 74 | DISALLOW_COPY_AND_ASSIGN(Thread); 75 | }; 76 | 77 | } // namespace faas 78 | 79 | #endif //LUMINE_THREAD_H -------------------------------------------------------------------------------- /lib/common/config.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_FUNC_CONFIG_H 2 | #define LUMINE_FUNC_CONFIG_H 3 | 4 | #include "base/common.h" 5 | #include "common/protocol.h" 6 | 7 | namespace faas::config { 8 | 9 | struct System { 10 | uint64_t timeout_as_ms_for_poller; 11 | uint64_t timeout_as_ms_for_drc; 12 | uint64_t poll_time_internal_as_us; 13 | uint64_t memory_region_size; 14 | uint64_t message_size_of_fabric; 15 | }; 16 | 17 | struct Gateway { 18 | std::string gateway_ip; // Address to listen 19 | int http_port; // Port for HTTP connections 20 | int grpc_port; // Port for gRPC connections 21 | int engine_conn_port; // Port for engine connections 22 | int num_io_workers; // Number of IO workers 23 | int listen_backlog; 24 | }; 25 | 26 | struct EngineEntry { 27 | uint16_t guid; // Engine guid 28 | std::string engine_ip; // Engine ip address, will be used by agent 29 | int agent_conn_port; // Port for agent connections 30 | int func_worker_use_engine_socket; // If set not 0, Launcher and FuncWorker will communicate with engine via TCP socket 31 | int engine_tcp_port; // Engine TCP port for Launcher and FuncWorker 32 | int num_io_workers; // Number of IO workers 33 | int listen_backlog; 34 | int gateway_conn_per_worker; // Number of gateway connections per IO worker 35 | std::string root_path_for_ipc; // Root directory for IPCs used by FaaS 36 | std::string device_name; // RDMA device name 37 | int device_port; // RDMA port 38 | int device_gid_index; // RDMA local port gid index 39 | uint64_t time_internal_for_reclaim_drc; // units: ms 40 | }; 41 | 42 | struct AgentEntry { 43 | uint16_t guid; // Agent guid 44 | std::string device_name; // RDMA device name 45 | int device_port; // RDMA port 46 | int device_gid_index; // RDMA local port gid index 47 | int num_io_workers; // Number of IO workers 48 | uint16_t bind_engine_guid; // Indicates the binding relationship between the DPU-Agent and Host-Engine 49 | }; 50 | 51 | struct FunctionEntry { 52 | std::string func_name; 53 | int func_id; 54 | int min_workers; 55 | int max_workers; 56 | bool allow_http_get; 57 | bool qs_as_input; 58 | bool is_grpc_service; 59 | std::string grpc_service_name; 60 | std::vector grpc_methods; 61 | std::unordered_map grpc_method_ids; 62 | }; 63 | 64 | class Config { 65 | public: 66 | static constexpr int kMaxFuncId = (1 << protocol::kFuncIdBits) - 1; 67 | static constexpr int kMaxMethodId = (1 << protocol::kMethodIdBits) - 1; 68 | 69 | bool Load(std::string_view json_contents); 70 | 71 | const FunctionEntry *FindFunctionByFuncName(std::string_view func_name); 72 | 73 | const FunctionEntry * FindFunctionByFuncId(int func_id) const; 74 | 75 | const Gateway *GetGatewayConfig(); 76 | 77 | const System *GetSystemConfig(); 78 | 79 | const EngineEntry *GetEngineConfigByGuid(uint64_t guid); 80 | 81 | const AgentEntry *GetAgentConfigByGuid(uint64_t guid); 82 | 83 | std::vector GetEngineList(); 84 | 85 | 86 | private: 87 | // for system 88 | std::unique_ptr system_; 89 | 90 | // for gateway 91 | std::unique_ptr gateway_; 92 | 93 | // for engines 94 | std::unordered_map> engine_entries_; 95 | std::vector engines_; 96 | 97 | // for agents 98 | std::unordered_map> agent_entries_; 99 | 100 | std::unordered_set bind_engines_; 101 | 102 | // for functions 103 | std::vector> func_entries_{}; 104 | std::unordered_map entries_by_func_name_{}; 105 | std::unordered_map entries_by_func_id_{}; 106 | 107 | static bool ValidateFuncId(int func_id); 108 | 109 | static bool ValidateFuncName(std::string_view func_name); 110 | 111 | //DISALLOW_COPY_AND_ASSIGN(Config); 112 | }; 113 | 114 | } // namespace faas 115 | 116 | #endif //LUMINE_FUNC_CONFIG_H -------------------------------------------------------------------------------- /lib/common/http_status.cpp: -------------------------------------------------------------------------------- 1 | #include "common/http_status.h" 2 | #include "base/logging.h" 3 | 4 | namespace faas { 5 | 6 | std::string_view GetHttpStatusString(HttpStatus status) { 7 | switch (status) { 8 | case HttpStatus::OK: 9 | return "200 OK"; 10 | case HttpStatus::BAD_REQUEST: 11 | return "400 Bad Request"; 12 | case HttpStatus::NOT_FOUND: 13 | return "404 Not Found"; 14 | case HttpStatus::INTERNAL_SERVER_ERROR: 15 | return "500 Internal Server Error"; 16 | default: 17 | LOG(FATAL) << "Unknown HTTP status: " << static_cast(status); 18 | } 19 | } 20 | 21 | } // namespace faas 22 | -------------------------------------------------------------------------------- /lib/common/http_status.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_HTTP_STATUS 2 | #define LUMINE_HTTP_STATUS 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas { 7 | 8 | enum class HttpStatus : uint16_t { 9 | OK = 200, 10 | BAD_REQUEST = 400, 11 | NOT_FOUND = 404, 12 | INTERNAL_SERVER_ERROR = 500 13 | }; 14 | 15 | std::string_view GetHttpStatusString(HttpStatus status); 16 | 17 | } // namespace faas 18 | 19 | #endif //LUMINE_HTTP_STATUS -------------------------------------------------------------------------------- /lib/common/subprocess.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_SUBPROCESS_H 2 | #define LUMINE_SUBPROCESS_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | #include "utils/appendable_buffer.h" 7 | #include "utils/buffer_pool.h" 8 | 9 | namespace faas::uv { 10 | 11 | class Subprocess : public Base { 12 | public: 13 | static constexpr size_t kDefaultMaxStdoutSize = 16 * 1024 * 1024; // 16MB‬ 14 | static constexpr size_t kDefaultMaxStderrSize = 1 * 1024 * 1024; // 1MB 15 | static constexpr const char *kShellPath = "/bin/bash"; 16 | 17 | enum StandardPipe { 18 | kStdin = 0, kStdout = 1, kStderr = 2, kNumStdPipes = 3 19 | }; 20 | 21 | explicit Subprocess(std::string_view cmd, 22 | size_t max_stdout_size = kDefaultMaxStdoutSize, 23 | size_t max_stderr_size = kDefaultMaxStderrSize); 24 | 25 | ~Subprocess(); 26 | 27 | int pid() const { return pid_; } 28 | 29 | // Open file for standard file 30 | void SetStandardFile(StandardPipe pipe, std::string_view file_path); 31 | 32 | // For both CreateReadPipe and CreateWritePipe, fd is returned. stdin, stdout, 33 | // and stderr pipes will be created automatically, thus new pipes start with fd 3. 34 | // Also note that notions of readable/writable is from the perspective of created 35 | // subprocess. 36 | int CreateReadablePipe(); 37 | 38 | int CreateWritablePipe(); 39 | 40 | void SetWorkingDir(std::string_view path); 41 | 42 | void AddEnvVariable(std::string_view name, std::string_view value); 43 | 44 | void AddEnvVariable(std::string_view name, int value); 45 | 46 | typedef std::function /* stdout */, 47 | std::span /* stderr */)> ExitCallback; 48 | 49 | bool Start(uv_loop_t *uv_loop, utils::BufferPool *read_buffer_pool, 50 | ExitCallback exit_callback); 51 | 52 | void Kill(int signum = SIGKILL); 53 | 54 | // Caller should not close pipe by itself, but to call ClosePipe with fd. 55 | // Note that the caller should NOT touch stdout (fd = 1) and stderr (fd = 2) 56 | // with GetPipe and ClosePipe. These two pipes are fully managed by Subprocess 57 | // class. 58 | uv_pipe_t *GetPipe(int fd); 59 | 60 | void ClosePipe(int fd); 61 | 62 | bool PipeClosed(int fd); 63 | 64 | private: 65 | enum State { 66 | kCreated, kRunning, kExited, kClosed 67 | }; 68 | 69 | State state_; 70 | std::string cmd_; 71 | size_t max_stdout_size_; 72 | size_t max_stderr_size_; 73 | long exit_status_; 74 | HandleScope handle_scope_; 75 | ExitCallback exit_callback_; 76 | 77 | std::vector std_fds_; 78 | std::vector pipe_types_; 79 | std::string working_dir_; 80 | std::vector env_variables_; 81 | 82 | uv_process_t uv_process_handle_; 83 | std::vector uv_pipe_handles_; 84 | std::vector pipe_closed_; 85 | 86 | utils::BufferPool *read_buffer_pool_; 87 | utils::AppendableBuffer stdout_; 88 | utils::AppendableBuffer stderr_; 89 | 90 | int pid_; 91 | 92 | void OnAllHandlesClosed(); 93 | 94 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 95 | 96 | DECLARE_UV_READ_CB_FOR_CLASS(ReadStdout); 97 | 98 | DECLARE_UV_READ_CB_FOR_CLASS(ReadStderr); 99 | 100 | DECLARE_UV_EXIT_CB_FOR_CLASS(ProcessExit); 101 | 102 | DISALLOW_COPY_AND_ASSIGN(Subprocess); 103 | }; 104 | 105 | } // namespace faas 106 | 107 | #endif //LUMINE_SUBPROCESS_H -------------------------------------------------------------------------------- /lib/common/time.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_TIME_H 2 | #define LUMINE_TIME_H 3 | 4 | #include "base/common.h" 5 | #include "base/logging.h" 6 | 7 | namespace faas { 8 | 9 | inline int64_t TimeSpecToMicro(struct timespec *tp) { 10 | int64_t ret = 0; 11 | ret += int64_t{tp->tv_sec} * 1000000; 12 | ret += int64_t{tp->tv_nsec} / 1000; 13 | return ret; 14 | } 15 | 16 | inline int64_t GetMonotonicMicroTimestamp() { 17 | struct timespec tp; 18 | PCHECK(clock_gettime(CLOCK_MONOTONIC, &tp) == 0) << "clock_gettime failed"; 19 | return TimeSpecToMicro(&tp); 20 | } 21 | 22 | inline int64_t GetMilliTimestamp() { 23 | struct timespec tp; 24 | PCHECK(clock_gettime(CLOCK_MONOTONIC, &tp) == 0) << "clock_gettime failed"; 25 | return (tp.tv_sec * 1000 + tp.tv_nsec / 1000000); 26 | } 27 | 28 | inline int64_t GetRealtimeMicroTimestamp() { 29 | struct timespec tp; 30 | PCHECK(clock_gettime(CLOCK_REALTIME, &tp) == 0) << "clock_gettime failed"; 31 | return TimeSpecToMicro(&tp); 32 | } 33 | 34 | inline int64_t TimeSpecToNano(struct timespec *tp) { 35 | int64_t ret = 0; 36 | ret += int64_t{tp->tv_sec} * 1000000000; 37 | ret += int64_t{tp->tv_nsec}; 38 | return ret; 39 | } 40 | 41 | inline int64_t GetMonotonicNanoTimestamp() { 42 | struct timespec tp; 43 | PCHECK(clock_gettime(CLOCK_MONOTONIC, &tp) == 0) << "clock_gettime failed"; 44 | return TimeSpecToNano(&tp); 45 | } 46 | 47 | inline int64_t GetRealtimeNanoTimestamp() { 48 | struct timespec tp; 49 | PCHECK(clock_gettime(CLOCK_REALTIME, &tp) == 0) << "clock_gettime failed"; 50 | return TimeSpecToNano(&tp); 51 | } 52 | 53 | } // namespace faas 54 | #endif //LUMINE_TIME_H -------------------------------------------------------------------------------- /lib/common/uv.cpp: -------------------------------------------------------------------------------- 1 | #include "common/uv.h" 2 | 3 | namespace faas::uv { 4 | 5 | void HandleFreeCallback(uv_handle_t *handle) { 6 | free(handle); 7 | } 8 | 9 | HandleScope::HandleScope() 10 | : loop_(nullptr), num_handles_on_closing_(0) {} 11 | 12 | HandleScope::~HandleScope() { 13 | DCHECK(handles_.empty()); 14 | } 15 | 16 | void HandleScope::Init(uv_loop_t *loop, std::function finish_callback) { 17 | DCHECK(loop_ == nullptr); 18 | loop_ = loop; 19 | finish_callback_ = finish_callback; 20 | } 21 | 22 | void HandleScope::AddHandle(uv_handle_t *handle) { 23 | DCHECK(handle->loop == loop_); 24 | handles_.insert(handle); 25 | } 26 | 27 | void HandleScope::CloseHandle(uv_handle_t *handle) { 28 | DCHECK_IN_EVENT_LOOP_THREAD(loop_); 29 | DCHECK(handles_.contains(handle)); 30 | handles_.erase(handle); 31 | handle->data = this; 32 | num_handles_on_closing_++; 33 | uv_close(handle, &HandleScope::HandleCloseCallback); 34 | } 35 | 36 | UV_CLOSE_CB_FOR_CLASS(HandleScope, HandleClose) { 37 | num_handles_on_closing_--; 38 | if (num_handles_on_closing_ == 0 && handles_.empty()) { 39 | finish_callback_(); 40 | } 41 | } 42 | 43 | } // namespace faas 44 | -------------------------------------------------------------------------------- /lib/ipc/base.cpp: -------------------------------------------------------------------------------- 1 | #include "ipc/base.h" 2 | #include "utils/fs.h" 3 | #include "base/logging.h" 4 | 5 | namespace faas::ipc { 6 | 7 | namespace { 8 | std::string root_path_for_ipc = "NOT_SET"; 9 | std::string engine_unix_ipc_path = "NOT_SET"; 10 | std::string root_path_for_shm = "NOT_SET"; 11 | std::string root_path_for_fifo = "NOT_SET"; 12 | 13 | static constexpr const char *kEngineUnixSocket = "engine.sock"; 14 | static constexpr const char *kShmSubPath = "shm"; 15 | static constexpr const char *kFifoSubPath = "fifo"; 16 | } 17 | 18 | void SetRootPathForIpc(std::string_view path, bool create) { 19 | root_path_for_ipc = std::string(path); 20 | engine_unix_ipc_path = fs_utils::JoinPath(root_path_for_ipc, kEngineUnixSocket); 21 | root_path_for_shm = fs_utils::JoinPath(root_path_for_ipc, kShmSubPath); 22 | root_path_for_fifo = fs_utils::JoinPath(root_path_for_ipc, kFifoSubPath); 23 | if (create) { 24 | if (fs_utils::IsDirectory(root_path_for_ipc)) { 25 | PCHECK(fs_utils::RemoveDirectoryRecursively(root_path_for_ipc)); 26 | } else if (fs_utils::Exists(root_path_for_ipc)) { 27 | PCHECK(fs_utils::Remove(root_path_for_ipc)); 28 | } 29 | PCHECK(fs_utils::MakeDirectory(root_path_for_ipc)); 30 | PCHECK(fs_utils::MakeDirectory(root_path_for_shm)); 31 | PCHECK(fs_utils::MakeDirectory(root_path_for_fifo)); 32 | } else { 33 | CHECK(fs_utils::IsDirectory(root_path_for_ipc)) << root_path_for_ipc << " does not exist"; 34 | CHECK(fs_utils::IsDirectory(root_path_for_shm)) << root_path_for_shm << " does not exist"; 35 | CHECK(fs_utils::IsDirectory(root_path_for_fifo)) << root_path_for_fifo << " does not exist"; 36 | } 37 | } 38 | 39 | std::string_view GetRootPathForIpc() { 40 | CHECK_NE(root_path_for_ipc, "NOT_SET"); 41 | return root_path_for_ipc; 42 | } 43 | 44 | std::string_view GetEngineUnixSocketPath() { 45 | CHECK_NE(engine_unix_ipc_path, "NOT_SET"); 46 | return engine_unix_ipc_path; 47 | } 48 | 49 | std::string_view GetRootPathForShm() { 50 | CHECK_NE(root_path_for_shm, "NOT_SET"); 51 | return root_path_for_shm; 52 | } 53 | 54 | std::string_view GetRootPathForFifo() { 55 | CHECK_NE(root_path_for_fifo, "NOT_SET"); 56 | return root_path_for_fifo; 57 | } 58 | 59 | std::string GetFuncWorkerInputFifoName(uint16_t client_id) { 60 | return fmt::format("worker_{}_input", client_id); 61 | } 62 | 63 | std::string GetFuncWorkerOutputFifoName(uint16_t client_id) { 64 | return fmt::format("worker_{}_output", client_id); 65 | } 66 | 67 | std::string GetFuncCallInputShmName(uint64_t full_call_id) { 68 | return fmt::format("{}.i", full_call_id); 69 | } 70 | 71 | std::string GetFuncCallOutputShmName(uint64_t full_call_id) { 72 | return fmt::format("{}.o", full_call_id); 73 | } 74 | 75 | std::string GetFuncCallOutputFifoName(uint64_t full_call_id) { 76 | return fmt::format("{}.o", full_call_id); 77 | } 78 | 79 | } // namespace faas 80 | -------------------------------------------------------------------------------- /lib/ipc/base.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_IPC_BASE_H 2 | #define LUMINE_IPC_BASE_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::ipc { 7 | 8 | void SetRootPathForIpc(std::string_view path, bool create = false); 9 | 10 | std::string_view GetRootPathForIpc(); 11 | 12 | std::string_view GetEngineUnixSocketPath(); 13 | 14 | std::string_view GetRootPathForShm(); 15 | 16 | std::string_view GetRootPathForFifo(); 17 | 18 | std::string GetFuncWorkerInputFifoName(uint16_t client_id); 19 | 20 | std::string GetFuncWorkerOutputFifoName(uint16_t client_id); 21 | 22 | std::string GetFuncCallInputShmName(uint64_t full_call_id); 23 | 24 | std::string GetFuncCallOutputShmName(uint64_t full_call_id); 25 | 26 | std::string GetFuncCallOutputFifoName(uint64_t full_call_id); 27 | 28 | } // namespace faas 29 | 30 | #endif //LUMINE_IPC_BASE_H -------------------------------------------------------------------------------- /lib/ipc/fifo.cpp: -------------------------------------------------------------------------------- 1 | #include "ipc/fifo.h" 2 | #include "ipc/base.h" 3 | #include "utils/fs.h" 4 | #include "common/time.h" 5 | #include "base/common.h" 6 | 7 | namespace faas::ipc { 8 | 9 | bool FifoCreate(std::string_view name) { 10 | std::string full_path = fs_utils::JoinPath(GetRootPathForFifo(), name); 11 | if (mkfifo(full_path.c_str(), __FAAS_FILE_CREAT_MODE) != 0) { 12 | PLOG(ERROR) << "mkfifo " << full_path << " failed"; 13 | return false; 14 | } 15 | return true; 16 | } 17 | 18 | void FifoRemove(std::string_view name) { 19 | std::string full_path = fs_utils::JoinPath(GetRootPathForFifo(), name); 20 | if (!fs_utils::Remove(full_path)) { 21 | PLOG(ERROR) << "Failed to remove " << full_path; 22 | } 23 | } 24 | 25 | int FifoOpenForRead(std::string_view name, bool nonblocking) { 26 | std::string full_path = fs_utils::JoinPath(GetRootPathForFifo(), name); 27 | int flags = O_RDONLY | O_CLOEXEC; 28 | if (nonblocking) { 29 | flags |= O_NONBLOCK; 30 | } 31 | int fd = open(full_path.c_str(), flags); 32 | if (fd == -1) { 33 | PLOG(ERROR) << "open " << full_path << " failed"; 34 | } 35 | return fd; 36 | } 37 | 38 | int FifoOpenForWrite(std::string_view name, bool nonblocking) { 39 | std::string full_path = fs_utils::JoinPath(GetRootPathForFifo(), name); 40 | int flags = O_WRONLY | O_CLOEXEC; 41 | if (nonblocking) { 42 | flags |= O_NONBLOCK; 43 | } 44 | int fd = open(full_path.c_str(), flags); 45 | if (fd == -1) { 46 | PLOG(ERROR) << "open " << full_path << " failed"; 47 | } 48 | return fd; 49 | } 50 | 51 | int FifoOpenForReadWrite(std::string_view name, bool nonblocking) { 52 | std::string full_path = fs_utils::JoinPath(GetRootPathForFifo(), name); 53 | int flags = O_RDWR | O_CLOEXEC; 54 | if (nonblocking) { 55 | flags |= O_NONBLOCK; 56 | } 57 | int fd = open(full_path.c_str(), flags); 58 | if (fd == -1) { 59 | PLOG(ERROR) << "open " << full_path << " failed"; 60 | } 61 | return fd; 62 | } 63 | 64 | void FifoUnsetNonblocking(int fd) { 65 | int flags = fcntl(fd, F_GETFL, 0); 66 | PCHECK(flags != -1) << "fcntl F_GETFL failed"; 67 | PCHECK(fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == 0) 68 | << "fcntl F_SETFL failed"; 69 | } 70 | 71 | bool FifoPollForRead(int fd, int timeout_ms) { 72 | struct pollfd pfd; 73 | pfd.fd = fd; 74 | pfd.events = POLLIN; 75 | int ret = poll(&pfd, 1, timeout_ms); 76 | if (ret == -1) { 77 | PLOG(ERROR) << "poll failed"; 78 | return false; 79 | } 80 | if (ret == 0) { 81 | LOG(ERROR) << "poll on given fifo timeout"; 82 | return false; 83 | } 84 | if ((pfd.revents & POLLIN) == 0) { 85 | LOG(ERROR) << "Error happens on given fifo: revents=" << pfd.revents; 86 | return false; 87 | } 88 | return true; 89 | } 90 | 91 | } // namespace faas 92 | -------------------------------------------------------------------------------- /lib/ipc/fifo.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_FIFO_H 2 | #define LUMINE_FIFO_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::ipc { 7 | 8 | bool FifoCreate(std::string_view name); 9 | 10 | void FifoRemove(std::string_view name); 11 | 12 | // FifoOpenFor{Read, Write, ReadWrite} returns -1 on failure 13 | int FifoOpenForRead(std::string_view name, bool nonblocking = true); 14 | 15 | int FifoOpenForWrite(std::string_view name, bool nonblocking = true); 16 | 17 | int FifoOpenForReadWrite(std::string_view name, bool nonblocking = true); 18 | 19 | void FifoUnsetNonblocking(int fd); 20 | 21 | bool FifoPollForRead(int fd, int timeout_ms = -1); 22 | 23 | } // namespace faas 24 | 25 | #endif //LUMINE_FIFO_H -------------------------------------------------------------------------------- /lib/ipc/shm_region.cpp: -------------------------------------------------------------------------------- 1 | #include "ipc/shm_region.h" 2 | #include "ipc/base.h" 3 | #include "common/time.h" 4 | #include "utils/fs.h" 5 | #include "base/common.h" 6 | 7 | namespace faas::ipc { 8 | 9 | std::unique_ptr ShmCreate(std::string_view name, size_t size) { 10 | std::string full_path = fs_utils::JoinPath(GetRootPathForShm(), name); 11 | int fd = open(full_path.c_str(), O_CREAT | O_EXCL | O_RDWR, __FAAS_FILE_CREAT_MODE); 12 | if (fd == -1) { 13 | PLOG(ERROR) << "open " << full_path << " failed"; 14 | return nullptr; 15 | } 16 | PCHECK(ftruncate(fd, size) == 0) << "ftruncate failed"; 17 | void *ptr = nullptr; 18 | if (size > 0) { 19 | ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 20 | PCHECK(ptr != MAP_FAILED) << "mmap failed"; 21 | memset(ptr, 0, size); 22 | } 23 | PCHECK(close(fd) == 0) << "close failed"; 24 | return std::unique_ptr(new ShmRegion(name, reinterpret_cast(ptr), size)); 25 | } 26 | 27 | std::unique_ptr ShmOpen(std::string_view name, bool readonly) { 28 | std::string full_path = fs_utils::JoinPath(GetRootPathForShm(), name); 29 | int fd = open(full_path.c_str(), readonly ? O_RDONLY : O_RDWR); 30 | if (fd == -1) { 31 | PLOG(ERROR) << "open " << full_path << " failed"; 32 | return nullptr; 33 | } 34 | struct stat statbuf; 35 | PCHECK(fstat(fd, &statbuf) == 0) << "fstat failed"; 36 | size_t size = gsl::narrow_cast(statbuf.st_size); 37 | void *ptr = nullptr; 38 | if (size > 0) { 39 | ptr = mmap(0, size, readonly ? PROT_READ : (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0); 40 | PCHECK(ptr != MAP_FAILED) << "mmap failed"; 41 | } 42 | PCHECK(close(fd) == 0) << "close failed"; 43 | return std::unique_ptr(new ShmRegion(name, reinterpret_cast(ptr), size)); 44 | } 45 | 46 | ShmRegion::~ShmRegion() { 47 | if (size_ > 0) { 48 | PCHECK(munmap(base_, size_) == 0); 49 | } 50 | if (remove_on_destruction_) { 51 | std::string full_path = fs_utils::JoinPath(GetRootPathForShm(), name_); 52 | if (!fs_utils::Remove(full_path)) { 53 | PLOG(ERROR) << "Failed to remove " << full_path; 54 | } 55 | } 56 | } 57 | 58 | } // namespace faas 59 | -------------------------------------------------------------------------------- /lib/ipc/shm_region.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_SHM_REGION_H 2 | #define LUMINE_SHM_REGION_H 3 | 4 | #include "base/common.h" 5 | #include "base/macro.h" 6 | #include "ipc/base.h" 7 | 8 | namespace faas::ipc { 9 | 10 | class ShmRegion; 11 | 12 | // Shm{Create, Open} returns nullptr on failure 13 | std::unique_ptr ShmCreate(std::string_view name, size_t size); 14 | 15 | std::unique_ptr ShmOpen(std::string_view name, bool readonly = true); 16 | 17 | class ShmRegion { 18 | public: 19 | ~ShmRegion(); 20 | 21 | void EnableRemoveOnDestruction() { remove_on_destruction_ = true; } 22 | 23 | void DisableRemoveOnDestruction() { remove_on_destruction_ = false; } 24 | 25 | char *base() { return base_; } 26 | 27 | const char *base() const { return base_; } 28 | 29 | size_t size() const { return size_; } 30 | 31 | gsl::span to_span() const { 32 | return gsl::span(base_, size_); 33 | } 34 | 35 | private: 36 | // base_ is the ptr of shared memory region 37 | ShmRegion(std::string_view name, char *base, size_t size) 38 | : name_(name), base_(base), size_(size), remove_on_destruction_(false) {} 39 | 40 | std::string name_; 41 | char *base_; 42 | size_t size_; 43 | bool remove_on_destruction_; 44 | 45 | friend std::unique_ptr ShmCreate(std::string_view name, size_t size); 46 | 47 | friend std::unique_ptr ShmOpen(std::string_view name, bool readonly); 48 | 49 | DISALLOW_COPY_AND_ASSIGN(ShmRegion); 50 | }; 51 | 52 | } // namespace faas 53 | 54 | #endif -------------------------------------------------------------------------------- /lib/ipc/spsc_queue-inl.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_SPSC_QUEUE_INL_H 2 | #define LUMINE_SPSC_QUEUE_INL_H 3 | 4 | #include "base/asm.h" 5 | #include "base/common.h" 6 | #include "spsc_queue.h" 7 | #include "base/logging.h" 8 | 9 | #define LOAD(T, ptr) *reinterpret_cast(ptr) 10 | #define STORE(T, ptr, value) *reinterpret_cast(ptr) = (value) 11 | 12 | namespace faas { 13 | namespace ipc { 14 | 15 | template 16 | std::unique_ptr > SPSCQueue::Create(std::string_view name, size_t queue_size) { 17 | CHECK_GE(queue_size, 2U) << "Queue size must be at least 2"; 18 | auto region = ShmCreate(fmt::format("SPSCQueue_{}", name), compute_total_bytesize(queue_size)); 19 | CHECK(region != nullptr) << "ShmCreate failed"; 20 | BuildMemoryLayout(region->base(), queue_size); 21 | return std::unique_ptr < SPSCQueue < T >> (new SPSCQueue(true, std::move(region))); 22 | } 23 | 24 | template 25 | std::unique_ptr > SPSCQueue::Open(std::string_view name) { 26 | auto region = ShmOpen(fmt::format("SPSCQueue_{}", name), /* readonly= */ false); 27 | CHECK(region != nullptr) << "ShmOpen failed"; 28 | return std::unique_ptr < SPSCQueue < T >> (new SPSCQueue(false, std::move(region))); 29 | } 30 | 31 | template 32 | SPSCQueue::SPSCQueue(bool consumer, std::unique_ptr shm_region) { 33 | consumer_ = consumer; 34 | shm_region_ = std::move(shm_region); 35 | if (consumer_) { 36 | shm_region_->EnableRemoveOnDestruction(); 37 | } 38 | char *base_ptr = shm_region_->base(); 39 | size_t message_size = LOAD(size_t, base_ptr); 40 | CHECK_EQ(message_size, sizeof(T)); 41 | queue_size_ = LOAD(size_t, base_ptr + sizeof(size_t)); 42 | CHECK_EQ(shm_region_->size(), compute_total_bytesize(queue_size_)); 43 | head_ = reinterpret_cast(base_ptr + __FAAS_CACHE_LINE_SIZE); 44 | tail_ = reinterpret_cast(base_ptr + __FAAS_CACHE_LINE_SIZE * 2); 45 | cell_base_ = reinterpret_cast(base_ptr + __FAAS_CACHE_LINE_SIZE * 3); 46 | wakeup_consumer_flag_ = false; 47 | } 48 | 49 | template 50 | SPSCQueue::~SPSCQueue() {} 51 | 52 | template 53 | size_t SPSCQueue::compute_total_bytesize(size_t queue_size) { 54 | return __FAAS_CACHE_LINE_SIZE * 3 + sizeof(T) * queue_size; 55 | } 56 | 57 | template 58 | void SPSCQueue::BuildMemoryLayout(char *base_ptr, size_t queue_size) { 59 | STORE(size_t, base_ptr, sizeof(T)); // message_size 60 | STORE(size_t, base_ptr + sizeof(size_t), queue_size); // queue_size 61 | STORE(size_t, base_ptr + __FAAS_CACHE_LINE_SIZE, 0); // head 62 | STORE(size_t, base_ptr + __FAAS_CACHE_LINE_SIZE * 2, 0); // tail 63 | } 64 | 65 | template 66 | bool SPSCQueue::Push(const T &message) { 67 | DCHECK(!consumer_); 68 | size_t current = __atomic_load_n(tail_, __ATOMIC_RELAXED); 69 | size_t next = current + 1; 70 | if (next == queue_size_) { 71 | next = 0; 72 | } 73 | size_t head = __atomic_load_n(head_, __ATOMIC_ACQUIRE); 74 | if ((head & kConsumerSleepMask) == kConsumerSleepMask) { 75 | if (!wakeup_consumer_flag_) { 76 | VLOG(1) << "Consumer is sleeping, and will call wake function"; 77 | wakeup_consumer_flag_ = true; 78 | wakeup_consumer_fn_(); 79 | } 80 | head ^= kConsumerSleepMask; 81 | } else { 82 | wakeup_consumer_flag_ = false; 83 | } 84 | if (next != head) { 85 | STORE(T, cell(current), message); 86 | __atomic_store_n(tail_, next, __ATOMIC_RELEASE); 87 | asm_volatile_memory(); 88 | return true; 89 | } else { 90 | // Queue is full 91 | return false; 92 | } 93 | } 94 | 95 | template 96 | void SPSCQueue::SetWakeupConsumerFn(std::function fn) { 97 | DCHECK(!consumer_); 98 | wakeup_consumer_fn_ = fn; 99 | } 100 | 101 | template 102 | bool SPSCQueue::Pop(T *message) { 103 | DCHECK(consumer_); 104 | size_t current = __atomic_load_n(head_, __ATOMIC_RELAXED); 105 | if ((current & kConsumerSleepMask) == kConsumerSleepMask) { 106 | current ^= kConsumerSleepMask; 107 | __atomic_fetch_xor(head_, kConsumerSleepMask, __ATOMIC_RELEASE); 108 | asm_volatile_memory(); 109 | } 110 | if (current == __atomic_load_n(tail_, __ATOMIC_ACQUIRE)) { 111 | // Queue is empty 112 | return false; 113 | } else { 114 | size_t next = current + 1; 115 | if (next == queue_size_) { 116 | next = 0; 117 | } 118 | *message = LOAD(T, cell(current)); 119 | __atomic_store_n(head_, next, __ATOMIC_RELEASE); 120 | asm_volatile_memory(); 121 | return true; 122 | } 123 | } 124 | 125 | template 126 | void SPSCQueue::ConsumerEnterSleep() { 127 | DCHECK(consumer_); 128 | __atomic_fetch_or(head_, kConsumerSleepMask, __ATOMIC_RELEASE); 129 | asm_volatile_memory(); 130 | } 131 | 132 | } // namespace ipc 133 | } // namespace faas 134 | 135 | #undef STORE 136 | #undef LOAD 137 | 138 | #endif //LUMINE_SPSC_QUEUE_INL_H -------------------------------------------------------------------------------- /lib/ipc/spsc_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_SPSC_QUEUE_H 2 | #define LUMINE_SPSC_QUEUE_H 3 | 4 | 5 | #include "base/common.h" 6 | #include "ipc/shm_region.h" 7 | #include "ipc/spsc_queue-inl.h" 8 | #include "base/macro.h" 9 | 10 | namespace faas { 11 | namespace ipc { 12 | 13 | template 14 | class SPSCQueue { 15 | public: 16 | ~SPSCQueue(); 17 | 18 | static constexpr size_t kConsumerSleepMask = size_t{1} << (sizeof(size_t) * 8 - 1); 19 | 20 | // Called by the consumer 21 | static std::unique_ptr> Create(std::string_view name, size_t queue_size); 22 | 23 | // Called by the producer 24 | static std::unique_ptr> Open(std::string_view name); 25 | 26 | // Methods called by the producer 27 | void SetWakeupConsumerFn(std::function fn); 28 | 29 | bool Push(const T &message); // Return false if queue is full 30 | 31 | // Methods called by the consumer 32 | void ConsumerEnterSleep(); 33 | 34 | bool Pop(T *message); // Return false if queue is empty 35 | 36 | private: 37 | bool consumer_; 38 | std::unique_ptr shm_region_; 39 | size_t queue_size_; 40 | size_t *head_; 41 | size_t *tail_; 42 | char *cell_base_; 43 | 44 | std::function wakeup_consumer_fn_; 45 | bool wakeup_consumer_flag_; 46 | 47 | SPSCQueue(bool producer, std::unique_ptr shm_region); 48 | 49 | static size_t compute_total_bytesize(size_t queue_size); 50 | 51 | static void BuildMemoryLayout(char *base_ptr, size_t queue_size); 52 | 53 | void *cell(size_t idx) { return cell_base_ + idx * sizeof(T); } 54 | 55 | DISALLOW_COPY_AND_ASSIGN(SPSCQueue); 56 | }; 57 | 58 | } // namespace ipc 59 | } // namespace faas 60 | 61 | 62 | 63 | #endif //LUMINE_SPSC_QUEUE_H -------------------------------------------------------------------------------- /lib/rdma/bit_map.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 9/27/22. 3 | // 4 | 5 | #include "bit_map.h" 6 | #include 7 | 8 | #define BITMAP_MASK 1 9 | 10 | namespace faas::rdma { 11 | 12 | BitMap::BitMap(uint32_t bitmap_bytes_len) 13 | : bitmap_bytes_len_(bitmap_bytes_len), 14 | bits_(nullptr) { 15 | bits_ = new uint8_t[bitmap_bytes_len_]; 16 | memset(bits_, 0, bitmap_bytes_len_); 17 | } 18 | 19 | BitMap::~BitMap() { 20 | delete bits_; 21 | } 22 | 23 | bool BitMap::ScanTest(uint32_t bit_idx) { 24 | uint32_t byte_idx = bit_idx / 8; 25 | uint32_t bit_odd = bit_idx % 8; 26 | return bits_[byte_idx] & (BITMAP_MASK << bit_odd); 27 | } 28 | 29 | void BitMap::SetBitIdx(uint32_t bit_idx, int8_t value) { 30 | uint32_t byte_idx = bit_idx / 8; 31 | uint32_t bit_odd = bit_idx % 8; 32 | 33 | if (value) { 34 | bits_[byte_idx] |= (BITMAP_MASK << bit_odd); 35 | } else { 36 | bits_[byte_idx] &= ~(BITMAP_MASK << bit_odd); 37 | } 38 | } 39 | 40 | uint32_t BitMap::Scan(uint32_t cnt) { 41 | uint32_t idx_byte, idx_bit, bit_idx_start, found_count, next_bit; 42 | 43 | // bytes 44 | for (idx_byte = 0; idx_byte < bitmap_bytes_len_ && bits_[idx_byte] == 0xFF; idx_byte++) {} 45 | 46 | if (idx_byte == bitmap_bytes_len_) return -1; 47 | 48 | // bits 49 | for (idx_bit = 0; bits_[idx_byte] & (BITMAP_MASK << idx_bit); idx_bit++) {} 50 | 51 | bit_idx_start = idx_byte * 8 + idx_bit; 52 | for (next_bit = bit_idx_start + 1, found_count = 1; 53 | (next_bit < bitmap_bytes_len_ * 8) && (found_count < cnt); next_bit++) { 54 | if (!ScanTest(next_bit)){ 55 | if(found_count == 0){ 56 | bit_idx_start = next_bit; 57 | } 58 | found_count++; 59 | continue; 60 | } 61 | found_count = 0; 62 | } 63 | 64 | if (found_count != cnt) { 65 | return -1; 66 | } 67 | return bit_idx_start; 68 | } 69 | } -------------------------------------------------------------------------------- /lib/rdma/bit_map.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 9/27/22. 3 | // 4 | 5 | #ifndef LUMINE_HOST_BENCH_BIT_MAP_H 6 | #define LUMINE_HOST_BENCH_BIT_MAP_H 7 | 8 | #include 9 | 10 | namespace faas::rdma { 11 | class BitMap { 12 | public: 13 | BitMap(uint32_t bitmap_bytes_len); 14 | 15 | ~BitMap(); 16 | 17 | uint32_t Scan(uint32_t cnt); 18 | 19 | void SetBitIdx(uint32_t bit_idx, int8_t value); 20 | private: 21 | uint32_t bitmap_bytes_len_; 22 | uint8_t *bits_; 23 | 24 | bool ScanTest(uint32_t bit_idx); 25 | }; 26 | } 27 | 28 | #endif //LUMINE_HOST_BENCH_BIT_MAP_H 29 | -------------------------------------------------------------------------------- /lib/rdma/infinity.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 9/5/22. 3 | // 4 | 5 | #include "infinity.h" 6 | 7 | namespace faas::rdma { 8 | 9 | Infinity::Infinity(const std::string &device_name, uint8_t ibv_device_port, uint8_t ibv_device_gid_index) { 10 | 11 | this->ibv_device_port_ = ibv_device_port; 12 | this->ibv_device_gid_index_ = ibv_device_gid_index; 13 | 14 | // Get the list of IB devices 15 | int num_devices; 16 | struct ibv_device **device_list = ibv_get_device_list(&num_devices); 17 | 18 | // Get IB device by device_name 19 | for (int i = 0; i < num_devices; i++) { 20 | auto device_name_tmp = ibv_get_device_name(device_list[i]); 21 | if (device_name == device_name_tmp) { 22 | ibv_device_ = device_list[i]; 23 | break; 24 | } 25 | } 26 | ibv_free_device_list(device_list); 27 | INFINITY_ASSERT(ibv_device_ != nullptr, "infinity: Requested IB device is illegal") 28 | 29 | // Open IB device 30 | ibv_context_ = ibv_open_device(ibv_device_); 31 | INFINITY_ASSERT(ibv_context_ != nullptr, "infinity: Could not open device") 32 | 33 | // create send complete channel 34 | ibv_send_comp_channel_ = ibv_create_comp_channel(this->ibv_context_); 35 | INFINITY_ASSERT(ibv_send_comp_channel_ != nullptr, "infinity: Could not create send completion channel") 36 | 37 | // create recv complete channel 38 | ibv_recv_comp_channel_ = ibv_create_comp_channel(this->ibv_context_); 39 | INFINITY_ASSERT(ibv_recv_comp_channel_ != nullptr, "infinity: Could not create recv completion channel") 40 | 41 | int ret; 42 | 43 | ret = ibv_query_gid(ibv_context_, ibv_device_port, ibv_device_gid_index_, &gid_); 44 | INFINITY_ASSERT(ret == 0, "infinity: local port gid index is illegal") 45 | 46 | ibv_device_attr_ex attr{}; 47 | ret = ibv_query_device_ex(ibv_context_, nullptr, &attr); 48 | INFINITY_ASSERT(ret == 0, "infinity: Could not query device for its features") 49 | 50 | // Ability of the device to support atomic operations 51 | std::string atomic_cap_info; 52 | switch (attr.orig_attr.atomic_cap) { 53 | case IBV_ATOMIC_NONE: 54 | atomic_cap_info = "infinity: Atomic operations aren't supported at all"; 55 | break; 56 | case IBV_ATOMIC_HCA: 57 | atomic_cap_info = "infinity: Atomicity is guaranteed between QPs on this device only"; 58 | break; 59 | default: 60 | atomic_cap_info = "infinity: Atomicity is guaranteed between this device and any other component, such as CPUs, IO devices and other RDMA devices"; 61 | break; 62 | } 63 | INFINITY_DEBUG(atomic_cap_info) 64 | 65 | // Allocate protection domain 66 | ibv_protection_domain_ = ibv_alloc_pd(ibv_context_); 67 | INFINITY_ASSERT(ibv_protection_domain_ != nullptr, "infinity: Could not allocate protection domain") 68 | 69 | // Get the LID 70 | ibv_port_attr port_attributes{}; 71 | ret = ibv_query_port(ibv_context_, ibv_device_port_, &port_attributes); 72 | INFINITY_ASSERT(ret == 0, "infinity: Requested IB port is illegal") 73 | ibv_device_local_id_ = port_attributes.lid; 74 | 75 | // Allocate send completion queue 76 | ibv_send_completion_queue_ = ibv_create_cq(ibv_context_, SEND_COMPLETION_QUEUE_LENGTH, 77 | nullptr, ibv_send_comp_channel_, 0); 78 | INFINITY_ASSERT(ibv_send_completion_queue_ != nullptr, "infinity: Could not allocate send completion queue") 79 | 80 | // Allocate receive completion queue 81 | ibv_receive_completion_queue_ = ibv_create_cq(ibv_context_, RECV_COMPLETION_QUEUE_LENGTH, 82 | nullptr, ibv_recv_comp_channel_, 0); 83 | INFINITY_ASSERT(ibv_receive_completion_queue_ != nullptr, 84 | "infinity: Could not allocate receive completion queue") 85 | 86 | // Allocate shared receive queue 87 | MAKE_IBV_SRQ_INIT_ATTR(sia, ibv_context_) 88 | ibv_shared_receive_queue_ = ibv_create_srq(ibv_protection_domain_, &sia); 89 | INFINITY_ASSERT(ibv_shared_receive_queue_ != nullptr, 90 | "infinity: Could not allocate receive shared receive queue") 91 | } 92 | 93 | Infinity::~Infinity() { 94 | int ret; 95 | 96 | // Destroy shared completion queue 97 | ret = ibv_destroy_srq(ibv_shared_receive_queue_); 98 | INFINITY_ASSERT(ret == 0, "infinity: Could not destroy shared completion queue") 99 | 100 | // Destroy receive completion queue 101 | ret = ibv_destroy_cq(ibv_receive_completion_queue_); 102 | INFINITY_ASSERT(ret == 0, "infinity: Could not destroy receive completion queue") 103 | 104 | // Destroy send completion queue 105 | ret = ibv_destroy_cq(ibv_send_completion_queue_); 106 | INFINITY_ASSERT(ret == 0, "infinity: Could not destroy send completion queue") 107 | 108 | // Destroy send completion channel 109 | ret = ibv_destroy_comp_channel(ibv_send_comp_channel_); 110 | INFINITY_ASSERT(ret == 0, "infinity: Could not destroy send completion channel") 111 | 112 | // Destroy recv completion channel 113 | ret = ibv_destroy_comp_channel(ibv_recv_comp_channel_); 114 | INFINITY_ASSERT(ret == 0, "infinity: Could not destroy recv completion channel") 115 | 116 | // Destroy protection domain 117 | ret = ibv_dealloc_pd(ibv_protection_domain_); 118 | INFINITY_ASSERT(ret == 0, "infinity: Could not dealloc protection domain") 119 | 120 | // Close device 121 | ret = ibv_close_device(this->ibv_context_); 122 | INFINITY_ASSERT(ret == 0, "infinity: Could not close IB device"); 123 | } 124 | 125 | QueuePair *Infinity::CreateQueuePair(bool used_shared_cq) { 126 | auto queue_pair = new QueuePair(this, used_shared_cq); 127 | return queue_pair; 128 | } 129 | 130 | }; 131 | -------------------------------------------------------------------------------- /lib/rdma/infinity.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 9/5/22. 3 | // 4 | 5 | #ifndef LUMINE_HOST_BENCH_INFINITY_H 6 | #define LUMINE_HOST_BENCH_INFINITY_H 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include "base/logging.h" 13 | #include "rdma/rdma_macro.h" 14 | #include "rdma/queue_pair.h" 15 | 16 | namespace faas::rdma { 17 | 18 | class Infinity { 19 | 20 | friend class QueuePair; 21 | 22 | public: 23 | 24 | Infinity(const std::string &device_name, uint8_t ibv_device_port, uint8_t ibv_device_gid_index); 25 | 26 | ~Infinity(); 27 | 28 | QueuePair *CreateQueuePair(bool used_shared_cq = false); 29 | 30 | private: 31 | // IB device 32 | struct ibv_device *ibv_device_; 33 | 34 | // IB context 35 | struct ibv_context *ibv_context_; 36 | 37 | // IB send complete channel 38 | struct ibv_comp_channel *ibv_send_comp_channel_; 39 | 40 | // IB recv complete channel 41 | struct ibv_comp_channel *ibv_recv_comp_channel_; 42 | 43 | // protection domain 44 | struct ibv_pd *ibv_protection_domain_; 45 | 46 | // local device id 47 | uint16_t ibv_device_local_id_; 48 | 49 | // device port 50 | uint8_t ibv_device_port_; 51 | 52 | uint8_t ibv_device_gid_index_; 53 | 54 | union ibv_gid gid_; 55 | 56 | // IB send completion queue 57 | ibv_cq *ibv_send_completion_queue_; 58 | 59 | // IB receive completion queue 60 | ibv_cq *ibv_receive_completion_queue_; 61 | 62 | // Shared receive queue 63 | ibv_srq *ibv_shared_receive_queue_; 64 | }; 65 | 66 | } 67 | 68 | #endif //LUMINE_HOST_BENCH_INFINITY_H 69 | -------------------------------------------------------------------------------- /lib/rdma/rdma_macro.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 9/5/22. 3 | // 4 | 5 | #ifndef LUMINE_HOST_BENCH_CONFIGURE_H 6 | #define LUMINE_HOST_BENCH_CONFIGURE_H 7 | 8 | // For Debug 9 | #ifdef __DEBUG 10 | #define INFINITY_DEBUG(MESSAGE) {LOG(INFO) << MESSAGE;} 11 | #else 12 | #define INFINITY_DEBUG(MESSAGE) {while(false) LOG(INFO) << MESSAGE;} 13 | #endif 14 | #define INFINITY_ASSERT(B, MESSAGE) {if(!(B)){LOG(ERROR) << MESSAGE;}} 15 | 16 | // Queue length settings 17 | #define SEND_COMPLETION_QUEUE_LENGTH 1024 18 | #define RECV_COMPLETION_QUEUE_LENGTH 1024 19 | #define SHARED_RECV_QUEUE_LENGTH 1024 20 | #define MAX_NUMBER_OF_OUTSTANDING_REQUESTS 16 21 | #define MAX_NUMBER_OF_SGE_ELEMENTS 1 22 | #define MAX_NUMBER_OF_WORKER_COMPLETION_QUEUE_LENGTH 1024 23 | #define MAX_POLL_CQ_TIMEOUT_MS 2 24 | 25 | 26 | // shared memory setting 27 | #define CACHE_LINE 64 28 | #define META_DATA_ENTRY_SIZE 8 29 | #define MAX_MEMORY_REGION_SIZE 1048576 30 | #define BASIC_ALLOCATE_SIZE 1024 31 | #define MAX_META_DATA_ENTRY_NUM ((BASIC_ALLOCATE_SIZE - 2 * CACHE_LINE) / META_DATA_ENTRY_SIZE) 32 | //#define MAX_META_DATA_ENTRY_NUM 3 33 | 34 | // system setting 35 | #define PAGE_SIZE 4096 36 | 37 | #define MAKE_IBV_SRQ_INIT_ATTR(val, context) \ 38 | ibv_srq_init_attr val{}; \ 39 | memset(&val, 0, sizeof(ibv_srq_init_attr)); \ 40 | val.srq_context = context; \ 41 | val.attr.max_wr = SHARED_RECV_QUEUE_LENGTH; \ 42 | val.attr.max_sge = 1; 43 | 44 | #define MAKE_IBV_QP_ATTR_FOR_INIT(val, _port_num) \ 45 | ibv_qp_attr val{}; \ 46 | memset(&val, 0, sizeof(ibv_qp_attr)); \ 47 | val.qp_state = IBV_QPS_INIT; \ 48 | val.pkey_index = 0; \ 49 | val.port_num = _port_num; \ 50 | val.qp_access_flags = IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_ATOMIC; 51 | 52 | #define MAKE_IBV_QP_ATTR_FOR_RTS(val) \ 53 | ibv_qp_attr val{}; \ 54 | memset(&(val), 0, sizeof(ibv_qp_attr)); \ 55 | val.qp_state = IBV_QPS_RTS; \ 56 | val.timeout = 14; \ 57 | val.retry_cnt = 7; \ 58 | val.rnr_retry = 7; \ 59 | val.sq_psn = 0; \ 60 | val.max_rd_atomic = 1; 61 | 62 | #define MAKE_IBV_SGE(sge, _addr, _length, _lkey) \ 63 | struct ibv_sge sge{}; \ 64 | memset(&sge, 0, sizeof(ibv_sge)); \ 65 | sge.addr = _addr; \ 66 | sge.length = _length; \ 67 | sge.lkey = _lkey; 68 | 69 | #define MAKE_WORKER_REQUEST(wr, op_type, sge, _remote_addr, _remote_rkey) \ 70 | struct ibv_send_wr wr{}; \ 71 | memset(&wr, 0, sizeof(ibv_send_wr)); \ 72 | wr.sg_list = &sge; \ 73 | wr.next = nullptr; \ 74 | wr.wr_id = 0; \ 75 | wr.num_sge = 1; \ 76 | wr.opcode = op_type; \ 77 | wr.send_flags = IBV_SEND_SIGNALED ; \ 78 | wr.wr.rdma.remote_addr = _remote_addr; \ 79 | wr.wr.rdma.rkey = _remote_rkey; 80 | 81 | #endif //LUMINE_HOST_BENCH_CONFIGURE_H 82 | -------------------------------------------------------------------------------- /lib/rdma/shared_memory.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 9/25/22. 3 | // 4 | 5 | #ifndef LUMINE_HOST_BENCH_SHARED_MEMORY_H 6 | #define LUMINE_HOST_BENCH_SHARED_MEMORY_H 7 | 8 | #ifndef __SHARED_MEMORY_TEST 9 | 10 | #include "base/logging.h" 11 | 12 | #endif 13 | 14 | #include 15 | #include 16 | #include "rdma_macro.h" 17 | #include "bit_map.h" 18 | 19 | namespace faas::rdma { 20 | 21 | struct MetaDataEntry { 22 | uint32_t offset; 23 | uint32_t size; 24 | }; 25 | 26 | struct SharedMemoryInfo { 27 | uint64_t addr; 28 | uint64_t length; 29 | }; 30 | 31 | class SharedMemory { 32 | 33 | public: 34 | 35 | explicit SharedMemory(uint64_t buf_size = MAX_MEMORY_REGION_SIZE); 36 | 37 | ~SharedMemory(); 38 | 39 | SharedMemoryInfo GetSharedMemoryInfo(); 40 | 41 | uint32_t PopMessage(uint64_t &addr, uint32_t &size); 42 | 43 | uint64_t AllocateMemory(uint64_t expected_size); 44 | 45 | void ReturnMemory(uint64_t addr, uint32_t size); 46 | 47 | bool PushMetaDataEntry(uint64_t addr, uint32_t size); 48 | 49 | void PopMetaDataEntry(uint32_t idx); 50 | 51 | // direction : false -> previous, true -> idx 52 | void GetPreviousOrNextIdx(uint32_t &idx, bool direction); 53 | 54 | private: 55 | std::string shared_memory_name_; 56 | SharedMemoryInfo *shared_memory_info_; 57 | 58 | uint64_t base_addr_; 59 | uint64_t metadata_start_addr_; 60 | 61 | uint32_t *head_ptr_, *tail_ptr_; 62 | 63 | // relate control region 64 | uint32_t max_meta_data_entry_num_; 65 | uint32_t old_head_idx_; 66 | 67 | // related memory control 68 | BitMap *bit_map_; 69 | 70 | void PopMetaDataEntry(uint32_t idx, bool update_head); 71 | 72 | }; 73 | } 74 | 75 | #endif //LUMINE_HOST_BENCH_SHARED_MEMORY_H 76 | -------------------------------------------------------------------------------- /lib/runtime/event_driven_worker.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_EVENT_DRIVEN_WOEKER_H 2 | #define LUMINE_EVENT_DRIVEN_WOEKER_H 3 | 4 | #include "base/common.h" 5 | #include "common/config.h" 6 | #include "common/protocol.h" 7 | #include "ipc/shm_region.h" 8 | #include "utils/object_pool.h" 9 | 10 | namespace faas::worker_lib { 11 | 12 | class EventDrivenWorker { 13 | public: 14 | EventDrivenWorker(std::string debug_file_path = ""); 15 | 16 | ~EventDrivenWorker(); 17 | 18 | void Start(); 19 | 20 | bool is_grpc_service() { return function_config_entry_->is_grpc_service; } 21 | 22 | std::string_view func_name() { return function_config_entry_->func_name; } 23 | 24 | std::string_view grpc_service_name() { return function_config_entry_->grpc_service_name; } 25 | 26 | typedef std::function WatchFdReadableCallback; 27 | 28 | void SetWatchFdReadableCallback(WatchFdReadableCallback callback) { 29 | watch_fd_readable_cb_ = callback; 30 | } 31 | 32 | typedef std::function StopWatchFdCallback; 33 | 34 | void SetStopWatchFdCallback(StopWatchFdCallback callback) { 35 | stop_watch_fd_cb_ = callback; 36 | } 37 | 38 | typedef std::function /* request */)> 40 | IncomingFuncCallCallback; 41 | 42 | void SetIncomingFuncCallCallback(IncomingFuncCallCallback callback) { 43 | incoming_func_call_cb_ = callback; 44 | } 45 | 46 | typedef std::function /* output */)> 48 | OutgoingFuncCallCompleteCallback; 49 | 50 | void SetOutgoingFuncCallCompleteCallback(OutgoingFuncCallCompleteCallback callback) { 51 | outgoing_func_call_complete_cb_ = callback; 52 | } 53 | 54 | void OnFdReadable(int fd); 55 | 56 | void OnFuncExecutionFinished(int64_t handle, bool success, std::span output); 57 | 58 | bool NewOutgoingFuncCall(int64_t parent_handle, std::string_view func_name, 59 | std::span input, int64_t *handle); 60 | 61 | bool NewOutgoingGrpcCall(int64_t parent_handle, std::string_view service, 62 | std::string_view method, std::span request, 63 | int64_t *handle); 64 | 65 | 66 | private: 67 | WatchFdReadableCallback watch_fd_readable_cb_; 68 | StopWatchFdCallback stop_watch_fd_cb_; 69 | IncomingFuncCallCallback incoming_func_call_cb_; 70 | OutgoingFuncCallCompleteCallback outgoing_func_call_complete_cb_; 71 | 72 | bool use_fifo_for_nested_call_; 73 | int message_pipe_fd_; 74 | config::Config config_; 75 | const config::FunctionEntry *function_config_entry_; 76 | uint16_t initial_client_id_; 77 | char main_pipe_buf_[PIPE_BUF]; 78 | 79 | // Only for debug 80 | std::string debug_file_path_; 81 | int func_id_; 82 | 83 | struct FuncWorkerState { 84 | uint16_t client_id; 85 | int engine_sock_fd; 86 | int input_pipe_fd; 87 | int output_pipe_fd; 88 | uint32_t next_call_id; 89 | }; 90 | std::unordered_map> 91 | func_workers_; 92 | std::unordered_map 93 | func_worker_by_input_fd_; 94 | 95 | struct IncomingFuncCallState { 96 | protocol::FuncCall func_call; 97 | uint16_t recv_client_id; 98 | int32_t dispatch_delay; 99 | int64_t start_timestamp; 100 | }; 101 | utils::SimpleObjectPool incoming_func_call_pool_; 102 | std::unordered_map 103 | incoming_func_calls_; 104 | 105 | struct OutgoingFuncCallState { 106 | protocol::FuncCall func_call; 107 | std::unique_ptr input_region; 108 | int output_pipe_fd; 109 | }; 110 | utils::SimpleObjectPool outgoing_func_call_pool_; 111 | std::unordered_map 112 | outgoing_func_calls_; 113 | std::unordered_map 114 | outgoing_func_call_by_output_pipe_fd_; 115 | 116 | inline int64_t func_call_to_handle(const protocol::FuncCall &func_call) { 117 | return gsl::narrow_cast(func_call.full_call_id); 118 | } 119 | 120 | inline protocol::FuncCall handle_to_func_call(int64_t handle) { 121 | protocol::FuncCall func_call; 122 | memset(&func_call, 0, sizeof(protocol::FuncCall)); 123 | func_call.full_call_id = gsl::narrow_cast(handle); 124 | return func_call; 125 | } 126 | 127 | FuncWorkerState *GetAssociatedFuncWorkerState(const protocol::FuncCall &incoming_func_call); 128 | 129 | void NewFuncWorker(uint16_t client_id); 130 | 131 | void ExecuteFunc(FuncWorkerState *state, const protocol::Message &dispatch_func_call_message); 132 | 133 | bool NewOutgoingFuncCallCommon(const protocol::FuncCall &parent_call, 134 | const protocol::FuncCall &func_call, 135 | FuncWorkerState *worker_state, std::span input); 136 | 137 | void OnMessagePipeReadable(); 138 | 139 | void OnEnginePipeReadable(FuncWorkerState *state); 140 | 141 | void OnOutputPipeReadable(OutgoingFuncCallState *state); 142 | 143 | void OnOutgoingFuncCallFinished(const protocol::Message &message, OutgoingFuncCallState *state); 144 | 145 | DISALLOW_COPY_AND_ASSIGN(EventDrivenWorker); 146 | }; 147 | 148 | } // namespace faas 149 | 150 | #endif //LUMINE_EVENT_DRIVEN_WOEKER_H -------------------------------------------------------------------------------- /lib/runtime/worker_lib.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_WORKER_LIB_H 2 | #define LUMINE_WORKER_LIB_H 3 | 4 | #include "base/common.h" 5 | #include "common/protocol.h" 6 | #include "ipc/shm_region.h" 7 | 8 | namespace faas::worker_lib { 9 | 10 | bool GetFuncCallInput(const protocol::Message &dispatch_func_call_message, 11 | gsl::span *input, 12 | std::unique_ptr *shm_region); 13 | 14 | void FuncCallFinished(const protocol::FuncCall &func_call, 15 | bool success, gsl::span output, int32_t processing_time, 16 | protocol::Message *response); 17 | 18 | // pipe_buf is supposed to have a size of at least PIPE_BUF 19 | void FifoFuncCallFinished(const protocol::FuncCall &func_call, 20 | bool success, gsl::span output, int32_t processing_time, 21 | char *pipe_buf, protocol::Message *response); 22 | 23 | bool PrepareNewFuncCall(const protocol::FuncCall &func_call, uint64_t parent_func_call, 24 | gsl::span input, 25 | std::unique_ptr *shm_region, 26 | protocol::Message *invoke_func_message, bool across_hosts = false); 27 | 28 | // pipe_buf is supposed to have a size of at least PIPE_BUF 29 | bool FifoGetFuncCallOutput(const protocol::FuncCall &func_call, 30 | int output_fifo_fd, char *pipe_buf, 31 | bool *success, gsl::span *output, 32 | std::unique_ptr *shm_region, 33 | bool *pipe_buf_used); 34 | 35 | } // namespace faas 36 | 37 | #endif //LUMINE_WORKER_LIB_H -------------------------------------------------------------------------------- /lib/server/connection_base.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_CONNECTION_BASE_H 2 | #define LUMINE_CONNECTION_BASE_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | 7 | namespace faas::server { 8 | 9 | class IOWorker; 10 | 11 | class ConnectionBase : public uv::Base, public std::enable_shared_from_this { 12 | public: 13 | explicit ConnectionBase(int type = -1) : type_(type), id_(-1) {} 14 | 15 | virtual ~ConnectionBase() {} 16 | 17 | int type() const { return type_; } 18 | 19 | int id() const { return id_; } 20 | 21 | template 22 | T *as_ptr() { return static_cast(this); } 23 | 24 | std::shared_ptr ref_self() { return shared_from_this(); } 25 | 26 | virtual uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) = 0; 27 | 28 | virtual void Start(IOWorker *io_worker) = 0; 29 | 30 | virtual void ScheduleClose() = 0; 31 | 32 | // Only used for transferring connection from Server to IOWorker 33 | void set_id(int id) { id_ = id; } 34 | 35 | uv_write_t *uv_write_req_for_transfer() { return &uv_write_req_for_transfer_; } 36 | 37 | uv_write_t *uv_write_req_for_back_transfer() { return &uv_write_req_for_back_transfer_; } 38 | 39 | char *pipe_write_buf_for_transfer() { return pipe_write_buf_for_transfer_; } 40 | 41 | protected: 42 | int type_; 43 | int id_; 44 | 45 | private: 46 | uv_write_t uv_write_req_for_transfer_; 47 | uv_write_t uv_write_req_for_back_transfer_; 48 | char pipe_write_buf_for_transfer_[sizeof(void *)]; 49 | 50 | DISALLOW_COPY_AND_ASSIGN(ConnectionBase); 51 | }; 52 | 53 | } // namespace faas 54 | 55 | #endif //LUMINE_CONNECTION_BASE_H -------------------------------------------------------------------------------- /lib/server/io_worker.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_IO_WORKER_H 2 | #define LUMINE_IO_WORKER_H 3 | 4 | #include "base/common.h" 5 | #include "base/thread.h" 6 | #include "common/uv.h" 7 | #include "common/stat.h" 8 | #include "utils/buffer_pool.h" 9 | #include "utils/object_pool.h" 10 | #include "server/connection_base.h" 11 | 12 | namespace faas::server { 13 | 14 | class IOWorker final : public uv::Base { 15 | public: 16 | IOWorker(std::string_view worker_name, size_t read_buffer_size, size_t write_buffer_size); 17 | 18 | ~IOWorker(); 19 | 20 | std::string_view worker_name() const { return worker_name_; } 21 | 22 | // Return current IOWorker within event loop thread 23 | static IOWorker *current() { return current_; } 24 | 25 | void Start(int pipe_to_server_fd); 26 | 27 | void ScheduleStop(); 28 | 29 | void WaitForFinish(); 30 | 31 | // Called by Connection for ONLY once 32 | void OnConnectionClose(ConnectionBase *connection); 33 | 34 | // Can only be called from uv_loop_ 35 | void NewReadBuffer(size_t suggested_size, uv_buf_t *buf); 36 | 37 | void ReturnReadBuffer(const uv_buf_t *buf); 38 | 39 | void NewWriteBuffer(uv_buf_t *buf); 40 | 41 | void ReturnWriteBuffer(char *buf); 42 | 43 | uv_write_t *NewWriteRequest(); 44 | 45 | void ReturnWriteRequest(uv_write_t *write_req); 46 | 47 | // Pick a connection of given type managed by this IOWorker 48 | ConnectionBase *PickConnection(int type); 49 | 50 | // Schedule a function to run on this IO worker's event loop 51 | // thread. It can be called safely from other threads. 52 | // When the function is ready to run, IO worker will check if its 53 | // owner connection is still active, and will not run the function 54 | // if it is closed. 55 | void ScheduleFunction(ConnectionBase *owner, std::function fn); 56 | 57 | private: 58 | enum State { 59 | kCreated, kRunning, kStopping, kStopped 60 | }; 61 | 62 | std::string worker_name_; 63 | std::atomic state_; 64 | static thread_local IOWorker *current_; 65 | 66 | std::string log_header_; 67 | 68 | uv_loop_t uv_loop_; 69 | uv_async_t stop_event_; 70 | uv_pipe_t pipe_to_server_; 71 | uv_async_t run_fn_event_; 72 | 73 | base::Thread event_loop_thread_; 74 | absl::flat_hash_map connections_; 75 | absl::flat_hash_map> connections_by_type_; 76 | absl::flat_hash_map> connections_for_pick_; 77 | absl::flat_hash_map connections_for_pick_rr_; 78 | utils::BufferPool read_buffer_pool_; 79 | utils::BufferPool write_buffer_pool_; 80 | utils::SimpleObjectPool write_req_pool_; 81 | int connections_on_closing_; 82 | 83 | struct ScheduledFunction { 84 | int owner_id; 85 | std::function fn; 86 | }; 87 | absl::Mutex scheduled_function_mu_; 88 | absl::InlinedVector, 16> 89 | scheduled_functions_ ABSL_GUARDED_BY(scheduled_function_mu_); 90 | std::atomic async_event_recv_timestamp_; 91 | 92 | stat::StatisticsCollector uv_async_delay_stat_; 93 | 94 | void EventLoopThreadMain(); 95 | 96 | DECLARE_UV_ASYNC_CB_FOR_CLASS(Stop); 97 | 98 | DECLARE_UV_READ_CB_FOR_CLASS(NewConnection); 99 | 100 | DECLARE_UV_WRITE_CB_FOR_CLASS(PipeWrite); 101 | 102 | DECLARE_UV_ASYNC_CB_FOR_CLASS(RunScheduledFunctions); 103 | 104 | DISALLOW_COPY_AND_ASSIGN(IOWorker); 105 | }; 106 | 107 | } // namespace faas 108 | 109 | #endif //LUMINE_IO_WORKER_H -------------------------------------------------------------------------------- /lib/server/poller.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 10/5/22. 3 | // 4 | 5 | #include "poller.h" 6 | 7 | namespace faas::server { 8 | 9 | int64_t GetCurTimeMillis() { 10 | struct timeval tv{}; 11 | gettimeofday(&tv, nullptr); 12 | return tv.tv_sec * 1000 + tv.tv_usec / 1000; 13 | } 14 | 15 | Poller::Poller(std::function &fn, uint64_t timeout_as_ms) : 16 | base::Thread("Poller", fn), 17 | timeout_as_ms_(timeout_as_ms), 18 | old_time_ms_(0), 19 | create_thread_flag_(false), 20 | stop_flag_(false) {} 21 | 22 | void Poller::Start() { 23 | if (!create_thread_flag_) { 24 | create_thread_flag_ = true; 25 | old_time_ms_ = GetCurTimeMillis(); 26 | this->base::Thread::Start(); 27 | } 28 | mu_.Lock(); 29 | old_time_ms_ = GetCurTimeMillis(); 30 | mu_.Unlock(); 31 | 32 | } 33 | 34 | void Poller::Pause() { 35 | mu_.Lock(); 36 | old_time_ms_ -= timeout_as_ms_; 37 | mu_.Unlock(); 38 | } 39 | 40 | bool JudgeTimeoutNotExpired(void *arg) { 41 | uint64_t current_time = GetCurTimeMillis(); 42 | auto self = reinterpret_cast(arg); 43 | 44 | return (self->old_time_ms_ + self->timeout_as_ms_ >= current_time); 45 | } 46 | 47 | void Poller::Run(void *arg, bool non_blocking) { 48 | tid_ = gettid(); 49 | state_.store(kRunning); 50 | started_.Notify(); 51 | LOG(INFO) << "Start thread: " << name_; 52 | while (true) { 53 | mu_.Lock(); 54 | if (stop_flag_) { 55 | mu_.Unlock(); 56 | break; 57 | } 58 | mu_.Await(absl::Condition(JudgeTimeoutNotExpired, this)); 59 | this->fn_(); 60 | mu_.Unlock(); 61 | } 62 | state_.store(kFinished); 63 | } 64 | 65 | void Poller::Stop() { 66 | mu_.Lock(); 67 | stop_flag_ = true; 68 | mu_.Unlock(); 69 | 70 | Start(); 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /lib/server/poller.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 10/5/22. 3 | // 4 | 5 | #ifndef LUMINE_HOST_BENCH_POLLER_H 6 | #define LUMINE_HOST_BENCH_POLLER_H 7 | 8 | #include "base/logging.h" 9 | #include "base/common.h" 10 | #include "base/thread.h" 11 | 12 | #define POLLER_ASSERT(B, MESSAGE) {if(!(B)){std::cerr << MESSAGE;}} 13 | 14 | namespace faas::server { 15 | 16 | class Poller : public base::Thread { 17 | 18 | friend bool JudgeTimeoutNotExpired(void *arg); 19 | 20 | public: 21 | Poller(std::function &fn, uint64_t timeout_as_ms = 20000); 22 | 23 | void Start(); 24 | 25 | void Pause(); 26 | 27 | void Stop(); 28 | 29 | private: 30 | 31 | absl::Mutex mu_; 32 | 33 | uint64_t timeout_as_ms_; // ms 34 | uint64_t old_time_ms_; 35 | 36 | bool create_thread_flag_; 37 | bool stop_flag_; 38 | 39 | void Run(void *arg, bool non_blocking) override; 40 | 41 | }; 42 | } 43 | 44 | #endif //LUMINE_HOST_BENCH_POLLER_H 45 | -------------------------------------------------------------------------------- /lib/server/server_base.cpp: -------------------------------------------------------------------------------- 1 | #include "server/server_base.h" 2 | 3 | #define HLOG(l) LOG(l) << "Server: " 4 | #define HVLOG(l) VLOG(l) << "Server: " 5 | 6 | namespace faas::server { 7 | 8 | ServerBase::ServerBase() 9 | : state_(kCreated), 10 | event_loop_thread_("Server EL", absl::bind_front(&ServerBase::EventLoopThreadMain, this)), 11 | next_connection_id_(0) { 12 | UV_DCHECK_OK(uv_loop_init(&uv_loop_)); 13 | uv_loop_.data = &event_loop_thread_; 14 | UV_DCHECK_OK(uv_async_init(&uv_loop_, &stop_event_, &ServerBase::StopCallback)); 15 | stop_event_.data = this; 16 | } 17 | 18 | 19 | ServerBase::~ServerBase() { 20 | State state = state_.load(); 21 | DCHECK(state == kCreated || state == kStopped); 22 | UV_DCHECK_OK(uv_loop_close(&uv_loop_)); 23 | } 24 | 25 | void ServerBase::Start() { 26 | DCHECK(state_.load() == kCreated); 27 | StartInternal(); 28 | // Start thread for running event loop 29 | event_loop_thread_.Start(); 30 | state_.store(kRunning); 31 | } 32 | 33 | void ServerBase::ScheduleStop() { 34 | HLOG(INFO) << "Scheduled to stop"; 35 | UV_DCHECK_OK(uv_async_send(&stop_event_)); 36 | } 37 | 38 | void ServerBase::WaitForFinish() { 39 | DCHECK(state_.load() != kCreated); 40 | for (const auto &io_worker: io_workers_) { 41 | io_worker->WaitForFinish(); 42 | } 43 | event_loop_thread_.Join(); 44 | DCHECK(state_.load() == kStopped); 45 | HLOG(INFO) << "Stopped"; 46 | } 47 | 48 | void ServerBase::EventLoopThreadMain() { 49 | HLOG(INFO) << "Event loop starts"; 50 | int ret = uv_run(&uv_loop_, UV_RUN_DEFAULT); 51 | if (ret != 0) { 52 | HLOG(WARNING) << "uv_run returns non-zero value: " << ret; 53 | } 54 | HLOG(INFO) << "Event loop finishes"; 55 | state_.store(kStopped); 56 | } 57 | 58 | namespace { 59 | static void PipeReadBufferAllocCallback(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 60 | size_t buf_size = 256; 61 | buf->base = reinterpret_cast(malloc(buf_size)); 62 | buf->len = buf_size; 63 | } 64 | } 65 | 66 | IOWorker *ServerBase::CreateIOWorker(std::string_view worker_name, 67 | size_t read_buffer_size, size_t write_buffer_size) { 68 | DCHECK(state_.load() == kCreated); 69 | auto io_worker = std::make_unique(worker_name, read_buffer_size, write_buffer_size); 70 | int pipe_fds[2] = {-1, -1}; 71 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) < 0) { 72 | PLOG(FATAL) << "socketpair failed"; 73 | } 74 | uv_pipe_t *pipe_to_worker = new uv_pipe_t; 75 | UV_CHECK_OK(uv_pipe_init(&uv_loop_, pipe_to_worker, 1)); 76 | pipe_to_worker->data = this; 77 | UV_CHECK_OK(uv_pipe_open(pipe_to_worker, pipe_fds[0])); 78 | UV_CHECK_OK(uv_read_start(UV_AS_STREAM(pipe_to_worker), 79 | &PipeReadBufferAllocCallback, 80 | &ServerBase::ReturnConnectionCallback)); 81 | io_worker->Start(pipe_fds[1]); 82 | pipes_to_io_worker_[io_worker.get()] = std::unique_ptr(pipe_to_worker); 83 | IOWorker *ret = io_worker.get(); 84 | io_workers_.insert(std::move(io_worker)); 85 | return ret; 86 | } 87 | 88 | void ServerBase::RegisterConnection(IOWorker *io_worker, ConnectionBase *connection, 89 | uv_stream_t *uv_handle) { 90 | DCHECK_IN_EVENT_LOOP_THREAD(&uv_loop_); 91 | uv_write_t *write_req = connection->uv_write_req_for_transfer(); 92 | void **buf = reinterpret_cast(connection->pipe_write_buf_for_transfer()); 93 | *buf = connection; 94 | uv_buf_t uv_buf = uv_buf_init(reinterpret_cast(buf), sizeof(void *)); 95 | uv_pipe_t *pipe_to_worker = pipes_to_io_worker_[io_worker].get(); 96 | write_req->data = uv_handle; 97 | connection->set_id(next_connection_id_++); 98 | UV_DCHECK_OK(uv_write2(write_req, UV_AS_STREAM(pipe_to_worker), 99 | &uv_buf, 1, UV_AS_STREAM(uv_handle), 100 | &PipeWrite2Callback)); 101 | } 102 | 103 | UV_ASYNC_CB_FOR_CLASS(ServerBase, Stop) { 104 | if (state_.load(std::memory_order_consume) == kStopping) { 105 | HLOG(WARNING) << "Already in stopping state"; 106 | return; 107 | } 108 | HLOG(INFO) << "Start stopping process"; 109 | for (const auto &io_worker: io_workers_) { 110 | io_worker->ScheduleStop(); 111 | uv_pipe_t *pipe = pipes_to_io_worker_[io_worker.get()].get(); 112 | UV_DCHECK_OK(uv_read_stop(UV_AS_STREAM(pipe))); 113 | uv_close(UV_AS_HANDLE(pipe), nullptr); 114 | } 115 | uv_close(UV_AS_HANDLE(&stop_event_), nullptr); 116 | StopInternal(); 117 | state_.store(kStopping); 118 | } 119 | 120 | UV_READ_CB_FOR_CLASS(ServerBase, ReturnConnection) { 121 | if (nread < 0) { 122 | if (nread == UV_EOF) { 123 | HLOG(WARNING) << "Pipe is closed by the corresponding IO worker"; 124 | } else { 125 | HLOG(ERROR) << "Failed to read from pipe: " << uv_strerror(nread); 126 | } 127 | } else if (nread > 0) { 128 | utils::ReadMessages( 129 | &return_connection_read_buffer_, buf->base, nread, 130 | [this](ConnectionBase **connection) { 131 | OnConnectionClose(*connection); 132 | }); 133 | } 134 | free(buf->base); 135 | } 136 | 137 | UV_WRITE_CB_FOR_CLASS(ServerBase, PipeWrite2) { 138 | DCHECK(status == 0) << "Failed to write to pipe: " << uv_strerror(status); 139 | uv_close(UV_AS_HANDLE(req->data), uv::HandleFreeCallback); 140 | } 141 | 142 | } // namespace faas 143 | -------------------------------------------------------------------------------- /lib/server/server_base.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_SERVER_BASE_H 2 | #define LUMINE_SERVER_BASE_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | #include "utils/appendable_buffer.h" 7 | #include "server/connection_base.h" 8 | #include "server/io_worker.h" 9 | 10 | namespace faas::server { 11 | 12 | class ServerBase : public uv::Base { 13 | public: 14 | static constexpr size_t kDefaultIOWorkerBufferSize = 65536; 15 | 16 | ServerBase(); 17 | 18 | ~ServerBase() override; 19 | 20 | void Start(); 21 | 22 | void ScheduleStop(); 23 | 24 | void WaitForFinish(); 25 | 26 | protected: 27 | enum State { 28 | kCreated, kRunning, kStopping, kStopped 29 | }; 30 | std::atomic state_; 31 | 32 | uv_loop_t *uv_loop() { return &uv_loop_; } 33 | 34 | IOWorker *CreateIOWorker(std::string_view worker_name, 35 | size_t read_buffer_size = kDefaultIOWorkerBufferSize, 36 | size_t write_buffer_size = kDefaultIOWorkerBufferSize); 37 | 38 | void RegisterConnection(IOWorker *io_worker, ConnectionBase *connection, 39 | uv_stream_t *uv_handle); 40 | 41 | // Supposed to be implemented by sub-class 42 | virtual void StartInternal() {} 43 | 44 | virtual void StopInternal() {} 45 | 46 | virtual void OnConnectionClose(ConnectionBase *connection) {} 47 | 48 | protected: 49 | absl::flat_hash_map> pipes_to_io_worker_; 50 | 51 | private: 52 | uv_loop_t uv_loop_; 53 | uv_async_t stop_event_; 54 | base::Thread event_loop_thread_; 55 | 56 | absl::flat_hash_set> io_workers_; 57 | utils::AppendableBuffer return_connection_read_buffer_; 58 | int next_connection_id_; 59 | 60 | void EventLoopThreadMain(); 61 | 62 | DECLARE_UV_ASYNC_CB_FOR_CLASS(Stop); 63 | 64 | DECLARE_UV_READ_CB_FOR_CLASS(ReturnConnection); 65 | 66 | DECLARE_UV_WRITE_CB_FOR_CLASS(PipeWrite2); 67 | 68 | DISALLOW_COPY_AND_ASSIGN(ServerBase); 69 | 70 | }; 71 | 72 | } // namespace faas 73 | 74 | #endif //LUMINE_SERVER_BASE_H -------------------------------------------------------------------------------- /lib/utils/appendable_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_APPENDABLE_BUFFER_H 2 | #define LUMINE_APPENDABLE_BUFFER_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::utils { 7 | 8 | // AppendableBuffer is NOT thread-safe 9 | class AppendableBuffer { 10 | public: 11 | static constexpr int kInlineBufferSize = 48; 12 | static constexpr int kDefaultInitialSize = kInlineBufferSize; 13 | 14 | explicit AppendableBuffer(int initial_size = kDefaultInitialSize) 15 | : buf_size_(initial_size), pos_(0) { 16 | if (initial_size <= kInlineBufferSize) { 17 | buf_ = inline_buf_; 18 | buf_size_ = kInlineBufferSize; 19 | } else { 20 | buf_ = reinterpret_cast(malloc(initial_size)); 21 | } 22 | } 23 | 24 | ~AppendableBuffer() { 25 | if (buf_ != inline_buf_) { 26 | delete[] buf_; 27 | } 28 | } 29 | 30 | void AppendData(const char *data, int length) { 31 | if (length == 0) { 32 | return; 33 | } 34 | int new_size = buf_size_; 35 | while (pos_ + length > new_size) { 36 | new_size *= 2; 37 | } 38 | if (new_size > buf_size_) { 39 | char *new_buf = reinterpret_cast(malloc(new_size)); 40 | memcpy(new_buf, buf_, pos_); 41 | if (buf_ != inline_buf_) { 42 | free(buf_); 43 | } 44 | buf_ = new_buf; 45 | buf_size_ = new_size; 46 | } 47 | memcpy(buf_ + pos_, data, length); 48 | pos_ += length; 49 | } 50 | 51 | void AppendData(gsl::span data) { 52 | AppendData(data.data(), data.size()); 53 | } 54 | 55 | void Reset() { pos_ = 0; } 56 | 57 | void ConsumeFront(int size) { 58 | DCHECK_LE(size, pos_); 59 | if (size < pos_) { 60 | memmove(buf_, buf_ + size, pos_ - size); 61 | } 62 | pos_ -= size; 63 | } 64 | 65 | gsl::span to_span() const { 66 | return gsl::span(buf_, pos_); 67 | } 68 | 69 | const char *data() const { return buf_; } 70 | 71 | char *data() { return buf_; } 72 | 73 | size_t length() const { return pos_; } 74 | 75 | size_t buffer_size() const { return buf_size_; } 76 | 77 | void Swap(AppendableBuffer &other) { 78 | // In all cases, buffer size and position can be directly swapped 79 | std::swap(pos_, other.pos_); 80 | std::swap(buf_size_, other.buf_size_); 81 | if (buf_ != inline_buf_) { 82 | // Myself uses malloc buffer 83 | if (other.buf_ != other.inline_buf_) { 84 | // The other also uses malloc buffer, just swap two buffers 85 | std::swap(buf_, other.buf_); 86 | } else { 87 | // The other uses inline buffer 88 | // Set other's buffer to my malloc buffer 89 | other.buf_ = buf_; 90 | // Copy other's inline buffer data to my inline buffer 91 | memcpy(inline_buf_, other.inline_buf_, kInlineBufferSize); 92 | // Set my buffer to use inline buffer 93 | buf_ = inline_buf_; 94 | } 95 | } else { 96 | // Myself uses inline buffer 97 | if (other.buf_ != other.inline_buf_) { 98 | // The other uses malloc buffer 99 | buf_ = other.buf_; 100 | memcpy(other.inline_buf_, inline_buf_, kInlineBufferSize); 101 | other.buf_ = other.inline_buf_; 102 | } else { 103 | // The other also uses inline buffer, just swap contents of two inline buffers 104 | char tmp_buffer[kInlineBufferSize]; 105 | memcpy(tmp_buffer, inline_buf_, kInlineBufferSize); 106 | memcpy(inline_buf_, other.inline_buf_, kInlineBufferSize); 107 | memcpy(other.inline_buf_, tmp_buffer, kInlineBufferSize); 108 | } 109 | } 110 | } 111 | 112 | private: 113 | int buf_size_; 114 | int pos_; 115 | char *buf_; 116 | char inline_buf_[kInlineBufferSize]; 117 | 118 | DISALLOW_COPY_AND_ASSIGN(AppendableBuffer); 119 | }; 120 | 121 | template 122 | void ReadMessages(AppendableBuffer *buffer, 123 | const char *new_data, size_t new_data_length, 124 | std::function callback) { 125 | DCHECK_LT(buffer->length(), sizeof(T)); 126 | while (new_data_length + buffer->length() >= sizeof(T)) { 127 | size_t copy_size = sizeof(T) - buffer->length(); 128 | buffer->AppendData(new_data, copy_size); 129 | DCHECK_EQ(buffer->length(), sizeof(T)); 130 | T *message = reinterpret_cast(buffer->data()); 131 | callback(message); 132 | buffer->Reset(); 133 | new_data += copy_size; 134 | new_data_length -= copy_size; 135 | } 136 | if (new_data_length > 0) { 137 | buffer->AppendData(new_data, new_data_length); 138 | } 139 | } 140 | 141 | } // namespace faas 142 | 143 | #endif //LUMINE_APPENDABLE_BUFFER_H -------------------------------------------------------------------------------- /lib/utils/bench.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/bench.h" 2 | 3 | #include "common/time.h" 4 | #include "utils/env_variables.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace faas { 10 | namespace bench_utils { 11 | 12 | void PinCurrentThreadToCpu(int cpu) { 13 | pid_t tid = syscall(SYS_gettid); 14 | LOG(INFO) << "Pin thread (tid=" << tid << ") to CPU " << cpu; 15 | cpu_set_t set; 16 | CPU_ZERO(&set); 17 | CPU_SET(cpu, &set); 18 | PCHECK(sched_setaffinity(0, sizeof(set), &set) == 0); 19 | } 20 | 21 | std::unique_ptr SetupCpuRelatedPerfEvents(int cpu) { 22 | auto perf_event_group = std::make_unique(); 23 | if (cpu != -1) { 24 | perf_event_group->set_cpu(cpu); 25 | } 26 | if (utils::GetEnvVariableAsInt("PERF_EVENT_KERNEL_ONLY", 0)) { 27 | perf_event_group->set_exclude_user(true); 28 | } else if (utils::GetEnvVariableAsInt("PERF_EVENT_USER_ONLY", 0)) { 29 | perf_event_group->set_exclude_kernel(true); 30 | } 31 | CHECK(perf_event_group->AddEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES)) 32 | << "Failed to add PERF_COUNT_HW_CPU_CYCLES event"; 33 | CHECK(perf_event_group->AddEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS)) 34 | << "Failed to add PERF_COUNT_HW_INSTRUCTIONS event"; 35 | return perf_event_group; 36 | } 37 | 38 | void ReportCpuRelatedPerfEventValues(std::string_view header, 39 | utils::PerfEventGroup *perf_event_group, 40 | absl::Duration duration, size_t loop_count) { 41 | std::vector values = perf_event_group->ReadValues(); 42 | LOG(INFO) << header << ": value of PERF_COUNT_HW_CPU_CYCLES: " << values[0]; 43 | LOG(INFO) << header << ": value of PERF_COUNT_HW_INSTRUCTIONS: " << values[1]; 44 | LOG(INFO) << header << ": rate of PERF_COUNT_HW_CPU_CYCLES: " 45 | << values[0] / absl::ToDoubleMicroseconds(duration) << " per us, " 46 | << gsl::narrow_cast(values[0]) / loop_count << " per loop"; 47 | LOG(INFO) << header << ": rate of PERF_COUNT_HW_INSTRUCTIONS: " 48 | << values[1] / absl::ToDoubleMicroseconds(duration) << " per us, " 49 | << gsl::narrow_cast(values[1]) / loop_count << " per loop"; 50 | } 51 | 52 | BenchLoop::BenchLoop(LoopFn fn) 53 | : fn_(fn), max_duration_(absl::InfiniteDuration()), 54 | max_loop_count_(std::numeric_limits::max()), 55 | finished_(false) { 56 | Run(); 57 | } 58 | 59 | BenchLoop::BenchLoop(absl::Duration max_duration, LoopFn fn) 60 | : fn_(fn), max_duration_(max_duration), 61 | max_loop_count_(std::numeric_limits::max()), 62 | finished_(false) { 63 | Run(); 64 | } 65 | 66 | BenchLoop::BenchLoop(size_t max_loop_count, LoopFn fn) 67 | : fn_(fn), max_duration_(absl::InfiniteDuration()), 68 | max_loop_count_(max_loop_count), finished_(false) { 69 | Run(); 70 | } 71 | 72 | BenchLoop::~BenchLoop() { 73 | CHECK(finished_); 74 | } 75 | 76 | void BenchLoop::Run() { 77 | CHECK(!finished_); 78 | int64_t start_timestamp = GetMonotonicNanoTimestamp(); 79 | int64_t stop_timestamp = std::numeric_limits::max(); 80 | if (max_duration_ != absl::InfiniteDuration()) { 81 | stop_timestamp = start_timestamp + absl::ToInt64Nanoseconds(max_duration_); 82 | } 83 | loop_count_ = 0; 84 | do { 85 | if (loop_count_ >= max_loop_count_ || GetMonotonicNanoTimestamp() >= stop_timestamp) { 86 | break; 87 | } 88 | loop_count_++; 89 | if (!fn_()) { 90 | break; 91 | } 92 | } while (true); 93 | duration_ = absl::Nanoseconds(GetMonotonicNanoTimestamp() - start_timestamp); 94 | finished_ = true; 95 | } 96 | 97 | absl::Duration BenchLoop::elapsed_time() const { 98 | CHECK(finished_); 99 | return duration_; 100 | } 101 | 102 | size_t BenchLoop::loop_count() const { 103 | CHECK(finished_); 104 | return loop_count_; 105 | } 106 | 107 | } // namespace bench_utils 108 | } // namespace faas 109 | -------------------------------------------------------------------------------- /lib/utils/bench.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error utils/bench.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | #include "utils/perf_event.h" 9 | 10 | namespace faas { 11 | namespace bench_utils { 12 | 13 | void PinCurrentThreadToCpu(int cpu); 14 | 15 | std::unique_ptr SetupCpuRelatedPerfEvents(int cpu = -1); 16 | 17 | void ReportCpuRelatedPerfEventValues(std::string_view header, 18 | utils::PerfEventGroup *perf_event_group, 19 | absl::Duration duration, size_t loop_count); 20 | 21 | class BenchLoop { 22 | public: 23 | typedef std::function LoopFn; 24 | 25 | explicit BenchLoop(LoopFn fn); 26 | 27 | BenchLoop(absl::Duration max_duration, LoopFn fn); 28 | 29 | BenchLoop(size_t max_loop_count, LoopFn fn); 30 | 31 | ~BenchLoop(); 32 | 33 | absl::Duration elapsed_time() const; 34 | 35 | size_t loop_count() const; 36 | 37 | private: 38 | LoopFn fn_; 39 | absl::Duration max_duration_; 40 | size_t max_loop_count_; 41 | 42 | bool finished_; 43 | absl::Duration duration_; 44 | size_t loop_count_; 45 | 46 | void Run(); 47 | 48 | DISALLOW_COPY_AND_ASSIGN(BenchLoop); 49 | }; 50 | 51 | template 52 | class Samples { 53 | public: 54 | explicit Samples(size_t buffer_size) 55 | : buffer_size_(buffer_size), 56 | buffer_(new T[buffer_size]), 57 | count_(0), pos_(0) {} 58 | 59 | ~Samples() { delete[] buffer_; } 60 | 61 | void Add(T value) { 62 | count_++; 63 | pos_++; 64 | if (pos_ == buffer_size_) { 65 | LOG(WARNING) << "Internal buffer of Samples not big enough"; 66 | pos_ = 0; 67 | } 68 | buffer_[pos_] = value; 69 | } 70 | 71 | size_t count() const { return count_; } 72 | 73 | void ReportStatistics(std::string_view header) { 74 | size_t size = std::min(count_, buffer_size_); 75 | std::sort(buffer_, buffer_ + size); 76 | LOG(INFO) << header << ": count=" << count_ << ", " 77 | << "p50=" << buffer_[percentile(size, 0.5)] << ", " 78 | << "p70=" << buffer_[percentile(size, 0.7)] << ", " 79 | << "p90=" << buffer_[percentile(size, 0.9)] << ", " 80 | << "p99=" << buffer_[percentile(size, 0.99)] << ", " 81 | << "p99.9=" << buffer_[percentile(size, 0.999)]; 82 | } 83 | 84 | private: 85 | size_t buffer_size_; 86 | T *buffer_; 87 | size_t count_; 88 | size_t pos_; 89 | 90 | size_t percentile(size_t size, double p) { 91 | size_t idx = gsl::narrow_cast(size * p + 0.5); 92 | if (idx < 0) { 93 | return 0; 94 | } else if (idx >= size) { 95 | return size - 1; 96 | } else { 97 | return idx; 98 | } 99 | } 100 | 101 | DISALLOW_COPY_AND_ASSIGN(Samples); 102 | }; 103 | 104 | } // namespace bench_utils 105 | } // namespace faas 106 | -------------------------------------------------------------------------------- /lib/utils/bst.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas::utils { 6 | 7 | template 8 | class RankingBST { 9 | public: 10 | static constexpr size_t kDefaultInitialNodePoolSize = 100000; 11 | 12 | explicit RankingBST(size_t initial_node_pool_size = kDefaultInitialNodePoolSize); 13 | 14 | ~RankingBST(); 15 | 16 | void Clear(); 17 | 18 | size_t Size(); 19 | 20 | void Insert(T value); 21 | 22 | bool GetKthElement(size_t kth, T *value); 23 | 24 | private: 25 | struct Node { 26 | Node *left_child; 27 | Node *right_child; 28 | T value; 29 | size_t size; 30 | }; 31 | 32 | Node *root_; 33 | std::vector node_pool_; 34 | 35 | Node *NewNode(T value); 36 | 37 | void InsertInternal(Node **current_node, Node *new_node); 38 | 39 | void Maintain(Node *node); 40 | 41 | T GetKthElementInternal(Node *current_node, size_t kth); 42 | 43 | DISALLOW_COPY_AND_ASSIGN(RankingBST); 44 | }; 45 | 46 | template 47 | RankingBST::RankingBST(size_t initial_node_pool_size) { 48 | root_ = nullptr; 49 | node_pool_.resize(initial_node_pool_size); 50 | for (size_t i = 0; i < initial_node_pool_size; i++) { 51 | Node *node = new Node; 52 | node->left_child = nullptr; 53 | node->right_child = nullptr; 54 | node_pool_[i] = node; 55 | } 56 | } 57 | 58 | template 59 | RankingBST::~RankingBST() { 60 | while (!node_pool_.empty()) { 61 | Node *node = node_pool_.back(); 62 | node_pool_.pop_back(); 63 | if (node->left_child != nullptr) { 64 | node_pool_.push_back(node->left_child); 65 | } 66 | if (node->right_child != nullptr) { 67 | node_pool_.push_back(node->right_child); 68 | } 69 | delete node; 70 | } 71 | } 72 | 73 | template 74 | void RankingBST::Clear() { 75 | node_pool_.push_back(root_); 76 | root_ = nullptr; 77 | } 78 | 79 | template 80 | size_t RankingBST::Size() { 81 | return (root_ == nullptr) ? 0 : root_->size; 82 | } 83 | 84 | template 85 | void RankingBST::Insert(T value) { 86 | InsertInternal(&root_, NewNode(value)); 87 | } 88 | 89 | template 90 | bool RankingBST::GetKthElement(size_t kth, T *value) { 91 | if (root_ == nullptr || kth >= root_->size) { 92 | return false; 93 | } 94 | *value = GetKthElementInternal(root_, kth); 95 | return true; 96 | } 97 | 98 | template 99 | typename RankingBST::Node *RankingBST::NewNode(T value) { 100 | Node *node; 101 | if (node_pool_.empty()) { 102 | node = new Node; 103 | node->left_child = nullptr; 104 | node->right_child = nullptr; 105 | } else { 106 | node = node_pool_.back(); 107 | node_pool_.pop_back(); 108 | if (node->left_child != nullptr) { 109 | node_pool_.push_back(node->left_child); 110 | node->left_child = nullptr; 111 | } 112 | if (node->right_child != nullptr) { 113 | node_pool_.push_back(node->right_child); 114 | node->right_child = nullptr; 115 | } 116 | } 117 | node->value = value; 118 | node->size = 1; 119 | return node; 120 | } 121 | 122 | template 123 | void RankingBST::InsertInternal(Node **current_node, Node *new_node) { 124 | if (*current_node == nullptr) { 125 | *current_node = new_node; 126 | return; 127 | } 128 | T current_value = (*current_node)->value; 129 | if (new_node->value < current_value) { 130 | InsertInternal(&(*current_node)->left_child, new_node); 131 | } else { 132 | InsertInternal(&(*current_node)->right_child, new_node); 133 | } 134 | Maintain(*current_node); 135 | } 136 | 137 | template 138 | void RankingBST::Maintain(Node *node) { 139 | node->size = 1; 140 | if (node->left_child != nullptr) { 141 | node->size += node->left_child->size; 142 | } 143 | if (node->right_child != nullptr) { 144 | node->size += node->right_child->size; 145 | } 146 | } 147 | 148 | template 149 | T RankingBST::GetKthElementInternal(Node *current_node, size_t kth) { 150 | DCHECK(current_node != nullptr); 151 | size_t left_size = current_node->left_child == nullptr ? 0 : current_node->left_child->size; 152 | if (kth < left_size) { 153 | return GetKthElementInternal(current_node->left_child, kth); 154 | } else if (kth == left_size) { 155 | return current_node->value; 156 | } else { 157 | return GetKthElementInternal(current_node->right_child, kth - (left_size + 1)); 158 | } 159 | } 160 | 161 | } // namespace faas 162 | -------------------------------------------------------------------------------- /lib/utils/buffer_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_BUFFER_POOL_H 2 | #define LUMINE_BUFFER_POOL_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | 7 | namespace faas::utils { 8 | 9 | // BufferPool is NOT thread-safe 10 | class BufferPool { 11 | public: 12 | BufferPool(std::string_view pool_name, size_t buffer_size) 13 | : pool_name_(std::string(pool_name)), buffer_size_(buffer_size) {} 14 | 15 | ~BufferPool() {} 16 | 17 | void Get(char **buf, size_t *size) { 18 | if (available_buffers_.empty()) { 19 | std::unique_ptr new_buffer(new char[buffer_size_]); 20 | available_buffers_.push_back(new_buffer.get()); 21 | all_buffers_.push_back(std::move(new_buffer)); 22 | } 23 | *buf = available_buffers_.back(); 24 | available_buffers_.pop_back(); 25 | *size = buffer_size_; 26 | 27 | // LOG(INFO) << "BufferPool[" << pool_name_ << "]: Allocate buffer, " 28 | // << fmt::format("available/all : {}/{} ", available_buffers_.size(), all_buffers_.size()); 29 | } 30 | 31 | void Get(uv_buf_t *buf) { 32 | Get(&buf->base, &buf->len); 33 | } 34 | 35 | void Return(char *buf) { 36 | available_buffers_.push_back(buf); 37 | 38 | // LOG(INFO) << "BufferPool[" << pool_name_ << "]: Reclaim buffer, " 39 | // << fmt::format("available/all : {}/{} ", available_buffers_.size(), all_buffers_.size()); 40 | } 41 | 42 | void Return(const uv_buf_t *buf) { 43 | DCHECK_EQ(buf->len, buffer_size_); 44 | Return(buf->base); 45 | } 46 | 47 | private: 48 | std::string pool_name_; 49 | size_t buffer_size_; 50 | absl::InlinedVector available_buffers_; 51 | absl::InlinedVector, 16> all_buffers_; 52 | 53 | DISALLOW_COPY_AND_ASSIGN(BufferPool); 54 | }; 55 | 56 | } // namespace faas 57 | 58 | #endif //LUMINE_BUFFER_POOL_H -------------------------------------------------------------------------------- /lib/utils/docker.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/docker.h" 2 | #include "common/time.h" 3 | #include "utils/fs.h" 4 | 5 | namespace faas::docker_utils { 6 | 7 | namespace { 8 | std::string cgroupfs_root = "/sys/fs/cgroup"; 9 | 10 | template 11 | bool ReadIntegerFromFile(std::string_view path, T *value) { 12 | std::string contents; 13 | if (!fs_utils::ReadContents(path, &contents)) { 14 | return false; 15 | } 16 | return absl::SimpleAtoi(contents, value); 17 | } 18 | 19 | bool ReadCpuAcctStat(std::string_view container_id, int32_t *user, int32_t *system) { 20 | std::string full_path(fmt::format( 21 | "{}/cpuacct/docker/{}/cpuacct.stat", cgroupfs_root, container_id)); 22 | std::string contents; 23 | if (!fs_utils::ReadContents(full_path, &contents)) { 24 | return false; 25 | } 26 | for (const auto &line: absl::StrSplit(contents, '\n', absl::SkipWhitespace())) { 27 | if (absl::StartsWith(line, "user ")) { 28 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "user "), user)) { 29 | return false; 30 | } 31 | } 32 | if (absl::StartsWith(line, "system ")) { 33 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "system "), system)) { 34 | return false; 35 | } 36 | } 37 | } 38 | return true; 39 | } 40 | } 41 | 42 | void SetCgroupFsRoot(std::string_view path) { 43 | cgroupfs_root = std::string(path); 44 | } 45 | 46 | const std::string kInvalidContainerId(kContainerIdLength, '0'); 47 | 48 | std::string GetSelfContainerId() { 49 | std::string contents; 50 | if (!fs_utils::ReadContents("/proc/self/cgroup", &contents)) { 51 | LOG(ERROR) << "Failed to read /proc/self/cgroup"; 52 | return kInvalidContainerId; 53 | } 54 | size_t pos = contents.find("/docker/"); 55 | if (pos == std::string::npos 56 | || pos + strlen("/docker/") + kContainerIdLength >= contents.length()) { 57 | LOG(ERROR) << "Cannot find docker's cgroup in /proc/self/cgroup"; 58 | return kInvalidContainerId; 59 | } 60 | return contents.substr(pos + strlen("/docker/"), kContainerIdLength); 61 | } 62 | 63 | bool ReadContainerStat(std::string_view container_id, ContainerStat *stat) { 64 | stat->timestamp = GetMonotonicNanoTimestamp(); 65 | if (!ReadIntegerFromFile(fmt::format("{}/cpuacct/docker/{}/cpuacct.usage", 66 | cgroupfs_root, container_id), 67 | &stat->cpu_usage)) { 68 | return false; 69 | } 70 | if (!ReadCpuAcctStat(container_id, &stat->cpu_stat_user, &stat->cpu_stat_sys)) { 71 | return false; 72 | } 73 | return true; 74 | } 75 | 76 | } // namespace faas 77 | -------------------------------------------------------------------------------- /lib/utils/docker.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_DOCKER_H 2 | #define LUMINE_DOCKER_H 3 | 4 | 5 | #include "base/common.h" 6 | 7 | namespace faas::docker_utils { 8 | 9 | // cgroup_fs root by default is /sys/fs/cgroup 10 | void SetCgroupFsRoot(std::string_view path); 11 | 12 | constexpr size_t kContainerIdLength = 64; 13 | extern const std::string kInvalidContainerId; 14 | 15 | // Get container ID of the running process 16 | // Will return kInvalidContainerId if failed 17 | std::string GetSelfContainerId(); 18 | 19 | struct ContainerStat { 20 | int64_t timestamp; // in ns 21 | int64_t cpu_usage; // in ns, from cpuacct.usage 22 | int32_t cpu_stat_user; // in tick, from cpuacct.stat 23 | int32_t cpu_stat_sys; // in tick, from cpuacct.stat 24 | }; 25 | 26 | bool ReadContainerStat(std::string_view container_id, ContainerStat *stat); 27 | 28 | } // namespace faas 29 | #endif //LUMINE_DOCKER_H -------------------------------------------------------------------------------- /lib/utils/dynamic_library.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_DYNAMIC_LIBRARY_H 2 | #define LUMINE_DYNAMIC_LIBRARY_H 3 | 4 | #include "base/common.h" 5 | 6 | #include 7 | 8 | namespace faas::utils { 9 | 10 | class DynamicLibrary { 11 | public: 12 | ~DynamicLibrary() { 13 | if (dlclose(handle_) != 0) { 14 | LOG(FATAL) << "Failed to close dynamic library: " << dlerror(); 15 | } 16 | } 17 | 18 | static std::unique_ptr Create(std::string_view path) { 19 | void *handle = dlopen(std::string(path).c_str(), RTLD_LAZY); 20 | if (handle == nullptr) { 21 | LOG(FATAL) << "Failed to open dynamic library " << path << ": " << dlerror(); 22 | } 23 | DynamicLibrary *dynamic_library = new DynamicLibrary(handle); 24 | return std::unique_ptr(dynamic_library); 25 | } 26 | 27 | template 28 | T LoadSymbol(std::string_view name) { 29 | void *ptr = dlsym(handle_, std::string(name).c_str()); 30 | if (ptr == nullptr) { 31 | LOG(FATAL) << "Cannot load symbol " << name << " from the dynamic library"; 32 | } 33 | return reinterpret_cast(ptr); 34 | } 35 | 36 | private: 37 | void *handle_; 38 | 39 | explicit DynamicLibrary(void *handle) : handle_(handle) {} 40 | 41 | DISALLOW_COPY_AND_ASSIGN(DynamicLibrary); 42 | }; 43 | 44 | } // namespace faas 45 | 46 | #endif //LUMINE_DYNAMIC_LIBRARY_H -------------------------------------------------------------------------------- /lib/utils/env_variables.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_UTILS_ENV_VARIABLES_H 2 | #define LUMINE_UTILS_ENV_VARIABLES_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::utils { 7 | 8 | inline std::string_view GetEnvVariable(std::string_view name, 9 | std::string_view default_value = "") { 10 | char *value = getenv(std::string(name).c_str()); 11 | return value != nullptr ? value : default_value; 12 | } 13 | 14 | inline int GetEnvVariableAsInt(std::string_view name, int default_value = 0) { 15 | char *value = getenv(std::string(name).c_str()); 16 | if (value == nullptr) { 17 | return default_value; 18 | } 19 | return atoi(value); 20 | } 21 | 22 | } // namespace faas 23 | 24 | #endif -------------------------------------------------------------------------------- /lib/utils/exp_moving_avg.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_EXP_MOVING_AVG_H 2 | #define LUMINE_EXP_MOVING_AVG_H 3 | 4 | #include "base/common.h" 5 | #include "base/macro.h" 6 | #include "base/logging.h" 7 | 8 | namespace faas::utils { 9 | 10 | class ExpMovingAvg { 11 | public: 12 | explicit ExpMovingAvg(double alpha = 0.001, size_t min_samples = 128) 13 | : alpha_(alpha), min_samples_(min_samples), avg_(0), n_samples_(0) {} 14 | 15 | ~ExpMovingAvg() {} 16 | 17 | template 18 | void AddSample(T sample) { 19 | if (n_samples_ < min_samples_) { 20 | avg_ += static_cast(sample) / min_samples_; 21 | } else { 22 | avg_ += alpha_ * (static_cast(sample) - avg_); 23 | } 24 | n_samples_++; 25 | } 26 | 27 | double GetValue() { 28 | if (n_samples_ < min_samples_) { 29 | return 0; 30 | } else { 31 | return avg_; 32 | } 33 | } 34 | 35 | void Reset() { 36 | n_samples_ = 0; 37 | avg_ = 0; 38 | } 39 | 40 | private: 41 | double alpha_; 42 | size_t min_samples_; 43 | double avg_; 44 | size_t n_samples_; 45 | 46 | DISALLOW_COPY_AND_ASSIGN(ExpMovingAvg); 47 | }; 48 | 49 | class ExpMovingAvgExt { 50 | public: 51 | explicit ExpMovingAvgExt(double tau_ms = 0, double alpha = 0.001, 52 | double p = 1.0, size_t min_samples = 128) 53 | : tau_us_(tau_ms * 1000), alpha_(alpha), 54 | p_(p), min_samples_(min_samples), avg_(0), 55 | n_samples_(0), last_timestamp_us_(-1) {} 56 | 57 | ~ExpMovingAvgExt() {} 58 | 59 | template 60 | void AddSample(int64_t timestamp_us, T sample) { 61 | if (sample < 0) { 62 | LOG(WARNING) << "ExpMovingAvgExt is supposed to handle non-negative sample values"; 63 | return; 64 | } 65 | if (last_timestamp_us_ > 0 && timestamp_us <= last_timestamp_us_) { 66 | return; 67 | } 68 | if (n_samples_ < min_samples_) { 69 | if (p_ == 0) { 70 | avg_ += std::log(static_cast(sample)) / min_samples_; 71 | } else { 72 | avg_ += std::pow(static_cast(sample), p_) / min_samples_; 73 | } 74 | } else { 75 | double alpha = alpha_; 76 | if (tau_us_ > 0) { 77 | alpha = 1.0 - std::exp(-(timestamp_us - last_timestamp_us_) / tau_us_); 78 | } 79 | if (p_ == 0) { 80 | avg_ += alpha * (std::log(static_cast(sample)) - avg_); 81 | } else { 82 | avg_ += alpha * (std::pow(static_cast(sample), p_) - avg_); 83 | } 84 | } 85 | last_timestamp_us_ = timestamp_us; 86 | n_samples_++; 87 | } 88 | 89 | double GetValue() { 90 | if (n_samples_ < min_samples_) { 91 | return 0; 92 | } else { 93 | if (p_ == 0) { 94 | return std::exp(avg_); 95 | } else { 96 | return std::pow(avg_, 1.0 / p_); 97 | } 98 | } 99 | } 100 | 101 | void Reset() { 102 | n_samples_ = 0; 103 | avg_ = 0; 104 | last_timestamp_us_ = -1; 105 | } 106 | 107 | private: 108 | double tau_us_; 109 | double alpha_; 110 | double p_; 111 | size_t min_samples_; 112 | double avg_; 113 | size_t n_samples_; 114 | int64_t last_timestamp_us_; 115 | 116 | DISALLOW_COPY_AND_ASSIGN(ExpMovingAvgExt); 117 | }; 118 | 119 | } // namespace faas 120 | 121 | #endif //LUMINE_EXP_MOVING_AVG_H -------------------------------------------------------------------------------- /lib/utils/fs.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/fs.h" 2 | #include "base/logging.h" 3 | #include "base/common.h" 4 | 5 | namespace faas::fs_utils { 6 | 7 | namespace { 8 | bool Stat(std::string_view path, struct stat *statbuf) { 9 | return stat(std::string(path).c_str(), statbuf) == 0; 10 | } 11 | 12 | int RemoveFileFtwFn(const char *fpath, const struct stat *sb, 13 | int typeflag, struct FTW *ftwbuf) { 14 | return remove(fpath) != 0; 15 | } 16 | } 17 | 18 | bool Exists(std::string_view path) { 19 | return access(std::string(path).c_str(), F_OK) == 0; 20 | } 21 | 22 | bool IsFile(std::string_view path) { 23 | struct stat statbuf; 24 | if (!Stat(path, &statbuf)) { 25 | return false; 26 | } 27 | return S_ISREG(statbuf.st_mode) != 0; 28 | } 29 | 30 | bool IsDirectory(std::string_view path) { 31 | struct stat statbuf; 32 | if (!Stat(path, &statbuf)) { 33 | return false; 34 | } 35 | return S_ISDIR(statbuf.st_mode) != 0; 36 | } 37 | 38 | std::string GetRealPath(std::string_view path) { 39 | char *result = realpath(std::string(path).c_str(), nullptr); 40 | if (result == nullptr) { 41 | LOG(WARNING) << path << " is not a valid path"; 42 | return std::string(path); 43 | } 44 | std::string result_str(result); 45 | free(result); 46 | return result_str; 47 | } 48 | 49 | bool MakeDirectory(std::string_view path) { 50 | return mkdir(std::string(path).c_str(), __FAAS_DIR_CREAT_MODE) == 0; 51 | } 52 | 53 | bool Remove(std::string_view path) { 54 | return remove(std::string(path).c_str()) == 0; 55 | } 56 | 57 | bool RemoveDirectoryRecursively(std::string_view path) { 58 | return nftw(std::string(path).c_str(), RemoveFileFtwFn, 8, 59 | FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0; 60 | } 61 | 62 | bool ReadContents(std::string_view path, std::string *contents) { 63 | FILE *fin = fopen(std::string(path).c_str(), "rb"); 64 | if (fin == nullptr) { 65 | LOG(ERROR) << "Failed to open file: " << path; 66 | return false; 67 | } 68 | auto close_file = gsl::finally([fin] { fclose(fin); }); 69 | char buffer[128]; 70 | contents->clear(); 71 | while (feof(fin) == 0) { 72 | size_t nread = fread(buffer, 1, sizeof(buffer), fin); 73 | if (nread > 0) { 74 | contents->append(buffer, nread); 75 | } else { 76 | break; 77 | } 78 | } 79 | return true; 80 | } 81 | 82 | std::string JoinPath(std::string_view path1, std::string_view path2) { 83 | return fmt::format("{}/{}", path1, path2); 84 | } 85 | 86 | std::string JoinPath(std::string_view path1, std::string_view path2, std::string_view path3) { 87 | return fmt::format("{}/{}/{}", path1, path2, path3); 88 | } 89 | 90 | } // namespace faas 91 | -------------------------------------------------------------------------------- /lib/utils/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_FS_H 2 | #define LUMINE_FS_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::fs_utils { 7 | 8 | bool Exists(std::string_view path); 9 | 10 | bool IsFile(std::string_view path); 11 | 12 | bool IsDirectory(std::string_view path); 13 | 14 | std::string GetRealPath(std::string_view path); 15 | 16 | bool MakeDirectory(std::string_view path); 17 | 18 | bool Remove(std::string_view path); 19 | 20 | bool RemoveDirectoryRecursively(std::string_view path); 21 | 22 | bool ReadContents(std::string_view path, std::string *contents); 23 | 24 | std::string JoinPath(std::string_view path1, std::string_view path2); 25 | 26 | std::string JoinPath(std::string_view path1, std::string_view path2, std::string_view path3); 27 | 28 | } // namespace faas 29 | 30 | #endif //LUMINE_FS_H -------------------------------------------------------------------------------- /lib/utils/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas::io_utils { 6 | 7 | template 8 | bool SendMessage(int fd, const T &message) { 9 | const char *buffer = reinterpret_cast(&message); 10 | size_t pos = 0; 11 | while (pos < sizeof(T)) { 12 | ssize_t nwrite = write(fd, buffer + pos, sizeof(T) - pos); 13 | DCHECK(nwrite != 0) << "write() returns 0"; 14 | if (nwrite < 0) { 15 | if (errno == EAGAIN || errno == EINTR) { 16 | continue; 17 | } 18 | return false; 19 | } 20 | pos += nwrite; 21 | } 22 | return true; 23 | } 24 | 25 | template 26 | bool RecvMessage(int fd, T *message, bool *eof) { 27 | char *buffer = reinterpret_cast(message); 28 | size_t pos = 0; 29 | if (eof != nullptr) { 30 | *eof = false; 31 | } 32 | while (pos < sizeof(T)) { 33 | ssize_t nread = read(fd, buffer + pos, sizeof(T) - pos); 34 | if (nread == 0) { 35 | if (eof != nullptr) { 36 | *eof = true; 37 | } 38 | return false; 39 | } 40 | if (nread < 0) { 41 | if (errno == EAGAIN || errno == EINTR) { 42 | continue; 43 | } 44 | return false; 45 | } 46 | pos += nread; 47 | } 48 | return true; 49 | } 50 | 51 | inline bool SendData(int fd, const char *data, size_t size) { 52 | size_t pos = 0; 53 | while (pos < size) { 54 | ssize_t nwrite = write(fd, data + pos, size - pos); 55 | DCHECK(nwrite != 0) << "write() returns 0"; 56 | if (nwrite < 0) { 57 | if (errno == EAGAIN || errno == EINTR) { 58 | continue; 59 | } 60 | return false; 61 | } 62 | pos += nwrite; 63 | } 64 | return true; 65 | } 66 | 67 | inline bool SendData(int fd, gsl::span data) { 68 | return SendData(fd, data.data(), data.size()); 69 | } 70 | 71 | inline bool RecvData(int fd, char *buffer, size_t size, bool *eof) { 72 | size_t pos = 0; 73 | if (eof != nullptr) { 74 | *eof = false; 75 | } 76 | while (pos < size) { 77 | ssize_t nread = read(fd, buffer + pos, size - pos); 78 | if (nread == 0) { 79 | if (eof != nullptr) { 80 | *eof = true; 81 | } 82 | return false; 83 | } 84 | if (nread < 0) { 85 | if (errno == EAGAIN || errno == EINTR) { 86 | continue; 87 | } 88 | return false; 89 | } 90 | pos += nread; 91 | } 92 | return true; 93 | } 94 | 95 | } // namespace faas 96 | -------------------------------------------------------------------------------- /lib/utils/object_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_OBJECT_POOL_H 2 | #define LUMINE_OBJECT_POOL_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::utils { 7 | 8 | template 9 | T *DefaultObjectConstructor() { 10 | return new T(); 11 | } 12 | 13 | // SimpleObjectPool is NOT thread-safe 14 | template 15 | class SimpleObjectPool { 16 | public: 17 | explicit SimpleObjectPool(std::function object_constructor = DefaultObjectConstructor) 18 | : object_constructor_(object_constructor) {} 19 | 20 | ~SimpleObjectPool() {} 21 | 22 | T *Get() { 23 | if (free_objs_.empty()) { 24 | T *new_obj = object_constructor_(); 25 | free_objs_.push_back(new_obj); 26 | objs_.emplace_back(new_obj); 27 | } 28 | DCHECK(!free_objs_.empty()); 29 | T *obj = free_objs_.back(); 30 | free_objs_.pop_back(); 31 | return obj; 32 | } 33 | 34 | void Return(T *obj) { 35 | free_objs_.push_back(obj); 36 | } 37 | 38 | private: 39 | std::function object_constructor_; 40 | 41 | absl::InlinedVector, 16> objs_; 42 | absl::InlinedVector free_objs_; 43 | 44 | 45 | DISALLOW_COPY_AND_ASSIGN(SimpleObjectPool); 46 | }; 47 | 48 | } // namespace faas 49 | 50 | #endif //LUMINE_OBJECT_POOL_H -------------------------------------------------------------------------------- /lib/utils/perf_event.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/perf_event.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace faas { 7 | namespace utils { 8 | 9 | namespace { 10 | long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, 11 | int group_fd, unsigned long flags) { 12 | return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); 13 | } 14 | } 15 | 16 | PerfEventGroup::PerfEventGroup() 17 | : cpu_(-1), exclude_user_(false), exclude_kernel_(false), group_fd_(-1) {} 18 | 19 | PerfEventGroup::~PerfEventGroup() { 20 | for (int fd: event_fds_) { 21 | PCHECK(close(fd) == 0); 22 | } 23 | } 24 | 25 | bool PerfEventGroup::AddEvent(uint32_t type, uint64_t config) { 26 | struct perf_event_attr pe; 27 | memset(&pe, 0, sizeof(pe)); 28 | pe.type = type; 29 | pe.size = sizeof(pe); 30 | pe.config = config; 31 | pe.read_format = PERF_FORMAT_GROUP; 32 | pe.disabled = 1; 33 | pe.exclude_kernel = exclude_kernel_; 34 | pe.exclude_user = exclude_user_; 35 | int fd = perf_event_open(&pe, 0, cpu_, group_fd_, 0); 36 | if (fd == -1) { 37 | return false; 38 | } 39 | if (group_fd_ == -1) { 40 | group_fd_ = fd; 41 | } 42 | event_fds_.push_back(fd); 43 | return true; 44 | } 45 | 46 | void PerfEventGroup::Reset() { 47 | CHECK(group_fd_ != -1) << "No event has been added yet"; 48 | for (int fd: event_fds_) { 49 | PCHECK(ioctl(fd, PERF_EVENT_IOC_RESET, 0) == 0) << "ioctl (reset) failed"; 50 | } 51 | } 52 | 53 | void PerfEventGroup::Enable() { 54 | CHECK(group_fd_ != -1) << "No event has been added yet"; 55 | for (int fd: event_fds_) { 56 | PCHECK(ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) == 0) << "ioctl (reset) failed"; 57 | } 58 | } 59 | 60 | void PerfEventGroup::Disable() { 61 | CHECK(group_fd_ != -1) << "No event has been added yet"; 62 | for (int fd: event_fds_) { 63 | PCHECK(ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) == 0) << "ioctl (reset) failed"; 64 | } 65 | } 66 | 67 | std::vector PerfEventGroup::ReadValues() { 68 | CHECK(group_fd_ != -1) << "No event has been added yet"; 69 | uint64_t *values = new uint64_t[event_fds_.size() + 1]; 70 | ssize_t read_size = sizeof(uint64_t) * (event_fds_.size() + 1); 71 | PCHECK(read(group_fd_, values, read_size) == read_size); 72 | CHECK_EQ(static_cast(values[0]), event_fds_.size()); 73 | std::vector ret; 74 | for (size_t i = 0; i < event_fds_.size(); i++) { 75 | ret.push_back(values[1 + i]); 76 | } 77 | delete[] values; 78 | return ret; 79 | } 80 | 81 | } // namespace utils 82 | } // namespace faas 83 | -------------------------------------------------------------------------------- /lib/utils/perf_event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error utils/perf_event.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | 9 | #include 10 | 11 | namespace faas { 12 | namespace utils { 13 | 14 | class PerfEventGroup { 15 | public: 16 | PerfEventGroup(); 17 | 18 | ~PerfEventGroup(); 19 | 20 | void set_cpu(int cpu) { cpu_ = cpu; } 21 | 22 | void set_exclude_user(bool value) { exclude_user_ = value; } 23 | 24 | void set_exclude_kernel(bool value) { exclude_kernel_ = value; } 25 | 26 | bool AddEvent(uint32_t type, uint64_t config); 27 | 28 | void Reset(); 29 | 30 | void Enable(); 31 | 32 | void Disable(); 33 | 34 | std::vector ReadValues(); 35 | 36 | void ResetAndEnable() { 37 | Reset(); 38 | Enable(); 39 | } 40 | 41 | private: 42 | int cpu_; 43 | bool exclude_user_; 44 | bool exclude_kernel_; 45 | 46 | int group_fd_; 47 | std::vector event_fds_; 48 | 49 | DISALLOW_COPY_AND_ASSIGN(PerfEventGroup); 50 | }; 51 | 52 | } // namespace utils 53 | } // namespace faas 54 | -------------------------------------------------------------------------------- /lib/utils/procfs.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/procfs.h" 2 | 3 | #include "common/time.h" 4 | #include "utils/fs.h" 5 | 6 | namespace faas::procfs_utils { 7 | 8 | bool ReadThreadStat(int tid, ThreadStat *stat) { 9 | stat->timestamp = GetMonotonicNanoTimestamp(); 10 | 11 | std::string procfs_stat_path(fmt::format("/proc/self/task/{}/stat", tid)); 12 | std::string stat_contents; 13 | if (!fs_utils::ReadContents(procfs_stat_path, &stat_contents)) { 14 | LOG(ERROR) << "Failed to read " << procfs_stat_path; 15 | return false; 16 | } 17 | size_t first_parentheses_pos = stat_contents.find('('); 18 | size_t last_parentheses_pos = stat_contents.find_last_of(')'); 19 | if (first_parentheses_pos == std::string::npos 20 | || last_parentheses_pos == std::string::npos) { 21 | LOG(ERROR) << "Invalid /proc/[tid]/stat contents"; 22 | return false; 23 | } 24 | std::vector parts = absl::StrSplit( 25 | stat_contents.substr(last_parentheses_pos + 1), ' ', absl::SkipWhitespace()); 26 | if (parts.size() != 50) { 27 | LOG(ERROR) << "Invalid /proc/[tid]/stat contents"; 28 | return false; 29 | } 30 | if (!absl::SimpleAtoi(parts[11], &stat->cpu_stat_user)) { 31 | return false; 32 | } 33 | if (!absl::SimpleAtoi(parts[12], &stat->cpu_stat_sys)) { 34 | return false; 35 | } 36 | 37 | std::string procfs_status_path(fmt::format("/proc/self/task/{}/status", tid)); 38 | std::string status_contents; 39 | if (!fs_utils::ReadContents(procfs_status_path, &status_contents)) { 40 | LOG(ERROR) << "Failed to read " << procfs_status_path; 41 | return false; 42 | } 43 | stat->voluntary_ctxt_switches = -1; 44 | stat->nonvoluntary_ctxt_switches = -1; 45 | for (const auto &line: absl::StrSplit(status_contents, '\n', absl::SkipWhitespace())) { 46 | if (absl::StartsWith(line, "voluntary_ctxt_switches:")) { 47 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "voluntary_ctxt_switches:"), 48 | &stat->voluntary_ctxt_switches)) { 49 | return false; 50 | } 51 | } 52 | if (absl::StartsWith(line, "nonvoluntary_ctxt_switches:")) { 53 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "nonvoluntary_ctxt_switches:"), 54 | &stat->nonvoluntary_ctxt_switches)) { 55 | return false; 56 | } 57 | } 58 | } 59 | if (stat->voluntary_ctxt_switches == -1 || stat->nonvoluntary_ctxt_switches == -1) { 60 | LOG(ERROR) << "Invalid /proc/[tid]/status contents"; 61 | return false; 62 | } 63 | 64 | return true; 65 | } 66 | 67 | } // namespace faas 68 | -------------------------------------------------------------------------------- /lib/utils/procfs.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_PROCFS_H 2 | #define LUMINE_PROCFS_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::procfs_utils { 7 | 8 | struct ThreadStat { 9 | int64_t timestamp; // in ns 10 | int32_t cpu_stat_user; // in tick, from /proc/[tid]/stat utime 11 | int32_t cpu_stat_sys; // in tick, from /proc/[tid]/stat stime 12 | int32_t voluntary_ctxt_switches; // from /proc/[tid]/status 13 | int32_t nonvoluntary_ctxt_switches; // from /proc/[tid]/status 14 | }; 15 | 16 | bool ReadThreadStat(int tid, ThreadStat *stat); 17 | 18 | } // namespace faas 19 | 20 | #endif //LUMINE_PROCFS_H -------------------------------------------------------------------------------- /lib/utils/random.cpp: -------------------------------------------------------------------------------- 1 | #include "base/common.h" 2 | #include "utils/random.h" 3 | 4 | namespace faas::utils { 5 | 6 | namespace { 7 | static thread_local std::mt19937_64 rd_gen(syscall(SYS_gettid)); 8 | } 9 | 10 | float GetRandomFloat(float a, float b) { 11 | std::uniform_real_distribution distribution(a, b); 12 | return distribution(rd_gen); 13 | } 14 | 15 | } // namespace faas 16 | -------------------------------------------------------------------------------- /lib/utils/random.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_RANDOM_H 2 | #define LUMINE_RANDOM_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::utils { 7 | 8 | // Thread-safe 9 | float GetRandomFloat(float a = 0.0f, float b = 1.0f); // In [a,b] 10 | 11 | } // namespace faas 12 | 13 | #endif //LUMINE_RANDOM_H -------------------------------------------------------------------------------- /lib/utils/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_SOCKET_H 2 | #define LUMINE_SOCKET_H 3 | 4 | #include "base/common.h" 5 | 6 | namespace faas::utils { 7 | 8 | // Return sockfd on success, and return -1 on error 9 | int UnixDomainSocketConnect(std::string_view path); 10 | 11 | int TcpSocketBindAndListen(std::string_view addr, uint16_t port, int backlog = 4); 12 | 13 | int TcpSocketConnect(std::string_view addr, uint16_t port); 14 | 15 | int Tcp6SocketBindAndListen(std::string_view ip, uint16_t port, int backlog = 4); 16 | 17 | int Tcp6SocketConnect(std::string_view ip, uint16_t port); 18 | 19 | // Will use `getaddrinfo` to resolve IP address if necessary 20 | bool FillTcpSocketAddr(struct sockaddr_in *addr, std::string_view host_or_ip, uint16_t port); 21 | 22 | } // namespace faas 23 | 24 | #endif //LUMINE_SOCKET_H -------------------------------------------------------------------------------- /src/dpu/agent/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(agent) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | set(AGENT_ABSL 6 | absl::flags 7 | absl::random_random 8 | absl::bind_front 9 | absl::failure_signal_handler 10 | absl::synchronization 11 | absl::flags_parse) 12 | 13 | set(AGENT_LIBRARIES 14 | ${AGENT_ABSL} 15 | fmt 16 | GSL 17 | nlohmann_json 18 | uv_a 19 | nghttp2 20 | pthread) 21 | 22 | SET(AGENT_SOURCE_FILES 23 | ${BASE_DIR}/lib/base/init.cpp 24 | ${BASE_DIR}/lib/base/logging.cpp 25 | ${BASE_DIR}/lib/base/thread.cpp 26 | ${BASE_DIR}/lib/common/uv.cpp 27 | ${BASE_DIR}/lib/common/config.cpp 28 | ${BASE_DIR}/lib/common/http_status.cpp 29 | ${BASE_DIR}/lib/server/io_worker.cpp 30 | ${BASE_DIR}/lib/server/poller.cpp 31 | ${BASE_DIR}/lib/server/server_base.cpp 32 | ${BASE_DIR}/lib/rdma/bit_map.cpp 33 | ${BASE_DIR}/lib/rdma/infinity.cpp 34 | ${BASE_DIR}/lib/rdma/queue_pair.cpp 35 | ${BASE_DIR}/lib/rdma/shared_memory.cpp 36 | ${BASE_DIR}/lib/utils/docker.cpp 37 | ${BASE_DIR}/lib/utils/http_parser.c 38 | ${BASE_DIR}/lib/utils/socket.cpp 39 | ${BASE_DIR}/lib/utils/random.cpp 40 | ${BASE_DIR}/lib/utils/fs.cpp 41 | ${BASE_DIR}/src/dpu/agent/engine_connection.cpp 42 | ${BASE_DIR}/src/dpu/agent/gateway_connection.cpp 43 | ${BASE_DIR}/src/dpu/agent/agent.cpp 44 | ${BASE_DIR}/src/dpu/agent/agent_main.cpp) 45 | 46 | add_executable(${PROJECT_NAME} ${AGENT_SOURCE_FILES}) 47 | target_link_libraries(${PROJECT_NAME} ${AGENT_LIBRARIES} PkgConfig::LIBIBVERBS) -------------------------------------------------------------------------------- /src/dpu/agent/agent.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LSwor on 2022/9/8. 3 | // 4 | 5 | #ifndef LUMINE_DPU_BENCH_AGENT_H 6 | #define LUMINE_DPU_BENCH_AGENT_H 7 | 8 | #include "base/common.h" 9 | #include "common/config.h" 10 | #include "common/protocol.h" 11 | #include "base/logging.h" 12 | #include "server/server_base.h" 13 | #include "server/poller.h" 14 | #include "utils/socket.h" 15 | #include "utils/fs.h" 16 | #include "rdma/infinity.h" 17 | #include "rdma/queue_pair.h" 18 | #include "rdma/shared_memory.h" 19 | 20 | namespace faas::agent { 21 | 22 | // forward declaration 23 | class EngineConnection; 24 | 25 | class Agent; 26 | 27 | struct Tuple{ 28 | Agent *agent; 29 | bool bind; 30 | }; 31 | 32 | class Agent final : public server::ServerBase { 33 | 34 | friend class EngineConnection; 35 | 36 | static constexpr int kDefaultNumIOWorkers = 1; 37 | 38 | public: 39 | Agent(); 40 | 41 | ~Agent() override; 42 | 43 | void SetConfigFile(std::string config_file) { config_file_ = std::move(config_file); } 44 | 45 | void SetGuid(uint16_t guid) { guid_ = guid; } 46 | 47 | void OnDataCollectionCallBack(); 48 | 49 | private: 50 | 51 | // Set only once 52 | uint16_t guid_; 53 | std::string config_file_; 54 | 55 | // thread-safe 56 | config::Config config_; 57 | uint16_t engine_guid_; 58 | rdma::Infinity *infinity_; 59 | server::Poller *poller_; 60 | size_t next_agent_conn_worker_id_; 61 | std::vector io_workers_; 62 | size_t next_engine_conn_id_; 63 | rdma::SharedMemoryInfo shared_memory_info_; 64 | 65 | absl::Mutex mutex_for_conn_; 66 | absl::Mutex mutex_for_shared_memory_; 67 | 68 | // thread unsafe 69 | rdma::SharedMemory *shared_memory_ GUARDED_BY(mutex_for_shared_memory_); 70 | absl::flat_hash_map> 71 | engine_connections_ GUARDED_BY(mutex_for_conn_); 72 | 73 | void StartInternal() override; 74 | 75 | void StopInternal() override; 76 | 77 | void OnEngineConnect(uv_connect_t *req, int status, bool bind); 78 | 79 | static void EngineConnectCallback(uv_connect_t *req, int status); 80 | 81 | void ConnectToEngine(void *arg); 82 | 83 | void RestartPoller(); 84 | 85 | rdma::MemoryRegionInfo RegisterMemoryRegion(rdma::QueuePair *queue_pair, std::string_view memory_region_name); 86 | 87 | uint64_t AllocateMemoryWithLock(uint32_t expected_size); 88 | 89 | void PopMetaDataEntryWithLock(uint32_t idx); 90 | 91 | void PushMetaDataEntryWithLock(uint64_t addr, uint32_t size); 92 | 93 | uint32_t PopMessageWithLock(uint32_t &offset, uint32_t &size); 94 | 95 | void GetPreviousOrNextIdxWithLock(uint32_t &idx, bool direction); 96 | 97 | }; 98 | 99 | } 100 | 101 | #endif //LUMINE_DPU_BENCH_AGENT_H 102 | -------------------------------------------------------------------------------- /src/dpu/agent/agent_main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LSwor on 2022/9/8. 3 | // 4 | 5 | #include "base/common.h" 6 | #include "base/init.h" 7 | #include "agent.h" 8 | 9 | ABSL_FLAG(std::string, config_file, "", "Path to config file"); 10 | ABSL_FLAG(int, guid, 0, "Agent guid"); 11 | 12 | static std::atomic agent_ptr(nullptr); 13 | 14 | static void SignalHandlerToStopAgent(int signal) { 15 | faas::agent::Agent *agent = agent_ptr.exchange(nullptr); 16 | if (agent != nullptr) { 17 | agent->ScheduleStop(); 18 | } 19 | } 20 | 21 | int main(int argc, char *argv[]) { 22 | signal(SIGINT, SignalHandlerToStopAgent); 23 | faas::base::InitMain(argc, argv); 24 | 25 | auto agent = std::make_unique(); 26 | agent->SetConfigFile(absl::GetFlag(FLAGS_config_file)); 27 | agent->SetGuid(absl::GetFlag(FLAGS_guid)); 28 | 29 | agent->Start(); 30 | agent_ptr.store(agent.get()); 31 | agent->WaitForFinish(); 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /src/dpu/agent/engine_connection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LSwor on 2022/9/8. 3 | // 4 | 5 | #ifndef LUMINE_DPU_BENCH_ENGINE_CONNECTION_H 6 | #define LUMINE_DPU_BENCH_ENGINE_CONNECTION_H 7 | 8 | #include "base/common.h" 9 | #include "common/protocol.h" 10 | #include "server/connection_base.h" 11 | #include "server/io_worker.h" 12 | #include "rdma/infinity.h" 13 | #include "rdma/queue_pair.h" 14 | #include "utils/appendable_buffer.h" 15 | #include "rdma/shared_memory.h" 16 | 17 | namespace faas::agent { 18 | 19 | class Agent; 20 | 21 | using protocol::AgentMessage; 22 | using protocol::NewAgentHandshakeMessage; 23 | using protocol::IsMemoryRegionRegisterMessage; 24 | using protocol::IsPollerNotification; 25 | using protocol::NewPollerNotificationMessage; 26 | using protocol::AgentMessage; 27 | using protocol::IsAgentHandshakeResponseMessage; 28 | using rdma::ParseWorkID; 29 | using rdma::MakeWorkID; 30 | using rdma::WorkRequest; 31 | using rdma::MakeWorkRequest; 32 | using rdma::MetaDataEntry; 33 | using rdma::WorkIDType; 34 | 35 | enum State { 36 | kCreated, kHandshake, kRunning, kRDMACreating, kRDMACreated, kClosing, kClosed 37 | }; 38 | 39 | class EngineConnection final : public server::ConnectionBase { 40 | public: 41 | EngineConnection(Agent *agent, uint16_t conn_id, bool bind); 42 | 43 | ~EngineConnection() override; 44 | 45 | uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) override; 46 | 47 | void Start(server::IOWorker *io_worker) override; 48 | 49 | void ScheduleClose() override; 50 | 51 | void Notification(); 52 | 53 | void Execute(); 54 | 55 | private: 56 | 57 | std::atomic state_; 58 | std::string log_header_; 59 | 60 | Agent *agent_; 61 | uint16_t conn_id_; 62 | bool bind_; 63 | int64_t last_time_us_; 64 | uint64_t poll_time_internal_; 65 | uint64_t timeout_as_ms_for_poller_; 66 | 67 | uv_tcp_t *uv_tcp_handle_; 68 | server::IOWorker *io_worker_; 69 | 70 | utils::AppendableBuffer read_buffer_; 71 | 72 | rdma::QueuePair *queue_pair_; 73 | 74 | std::string memory_region_name_; 75 | rdma::MemoryRegionInfo dest_memory_region_info_; 76 | rdma::MemoryRegionInfo local_memory_region_info_; 77 | rdma::WorkRequest wr_for_read_metadata_; 78 | 79 | uint32_t old_head_idx_; 80 | uint32_t old_tail_idx_; 81 | 82 | absl::Mutex mu_; 83 | 84 | uint64_t timestamp_for_last_send_notification_ GUARDED_BY(mu_); 85 | 86 | std::function poll_completion_cb_; 87 | 88 | void ProcessEngineMessages(); 89 | 90 | void PollCompletionCb(uint64_t wr_id); 91 | 92 | rdma::WorkRequest NewWorkRequest(rdma::WorkIDType work_id_type, uint32_t remote_offset, uint32_t expected_size, void *value = nullptr, uint32_t idx = -1); 93 | 94 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 95 | 96 | DECLARE_UV_WRITE_CB_FOR_CLASS(HandshakeSent); 97 | 98 | DECLARE_UV_WRITE_CB_FOR_CLASS(MessageSent); 99 | 100 | DECLARE_UV_READ_CB_FOR_CLASS(RecvData); 101 | 102 | DECLARE_UV_CLOSE_CB_FOR_CLASS(Close); 103 | 104 | }; 105 | } 106 | 107 | 108 | #endif //LUMINE_DPU_BENCH_ENGINE_CONNECTION_H 109 | -------------------------------------------------------------------------------- /src/dpu/agent/gateway_connection.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LSwor on 2022/9/8. 3 | // 4 | 5 | #include "gateway_connection.h" 6 | -------------------------------------------------------------------------------- /src/dpu/agent/gateway_connection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LSwor on 2022/9/8. 3 | // 4 | 5 | #ifndef LUMINE_DPU_BENCH_GATEWAY_CONNECTION_H 6 | #define LUMINE_DPU_BENCH_GATEWAY_CONNECTION_H 7 | 8 | 9 | namespace faas::agent{ 10 | 11 | class GatewayConnection { 12 | 13 | }; 14 | 15 | } 16 | 17 | 18 | 19 | #endif //LUMINE_DPU_BENCH_GATEWAY_CONNECTION_H 20 | -------------------------------------------------------------------------------- /src/dpu/gateway/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(gateway) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | set(GATEWAY_ABSL 6 | absl::flags 7 | absl::random_random 8 | absl::bind_front 9 | absl::failure_signal_handler 10 | absl::flags_parse) 11 | 12 | set(GATEWAY_LIBRARIES 13 | ${GATEWAY_ABSL} 14 | fmt 15 | GSL 16 | nlohmann_json 17 | uv_a 18 | nghttp2 19 | pthread) 20 | 21 | SET(GATEWAY_SOURCE_FILES 22 | ${BASE_DIR}/lib/base/init.cpp 23 | ${BASE_DIR}/lib/base/logging.cpp 24 | ${BASE_DIR}/lib/base/thread.cpp 25 | ${BASE_DIR}/lib/common/uv.cpp 26 | ${BASE_DIR}/lib/common/config.cpp 27 | ${BASE_DIR}/lib/common/http_status.cpp 28 | ${BASE_DIR}/lib/server/io_worker.cpp 29 | ${BASE_DIR}/lib/server/server_base.cpp 30 | ${BASE_DIR}/lib/utils/docker.cpp 31 | ${BASE_DIR}/lib/utils/http_parser.c 32 | ${BASE_DIR}/lib/utils/socket.cpp 33 | ${BASE_DIR}/lib/utils/random.cpp 34 | ${BASE_DIR}/lib/utils/fs.cpp 35 | ${BASE_DIR}/src/dpu/gateway/engine_connection.cpp 36 | ${BASE_DIR}/src/dpu/gateway/grpc_connection.cpp 37 | ${BASE_DIR}/src/dpu/gateway/http_connection.cpp 38 | ${BASE_DIR}/src/dpu/gateway/server.cpp 39 | ${BASE_DIR}/src/dpu/gateway/gateway_main.cpp) 40 | 41 | add_executable(${PROJECT_NAME} ${GATEWAY_SOURCE_FILES}) 42 | 43 | target_link_libraries(${PROJECT_NAME} ${GATEWAY_LIBRARIES}) -------------------------------------------------------------------------------- /src/dpu/gateway/engine_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_ENGINE_CONNECTION_H 2 | #define LUMINE_ENGINE_CONNECTION_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | #include "common/protocol.h" 7 | #include "common/stat.h" 8 | #include "utils/appendable_buffer.h" 9 | #include "server/io_worker.h" 10 | #include "server/connection_base.h" 11 | 12 | namespace faas::gateway { 13 | 14 | class Server; 15 | 16 | class EngineConnection final : public server::ConnectionBase { 17 | public: 18 | static constexpr int kBaseTypeId = 2; 19 | 20 | static int type_id(uint16_t node_id) { return kBaseTypeId + node_id; } 21 | 22 | EngineConnection(Server *server, uint16_t node_id, uint16_t conn_id, 23 | gsl::span initial_data); 24 | 25 | ~EngineConnection(); 26 | 27 | uint16_t node_id() const { return node_id_; } 28 | 29 | uint16_t conn_id() const { return conn_id_; } 30 | 31 | uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) override; 32 | 33 | void Start(server::IOWorker *io_worker) override; 34 | 35 | void ScheduleClose() override; 36 | 37 | void SendMessage(const protocol::GatewayMessage &message, gsl::span payload); 38 | 39 | private: 40 | enum State { 41 | kCreated, kRunning, kClosing, kClosed 42 | }; 43 | 44 | Server *server_; 45 | uint16_t node_id_; 46 | uint16_t conn_id_; 47 | server::IOWorker *io_worker_; 48 | State state_; 49 | uv_tcp_t uv_tcp_handle_; 50 | 51 | std::string log_header_; 52 | 53 | utils::AppendableBuffer read_buffer_; 54 | 55 | void ProcessGatewayMessages(); 56 | 57 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 58 | 59 | DECLARE_UV_READ_CB_FOR_CLASS(RecvData); 60 | 61 | DECLARE_UV_WRITE_CB_FOR_CLASS(DataSent); 62 | 63 | DECLARE_UV_CLOSE_CB_FOR_CLASS(Close); 64 | 65 | DISALLOW_COPY_AND_ASSIGN(EngineConnection); 66 | }; 67 | 68 | } // namespace faas 69 | 70 | #endif //LUMINE_ENGINE_CONNECTION_H -------------------------------------------------------------------------------- /src/dpu/gateway/func_call_context.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_FUNC_CALL_CONTEXT_H 2 | #define LUMINE_FUNC_CALL_CONTEXT_H 3 | 4 | #include "base/common.h" 5 | #include "common/protocol.h" 6 | #include "utils/appendable_buffer.h" 7 | #include "server/connection_base.h" 8 | 9 | namespace faas::gateway { 10 | 11 | // FuncCallContext is owned by corresponding Connection, NOT Server 12 | class FuncCallContext { 13 | public: 14 | enum Status { 15 | kCreated = 0, 16 | kSuccess = 1, 17 | kFailed = 2, 18 | kNoNode = 3, 19 | kNotFound = 4 20 | }; 21 | 22 | explicit FuncCallContext() {} 23 | 24 | ~FuncCallContext() {} 25 | 26 | void set_func_name(std::string_view func_name) { func_name_.assign(func_name); } 27 | 28 | void set_method_name(std::string_view method_name) { method_name_.assign(method_name); } 29 | 30 | void set_h2_stream_id(int32_t h2_stream_id) { h2_stream_id_ = h2_stream_id; } 31 | 32 | void set_func_call(const protocol::FuncCall &func_call) { func_call_ = func_call; } 33 | 34 | void append_input(gsl::span input) { input_.AppendData(input); } 35 | 36 | void append_output(gsl::span output) { output_.AppendData(output); } 37 | 38 | void set_status(Status status) { status_ = status; } 39 | 40 | std::string_view func_name() const { return func_name_; } 41 | 42 | std::string_view method_name() const { return method_name_; } 43 | 44 | int32_t h2_stream_id() const { return h2_stream_id_; } 45 | 46 | protocol::FuncCall func_call() const { return func_call_; } 47 | 48 | gsl::span input() const { return input_.to_span(); } 49 | 50 | gsl::span output() const { return output_.to_span(); } 51 | 52 | Status status() const { return status_; } 53 | 54 | void Reset() { 55 | status_ = kCreated; 56 | func_name_.clear(); 57 | method_name_.clear(); 58 | func_call_ = protocol::kInvalidFuncCall; 59 | input_.Reset(); 60 | output_.Reset(); 61 | } 62 | 63 | private: 64 | Status status_; 65 | std::string func_name_; 66 | std::string method_name_; 67 | int32_t h2_stream_id_; 68 | protocol::FuncCall func_call_; 69 | utils::AppendableBuffer input_; 70 | utils::AppendableBuffer output_; 71 | 72 | DISALLOW_COPY_AND_ASSIGN(FuncCallContext); 73 | }; 74 | 75 | } // namespace faas 76 | 77 | #endif //LUMINE_FUNC_CALL_CONTEXT_H -------------------------------------------------------------------------------- /src/dpu/gateway/gateway_main.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "server.h" 4 | 5 | ABSL_FLAG(std::string, config_file, "", "Path to config file"); 6 | 7 | static std::atomic server_ptr(nullptr); 8 | 9 | void SignalHandlerToStopServer(int signal) { 10 | faas::gateway::Server *server = server_ptr.exchange(nullptr); 11 | if (server != nullptr) { 12 | server->ScheduleStop(); 13 | } 14 | } 15 | 16 | int main(int argc, char *argv[]) { 17 | signal(SIGINT, SignalHandlerToStopServer); 18 | faas::base::InitMain(argc, argv); 19 | 20 | auto server = std::make_unique(); 21 | server->SetConfigFile(absl::GetFlag(FLAGS_config_file)); 22 | 23 | server->Start(); 24 | server_ptr.store(server.get()); 25 | server->WaitForFinish(); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/dpu/gateway/grpc_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINR_GRPC_CONNECTION_H 2 | #define LUMINR_GRPC_CONNECTION_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | #include "utils/appendable_buffer.h" 7 | #include "utils/object_pool.h" 8 | #include "server/io_worker.h" 9 | #include "server/connection_base.h" 10 | #include "func_call_context.h" 11 | 12 | namespace faas::gateway { 13 | 14 | class Server; 15 | 16 | class GrpcConnection final : public server::ConnectionBase { 17 | public: 18 | static constexpr int kTypeId = 1; 19 | 20 | static constexpr size_t kH2FrameHeaderByteSize = 9; 21 | static constexpr size_t kGrpcLPMPrefixByteSize = 5; 22 | 23 | GrpcConnection(Server *server, int connection_id); 24 | 25 | ~GrpcConnection(); 26 | 27 | uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) override; 28 | 29 | void Start(server::IOWorker *io_worker) override; 30 | 31 | void ScheduleClose() override; 32 | 33 | void OnFuncCallFinished(FuncCallContext *func_call_context); 34 | 35 | private: 36 | enum State { 37 | kCreated, kRunning, kClosing, kClosed 38 | }; 39 | 40 | Server *server_; 41 | server::IOWorker *io_worker_; 42 | uv_tcp_t uv_tcp_handle_; 43 | State state_; 44 | int closed_uv_handles_; 45 | int total_uv_handles_; 46 | 47 | std::string log_header_; 48 | 49 | nghttp2_session *h2_session_; 50 | nghttp2_error_code h2_error_code_; 51 | bool uv_write_for_mem_send_ongoing_; 52 | uv_write_t write_req_for_mem_send_; 53 | 54 | struct H2StreamContext; 55 | utils::SimpleObjectPool h2_stream_context_pool_; 56 | utils::SimpleObjectPool func_call_contexts_; 57 | absl::flat_hash_map grpc_calls_; 58 | 59 | DECLARE_UV_READ_CB_FOR_CLASS(RecvData); 60 | 61 | DECLARE_UV_WRITE_CB_FOR_CLASS(DataWritten); 62 | 63 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 64 | 65 | DECLARE_UV_CLOSE_CB_FOR_CLASS(Close); 66 | 67 | H2StreamContext *H2NewStreamContext(int stream_id); 68 | 69 | H2StreamContext *H2GetStreamContext(int stream_id); 70 | 71 | void H2ReclaimStreamContext(H2StreamContext *stream_context); 72 | 73 | void H2TerminateWithError(nghttp2_error_code error_code); 74 | 75 | bool H2SessionTerminated(); 76 | 77 | void H2SendPendingDataIfNecessary(); 78 | 79 | void H2SendSettingsFrame(); 80 | 81 | bool H2ValidateAndPopulateHeader(H2StreamContext *stream_context, 82 | std::string_view name, std::string_view value); 83 | 84 | void H2SendResponse(H2StreamContext *stream_context); 85 | 86 | bool H2HasTrailersToSend(H2StreamContext *stream_context); 87 | 88 | void H2SendTrailers(H2StreamContext *stream_context); 89 | 90 | void OnNewGrpcCall(H2StreamContext *stream_context); 91 | 92 | void OnFuncCallFinishedInternal(int32_t stream_id); 93 | 94 | int H2OnFrameRecv(const nghttp2_frame *frame); 95 | 96 | int H2OnStreamClose(int32_t stream_id, uint32_t error_code); 97 | 98 | int H2OnHeader(const nghttp2_frame *frame, std::string_view name, 99 | std::string_view value, uint8_t flags); 100 | 101 | int H2OnBeginHeaders(const nghttp2_frame *frame); 102 | 103 | int H2OnDataChunkRecv(uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len); 104 | 105 | ssize_t H2DataSourceRead(H2StreamContext *stream_context, 106 | uint8_t *buf, size_t length, uint32_t *data_flags); 107 | 108 | int H2SendData(H2StreamContext *stream_context, nghttp2_frame *frame, 109 | const uint8_t *framehd, size_t length); 110 | 111 | static int H2ErrorCallback(nghttp2_session *session, int lib_error_code, const char *msg, 112 | size_t len, void *user_data); 113 | 114 | static int H2OnFrameRecvCallback(nghttp2_session *session, const nghttp2_frame *frame, 115 | void *user_data); 116 | 117 | static int H2OnStreamCloseCallback(nghttp2_session *session, int32_t stream_id, 118 | uint32_t error_code, void *user_data); 119 | 120 | static int H2OnHeaderCallback(nghttp2_session *session, const nghttp2_frame *frame, 121 | const uint8_t *name, size_t namelen, 122 | const uint8_t *value, size_t valuelen, 123 | uint8_t flags, void *user_data); 124 | 125 | static int H2OnBeginHeadersCallback(nghttp2_session *session, const nghttp2_frame *frame, 126 | void *user_data); 127 | 128 | static int H2OnDataChunkRecvCallback(nghttp2_session *session, uint8_t flags, int32_t stream_id, 129 | const uint8_t *data, size_t len, void *user_data); 130 | 131 | static ssize_t H2DataSourceReadCallback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, 132 | size_t length, uint32_t *data_flags, 133 | nghttp2_data_source *source, void *user_data); 134 | 135 | static int H2SendDataCallback(nghttp2_session *session, nghttp2_frame *frame, 136 | const uint8_t *framehd, size_t length, 137 | nghttp2_data_source *source, void *user_data); 138 | 139 | DISALLOW_COPY_AND_ASSIGN(GrpcConnection); 140 | }; 141 | 142 | } // namespace faas 143 | 144 | #endif //LUMINR_GRPC_CONNECTION_H -------------------------------------------------------------------------------- /src/dpu/gateway/http_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_HTTPCONNECTION_H 2 | #define LUMINE_HTTPCONNECTION_H 3 | 4 | #include "base/common.h" 5 | #include "base/macro.h" 6 | #include "common/uv.h" 7 | #include "common/http_status.h" 8 | #include "utils/appendable_buffer.h" 9 | #include "utils/http_parser.h" 10 | #include "server/io_worker.h" 11 | #include "server/connection_base.h" 12 | #include "func_call_context.h" 13 | 14 | namespace faas::gateway { 15 | 16 | class Server; 17 | 18 | class HttpConnection final : public server::ConnectionBase { 19 | public: 20 | static constexpr int kTypeId = 0; 21 | 22 | static constexpr const char *kServerString = "FaaS/0.1"; 23 | static constexpr const char *kResponseContentType = "text/plain"; 24 | 25 | HttpConnection(Server *server, int connection_id); 26 | 27 | ~HttpConnection(); 28 | 29 | uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) override; 30 | 31 | void Start(server::IOWorker *io_worker) override; 32 | 33 | void ScheduleClose() override; 34 | 35 | void OnFuncCallFinished(FuncCallContext *func_call_context); 36 | 37 | private: 38 | enum State { 39 | kCreated, kRunning, kClosing, kClosed 40 | }; 41 | 42 | Server *server_; 43 | server::IOWorker *io_worker_; 44 | uv_tcp_t uv_tcp_handle_; 45 | State state_; 46 | 47 | std::string log_header_; 48 | 49 | FuncCallContext func_call_context_; 50 | http_parser_settings http_parser_settings_; 51 | http_parser http_parser_; 52 | int header_field_value_flag_; 53 | 54 | // For request 55 | utils::AppendableBuffer url_buffer_; 56 | utils::AppendableBuffer header_field_buffer_; 57 | utils::AppendableBuffer header_value_buffer_; 58 | utils::AppendableBuffer body_buffer_; 59 | size_t header_field_buffer_pos_; 60 | size_t header_value_buffer_pos_; 61 | absl::flat_hash_map headers_; 62 | 63 | // For response 64 | std::string response_header_; 65 | uv_write_t response_write_req_; 66 | 67 | void StartRecvData(); 68 | 69 | void StopRecvData(); 70 | 71 | DECLARE_UV_READ_CB_FOR_CLASS(RecvData); 72 | 73 | DECLARE_UV_WRITE_CB_FOR_CLASS(DataWritten); 74 | 75 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 76 | 77 | DECLARE_UV_CLOSE_CB_FOR_CLASS(Close); 78 | 79 | void HttpParserOnMessageBegin(); 80 | 81 | void HttpParserOnUrl(const char *data, size_t length); 82 | 83 | void HttpParserOnHeaderField(const char *data, size_t length); 84 | 85 | void HttpParserOnHeaderValue(const char *data, size_t length); 86 | 87 | void HttpParserOnHeadersComplete(); 88 | 89 | void HttpParserOnBody(const char *data, size_t length); 90 | 91 | void HttpParserOnMessageComplete(); 92 | 93 | void HttpParserOnNewHeader(); 94 | 95 | void ResetHttpParser(); 96 | 97 | void OnNewHttpRequest(std::string_view method, std::string_view path, 98 | std::string_view qs = std::string_view{}); 99 | 100 | void SendHttpResponse(HttpStatus status, gsl::span body = gsl::span()); 101 | 102 | void OnFuncCallFinishedInternal(); 103 | 104 | static int HttpParserOnMessageBeginCallback(http_parser *http_parser); 105 | 106 | static int HttpParserOnUrlCallback(http_parser *http_parser, const char *data, size_t length); 107 | 108 | static int HttpParserOnHeaderFieldCallback(http_parser *http_parser, const char *data, size_t length); 109 | 110 | static int HttpParserOnHeaderValueCallback(http_parser *http_parser, const char *data, size_t length); 111 | 112 | static int HttpParserOnHeadersCompleteCallback(http_parser *http_parser); 113 | 114 | static int HttpParserOnBodyCallback(http_parser *http_parser, const char *data, size_t length); 115 | 116 | static int HttpParserOnMessageCompleteCallback(http_parser *http_parser); 117 | 118 | DISALLOW_COPY_AND_ASSIGN(HttpConnection); 119 | }; 120 | 121 | } // namespace faas 122 | 123 | #endif //LUMINE_HTTPCONNECTION_H -------------------------------------------------------------------------------- /src/host/engine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(engine) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | set(ENGINE_ABSL 6 | absl::flags 7 | absl::random_random 8 | absl::bind_front 9 | absl::failure_signal_handler 10 | absl::flags_parse) 11 | 12 | set(ENGINE_LIBRARIES 13 | ${ENGINE_ABSL} 14 | fmt 15 | GSL 16 | nlohmann_json 17 | uv_a 18 | nghttp2 19 | pthread) 20 | 21 | set(ENGINE_SOURCE_FILES 22 | ${BASE_DIR}/lib/base/init.cpp 23 | ${BASE_DIR}/lib/base/logging.cpp 24 | ${BASE_DIR}/lib/base/thread.cpp 25 | ${BASE_DIR}/lib/common/protocol.h 26 | ${BASE_DIR}/lib/common/stat.h 27 | ${BASE_DIR}/lib/common/uv.cpp 28 | ${BASE_DIR}/lib/common/config.cpp 29 | ${BASE_DIR}/lib/common/http_status.cpp 30 | ${BASE_DIR}/lib/ipc/fifo.cpp 31 | ${BASE_DIR}/lib/ipc/shm_region.cpp 32 | ${BASE_DIR}/lib/rdma/bit_map.cpp 33 | ${BASE_DIR}/lib/rdma/queue_pair.cpp 34 | ${BASE_DIR}/lib/rdma/infinity.cpp 35 | ${BASE_DIR}/lib/rdma/shared_memory.cpp 36 | ${BASE_DIR}/lib/server/io_worker.cpp 37 | ${BASE_DIR}/lib/server/poller.cpp 38 | ${BASE_DIR}/lib/server/server_base.cpp 39 | ${BASE_DIR}/lib/utils/docker.cpp 40 | ${BASE_DIR}/lib/utils/fs.cpp 41 | ${BASE_DIR}/lib/utils/procfs.cpp 42 | ${BASE_DIR}/lib/utils/random.cpp 43 | ${BASE_DIR}/lib/utils/http_parser.c 44 | ${BASE_DIR}/lib/utils/socket.cpp 45 | ${BASE_DIR}/lib/runtime/event_driven_worker.cpp 46 | ${BASE_DIR}/lib/runtime/worker_lib.cpp 47 | ${BASE_DIR}/lib/ipc/base.cpp 48 | ${BASE_DIR}/src/host/engine/agent_connection.cpp 49 | ${BASE_DIR}/src/host/engine/dispatcher.cpp 50 | ${BASE_DIR}/src/host/engine/engine.cpp 51 | ${BASE_DIR}/src/host/engine/gateway_connection.cpp 52 | ${BASE_DIR}/src/host/engine/message_connection.cpp 53 | ${BASE_DIR}/src/host/engine/monitor.cpp 54 | ${BASE_DIR}/src/host/engine/tracer.cpp 55 | ${BASE_DIR}/src/host/engine/worker_manager.cpp 56 | ${BASE_DIR}/src/host/engine/engine_main.cpp) 57 | 58 | # executable 59 | add_executable(${PROJECT_NAME} ${ENGINE_SOURCE_FILES}) 60 | 61 | target_link_libraries(${PROJECT_NAME} ${ENGINE_LIBRARIES} PkgConfig::LIBIBVERBS) -------------------------------------------------------------------------------- /src/host/engine/agent_connection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by tank on 9/5/22. 3 | // 4 | 5 | #ifndef LUMINE_HOST_BENCH_AGENT_CONNECTION_H 6 | #define LUMINE_HOST_BENCH_AGENT_CONNECTION_H 7 | 8 | #include "base/common.h" 9 | #include "common/protocol.h" 10 | #include "common/uv.h" 11 | #include "server/connection_base.h" 12 | #include "server/io_worker.h" 13 | #include "rdma/infinity.h" 14 | #include "rdma/queue_pair.h" 15 | #include "rdma/shared_memory.h" 16 | #include "utils/appendable_buffer.h" 17 | 18 | namespace faas::engine { 19 | 20 | using protocol::AgentMessage; 21 | using protocol::IsAgentHandshakeMessage; 22 | using protocol::NewAgentHandshakeResponseMessage; 23 | using protocol::NewMemoryRegionRegisterMessage; 24 | using protocol::NewPollerNotificationMessage; 25 | using protocol::IsPollerNotification; 26 | 27 | enum State { 28 | kCreated, kHandshake, kRunning, kClosing, kClosed 29 | }; 30 | 31 | // forward declaration 32 | class Engine; 33 | 34 | class AgentConnection final : public server::ConnectionBase { 35 | 36 | friend class Engine; 37 | 38 | public: 39 | static constexpr int kTypeId = 2; 40 | 41 | AgentConnection(Engine *engine, uint16_t conn_id); 42 | 43 | ~AgentConnection() override; 44 | 45 | uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) override; 46 | 47 | void Start(server::IOWorker *io_worker) override; 48 | 49 | void ScheduleClose() override; 50 | 51 | void set_conn_id(uint64_t conn_id) { conn_id_ = conn_id; } 52 | 53 | uint16_t conn_id() const { return conn_id_; } 54 | 55 | void Notification(); 56 | 57 | bool IsBind() const { return bind_; } 58 | 59 | uint16_t GetEngineGuid() const { return engine_guid_; } 60 | 61 | private: 62 | State state_; 63 | uv_tcp_t *uv_tcp_handle_; 64 | std::string log_header_; 65 | uint16_t conn_id_; 66 | Engine *engine_; 67 | server::IOWorker *io_worker_; 68 | utils::AppendableBuffer message_buffer_; 69 | uv::HandleScope handle_scope_; 70 | uint16_t engine_guid_; 71 | bool bind_; 72 | 73 | // thread-safe 74 | std::string memory_region_name_; 75 | rdma::MemoryRegionInfo memory_region_info_; 76 | rdma::QueuePair *queue_pair_; 77 | rdma::SharedMemoryInfo shared_memory_info_; 78 | uint64_t timeout_as_ms_for_poller_; 79 | 80 | absl::Mutex mutex_; 81 | 82 | // thread-unsafe 83 | rdma::SharedMemory *shared_memory_ GUARDED_BY(mutex_); 84 | uint64_t timestamp_for_last_send_notification_ GUARDED_BY(mutex_); 85 | 86 | void RecvHandshakeMessage(); 87 | 88 | void OnAllHandlesClosed(); 89 | 90 | void ProcessAgentMessages(); 91 | 92 | uint64_t AllocateMemoryWithLock(uint32_t expected_size); 93 | 94 | void PopMetaDataEntryWithLock(uint32_t idx); 95 | 96 | void PushMetaDataEntryWithLock(uint64_t addr, uint32_t size); 97 | 98 | uint32_t PopMessageWithLock(uint32_t &offset, uint32_t &size); 99 | 100 | uint32_t PopMessageWithLock(uint64_t &addr, uint32_t &size); 101 | 102 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 103 | 104 | DECLARE_UV_READ_CB_FOR_CLASS(ReadHandshake); 105 | 106 | DECLARE_UV_WRITE_CB_FOR_CLASS(HandshakeSent); 107 | 108 | DECLARE_UV_WRITE_CB_FOR_CLASS(PollerNotification); 109 | 110 | DECLARE_UV_READ_CB_FOR_CLASS(RecvData); 111 | 112 | }; 113 | } 114 | 115 | #endif //LUMINE_HOST_BENCH_AGENT_CONNECTION_H 116 | -------------------------------------------------------------------------------- /src/host/engine/dispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_DISPATCHER_H 2 | #define LUMINE_DISPATCHER_H 3 | 4 | #include "base/common.h" 5 | #include "common/stat.h" 6 | #include "common/protocol.h" 7 | #include "common/config.h" 8 | #include "utils/object_pool.h" 9 | #include "tracer.h" 10 | 11 | namespace faas::engine { 12 | 13 | class Engine; 14 | 15 | class FuncWorker; 16 | 17 | class Dispatcher { 18 | public: 19 | Dispatcher(Engine *engine, uint16_t func_id); 20 | 21 | ~Dispatcher(); 22 | 23 | uint16_t func_id() const { return func_id_; } 24 | 25 | // All must be thread-safe 26 | bool OnFuncWorkerConnected(std::shared_ptr func_worker); 27 | 28 | void OnFuncWorkerDisconnected(FuncWorker *func_worker); 29 | 30 | bool OnNewFuncCall(const protocol::FuncCall &func_call, 31 | const protocol::FuncCall &parent_func_call, 32 | size_t input_size, std::span inline_input, bool shm_input); 33 | 34 | bool OnFuncCallCompleted(const protocol::FuncCall &func_call, 35 | int32_t processing_time, int32_t dispatch_delay, size_t output_size, bool is_rdma_request = false); 36 | 37 | bool OnFuncCallFailed(const protocol::FuncCall &func_call, int32_t dispatch_delay); 38 | 39 | bool OnRDMAReclaimMessage(const protocol::MessageTuple &message_tuple); 40 | 41 | void OnRDMAReclaimCompletionMessage(const protocol::FuncCall &func_call); 42 | 43 | void OnRDMARequestCompletionMessage(const protocol::FuncCall &func_call); 44 | 45 | private: 46 | Engine *engine_; 47 | uint16_t func_id_; 48 | const config::FunctionEntry *func_config_entry_; 49 | size_t min_workers_; 50 | size_t max_workers_; 51 | absl::Mutex mu_; 52 | 53 | std::string log_header_; 54 | 55 | absl::flat_hash_map> 56 | workers_ ABSL_GUARDED_BY(mu_); 57 | absl::flat_hash_map 58 | running_workers_ ABSL_GUARDED_BY(mu_); 59 | std::vector idle_workers_ ABSL_GUARDED_BY(mu_); 60 | utils::SimpleObjectPool message_pool_ ABSL_GUARDED_BY(mu_); 61 | 62 | absl::flat_hash_map 63 | requested_workers_ ABSL_GUARDED_BY(mu_); 64 | int64_t last_request_worker_timestamp_ ABSL_GUARDED_BY(mu_); 65 | 66 | struct PendingFuncCall { 67 | protocol::Message *dispatch_func_call_message; 68 | Tracer::FuncCallInfo *func_call_info; 69 | }; 70 | 71 | std::queue pending_func_calls_ ABSL_GUARDED_BY(mu_); 72 | absl::flat_hash_map 73 | assigned_workers_ ABSL_GUARDED_BY(mu_); 74 | 75 | stat::StatisticsCollector idle_workers_stat_ ABSL_GUARDED_BY(mu_); 76 | stat::StatisticsCollector running_workers_stat_ ABSL_GUARDED_BY(mu_); 77 | stat::StatisticsCollector max_concurrency_stat_ ABSL_GUARDED_BY(mu_); 78 | stat::StatisticsCollector estimated_rps_stat_ ABSL_GUARDED_BY(mu_); 79 | stat::StatisticsCollector estimated_concurrency_stat_ ABSL_GUARDED_BY(mu_); 80 | 81 | void FuncWorkerFinished(FuncWorker *func_worker) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 82 | 83 | void DispatchFuncCall(FuncWorker *func_worker, protocol::Message *dispatch_func_call_message) 84 | ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 85 | 86 | bool DispatchPendingFuncCall(FuncWorker *idle_func_worker) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 87 | 88 | FuncWorker *PickIdleWorker() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 89 | 90 | void UpdateWorkerLoadStat() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 91 | 92 | size_t DetermineExpectedConcurrency() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 93 | 94 | size_t DetermineConcurrencyLimit() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 95 | 96 | void MayRequestNewFuncWorker() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); 97 | 98 | DISALLOW_COPY_AND_ASSIGN(Dispatcher); 99 | }; 100 | 101 | } // namespace faas 102 | 103 | #endif //LUMINE_DISPATCHER_H -------------------------------------------------------------------------------- /src/host/engine/engine_main.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "ipc/base.h" 4 | #include "utils/docker.h" 5 | #include "utils/fs.h" 6 | #include "utils/env_variables.h" 7 | #include "engine.h" 8 | 9 | ABSL_FLAG(std::string, config_file, "", "Path to config file"); 10 | ABSL_FLAG(int, guid, 0, "Engine guid"); 11 | 12 | static std::atomic engine_ptr(nullptr); 13 | 14 | static void SignalHandlerToStopEngine(int signal) { 15 | faas::engine::Engine *engine = engine_ptr.exchange(nullptr); 16 | if (engine != nullptr) { 17 | engine->ScheduleStop(); 18 | } 19 | } 20 | 21 | int main(int argc, char *argv[]) { 22 | signal(SIGINT, SignalHandlerToStopEngine); 23 | faas::base::InitMain(argc, argv); 24 | 25 | std::string cgroup_fs_root(faas::utils::GetEnvVariable("FAAS_CGROUP_FS_ROOT", "")); 26 | if (cgroup_fs_root.length() > 0) { 27 | faas::docker_utils::SetCgroupFsRoot(cgroup_fs_root); 28 | } 29 | 30 | auto engine = std::make_unique(); 31 | engine->SetConfigFile(absl::GetFlag(FLAGS_config_file)); 32 | engine->SetGuid(absl::GetFlag(FLAGS_guid)); 33 | 34 | engine->Start(); 35 | engine_ptr.store(engine.get()); 36 | engine->WaitForFinish(); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /src/host/engine/gateway_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_GATEWAY_CONNECTION_H 2 | #define LUMINE_GATEWAY_CONNECTION_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | #include "common/protocol.h" 7 | #include "common/stat.h" 8 | #include "utils/appendable_buffer.h" 9 | #include "server/io_worker.h" 10 | #include "server/connection_base.h" 11 | 12 | namespace faas::engine { 13 | 14 | class Engine; 15 | 16 | class GatewayConnection final : public server::ConnectionBase { 17 | public: 18 | static constexpr int kTypeId = 0; 19 | 20 | GatewayConnection(Engine *engine, uint16_t conn_id); 21 | 22 | ~GatewayConnection(); 23 | 24 | uint16_t conn_id() const { return conn_id_; } 25 | 26 | uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) override; 27 | 28 | void Start(server::IOWorker *io_worker) override; 29 | 30 | void ScheduleClose() override; 31 | 32 | void SendMessage(const protocol::GatewayMessage &message, 33 | std::span payload = std::span()); 34 | 35 | private: 36 | enum State { 37 | kCreated, kHandshake, kRunning, kClosing, kClosed 38 | }; 39 | 40 | Engine *engine_; 41 | uint16_t conn_id_; 42 | server::IOWorker *io_worker_; 43 | State state_; 44 | uv_tcp_t uv_tcp_handle_; 45 | 46 | std::string log_header_; 47 | 48 | protocol::GatewayMessage handshake_message_; 49 | utils::AppendableBuffer read_buffer_; 50 | 51 | void ProcessGatewayMessages(); 52 | 53 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 54 | 55 | DECLARE_UV_READ_CB_FOR_CLASS(RecvData); 56 | 57 | DECLARE_UV_WRITE_CB_FOR_CLASS(DataSent); 58 | 59 | DECLARE_UV_WRITE_CB_FOR_CLASS(HandshakeSent); 60 | 61 | DECLARE_UV_CLOSE_CB_FOR_CLASS(Close); 62 | 63 | DISALLOW_COPY_AND_ASSIGN(GatewayConnection); 64 | }; 65 | 66 | } // namespace faas 67 | 68 | #endif //LUMINE_GATEWAY_CONNECTION_H -------------------------------------------------------------------------------- /src/host/engine/message_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_MESSAGE_CONNECTION_H 2 | #define LUMINE_MESSAGE_CONNECTION_H 3 | 4 | #include "base/common.h" 5 | #include "common/uv.h" 6 | #include "common/protocol.h" 7 | #include "common/stat.h" 8 | #include "utils/appendable_buffer.h" 9 | #include "utils/object_pool.h" 10 | #include "server/io_worker.h" 11 | #include "server/connection_base.h" 12 | 13 | namespace faas::engine { 14 | 15 | class Engine; 16 | 17 | class MessageConnection final : public server::ConnectionBase { 18 | public: 19 | static constexpr int kTypeId = 1; 20 | 21 | explicit MessageConnection(Engine *engine); 22 | 23 | ~MessageConnection(); 24 | 25 | uint16_t func_id() const { return func_id_; } 26 | 27 | uint16_t client_id() const { return client_id_; } 28 | 29 | bool handshake_done() const { return handshake_done_; } 30 | 31 | bool is_launcher_connection() const { return client_id_ == 0; } 32 | 33 | bool is_func_worker_connection() const { return client_id_ > 0; } 34 | 35 | uv_stream_t *InitUVHandle(uv_loop_t *uv_loop) override; 36 | 37 | void Start(server::IOWorker *io_worker) override; 38 | 39 | void ScheduleClose() override; 40 | 41 | // Must be thread-safe 42 | void WriteMessage(const protocol::Message &message); 43 | 44 | private: 45 | enum State { 46 | kCreated, kHandshake, kRunning, kClosing, kClosed 47 | }; 48 | 49 | Engine *engine_; 50 | server::IOWorker *io_worker_; 51 | State state_; 52 | uint16_t func_id_; 53 | uint16_t client_id_; 54 | bool handshake_done_; 55 | uv_stream_t *uv_handle_; 56 | 57 | uv_pipe_t in_fifo_handle_; 58 | uv_pipe_t out_fifo_handle_; 59 | uv_stream_t *handle_for_read_message_; 60 | uv_stream_t *handle_for_write_message_; 61 | uv::HandleScope handle_scope_; 62 | std::atomic pipe_for_write_fd_; 63 | 64 | std::string log_header_; 65 | 66 | utils::AppendableBuffer message_buffer_; 67 | protocol::Message handshake_response_; 68 | utils::AppendableBuffer write_message_buffer_; 69 | 70 | absl::Mutex write_message_mu_; 71 | absl::InlinedVector 72 | pending_messages_ ABSL_GUARDED_BY(write_message_mu_); 73 | 74 | void RecvHandshakeMessage(); 75 | 76 | void SendPendingMessages(); 77 | 78 | void OnAllHandlesClosed(); 79 | 80 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 81 | 82 | DECLARE_UV_READ_CB_FOR_CLASS(ReadHandshake); 83 | 84 | DECLARE_UV_WRITE_CB_FOR_CLASS(WriteHandshakeResponse); 85 | 86 | DECLARE_UV_READ_CB_FOR_CLASS(ReadMessage); 87 | 88 | DECLARE_UV_WRITE_CB_FOR_CLASS(WriteMessage); 89 | 90 | DISALLOW_COPY_AND_ASSIGN(MessageConnection); 91 | }; 92 | 93 | } // namespace faas 94 | 95 | #endif //LUMINE_MESSAGE_CONNECTION_H -------------------------------------------------------------------------------- /src/host/engine/monitor.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_MONITOR_H 2 | #define LUMINE_MONITOR_H 3 | 4 | #include "base/common.h" 5 | #include "base/thread.h" 6 | 7 | namespace faas::engine { 8 | 9 | class Engine; 10 | 11 | class MessageConnection; 12 | 13 | class Monitor { 14 | public: 15 | static constexpr float kDefaultFrequencyHz = 0.3; 16 | 17 | explicit Monitor(Engine *engine); 18 | 19 | ~Monitor(); 20 | 21 | void set_frequency(float hz); 22 | 23 | void Start(); 24 | 25 | void ScheduleStop(); 26 | 27 | void WaitForFinish(); 28 | 29 | void OnIOWorkerCreated(std::string_view worker_name, int event_loop_thread_tid); 30 | 31 | void OnNewFuncContainer(uint16_t func_id, std::string_view container_id); 32 | 33 | private: 34 | enum State { 35 | kCreated, kRunning, kStopping, kStopped 36 | }; 37 | std::atomic state_; 38 | Engine *engine_; 39 | float frequency_hz_; 40 | base::Thread background_thread_; 41 | 42 | absl::Mutex mu_; 43 | std::string self_container_id_; 44 | absl::flat_hash_map 45 | io_workers_ ABSL_GUARDED_BY(mu_); 46 | absl::flat_hash_map 47 | func_container_ids_ ABSL_GUARDED_BY(mu_); 48 | 49 | void BackgroundThreadMain(); 50 | 51 | DISALLOW_COPY_AND_ASSIGN(Monitor); 52 | }; 53 | 54 | } // namespace faas 55 | 56 | #endif //LUMINE_MONITOR_H -------------------------------------------------------------------------------- /src/host/engine/tracer.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_TRACER_H 2 | #define LUMINE_TRACER_H 3 | 4 | #include "base/common.h" 5 | #include "common/protocol.h" 6 | #include "common/stat.h" 7 | #include "utils/object_pool.h" 8 | #include "utils/exp_moving_avg.h" 9 | 10 | namespace faas::engine { 11 | 12 | class Engine; 13 | 14 | class FuncWorker; 15 | 16 | class Tracer { 17 | public: 18 | explicit Tracer(Engine *engine); 19 | 20 | ~Tracer(); 21 | 22 | void Init(); 23 | 24 | enum class FuncCallState { 25 | kInvalid, 26 | kReceived, 27 | kDispatched, 28 | kCompleted, 29 | kFailed 30 | }; 31 | 32 | struct FuncCallInfo { 33 | absl::Mutex mu; 34 | FuncCallState state ABSL_GUARDED_BY(mu); 35 | protocol::FuncCall func_call ABSL_GUARDED_BY(mu); 36 | protocol::FuncCall parent_func_call ABSL_GUARDED_BY(mu); 37 | size_t input_size ABSL_GUARDED_BY(mu); 38 | size_t output_size ABSL_GUARDED_BY(mu); 39 | int64_t recv_timestamp ABSL_GUARDED_BY(mu); 40 | int64_t dispatch_timestamp ABSL_GUARDED_BY(mu); 41 | int64_t finish_timestamp ABSL_GUARDED_BY(mu); 42 | uint16_t assigned_worker ABSL_GUARDED_BY(mu); // Saved as client_id 43 | int32_t processing_time ABSL_GUARDED_BY(mu); 44 | int32_t dispatch_delay ABSL_GUARDED_BY(mu); 45 | int32_t total_queuing_delay ABSL_GUARDED_BY(mu); 46 | }; 47 | 48 | FuncCallInfo *OnNewFuncCall(const protocol::FuncCall &func_call, 49 | const protocol::FuncCall &parent_func_call, 50 | size_t input_size); 51 | 52 | FuncCallInfo *OnFuncCallDispatched(const protocol::FuncCall &func_call, 53 | FuncWorker *func_worker); 54 | 55 | FuncCallInfo *OnFuncCallCompleted(const protocol::FuncCall &func_call, 56 | int32_t processing_time, int32_t dispatch_delay, 57 | size_t output_size); 58 | 59 | FuncCallInfo *OnFuncCallFailed(const protocol::FuncCall &func_call, int32_t dispatch_delay); 60 | 61 | void DiscardFuncCallInfo(const protocol::FuncCall &func_call); 62 | 63 | double GetAverageInstantRps(uint16_t func_id); 64 | 65 | double GetAverageRunningDelay(uint16_t func_id); 66 | 67 | double GetAverageProcessingTime(uint16_t func_id); 68 | 69 | double GetAverageProcessingTime2(uint16_t func_id); 70 | 71 | private: 72 | Engine *engine_; 73 | absl::Mutex mu_; 74 | 75 | utils::SimpleObjectPool func_call_info_pool_ ABSL_GUARDED_BY(mu_); 76 | absl::flat_hash_map 77 | func_call_infos_ ABSL_GUARDED_BY(mu_); 78 | 79 | stat::StatisticsCollector dispatch_delay_stat_ ABSL_GUARDED_BY(mu_); 80 | stat::StatisticsCollector dispatch_overhead_stat_ ABSL_GUARDED_BY(mu_); 81 | 82 | struct PerFuncStatistics { 83 | absl::Mutex mu; 84 | uint16_t func_id; 85 | 86 | int inflight_requests ABSL_GUARDED_BY(mu); 87 | int64_t last_request_timestamp ABSL_GUARDED_BY(mu); 88 | 89 | stat::Counter incoming_requests_stat ABSL_GUARDED_BY(mu); 90 | stat::Counter failed_requests_stat ABSL_GUARDED_BY(mu); 91 | stat::StatisticsCollector instant_rps_stat ABSL_GUARDED_BY(mu); 92 | stat::StatisticsCollector input_size_stat ABSL_GUARDED_BY(mu); 93 | stat::StatisticsCollector output_size_stat ABSL_GUARDED_BY(mu); 94 | stat::StatisticsCollector queueing_delay_stat ABSL_GUARDED_BY(mu); 95 | stat::StatisticsCollector running_delay_stat ABSL_GUARDED_BY(mu); 96 | stat::StatisticsCollector inflight_requests_stat ABSL_GUARDED_BY(mu); 97 | 98 | utils::ExpMovingAvgExt instant_rps_ema ABSL_GUARDED_BY(mu); 99 | utils::ExpMovingAvg running_delay_ema ABSL_GUARDED_BY(mu); 100 | utils::ExpMovingAvg processing_time_ema ABSL_GUARDED_BY(mu); 101 | utils::ExpMovingAvg processing_time2_ema ABSL_GUARDED_BY(mu); 102 | 103 | explicit PerFuncStatistics(uint16_t func_id); 104 | }; 105 | 106 | PerFuncStatistics *per_func_stats_[protocol::kMaxFuncId]; 107 | 108 | DISALLOW_COPY_AND_ASSIGN(Tracer); 109 | }; 110 | 111 | } // namespace faas 112 | 113 | #endif //LUMINE_TRACER_H -------------------------------------------------------------------------------- /src/host/engine/worker_manager.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_WORKER_MANAGER_H 2 | #define LUMINE_WORKER_MANAGER_H 3 | 4 | #include "base/common.h" 5 | #include "common/protocol.h" 6 | #include "message_connection.h" 7 | 8 | namespace faas::engine { 9 | 10 | class Engine; 11 | 12 | class FuncWorker; 13 | 14 | class WorkerManager { 15 | public: 16 | static constexpr int kDefaultMinWorkersPerFunc = 4; 17 | 18 | explicit WorkerManager(Engine *engine); 19 | 20 | ~WorkerManager(); 21 | 22 | // All must be thread-safe 23 | bool OnLauncherConnected(MessageConnection *launcher_connection); 24 | 25 | void OnLauncherDisconnected(MessageConnection *launcher_connection); 26 | 27 | bool OnFuncWorkerConnected(MessageConnection *worker_connection); 28 | 29 | void OnFuncWorkerDisconnected(MessageConnection *worker_connection); 30 | 31 | bool RequestNewFuncWorker(uint16_t func_id, uint16_t *client_id); 32 | 33 | std::shared_ptr GetFuncWorker(uint16_t client_id); 34 | 35 | private: 36 | Engine *engine_; 37 | std::atomic next_client_id_; 38 | 39 | absl::Mutex mu_; 40 | absl::flat_hash_map> 41 | launcher_connections_ ABSL_GUARDED_BY(mu_); 42 | absl::flat_hash_map> 43 | func_workers_ ABSL_GUARDED_BY(mu_); 44 | 45 | bool RequestNewFuncWorkerInternal(MessageConnection *launcher_connection, uint16_t *client_id); 46 | 47 | DISALLOW_COPY_AND_ASSIGN(WorkerManager); 48 | }; 49 | 50 | class FuncWorker { 51 | public: 52 | explicit FuncWorker(MessageConnection *message_connection); 53 | 54 | ~FuncWorker(); 55 | 56 | uint16_t func_id() const { return func_id_; } 57 | 58 | uint16_t client_id() const { return client_id_; } 59 | 60 | // Must be thread-safe 61 | void SendMessage(protocol::Message *message); 62 | 63 | private: 64 | uint16_t func_id_; 65 | uint16_t client_id_; 66 | std::shared_ptr message_connection_; 67 | 68 | DISALLOW_COPY_AND_ASSIGN(FuncWorker); 69 | }; 70 | 71 | } // namespace faas 72 | 73 | #endif //LUMINE_WORKER_MANAGER_H -------------------------------------------------------------------------------- /src/host/launcher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(launcher) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | set(LAUNCHER_ABSL 6 | absl::flags 7 | absl::random_random 8 | absl::bind_front 9 | absl::failure_signal_handler 10 | absl::flags_parse) 11 | 12 | set(LAUNCHER_LIBRARIES 13 | ${LAUNCHER_ABSL} 14 | fmt 15 | GSL 16 | nlohmann_json 17 | uv_a 18 | nghttp2 19 | pthread) 20 | 21 | set(LAUNCHER_SOURCE_FILES 22 | ${BASE_DIR}/lib/base/init.cpp 23 | ${BASE_DIR}/lib/base/logging.cpp 24 | ${BASE_DIR}/lib/base/thread.cpp 25 | ${BASE_DIR}/lib/common/protocol.h 26 | ${BASE_DIR}/lib/common/stat.h 27 | ${BASE_DIR}/lib/common/uv.cpp 28 | ${BASE_DIR}/lib/common/subprocess.cpp 29 | ${BASE_DIR}/lib/common/config.cpp 30 | ${BASE_DIR}/lib/common/http_status.cpp 31 | ${BASE_DIR}/lib/ipc/fifo.cpp 32 | ${BASE_DIR}/lib/ipc/shm_region.cpp 33 | ${BASE_DIR}/lib/server/io_worker.cpp 34 | ${BASE_DIR}/lib/server/server_base.cpp 35 | ${BASE_DIR}/lib/utils/docker.cpp 36 | ${BASE_DIR}/lib/utils/fs.cpp 37 | ${BASE_DIR}/lib/utils/procfs.cpp 38 | ${BASE_DIR}/lib/utils/random.cpp 39 | ${BASE_DIR}/lib/utils/http_parser.c 40 | ${BASE_DIR}/lib/utils/socket.cpp 41 | ${BASE_DIR}/lib/runtime/event_driven_worker.cpp 42 | ${BASE_DIR}/lib/runtime/worker_lib.cpp 43 | ${BASE_DIR}/lib/ipc/base.cpp 44 | ${BASE_DIR}/src/host/launcher/engine_connection.cpp 45 | ${BASE_DIR}/src/host/launcher/func_process.cpp 46 | ${BASE_DIR}/src/host/launcher/launcher.cpp 47 | ${BASE_DIR}/src/host/launcher/launcher_main.cpp) 48 | 49 | add_executable(${PROJECT_NAME} ${LAUNCHER_SOURCE_FILES}) 50 | 51 | target_link_libraries(${PROJECT_NAME} ${LAUNCHER_LIBRARIES}) -------------------------------------------------------------------------------- /src/host/launcher/engine_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_ENGINE_CONNECTION_H 2 | #define LUMINE_ENGINE_CONNECTION_H 3 | 4 | #include "base/common.h" 5 | #include "common/protocol.h" 6 | #include "common/uv.h" 7 | #include "utils/appendable_buffer.h" 8 | #include "utils/buffer_pool.h" 9 | #include "utils/object_pool.h" 10 | 11 | namespace faas::launcher { 12 | 13 | class Launcher; 14 | 15 | class EngineConnection : public uv::Base { 16 | public: 17 | explicit EngineConnection(Launcher *launcher); 18 | 19 | ~EngineConnection(); 20 | 21 | void Start(uv_loop_t *uv_loop, int engine_tcp_port, 22 | const protocol::Message &handshake_message); 23 | 24 | void ScheduleClose(); 25 | 26 | void WriteMessage(const protocol::Message &message); 27 | 28 | private: 29 | enum State { 30 | kCreated, kHandshake, kRunning, kClosing, kClosed 31 | }; 32 | 33 | Launcher *launcher_; 34 | State state_; 35 | 36 | uv_connect_t connect_req_; 37 | uv_loop_t *uv_loop_; 38 | uv_stream_t *engine_conn_; 39 | 40 | utils::AppendableBuffer message_buffer_; 41 | protocol::Message handshake_message_; 42 | 43 | void RecvHandshakeResponse(); 44 | 45 | DECLARE_UV_CONNECT_CB_FOR_CLASS(Connect); 46 | 47 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 48 | 49 | DECLARE_UV_READ_CB_FOR_CLASS(ReadHandshakeResponse); 50 | 51 | DECLARE_UV_WRITE_CB_FOR_CLASS(WriteHandshake); 52 | 53 | DECLARE_UV_READ_CB_FOR_CLASS(ReadMessage); 54 | 55 | DECLARE_UV_WRITE_CB_FOR_CLASS(WriteMessage); 56 | 57 | DECLARE_UV_CLOSE_CB_FOR_CLASS(Close); 58 | 59 | DISALLOW_COPY_AND_ASSIGN(EngineConnection); 60 | }; 61 | 62 | } // namespace faas 63 | #endif //LUMINE_ENGINE_CONNECTION_H -------------------------------------------------------------------------------- /src/host/launcher/func_process.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_FUNC_PROCESS_H 2 | #define LUMINE_FUNC_PROCESS_H 3 | 4 | #include "base/common.h" 5 | #include "common/protocol.h" 6 | #include "common/uv.h" 7 | #include "common/subprocess.h" 8 | #include "utils/buffer_pool.h" 9 | #include "utils/object_pool.h" 10 | 11 | namespace faas::launcher { 12 | 13 | class Launcher; 14 | 15 | class FuncProcess : public uv::Base { 16 | public: 17 | FuncProcess(Launcher *launcher, int id, int initial_client_id = -1); 18 | 19 | ~FuncProcess(); 20 | 21 | int id() const { return id_; } 22 | 23 | bool Start(uv_loop_t *uv_loop, utils::BufferPool *read_buffer_pool); 24 | 25 | void SendMessage(const protocol::Message &message); 26 | 27 | void ScheduleClose(); 28 | 29 | private: 30 | enum State { 31 | kCreated, kRunning, kClosing, kClosed 32 | }; 33 | 34 | State state_; 35 | Launcher *launcher_; 36 | int id_; 37 | int initial_client_id_; 38 | uint32_t initial_payload_size_; 39 | 40 | std::string log_header_; 41 | 42 | uv_loop_t *uv_loop_; 43 | uv::Subprocess subprocess_; 44 | utils::BufferPool *read_buffer_pool_; 45 | int message_pipe_fd_; 46 | uv_pipe_t *message_pipe_; 47 | 48 | void OnSubprocessExit(int exit_status, std::span stdout, 49 | std::span stderr); 50 | 51 | DECLARE_UV_WRITE_CB_FOR_CLASS(SendMessage); 52 | 53 | DISALLOW_COPY_AND_ASSIGN(FuncProcess); 54 | }; 55 | 56 | } // namespace faas 57 | 58 | #endif -------------------------------------------------------------------------------- /src/host/launcher/launcher.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_LAUNCHER_H 2 | #define LUMINE_LAUNCHER_H 3 | 4 | #include "base/common.h" 5 | #include "base/thread.h" 6 | #include "common/stat.h" 7 | #include "common/protocol.h" 8 | #include "common/config.h" 9 | #include "common/uv.h" 10 | #include "utils/buffer_pool.h" 11 | #include "engine_connection.h" 12 | #include "func_process.h" 13 | 14 | namespace faas::launcher { 15 | 16 | class Launcher : public uv::Base { 17 | public: 18 | static constexpr size_t kBufferSize = 4096; 19 | static_assert(sizeof(protocol::Message) <= kBufferSize, "kBufferSize is too small"); 20 | 21 | enum Mode { 22 | kInvalidMode = 0, 23 | kCppMode = 1, 24 | kGoMode = 2, 25 | kNodeJsMode = 3, 26 | kPythonMode = 4 27 | }; 28 | 29 | Launcher(); 30 | 31 | ~Launcher(); 32 | 33 | void set_func_id(int func_id) { 34 | func_id_ = func_id; 35 | } 36 | 37 | void set_fprocess(std::string_view fprocess) { 38 | fprocess_ = std::string(fprocess); 39 | } 40 | 41 | void set_fprocess_working_dir(std::string_view path) { 42 | fprocess_working_dir_ = std::string(path); 43 | } 44 | 45 | void set_fprocess_output_dir(std::string_view path) { 46 | fprocess_output_dir_ = std::string(path); 47 | } 48 | 49 | void set_fprocess_mode(Mode mode) { 50 | fprocess_mode_ = mode; 51 | } 52 | 53 | void set_engine_tcp_port(int port) { 54 | engine_tcp_port_ = port; 55 | } 56 | 57 | int func_id() const { return func_id_; } 58 | 59 | std::string_view fprocess() const { return fprocess_; } 60 | 61 | std::string_view fprocess_working_dir() const { return fprocess_working_dir_; } 62 | 63 | std::string_view fprocess_output_dir() const { return fprocess_output_dir_; } 64 | 65 | std::string_view rdma_device_name() const { return rdma_device_name_; } 66 | 67 | int rdma_device_port() const { return rdma_device_port_; } 68 | 69 | int rdma_device_gidx() const { return rdma_device_gid_index_; } 70 | 71 | int engine_tcp_port() const { return engine_tcp_port_; } 72 | 73 | std::string_view func_name() const { 74 | const config::FunctionEntry *entry = config_.FindFunctionByFuncId(func_id_); 75 | return entry->func_name; 76 | } 77 | 78 | std::string_view func_config_json() const { return func_config_json_; } 79 | 80 | bool func_worker_use_engine_socket() const { return func_worker_use_engine_socket_; } 81 | 82 | void Start(); 83 | 84 | void ScheduleStop(); 85 | 86 | void WaitForFinish(); 87 | 88 | // Can only be called from uv_loop_ 89 | void NewReadBuffer(size_t suggested_size, uv_buf_t *buf); 90 | 91 | void ReturnReadBuffer(const uv_buf_t *buf); 92 | 93 | void NewWriteBuffer(uv_buf_t *buf); 94 | 95 | void ReturnWriteBuffer(char *buf); 96 | 97 | uv_write_t *NewWriteRequest(); 98 | 99 | void ReturnWriteRequest(uv_write_t *write_req); 100 | 101 | void OnEngineConnectionClose(); 102 | 103 | void OnFuncProcessExit(FuncProcess *func_process); 104 | 105 | bool OnRecvHandshakeResponse(const protocol::Message &handshake_response, 106 | std::span payload); 107 | 108 | void OnRecvMessage(const protocol::Message &message); 109 | 110 | void set_rdma_device_name(std::string name) { rdma_device_name_ = std::move(name); } 111 | 112 | void set_rdma_device_port(int port) { rdma_device_port_ = port; } 113 | 114 | void set_rdma_device_gid_index(int gid_index) { rdma_device_gid_index_ = gid_index; } 115 | 116 | private: 117 | enum State { 118 | kCreated, kRunning, kStopping, kStopped 119 | }; 120 | std::atomic state_; 121 | 122 | int func_id_; 123 | std::string fprocess_; 124 | std::string fprocess_working_dir_; 125 | std::string fprocess_output_dir_; 126 | Mode fprocess_mode_; 127 | int engine_tcp_port_; 128 | std::string rdma_device_name_; 129 | int rdma_device_port_; 130 | int rdma_device_gid_index_; 131 | 132 | uv_loop_t uv_loop_; 133 | uv_async_t stop_event_; 134 | base::Thread event_loop_thread_; 135 | utils::BufferPool buffer_pool_; 136 | utils::SimpleObjectPool write_req_pool_; 137 | 138 | config::Config config_; 139 | std::string func_config_json_; 140 | bool func_worker_use_engine_socket_; 141 | EngineConnection engine_connection_; 142 | std::vector> func_processes_; 143 | 144 | stat::StatisticsCollector engine_message_delay_stat_; 145 | 146 | void EventLoopThreadMain(); 147 | 148 | DECLARE_UV_ASYNC_CB_FOR_CLASS(Stop); 149 | 150 | DISALLOW_COPY_AND_ASSIGN(Launcher); 151 | }; 152 | 153 | } // namespace faas 154 | 155 | #endif //LUMINE_LAUNCHER_H -------------------------------------------------------------------------------- /src/host/launcher/launcher_main.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "ipc/base.h" 4 | #include "launcher.h" 5 | 6 | ABSL_FLAG(std::string, root_path_for_ipc, "/dev/shm/faas_ipc", 7 | "Root directory for IPCs used by FaaS"); 8 | ABSL_FLAG(int, func_id, -1, "Function ID of this launcher process"); 9 | ABSL_FLAG(std::string, fprocess, "", "Function process"); 10 | ABSL_FLAG(std::string, fprocess_mode, "cpp", 11 | "Operating mode of fprocess. Valid options are cpp, go, nodejs, and python."); 12 | ABSL_FLAG(std::string, fprocess_working_dir, "", 13 | "Working directory of function processes"); 14 | ABSL_FLAG(std::string, fprocess_output_dir, "", 15 | "If not empty, stdout and stderr of function processes will be saved in the given directory"); 16 | ABSL_FLAG(int, engine_tcp_port, -1, "If set, will connect to engine via localhost TCP socket"); 17 | ABSL_FLAG(std::string, device_name, "mlx5_0", "RDMA device name"); 18 | ABSL_FLAG(int, device_port, 0, "RDMA device port"); 19 | ABSL_FLAG(int, device_gidx, 0, "RDMA device gid index"); 20 | 21 | static std::atomic launcher_ptr(nullptr); 22 | 23 | void SignalHandlerToStopLauncher(int signal) { 24 | faas::launcher::Launcher *launcher = launcher_ptr.exchange(nullptr); 25 | if (launcher != nullptr) { 26 | launcher->ScheduleStop(); 27 | } 28 | } 29 | 30 | int main(int argc, char *argv[]) { 31 | signal(SIGINT, SignalHandlerToStopLauncher); 32 | faas::base::InitMain(argc, argv); 33 | faas::ipc::SetRootPathForIpc(absl::GetFlag(FLAGS_root_path_for_ipc)); 34 | 35 | auto launcher = std::make_unique(); 36 | launcher->set_func_id(absl::GetFlag(FLAGS_func_id)); 37 | launcher->set_fprocess(absl::GetFlag(FLAGS_fprocess)); 38 | launcher->set_fprocess_working_dir(absl::GetFlag(FLAGS_fprocess_working_dir)); 39 | launcher->set_fprocess_output_dir(absl::GetFlag(FLAGS_fprocess_output_dir)); 40 | launcher->set_engine_tcp_port(absl::GetFlag(FLAGS_engine_tcp_port)); 41 | launcher->set_rdma_device_name(absl::GetFlag(FLAGS_device_name)); 42 | launcher->set_rdma_device_port(absl::GetFlag(FLAGS_device_port)); 43 | launcher->set_rdma_device_gid_index(absl::GetFlag(FLAGS_device_gidx)); 44 | 45 | std::string fprocess_mode = absl::GetFlag(FLAGS_fprocess_mode); 46 | if (fprocess_mode == "cpp") { 47 | launcher->set_fprocess_mode(faas::launcher::Launcher::kCppMode); 48 | } else if (fprocess_mode == "go") { 49 | launcher->set_fprocess_mode(faas::launcher::Launcher::kGoMode); 50 | } else if (fprocess_mode == "nodejs") { 51 | launcher->set_fprocess_mode(faas::launcher::Launcher::kNodeJsMode); 52 | } else if (fprocess_mode == "python") { 53 | launcher->set_fprocess_mode(faas::launcher::Launcher::kPythonMode); 54 | } else { 55 | LOG(FATAL) << "Invalid fprocess_mode: " << fprocess_mode; 56 | } 57 | 58 | launcher->Start(); 59 | launcher_ptr.store(launcher.get()); 60 | launcher->WaitForFinish(); 61 | 62 | return 0; 63 | } -------------------------------------------------------------------------------- /src/host/worker/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(${FUNC_WORKER_CPP_LIBRARIES_NAME}) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | #include_directories(${BASE_DIR}/src/worker/cpp) 6 | 7 | set(FUNC_WORKER_CPP_ABSL 8 | absl::flags 9 | absl::random_random 10 | absl::bind_front 11 | absl::failure_signal_handler 12 | absl::flags_parse) 13 | 14 | set(FUNC_WORKER_CPP_LIBRARIES 15 | ${FUNC_WORKER_CPP_ABSL} 16 | fmt 17 | GSL 18 | nlohmann_json 19 | uv_a 20 | nghttp2 21 | pthread) 22 | 23 | set(FUNC_WORKER_CPP_SOURCE_FILES 24 | ${BASE_DIR}/lib/base/init.cpp 25 | ${BASE_DIR}/lib/base/logging.cpp 26 | ${BASE_DIR}/lib/base/thread.cpp 27 | ${BASE_DIR}/lib/common/uv.cpp 28 | ${BASE_DIR}/lib/common/subprocess.cpp 29 | ${BASE_DIR}/lib/common/config.cpp 30 | ${BASE_DIR}/lib/common/http_status.cpp 31 | ${BASE_DIR}/lib/ipc/fifo.cpp 32 | ${BASE_DIR}/lib/ipc/shm_region.cpp 33 | ${BASE_DIR}/lib/rdma/bit_map.cpp 34 | ${BASE_DIR}/lib/rdma/infinity.cpp 35 | ${BASE_DIR}/lib/rdma/queue_pair.cpp 36 | ${BASE_DIR}/lib/rdma/shared_memory.cpp 37 | ${BASE_DIR}/lib/server/io_worker.cpp 38 | ${BASE_DIR}/lib/server/server_base.cpp 39 | ${BASE_DIR}/lib/utils/docker.cpp 40 | ${BASE_DIR}/lib/utils/fs.cpp 41 | ${BASE_DIR}/lib/utils/procfs.cpp 42 | ${BASE_DIR}/lib/utils/random.cpp 43 | ${BASE_DIR}/lib/utils/http_parser.c 44 | ${BASE_DIR}/lib/utils/socket.cpp 45 | ${BASE_DIR}/lib/runtime/worker_lib.cpp 46 | ${BASE_DIR}/lib/ipc/base.cpp 47 | ${BASE_DIR}/src/host/worker/cpp/func_worker.cpp 48 | ${BASE_DIR}/src/host/worker/cpp/func_worker_main.cpp) 49 | 50 | # add library 51 | add_library(${FUNC_WORKER_CPP_LIBRARIES_NAME} SHARED ${FUNC_WORKER_CPP_SOURCE_FILES}) 52 | target_link_libraries(${FUNC_WORKER_CPP_LIBRARIES_NAME} ${FUNC_WORKER_CPP_LIBRARIES} PkgConfig::LIBIBVERBS) -------------------------------------------------------------------------------- /src/host/worker/cpp/func_worker_interface.h: -------------------------------------------------------------------------------- 1 | #ifndef LUMINE_WORKER_V1_INTERFACE_H_ 2 | #define LUMINE_WORKER_V1_INTERFACE_H_ 3 | 4 | #include 5 | 6 | typedef void (*faas_append_output_fn_t)( 7 | void *caller_context, const char *data, size_t length); 8 | 9 | enum PassingMethod{ 10 | IPC, 11 | Fabric, 12 | DRC_OVER_IPC, 13 | DRC_OVER_Fabric, 14 | DRC_OVER_IPC_POLLING, 15 | DRC_OVER_Fabric_POLLING 16 | }; 17 | 18 | // Return 0 on success. 19 | typedef int (*faas_invoke_func_fn_t)( 20 | void *caller_context, const char *func_name, 21 | const char *input_data, size_t input_length, 22 | const char **output_data, size_t *output_length, PassingMethod method); 23 | 24 | // Return address of allocated memory space 25 | typedef void* (*faas_memalloc_t)(const char *caller_context, void *alloc_size); 26 | 27 | // Below are APIs that function library must implement. 28 | // For all APIs, return 0 on success. 29 | 30 | // Initialize function library, will be called once after loading 31 | // the dynamic library. 32 | int faas_init(); 33 | 34 | // Create a new function worker. 35 | // When calling `invoke_func_fn` and `append_output_fn`, caller_context 36 | // received in `faas_create_func_worker` should be passed unchanged. 37 | int faas_create_func_worker( 38 | void *caller_context, 39 | faas_invoke_func_fn_t invoke_func_fn, 40 | faas_append_output_fn_t append_output_fn, 41 | void **worker_handle); 42 | 43 | // Destroy a function worker. 44 | int faas_destroy_func_worker(void *worker_handle); 45 | 46 | // Execute the function. `append_output_fn` can be called multiple 47 | // times to append new data to the output buffer. invoke_func_fn 48 | // can be used to invoke other functions in the system. 49 | // For the same worker_handle, faas_func_call will never be called 50 | // concurrently from different threads, i.e. the implementation 51 | // does not need to be thread-safe for a single function worker. 52 | int faas_func_call( 53 | void *worker_handle, 54 | const char *input, size_t input_length); 55 | 56 | #endif // LUMINE_WORKER_V1_INTERFACE_H_ 57 | -------------------------------------------------------------------------------- /src/host/worker/cpp/func_worker_main.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "ipc/base.h" 4 | #include "utils/env_variables.h" 5 | #include "utils/fs.h" 6 | #include "func_worker.h" 7 | #include 8 | 9 | using json = nlohmann::json; 10 | 11 | ABSL_FLAG(std::string, debug_file_path, "", "worker debug file path"); 12 | 13 | int main(int argc, char *argv[]) { 14 | std::vector positional_args; 15 | faas::base::InitMain(argc, argv, &positional_args); 16 | 17 | std::string faas_root_path_for_ipc, rdma_device_name; 18 | int faas_func_id, faas_fprocess_id, faas_client_id, 19 | faas_msg_pipe_id, faas_use_engine_socket, faas_engine_tcp_port, 20 | rdma_device_port, rdma_device_gid_index, timeout_for_drc; 21 | 22 | std::string debug_file_path = absl::GetFlag(FLAGS_debug_file_path); 23 | 24 | auto func_worker = std::make_unique(); 25 | 26 | if (!debug_file_path.empty()) { 27 | std::string func_config_json; 28 | faas::fs_utils::ReadContents(debug_file_path, &func_config_json); 29 | json config = json::parse(func_config_json); 30 | 31 | faas_root_path_for_ipc = config.at("FAAS_ROOT_PATH_FOR_IPC").get(); 32 | faas_func_id = config.at("FAAS_FUNC_ID").get(); 33 | faas_fprocess_id = config.at("FAAS_FPROCESS_ID").get(); 34 | faas_use_engine_socket = config.at("FAAS_USE_ENGINE_SOCKET").get(); 35 | faas_engine_tcp_port = config.at("FAAS_ENGINE_TCP_PORT").get(); 36 | rdma_device_name = config.at("RDMA_DEVICE_NAME").get(); 37 | rdma_device_port = config.at("RDMA_DEVICE_PORT").get(); 38 | rdma_device_gid_index = config.at("RDMA_DEVICE_GID_INDEX").get(); 39 | 40 | func_worker->set_debug_file_path(debug_file_path); 41 | if (faas_engine_tcp_port != -1) { 42 | func_worker->set_faas_engine_host(config.at("FAAS_ENGINE_HOST").get()); 43 | } 44 | } else { 45 | faas_root_path_for_ipc = faas::utils::GetEnvVariable("FAAS_ROOT_PATH_FOR_IPC", "/dev/shm/faas_ipc"); 46 | faas_func_id = faas::utils::GetEnvVariableAsInt("FAAS_FUNC_ID", -1); 47 | faas_fprocess_id = faas::utils::GetEnvVariableAsInt("FAAS_FPROCESS_ID", -1); 48 | faas_client_id = faas::utils::GetEnvVariableAsInt("FAAS_CLIENT_ID", 0); 49 | faas_msg_pipe_id = faas::utils::GetEnvVariableAsInt("FAAS_MSG_PIPE_FD", -1); 50 | faas_use_engine_socket = faas::utils::GetEnvVariableAsInt("FAAS_USE_ENGINE_SOCKET", 0); 51 | faas_engine_tcp_port = faas::utils::GetEnvVariableAsInt("FAAS_ENGINE_TCP_PORT", -1); 52 | rdma_device_name = faas::utils::GetEnvVariable("RDMA_DEVICE_NAME", "mlx5_0"); 53 | rdma_device_port = faas::utils::GetEnvVariableAsInt("RDMA_DEVICE_PORT", 0); 54 | rdma_device_gid_index = faas::utils::GetEnvVariableAsInt("RDMA_DEVICE_GID_INDEX", 0); 55 | } 56 | 57 | if (faas_use_engine_socket == 1) { 58 | func_worker->enable_use_engine_socket(); 59 | } else { 60 | faas::ipc::SetRootPathForIpc(faas_root_path_for_ipc); 61 | } 62 | 63 | func_worker->set_func_id(faas_func_id); 64 | func_worker->set_fprocess_id(faas_fprocess_id); 65 | func_worker->set_client_id(faas_client_id); 66 | func_worker->set_message_pipe_fd(faas_msg_pipe_id); 67 | 68 | func_worker->set_engine_tcp_port(faas_engine_tcp_port); 69 | func_worker->set_rdma_device_name(rdma_device_name); 70 | func_worker->set_rdma_device_port(rdma_device_port); 71 | func_worker->set_rdma_device_gid_index(rdma_device_gid_index); 72 | func_worker->Serve(); 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /src/host/worker/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(worker_python) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | add_compile_options(-D__WORKER_PYTHON_BUILD) 6 | add_compile_options(-frtti) 7 | 8 | include_directories(${BASE_DIR}/deps/pybind11/include) 9 | include_directories(${PYTHON_INCLUDE_DIR}) 10 | 11 | set(FUNC_WORKER_PYTHON_ABSL 12 | absl::flags 13 | absl::random_random 14 | absl::bind_front 15 | absl::failure_signal_handler 16 | absl::flags_parse) 17 | 18 | set(FUNC_WORKER_PYTHON_PYBIND11 19 | pybind11::pybind11) 20 | 21 | set(FUNC_WORKER_PYTHON_LIBRARIES 22 | ${FUNC_WORKER_PYTHON_ABSL} 23 | ${FUNC_WORKER_PYTHON_PYBIND11} 24 | uv_a 25 | fmt 26 | GSL 27 | nghttp2 28 | pthread 29 | nlohmann_json) 30 | 31 | set(FUNC_WORKER_PYTHON_SOURCE_FILES 32 | ${BASE_DIR}/lib/base/logging.cpp 33 | ${BASE_DIR}/lib/base/thread.cpp 34 | ${BASE_DIR}/lib/common/config.cpp 35 | ${BASE_DIR}/lib/ipc/base.cpp 36 | ${BASE_DIR}/lib/ipc/fifo.cpp 37 | ${BASE_DIR}/lib/ipc/shm_region.cpp 38 | ${BASE_DIR}/lib/utils/docker.cpp 39 | ${BASE_DIR}/lib/utils/fs.cpp 40 | ${BASE_DIR}/lib/utils/socket.cpp 41 | ${BASE_DIR}/lib/runtime/event_driven_worker.cpp 42 | ${BASE_DIR}/lib/runtime/worker_lib.cpp 43 | ${BASE_DIR}/src/host/worker/python/module.cpp) 44 | 45 | add_library(${PROJECT_NAME} SHARED ${FUNC_WORKER_PYTHON_SOURCE_FILES}) 46 | 47 | target_link_libraries(${PROJECT_NAME} ${FUNC_WORKER_PYTHON_LIBRARIES}) 48 | 49 | set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${FUNC_WORKER_PYTHON_OUTPUT_NAME}) 50 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") -------------------------------------------------------------------------------- /src/host/worker/python/module.cpp: -------------------------------------------------------------------------------- 1 | #include "base/logging.h" 2 | #include "utils/env_variables.h" 3 | #include "runtime/event_driven_worker.h" 4 | 5 | #include 6 | 7 | namespace py = pybind11; 8 | 9 | namespace faas::python { 10 | 11 | namespace { 12 | static std::span py_bytes_to_span(py::bytes obj) { 13 | DCHECK(PyBytes_CheckExact(obj.ptr())); 14 | return std::span( 15 | PyBytes_AsString(obj.ptr()), PyBytes_Size(obj.ptr())); 16 | } 17 | 18 | static py::bytes span_to_py_bytes(std::span s) { 19 | return py::bytes(s.data(), s.size()); 20 | } 21 | 22 | static py::str string_view_to_py_str(std::string_view s) { 23 | return py::str(s.data(), s.size()); 24 | } 25 | } 26 | 27 | void InitModule(py::module &m) { 28 | logging::Init(utils::GetEnvVariableAsInt("FAAS_VLOG_LEVEL", 0)); 29 | 30 | auto clz = py::class_(m, "Worker"); 31 | 32 | clz.def(py::init([](const std::string &debug_file_path) { 33 | return std::make_unique(debug_file_path); 34 | })); 35 | 36 | clz.def("start", [](worker_lib::EventDrivenWorker *self) { 37 | self->Start(); 38 | }); 39 | 40 | clz.def_property_readonly("is_grpc_service", [](worker_lib::EventDrivenWorker *self) { 41 | return self->is_grpc_service(); 42 | }); 43 | clz.def_property_readonly("func_name", [](worker_lib::EventDrivenWorker *self) { 44 | return self->func_name(); 45 | }); 46 | clz.def_property_readonly("grpc_service_name", [](worker_lib::EventDrivenWorker *self) { 47 | return self->grpc_service_name(); 48 | }); 49 | 50 | clz.def("set_watch_fd_readable_callback", [](worker_lib::EventDrivenWorker *self, 51 | py::function callback) { 52 | self->SetWatchFdReadableCallback([callback](int fd) { 53 | callback(py::int_(fd)); 54 | }); 55 | }); 56 | clz.def("set_stop_watch_fd_callback", [](worker_lib::EventDrivenWorker *self, 57 | py::function callback) { 58 | self->SetStopWatchFdCallback([callback](int fd) { 59 | callback(py::int_(fd)); 60 | }); 61 | }); 62 | clz.def("set_incoming_func_call_callback", [](worker_lib::EventDrivenWorker *self, 63 | py::function callback) { 64 | self->SetIncomingFuncCallCallback([callback](int64_t handle, std::string_view method, 65 | std::span input) { 66 | callback(py::int_(handle), string_view_to_py_str(method), span_to_py_bytes(input)); 67 | }); 68 | }); 69 | clz.def("set_outgoing_func_call_complete_callback", [](worker_lib::EventDrivenWorker *self, 70 | py::function callback) { 71 | self->SetOutgoingFuncCallCompleteCallback([callback](int64_t handle, bool success, 72 | std::span output) { 73 | callback(py::int_(handle), py::bool_(success), span_to_py_bytes(output)); 74 | }); 75 | }); 76 | 77 | clz.def("on_fd_readable", [](worker_lib::EventDrivenWorker *self, int fd) { 78 | self->OnFdReadable(fd); 79 | }); 80 | 81 | clz.def("on_func_execution_finished", [](worker_lib::EventDrivenWorker *self, int64_t handle, 82 | bool success, py::bytes output) { 83 | self->OnFuncExecutionFinished(handle, success, py_bytes_to_span(output)); 84 | }); 85 | 86 | clz.def("new_outgoing_func_call", [](worker_lib::EventDrivenWorker *self, int64_t parent_handle, 87 | std::string func_name, py::bytes input) -> py::object { 88 | int64_t handle; 89 | bool ret = self->NewOutgoingFuncCall(parent_handle, func_name, 90 | py_bytes_to_span(input), &handle); 91 | if (ret) { 92 | return py::int_(handle); 93 | } else { 94 | return py::none(); 95 | } 96 | }); 97 | 98 | clz.def("new_outgoing_grpc_call", [](worker_lib::EventDrivenWorker *self, int64_t parent_handle, 99 | std::string service, std::string method, 100 | py::bytes request) -> py::object { 101 | int64_t handle; 102 | bool ret = self->NewOutgoingGrpcCall(parent_handle, service, method, 103 | py_bytes_to_span(request), &handle); 104 | if (ret) { 105 | return py::int_(handle); 106 | } else { 107 | return py::none(); 108 | } 109 | }); 110 | } 111 | 112 | } // namespace faas 113 | 114 | PYBIND11_MODULE(_faas_native, m) { 115 | faas::python::InitModule(m); 116 | } 117 | --------------------------------------------------------------------------------