├── .dockerignore ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── build_deps.sh ├── deps └── zookeeper-client-c │ ├── .gitignore │ ├── CMakeLists.txt │ ├── ChangeLog │ ├── INSTALL │ ├── LICENSE │ ├── Makefile.am │ ├── NOTICE.txt │ ├── README │ ├── acinclude.m4 │ ├── aminclude.am │ ├── c-doc.Doxyfile │ ├── cmake_config.h.in │ ├── configure.ac │ ├── generated │ ├── zookeeper.jute.c │ └── zookeeper.jute.h │ ├── include │ ├── proto.h │ ├── recordio.h │ ├── win_getopt.h │ ├── winconfig.h │ ├── zookeeper.h │ ├── zookeeper_log.h │ └── zookeeper_version.h │ ├── pom.xml │ ├── src │ ├── addrvec.c │ ├── addrvec.h │ ├── cli.c │ ├── hashtable │ │ ├── LICENSE.txt │ │ ├── hashtable.c │ │ ├── hashtable.h │ │ ├── hashtable_itr.c │ │ ├── hashtable_itr.h │ │ └── hashtable_private.h │ ├── load_gen.c │ ├── mt_adaptor.c │ ├── recordio.c │ ├── st_adaptor.c │ ├── winport.c │ ├── winport.h │ ├── zk_adaptor.h │ ├── zk_hashtable.c │ ├── zk_hashtable.h │ ├── zk_log.c │ └── zookeeper.c │ ├── ssl │ └── gencerts.sh │ └── tests │ ├── CollectionUtil.h │ ├── CppAssertHelper.h │ ├── LibCMocks.cc │ ├── LibCMocks.h │ ├── LibCSymTable.cc │ ├── LibCSymTable.h │ ├── MocksBase.cc │ ├── MocksBase.h │ ├── PthreadMocks.cc │ ├── PthreadMocks.h │ ├── TestClient.cc │ ├── TestClientRetry.cc │ ├── TestDriver.cc │ ├── TestLogClientEnv.cc │ ├── TestMulti.cc │ ├── TestOperations.cc │ ├── TestReadOnlyClient.cc │ ├── TestReconfig.cc │ ├── TestReconfigServer.cc │ ├── TestServerRequireClientSASLAuth.cc │ ├── TestWatchers.cc │ ├── TestZookeeperClose.cc │ ├── TestZookeeperInit.cc │ ├── ThreadingUtil.cc │ ├── ThreadingUtil.h │ ├── Util.cc │ ├── Util.h │ ├── Vector.h │ ├── WatchUtil.h │ ├── ZKMocks.cc │ ├── ZKMocks.h │ ├── ZooKeeperQuorumServer.cc │ ├── ZooKeeperQuorumServer.h │ ├── wrappers-mt.opt │ ├── wrappers.opt │ ├── zkServer.sh │ └── zoo.cfg ├── slib ├── common │ ├── compress.go │ ├── constants.go │ ├── deepcopy.go │ └── hash.go ├── go.mod ├── go.sum ├── lib.go ├── statestore │ ├── env.go │ ├── error.go │ ├── log.go │ ├── object.go │ ├── op.go │ ├── txn.go │ └── value.go └── sync │ ├── queue.go │ └── sharded_queue.go ├── src ├── base │ ├── asm.h │ ├── common.h │ ├── diagnostic.h │ ├── init.cpp │ ├── init.h │ ├── logging.cpp │ ├── logging.h │ ├── macro.h │ ├── std_span.h │ ├── thread.cpp │ └── thread.h ├── bin │ ├── bench_eventfd.cpp │ ├── bench_futex.cpp │ ├── bench_ipc_shm.cpp │ ├── bench_shared_mem.cpp │ ├── bench_socket.cpp │ ├── bench_spsc_queue.cpp │ ├── controller.cpp │ ├── engine.cpp │ ├── gateway.cpp │ ├── launcher.cpp │ ├── sequencer.cpp │ └── storage.cpp ├── common │ ├── flags.cpp │ ├── flags.h │ ├── func_config.cpp │ ├── func_config.h │ ├── http_status.cpp │ ├── http_status.h │ ├── protocol.h │ ├── stat.h │ ├── subprocess.cpp │ ├── subprocess.h │ ├── time.h │ ├── uv.cpp │ ├── uv.h │ ├── zk.cpp │ ├── zk.h │ ├── zk_utils.cpp │ └── zk_utils.h ├── engine │ ├── dispatcher.cpp │ ├── dispatcher.h │ ├── engine.cpp │ ├── engine.h │ ├── flags.cpp │ ├── flags.h │ ├── message_connection.cpp │ ├── message_connection.h │ ├── monitor.cpp │ ├── monitor.h │ ├── tracer.cpp │ ├── tracer.h │ ├── worker_manager.cpp │ └── worker_manager.h ├── gateway │ ├── flags.cpp │ ├── flags.h │ ├── func_call_context.h │ ├── grpc_connection.cpp │ ├── grpc_connection.h │ ├── http_connection.cpp │ ├── http_connection.h │ ├── node_manager.cpp │ ├── node_manager.h │ ├── server.cpp │ └── server.h ├── ipc │ ├── base.cpp │ ├── base.h │ ├── fifo.cpp │ ├── fifo.h │ ├── shm_region.cpp │ ├── shm_region.h │ ├── spsc_queue-inl.h │ └── spsc_queue.h ├── launcher │ ├── engine_connection.cpp │ ├── engine_connection.h │ ├── func_process.cpp │ ├── func_process.h │ ├── launcher.cpp │ └── launcher.h ├── log │ ├── cache.cpp │ ├── cache.h │ ├── common.h │ ├── controller.cpp │ ├── controller.h │ ├── db.cpp │ ├── db.h │ ├── engine.cpp │ ├── engine.h │ ├── engine_base.cpp │ ├── engine_base.h │ ├── flags.cpp │ ├── flags.h │ ├── index.cpp │ ├── index.h │ ├── log_space.cpp │ ├── log_space.h │ ├── log_space_base.cpp │ ├── log_space_base.h │ ├── sequencer.cpp │ ├── sequencer.h │ ├── sequencer_base.cpp │ ├── sequencer_base.h │ ├── storage.cpp │ ├── storage.h │ ├── storage_base.cpp │ ├── storage_base.h │ ├── utils.cpp │ ├── utils.h │ ├── view.cpp │ ├── view.h │ ├── view_watcher.cpp │ └── view_watcher.h ├── proto │ └── shared_log.proto ├── server │ ├── constants.h │ ├── egress_hub.cpp │ ├── egress_hub.h │ ├── ingress_connection.cpp │ ├── ingress_connection.h │ ├── io_uring.cpp │ ├── io_uring.h │ ├── io_worker.cpp │ ├── io_worker.h │ ├── node_watcher.cpp │ ├── node_watcher.h │ ├── server_base.cpp │ ├── server_base.h │ ├── timer.cpp │ └── timer.h ├── utils │ ├── appendable_buffer.h │ ├── base64.h │ ├── bench.cpp │ ├── bench.h │ ├── bits.h │ ├── blocking_queue.h │ ├── bst.h │ ├── buffer_pool.h │ ├── docker.cpp │ ├── docker.h │ ├── env_variables.h │ ├── exp_moving_avg.h │ ├── fs.cpp │ ├── fs.h │ ├── hash.h │ ├── io.cpp │ ├── io.h │ ├── lockable_ptr.h │ ├── object_pool.h │ ├── perf_event.cpp │ ├── perf_event.h │ ├── procfs.cpp │ ├── procfs.h │ ├── random.cpp │ ├── random.h │ ├── round_robin_set.h │ ├── socket.cpp │ ├── socket.h │ ├── timerfd.cpp │ └── timerfd.h └── worker │ ├── event_driven_worker.cpp │ ├── event_driven_worker.h │ ├── worker_lib.cpp │ └── worker_lib.h └── worker ├── cpp ├── Makefile ├── deps │ ├── GSL │ ├── fmt │ └── json │ │ └── single_include ├── include │ └── faas │ │ └── worker_v1_interface.h ├── main_v1.cpp ├── src └── worker │ └── v1 │ ├── func_worker.cpp │ └── func_worker.h ├── golang ├── common │ └── time.go ├── config │ └── func_config.go ├── go.mod ├── ipc │ ├── base.go │ ├── fifo.go │ └── shm_region.go ├── lib.go ├── protocol │ └── protocol.go ├── types │ └── types.go └── worker │ └── func_worker.go ├── nodejs ├── addon.cpp ├── binding.gyp ├── deps │ ├── GSL │ ├── fmt │ └── json │ │ └── single_include ├── engine.cpp ├── engine.h ├── index.js ├── package-lock.json ├── package.json └── src └── python ├── Makefile ├── deps ├── GSL ├── fmt ├── json │ └── single_include └── pybind11 ├── faas └── __init__.py ├── module.cpp └── src /.dockerignore: -------------------------------------------------------------------------------- 1 | /.git/ 2 | /tmp/ 3 | 4 | /bin/ 5 | /build/ 6 | /examples/ 7 | 8 | /deps/out 9 | /deps/abseil-cpp/build/ 10 | /deps/libuv/build/ 11 | /deps/nghttp2/build/ 12 | /deps/json/benchmarks/ 13 | /deps/json/test/ 14 | 15 | /worker/golang/build/ 16 | /worker/nodejs/build/ 17 | /worker/nodejs/node_modules/ 18 | /worker/python/build/ 19 | /worker/python/faas/_faas_native.*.so 20 | /worker/python/faas/__pycache__/ 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build related 2 | /build/ 3 | /bin/ 4 | /deps/out* 5 | /.all_time 6 | /worker/cpp/.release_time 7 | /worker/cpp/bin/ 8 | /worker/cpp/build/ 9 | /worker/golang/build/ 10 | /worker/nodejs/build/ 11 | /worker/nodejs/node_modules/ 12 | /worker/python/.release_time 13 | /worker/python/build/ 14 | /worker/python/faas/_faas_native.*.so 15 | 16 | # Python related 17 | /worker/python/faas/__pycache__/ 18 | 19 | # Examples related 20 | /examples/*/outputs 21 | /examples/c/*.so 22 | /examples/go/main 23 | /examples/node/node_modules 24 | 25 | # Editor related 26 | /.vscode/ 27 | 28 | # Protobuf generated code 29 | /src/proto/*.pb.h 30 | /src/proto/*.pb.cc 31 | /src/proto/*.pb.cpp 32 | 33 | # jemalloc 34 | *.heap 35 | 36 | # Misc 37 | /src/bin/test_*.cpp 38 | /tmp/ 39 | /config.mk 40 | *~ 41 | -------------------------------------------------------------------------------- /.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 | [submodule "deps/liburing"] 26 | path = deps/liburing 27 | url = https://github.com/axboe/liburing.git 28 | [submodule "deps/protobuf"] 29 | path = deps/protobuf 30 | url = https://github.com/protocolbuffers/protobuf.git 31 | [submodule "deps/rocksdb"] 32 | path = deps/rocksdb 33 | url = https://github.com/facebook/rocksdb.git 34 | [submodule "deps/xxHash"] 35 | path = deps/xxHash 36 | url = https://github.com/Cyan4973/xxHash 37 | [submodule "deps/jemalloc"] 38 | path = deps/jemalloc 39 | url = https://github.com/jemalloc/jemalloc.git 40 | [submodule "deps/zstd"] 41 | path = deps/zstd 42 | url = https://github.com/facebook/zstd.git 43 | [submodule "deps/tkrzw"] 44 | path = deps/tkrzw 45 | url = https://github.com/estraier/tkrzw.git 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Boki 2 | ================================== 3 | 4 | Boki is a research FaaS runtime for stateful serverless computing with shared logs. 5 | Boki exports the shared log API to serverless functions, allowing them to manage states with strong consistency, durability, and fault tolerance. 6 | Boki uses [Nightcore](https://github.com/ut-osa/nightcore) as the runtime for serverless functions. 7 | 8 | Boki is the pronunciation of "簿記", meaning bookkeeping in Japanese. 9 | 10 | ### Building Boki ### 11 | 12 | Under Ubuntu 20.04, building Boki needs following dependencies installed: 13 | ~~~ 14 | sudo apt install g++ make cmake pkg-config autoconf automake libtool curl unzip 15 | ~~~ 16 | 17 | Once installed, build Boki with: 18 | 19 | ~~~ 20 | ./build_deps.sh 21 | make -j $(nproc) 22 | ~~~ 23 | 24 | ### Kernel requirements ### 25 | 26 | Boki uses [io_uring](https://en.wikipedia.org/wiki/Io_uring) for asynchronous I/Os. 27 | io_uring is a very new feature in the Linux kernel (introduced in 5.1), 28 | and evolves rapidly with newer Linux kernel version. 29 | 30 | Boki require Linux kernel 5.10 or later to run. 31 | 32 | ### Boki support libraries ### 33 | 34 | As presented in our SOSP paper, we build BokiFlow, BokiStore and BokiQueue for serverless use cases of 35 | transactional workflows, durable object storage, and message queues. 36 | 37 | `slib` directory in this repository contains implementations of BokiStore and BokiQueue. 38 | For BokiFlow, check out `workloads/workflow` directory in [ut-osa/boki-benchmarks](https://github.com/ut-osa/boki-benchmarks) repository. 39 | 40 | ### Running Boki's evaluation workloads ### 41 | 42 | A separate repository [ut-osa/boki-benchmarks](https://github.com/ut-osa/boki-benchmarks) 43 | includes scripts and detailed instructions on running evaluation workloads presented in our SOSP paper. 44 | 45 | ### Limitations of the current prototype ### 46 | 47 | The shared log API is only exported to functions written in Go. 48 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/.gitignore: -------------------------------------------------------------------------------- 1 | /config.log 2 | /Makefile.in 3 | /aclocal.m4 4 | /autom4te.cache/ 5 | /compile 6 | /config.guess 7 | /config.h.in 8 | /config.sub 9 | /configure 10 | /depcomp 11 | /install-sh 12 | /ltmain.sh 13 | /missing 14 | /.deps/ 15 | /.libs/ 16 | /Makefile 17 | /cli_mt 18 | /cli_st 19 | /config.h 20 | /config.status 21 | /libtool 22 | /load_gen 23 | /stamp-h1 24 | /build 25 | /core.* 26 | /TEST-*.txt 27 | /*.la 28 | /*.lo 29 | /*.o 30 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ut-osa/boki/8e8c9ca8586a120a2bf18b3a6ae5041da628fac9/deps/zookeeper-client-c/ChangeLog -------------------------------------------------------------------------------- /deps/zookeeper-client-c/NOTICE.txt: -------------------------------------------------------------------------------- 1 | Apache ZooKeeper 2 | Copyright 2009-2011 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | 7 | ---------- 8 | include/winstdint.h is included only for Windows Client support, as follows: 9 | 10 | // ISO C9x compliant stdint.h for Microsoft Visual Studio 11 | // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 12 | // 13 | // Copyright (c) 2006-2008 Alexander Chemeris 14 | // 15 | // Redistribution and use in source and binary forms, with or without 16 | // modification, are permitted provided that the following conditions are met: 17 | // 18 | // 1. Redistributions of source code must retain the above copyright notice, 19 | // this list of conditions and the following disclaimer. 20 | // 21 | // 2. Redistributions in binary form must reproduce the above copyright 22 | // notice, this list of conditions and the following disclaimer in the 23 | // documentation and/or other materials provided with the distribution. 24 | // 25 | // 3. The name of the author may be used to endorse or promote products 26 | // derived from this software without specific prior written permission. 27 | // 28 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 29 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 30 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 31 | // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 32 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 33 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 34 | // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 35 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 36 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 37 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | // 39 | /////////////////////////////////////////////////////////////////////////////// 40 | 41 | ---------- 42 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/include/proto.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | #ifndef PROTO_H_ 19 | #define PROTO_H_ 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #define ZOO_NOTIFY_OP 0 26 | #define ZOO_CREATE_OP 1 27 | #define ZOO_DELETE_OP 2 28 | #define ZOO_EXISTS_OP 3 29 | #define ZOO_GETDATA_OP 4 30 | #define ZOO_SETDATA_OP 5 31 | #define ZOO_GETACL_OP 6 32 | #define ZOO_SETACL_OP 7 33 | #define ZOO_GETCHILDREN_OP 8 34 | #define ZOO_SYNC_OP 9 35 | #define ZOO_PING_OP 11 36 | #define ZOO_GETCHILDREN2_OP 12 37 | #define ZOO_CHECK_OP 13 38 | #define ZOO_MULTI_OP 14 39 | #define ZOO_CREATE2_OP 15 40 | #define ZOO_RECONFIG_OP 16 41 | #define ZOO_CHECK_WATCHES 17 42 | #define ZOO_REMOVE_WATCHES 18 43 | #define ZOO_CREATE_CONTAINER_OP 19 44 | #define ZOO_DELETE_CONTAINER_OP 20 45 | #define ZOO_CREATE_TTL_OP 21 46 | #define ZOO_CLOSE_OP -11 47 | #define ZOO_SETAUTH_OP 100 48 | #define ZOO_SETWATCHES_OP 101 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif /*PROTO_H_*/ 55 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/include/recordio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | #ifndef __RECORDIO_H__ 19 | #define __RECORDIO_H__ 20 | 21 | #include 22 | #include /* for int64_t */ 23 | #ifdef WIN32 24 | #include "winconfig.h" 25 | #endif 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | struct buffer { 32 | int32_t len; 33 | char *buff; 34 | }; 35 | 36 | void deallocate_String(char **s); 37 | void deallocate_Buffer(struct buffer *b); 38 | void deallocate_vector(void *d); 39 | struct iarchive { 40 | int (*start_record)(struct iarchive *ia, const char *tag); 41 | int (*end_record)(struct iarchive *ia, const char *tag); 42 | int (*start_vector)(struct iarchive *ia, const char *tag, int32_t *count); 43 | int (*end_vector)(struct iarchive *ia, const char *tag); 44 | int (*deserialize_Bool)(struct iarchive *ia, const char *name, int32_t *); 45 | int (*deserialize_Int)(struct iarchive *ia, const char *name, int32_t *); 46 | int (*deserialize_Long)(struct iarchive *ia, const char *name, int64_t *); 47 | int (*deserialize_Buffer)(struct iarchive *ia, const char *name, 48 | struct buffer *); 49 | int (*deserialize_String)(struct iarchive *ia, const char *name, char **); 50 | void *priv; 51 | }; 52 | struct oarchive { 53 | int (*start_record)(struct oarchive *oa, const char *tag); 54 | int (*end_record)(struct oarchive *oa, const char *tag); 55 | int (*start_vector)(struct oarchive *oa, const char *tag, const int32_t *count); 56 | int (*end_vector)(struct oarchive *oa, const char *tag); 57 | int (*serialize_Bool)(struct oarchive *oa, const char *name, const int32_t *); 58 | int (*serialize_Int)(struct oarchive *oa, const char *name, const int32_t *); 59 | int (*serialize_Long)(struct oarchive *oa, const char *name, 60 | const int64_t *); 61 | int (*serialize_Buffer)(struct oarchive *oa, const char *name, 62 | const struct buffer *); 63 | int (*serialize_String)(struct oarchive *oa, const char *name, char **); 64 | void *priv; 65 | }; 66 | 67 | struct oarchive *create_buffer_oarchive(void); 68 | void close_buffer_oarchive(struct oarchive **oa, int free_buffer); 69 | struct iarchive *create_buffer_iarchive(char *buffer, int len); 70 | void close_buffer_iarchive(struct iarchive **ia); 71 | char *get_buffer(struct oarchive *); 72 | int get_buffer_len(struct oarchive *); 73 | 74 | int64_t zoo_htonll(int64_t v); 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/include/winconfig.h: -------------------------------------------------------------------------------- 1 | #ifndef WINCONFIG_H_ 2 | #define WINCONFIG_H_ 3 | 4 | /* Define to `__inline__' or `__inline' if that's what the C compiler 5 | calls it, or to nothing if 'inline' is not supported under any name. */ 6 | #ifndef __cplusplus 7 | #define inline __inline 8 | #endif 9 | 10 | #define __attribute__(x) 11 | #define __func__ __FUNCTION__ 12 | 13 | #define ACL ZKACL /* Conflict with windows API */ 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/include/zookeeper_log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef ZK_LOG_H_ 20 | #define ZK_LOG_H_ 21 | 22 | #include 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | extern ZOOAPI ZooLogLevel logLevel; 29 | #define LOGCALLBACK(_zh) zoo_get_log_callback(_zh) 30 | #define LOGSTREAM NULL 31 | 32 | #define LOG_ERROR(_cb, ...) if(logLevel>=ZOO_LOG_LEVEL_ERROR) \ 33 | log_message(_cb, ZOO_LOG_LEVEL_ERROR, __LINE__, __func__, __VA_ARGS__) 34 | #define LOG_WARN(_cb, ...) if(logLevel>=ZOO_LOG_LEVEL_WARN) \ 35 | log_message(_cb, ZOO_LOG_LEVEL_WARN, __LINE__, __func__, __VA_ARGS__) 36 | #define LOG_INFO(_cb, ...) if(logLevel>=ZOO_LOG_LEVEL_INFO) \ 37 | log_message(_cb, ZOO_LOG_LEVEL_INFO, __LINE__, __func__, __VA_ARGS__) 38 | #define LOG_DEBUG(_cb, ...) if(logLevel==ZOO_LOG_LEVEL_DEBUG) \ 39 | log_message(_cb, ZOO_LOG_LEVEL_DEBUG, __LINE__, __func__, __VA_ARGS__) 40 | 41 | ZOOAPI void log_message(log_callback_fn callback, ZooLogLevel curLevel, 42 | int line, const char* funcName, const char* format, ...); 43 | 44 | FILE* zoo_get_log_stream(); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif /*ZK_LOG_H_*/ 51 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/include/zookeeper_version.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | #ifndef ZOOKEEPER_VERSION_H_ 19 | #define ZOOKEEPER_VERSION_H_ 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #define ZOO_VERSION "3.6.0" 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif /* ZOOKEEPER_VERSION_H_ */ 32 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/src/hashtable/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2002, 2004, Christopher Clark 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the original author; nor the names of any contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/src/hashtable/hashtable_private.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2002, 2004 Christopher Clark */ 2 | 3 | #ifndef __HASHTABLE_PRIVATE_CWC22_H__ 4 | #define __HASHTABLE_PRIVATE_CWC22_H__ 5 | 6 | #include "hashtable.h" 7 | 8 | /*****************************************************************************/ 9 | struct entry 10 | { 11 | void *k, *v; 12 | unsigned int h; 13 | struct entry *next; 14 | }; 15 | 16 | struct hashtable { 17 | unsigned int tablelength; 18 | struct entry **table; 19 | unsigned int entrycount; 20 | unsigned int loadlimit; 21 | unsigned int primeindex; 22 | unsigned int (*hashfn) (void *k); 23 | int (*eqfn) (void *k1, void *k2); 24 | }; 25 | 26 | /*****************************************************************************/ 27 | unsigned int 28 | hash(struct hashtable *h, void *k); 29 | 30 | /*****************************************************************************/ 31 | /* indexFor */ 32 | static inline unsigned int 33 | indexFor(unsigned int tablelength, unsigned int hashvalue) { 34 | return (hashvalue % tablelength); 35 | }; 36 | 37 | /* Only works if tablelength == 2^N */ 38 | /*static inline unsigned int 39 | indexFor(unsigned int tablelength, unsigned int hashvalue) 40 | { 41 | return (hashvalue & (tablelength - 1u)); 42 | } 43 | */ 44 | 45 | /*****************************************************************************/ 46 | #define freekey(X) free(X) 47 | /*define freekey(X) ; */ 48 | 49 | 50 | /*****************************************************************************/ 51 | 52 | #endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ 53 | 54 | /* 55 | * Copyright (c) 2002, Christopher Clark 56 | * All rights reserved. 57 | * 58 | * Redistribution and use in source and binary forms, with or without 59 | * modification, are permitted provided that the following conditions 60 | * are met: 61 | * 62 | * * Redistributions of source code must retain the above copyright 63 | * notice, this list of conditions and the following disclaimer. 64 | * 65 | * * Redistributions in binary form must reproduce the above copyright 66 | * notice, this list of conditions and the following disclaimer in the 67 | * documentation and/or other materials provided with the distribution. 68 | * 69 | * * Neither the name of the original author; nor the names of any contributors 70 | * may be used to endorse or promote products derived from this software 71 | * without specific prior written permission. 72 | * 73 | * 74 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 75 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 76 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 77 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 78 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 79 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 80 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 81 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 82 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 83 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 84 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 85 | */ 86 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/src/st_adaptor.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #if !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB) 20 | # define USE_STATIC_LIB 21 | #endif 22 | 23 | #include "zk_adaptor.h" 24 | #include 25 | #include 26 | 27 | int zoo_lock_auth(zhandle_t *zh) 28 | { 29 | return 0; 30 | } 31 | int zoo_unlock_auth(zhandle_t *zh) 32 | { 33 | return 0; 34 | } 35 | int lock_buffer_list(buffer_head_t *l) 36 | { 37 | return 0; 38 | } 39 | int unlock_buffer_list(buffer_head_t *l) 40 | { 41 | return 0; 42 | } 43 | int lock_completion_list(completion_head_t *l) 44 | { 45 | return 0; 46 | } 47 | int unlock_completion_list(completion_head_t *l) 48 | { 49 | return 0; 50 | } 51 | int process_async(int outstanding_sync) 52 | { 53 | return outstanding_sync == 0; 54 | } 55 | 56 | int adaptor_init(zhandle_t *zh) 57 | { 58 | return 0; 59 | } 60 | 61 | void adaptor_finish(zhandle_t *zh){} 62 | 63 | void adaptor_destroy(zhandle_t *zh){} 64 | 65 | int flush_send_queue(zhandle_t *, int); 66 | 67 | int adaptor_send_queue(zhandle_t *zh, int timeout) 68 | { 69 | return flush_send_queue(zh, timeout); 70 | } 71 | 72 | int32_t inc_ref_counter(zhandle_t* zh,int i) 73 | { 74 | zh->ref_counter+=(i<0?-1:(i>0?1:0)); 75 | return zh->ref_counter; 76 | } 77 | 78 | int32_t get_xid() 79 | { 80 | static int32_t xid = -1; 81 | if (xid == -1) { 82 | xid = time(0); 83 | } 84 | return xid++; 85 | } 86 | 87 | int lock_reconfig(struct _zhandle *zh) 88 | { 89 | return 0; 90 | } 91 | 92 | int unlock_reconfig(struct _zhandle *zh) 93 | { 94 | return 0; 95 | } 96 | 97 | int lock_watchers(struct _zhandle *zh) 98 | { 99 | return 0; 100 | } 101 | 102 | int unlock_watchers(struct _zhandle *zh) 103 | { 104 | return 0; 105 | } 106 | 107 | int enter_critical(zhandle_t* zh) 108 | { 109 | return 0; 110 | } 111 | 112 | int leave_critical(zhandle_t* zh) 113 | { 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/CppAssertHelper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef CPPASSERTHELPER_H_ 20 | #define CPPASSERTHELPER_H_ 21 | 22 | #include 23 | 24 | // make it possible to specify location of the ASSERT call 25 | #define CPPUNIT_ASSERT_EQUAL_LOC(expected,actual,file,line) \ 26 | ( CPPUNIT_NS::assertEquals( (expected), \ 27 | (actual), \ 28 | CPPUNIT_NS::SourceLine(file,line), \ 29 | "" ) ) 30 | 31 | #define CPPUNIT_ASSERT_EQUAL_MESSAGE_LOC(message,expected,actual,file,line) \ 32 | ( CPPUNIT_NS::assertEquals( (expected), \ 33 | (actual), \ 34 | CPPUNIT_NS::SourceLine(file,line), \ 35 | (message) ) ) 36 | 37 | #endif /*CPPASSERTHELPER_H_*/ 38 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/LibCSymTable.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "LibCSymTable.h" 20 | #include // needed for _POSIX_MONOTONIC_CLOCK 21 | 22 | #define LOAD_SYM(sym) \ 23 | sym=(sym##_sig)dlsym(handle,#sym); \ 24 | assert("Unable to load "#sym" from libc"&&sym) 25 | 26 | 27 | LibCSymTable& LibCSymTable::instance(){ 28 | static LibCSymTable tbl; 29 | return tbl; 30 | } 31 | 32 | //****************************************************************************** 33 | // preload original libc symbols 34 | LibCSymTable::LibCSymTable() 35 | { 36 | void* handle=getHandle(); 37 | LOAD_SYM(gethostbyname); 38 | LOAD_SYM(calloc); 39 | LOAD_SYM(realloc); 40 | LOAD_SYM(free); 41 | LOAD_SYM(random); 42 | LOAD_SYM(srandom); 43 | LOAD_SYM(printf); 44 | LOAD_SYM(socket); 45 | LOAD_SYM(close); 46 | LOAD_SYM(getsockopt); 47 | LOAD_SYM(setsockopt); 48 | LOAD_SYM(fcntl); 49 | LOAD_SYM(connect); 50 | LOAD_SYM(send); 51 | LOAD_SYM(recv); 52 | LOAD_SYM(select); 53 | LOAD_SYM(poll); 54 | LOAD_SYM(gettimeofday); 55 | #ifdef _POSIX_MONOTONIC_CLOCK 56 | LOAD_SYM(clock_gettime); 57 | #endif 58 | #ifdef THREADED 59 | LOAD_SYM(pthread_create); 60 | LOAD_SYM(pthread_detach); 61 | LOAD_SYM(pthread_cond_broadcast); 62 | LOAD_SYM(pthread_cond_destroy); 63 | LOAD_SYM(pthread_cond_init); 64 | LOAD_SYM(pthread_cond_signal); 65 | LOAD_SYM(pthread_cond_timedwait); 66 | LOAD_SYM(pthread_cond_wait); 67 | LOAD_SYM(pthread_join); 68 | LOAD_SYM(pthread_mutex_destroy); 69 | LOAD_SYM(pthread_mutex_init); 70 | LOAD_SYM(pthread_mutex_lock); 71 | LOAD_SYM(pthread_mutex_trylock); 72 | LOAD_SYM(pthread_mutex_unlock); 73 | #endif 74 | } 75 | 76 | void* LibCSymTable::getHandle(){ 77 | static void* handle=0; 78 | if(!handle){ 79 | #ifdef __CYGWIN__ 80 | handle=dlopen("cygwin1.dll",RTLD_LAZY); 81 | assert("Unable to dlopen global sym table"&&handle); 82 | #else 83 | handle=RTLD_NEXT; 84 | #endif 85 | } 86 | return handle; 87 | } 88 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/MocksBase.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "MocksBase.h" 23 | #include "LibCSymTable.h" 24 | 25 | // ***************************************************************************** 26 | // Mock base 27 | void* Mock::operator new(std::size_t s){ 28 | void* p=malloc(s); 29 | if(!p) 30 | throw std::bad_alloc(); 31 | return p; 32 | } 33 | 34 | void Mock::operator delete(void* p){ 35 | LIBC_SYMBOLS.free(p); 36 | } 37 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/MocksBase.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef MOCKSBASE_H_ 20 | #define MOCKSBASE_H_ 21 | 22 | #include 23 | 24 | // ***************************************************************************** 25 | // Mock base 26 | 27 | class Mock 28 | { 29 | public: 30 | virtual ~Mock(){} 31 | 32 | static void* operator new(std::size_t s); 33 | static void operator delete(void* p); 34 | }; 35 | 36 | #endif /*MOCKSBASE_H_*/ 37 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/TestLogClientEnv.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include "CppAssertHelper.h" 21 | 22 | #include 23 | #include 24 | 25 | class Zookeeper_logClientEnv : public CPPUNIT_NS::TestFixture { 26 | CPPUNIT_TEST_SUITE(Zookeeper_logClientEnv); 27 | CPPUNIT_TEST(testLogClientEnv); 28 | CPPUNIT_TEST_SUITE_END(); 29 | 30 | static void log_no_clientenv(const char *message) { 31 | CPPUNIT_ASSERT(::strstr(message, "Client environment") == NULL); 32 | } 33 | 34 | static void log_clientenv(const char *message) { 35 | static int first; 36 | 37 | if (!first) { 38 | CPPUNIT_ASSERT(::strstr(message, "Client environment") != NULL); 39 | first = 1; 40 | } 41 | } 42 | 43 | public: 44 | 45 | void testLogClientEnv() { 46 | zhandle_t* zh; 47 | 48 | zh = zookeeper_init2("localhost:22181", NULL, 0, NULL, NULL, 0, log_clientenv); 49 | CPPUNIT_ASSERT(zh != 0); 50 | zookeeper_close(zh); 51 | 52 | zh = zookeeper_init2("localhost:22181", NULL, 0, NULL, NULL, ZOO_NO_LOG_CLIENTENV, log_no_clientenv); 53 | CPPUNIT_ASSERT(zh != 0); 54 | zookeeper_close(zh); 55 | } 56 | }; 57 | 58 | CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_logClientEnv); 59 | 60 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/ThreadingUtil.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include "ThreadingUtil.h" 21 | #include "LibCSymTable.h" 22 | 23 | #ifdef THREADED 24 | 25 | // **************************************************************************** 26 | // Mutex wrapper 27 | struct Mutex::Impl{ 28 | Impl(){ 29 | LIBC_SYMBOLS.pthread_mutex_init(&mut_, 0); 30 | } 31 | ~Impl(){ 32 | LIBC_SYMBOLS.pthread_mutex_destroy(&mut_); 33 | } 34 | pthread_mutex_t mut_; 35 | }; 36 | 37 | Mutex::Mutex():impl_(new Impl) {} 38 | Mutex::~Mutex() { delete impl_;} 39 | void Mutex::acquire() { 40 | LIBC_SYMBOLS.pthread_mutex_lock(&impl_->mut_); 41 | } 42 | void Mutex::release() { 43 | LIBC_SYMBOLS.pthread_mutex_unlock(&impl_->mut_); 44 | } 45 | 46 | // **************************************************************************** 47 | // Atomics 48 | int32_t atomic_post_incr(volatile int32_t* operand, int32_t incr) 49 | { 50 | #if defined(__GNUC__) 51 | return __sync_fetch_and_add(operand,incr); 52 | #else 53 | int32_t result; 54 | __asm__ __volatile__( 55 | "lock xaddl %0,%1\n" 56 | : "=r"(result), "=m"(*operand) 57 | : "0"(incr) 58 | : "memory"); 59 | return result; 60 | #endif 61 | } 62 | int32_t atomic_fetch_store(volatile int32_t *ptr, int32_t value) 63 | { 64 | #if defined(__GNUC__) 65 | return __sync_lock_test_and_set(ptr,value); 66 | #else 67 | int32_t result; 68 | __asm__ __volatile__("lock xchgl %0,%1\n" 69 | : "=r"(result), "=m"(*ptr) 70 | : "0"(value) 71 | : "memory"); 72 | return result; 73 | #endif 74 | } 75 | #else 76 | int32_t atomic_post_incr(volatile int32_t* operand, int32_t incr){ 77 | int32_t v=*operand; 78 | *operand+=incr; 79 | return v; 80 | } 81 | int32_t atomic_fetch_store(volatile int32_t *ptr, int32_t value) 82 | { 83 | int32_t result=*ptr; 84 | *ptr=value; 85 | return result; 86 | } 87 | #endif // THREADED 88 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/Util.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "Util.h" 20 | #include "string.h" 21 | 22 | const std::string EMPTY_STRING; 23 | 24 | TestConfig globalTestConfig; 25 | 26 | void millisleep(int ms){ 27 | timespec ts; 28 | ts.tv_sec=ms/1000; 29 | ts.tv_nsec=(ms%1000)*1000000; // to nanoseconds 30 | nanosleep(&ts,0); 31 | } 32 | 33 | FILE *openlogfile(const char* testname) { 34 | char name[1024]; 35 | strcpy(name, "TEST-"); 36 | strncpy(name + 5, testname, sizeof(name) - 5); 37 | #ifdef THREADED 38 | strcpy(name + strlen(name), "-mt.txt"); 39 | #else 40 | strcpy(name + strlen(name), "-st.txt"); 41 | #endif 42 | 43 | FILE *logfile = fopen(name, "a"); 44 | 45 | if (logfile == 0) { 46 | fprintf(stderr, "Can't open log file %s!\n", name); 47 | return 0; 48 | } 49 | 50 | return logfile; 51 | } 52 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/Vector.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | #ifndef _VECTOR_UTIL_H 19 | #define _VECTOR_UTIL_H 20 | 21 | #include 22 | 23 | // function to conveniently stream vectors 24 | template 25 | std::ostream& operator<<(std::ostream& os,const std::vector& c){ 26 | typedef std::vector V; 27 | os<<"["; 28 | if(c.size()>0){ 29 | for(typename V::const_iterator it=c.begin();it!=c.end();++it) 30 | os<<*it<<","; 31 | os.seekp(-1,std::ios::cur); 32 | } 33 | os<<"]"; 34 | return os; 35 | } 36 | 37 | #endif // _VECTOR_UTIL_H 38 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/ZooKeeperQuorumServer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with this 4 | * work for additional information regarding copyright ownership. The ASF 5 | * licenses this file to you under the Apache License, Version 2.0 (the 6 | * "License"); you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | #ifndef ZOOKEEPER_QUORUM_SERVER_H 18 | #define ZOOKEEPER_QUORUM_SERVER_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | class ZooKeeperQuorumServer { 26 | public: 27 | ~ZooKeeperQuorumServer(); 28 | typedef std::vector > tConfigPairs; 29 | static std::vector getCluster(uint32_t numServers); 30 | static std::vector getCluster(uint32_t numServers, 31 | tConfigPairs configs, /* Additional config options as a list of key/value pairs. */ 32 | std::string env /* Additional environment variables when starting zkServer.sh. */); 33 | std::string getHostPort(); 34 | uint32_t getClientPort(); 35 | void start(); 36 | void stop(); 37 | bool isLeader(); 38 | bool isFollower(); 39 | std::string getServerString(); 40 | 41 | private: 42 | ZooKeeperQuorumServer(); 43 | ZooKeeperQuorumServer(uint32_t id, uint32_t numServers, std::string config = "", 44 | std::string env = ""); 45 | ZooKeeperQuorumServer(const ZooKeeperQuorumServer& that); 46 | const ZooKeeperQuorumServer& operator=(const ZooKeeperQuorumServer& that); 47 | void createConfigFile(std::string config = ""); 48 | std::string getConfigFileName(); 49 | void createDataDirectory(); 50 | std::string getDataDirectory(); 51 | static std::string getServerString(uint32_t id); 52 | std::string getMode(); 53 | 54 | static const uint32_t SERVER_PORT_BASE = 2000; 55 | static const uint32_t ELECTION_PORT_BASE = 3000; 56 | static const uint32_t CLIENT_PORT_BASE = 4000; 57 | 58 | uint32_t id_; 59 | std::string env_; 60 | uint32_t numServers_; 61 | std::string root_; 62 | }; 63 | 64 | #endif // ZOOKEEPER_QUORUM_SERVER_H 65 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/wrappers-mt.opt: -------------------------------------------------------------------------------- 1 | -Wl,--wrap -Wl,pthread_mutex_lock 2 | -Wl,--wrap -Wl,pthread_mutex_trylock 3 | -Wl,--wrap -Wl,pthread_mutex_unlock 4 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/wrappers.opt: -------------------------------------------------------------------------------- 1 | -Wl,--wrap -Wl,calloc 2 | -Wl,--wrap -Wl,free 3 | -Wl,--wrap -Wl,flush_send_queue 4 | -Wl,--wrap -Wl,get_xid 5 | -Wl,--wrap -Wl,deliverWatchers 6 | -Wl,--wrap -Wl,activateWatcher 7 | -Wl,--wrap -Wl,realloc 8 | -------------------------------------------------------------------------------- /deps/zookeeper-client-c/tests/zoo.cfg: -------------------------------------------------------------------------------- 1 | tickTime=500 2 | initLimit=10 3 | syncLimit=5 4 | dataDir=TMPDIR/zkdata 5 | maxClientCnxns=MAXCLIENTCONNECTIONS 6 | 7 | clientPort=CLIENTPORT 8 | secureClientPort=22281 9 | serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory 10 | ssl.keyStore.location=CERTDIR/server.jks 11 | ssl.keyStore.password=password 12 | ssl.trustStore.location=CERTDIR/servertrust.jks 13 | ssl.trustStore.password=password 14 | 15 | -------------------------------------------------------------------------------- /slib/common/compress.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | 7 | "github.com/golang/snappy" 8 | ) 9 | 10 | func CompressData(uncompressed []byte) []byte { 11 | return snappy.Encode(nil, uncompressed) 12 | } 13 | 14 | func DecompressReader(compressed []byte) (io.Reader, error) { 15 | uncompressed, err := snappy.Decode(nil, compressed) 16 | if err != nil { 17 | return nil, err 18 | } 19 | return bytes.NewReader(uncompressed), nil 20 | } 21 | -------------------------------------------------------------------------------- /slib/common/constants.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const LogTagReserveBits = 3 4 | 5 | const TxnMetaLogTag = 1 6 | const ObjectLogTagLowBits = 2 7 | const TxnHistoryLogTagLowBits = 3 8 | const QueueLogTagLowBits = 4 9 | const QueuePushLogTagLowBits = 5 10 | -------------------------------------------------------------------------------- /slib/common/hash.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "hash/fnv" 5 | ) 6 | 7 | func NameHash(name string) uint64 { 8 | h := fnv.New64a() 9 | h.Write([]byte(name)) 10 | return h.Sum64() 11 | } 12 | -------------------------------------------------------------------------------- /slib/go.mod: -------------------------------------------------------------------------------- 1 | module cs.utexas.edu/zjia/faas/slib 2 | 3 | go 1.14 4 | 5 | require ( 6 | cs.utexas.edu/zjia/faas v0.0.0 7 | github.com/Jeffail/gabs/v2 v2.6.0 8 | github.com/go-redis/redis/v8 v8.8.2 // indirect 9 | github.com/golang/snappy v0.0.2 10 | ) 11 | 12 | replace cs.utexas.edu/zjia/faas => ../worker/golang 13 | -------------------------------------------------------------------------------- /slib/lib.go: -------------------------------------------------------------------------------- 1 | package slib 2 | -------------------------------------------------------------------------------- /slib/statestore/env.go: -------------------------------------------------------------------------------- 1 | package statestore 2 | 3 | import ( 4 | "context" 5 | 6 | "cs.utexas.edu/zjia/faas/types" 7 | ) 8 | 9 | type Env interface { 10 | Object(name string) *ObjectRef 11 | TxnCommit() (bool /* committed */, error) 12 | TxnAbort() error 13 | } 14 | 15 | type envImpl struct { 16 | faasCtx context.Context 17 | faasEnv types.Environment 18 | objs map[string]*ObjectRef 19 | txnCtx *txnContext 20 | } 21 | 22 | func CreateEnv(ctx context.Context, faasEnv types.Environment) Env { 23 | return &envImpl{ 24 | faasCtx: ctx, 25 | faasEnv: faasEnv, 26 | objs: make(map[string]*ObjectRef), 27 | txnCtx: nil, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /slib/statestore/error.go: -------------------------------------------------------------------------------- 1 | package statestore 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | const ( 9 | ERROR_Runtime = iota 10 | ERROR_BadArguments 11 | ERROR_PathNotExist 12 | ERROR_PathConflict 13 | ERROR_WrongDataType 14 | ERROR_GabsError 15 | ) 16 | 17 | type Error struct { 18 | code int 19 | message string 20 | } 21 | 22 | func (e *Error) GetCode() int { 23 | return e.code 24 | } 25 | 26 | func (e *Error) Error() string { 27 | switch e.code { 28 | case ERROR_Runtime: 29 | return fmt.Sprintf("RuntimeError: %s", e.message) 30 | case ERROR_BadArguments: 31 | return fmt.Sprintf("BadArguments: %s", e.message) 32 | case ERROR_PathNotExist: 33 | return fmt.Sprintf("Path %s not exists", e.message) 34 | case ERROR_PathConflict: 35 | return fmt.Sprintf("Path %s conflicts", e.message) 36 | case ERROR_GabsError: 37 | return fmt.Sprintf("GabsError: %s", e.message) 38 | default: 39 | panic("Unknown error type") 40 | } 41 | } 42 | 43 | func newRuntimeError(message string) error { 44 | log.Fatalf("[FATAL] RuntimeError: %s", message) 45 | return &Error{code: ERROR_Runtime, message: message} 46 | } 47 | 48 | func newBadArgumentsError(message string) error { 49 | return &Error{code: ERROR_BadArguments, message: message} 50 | } 51 | 52 | func newPathNotExistError(path string) error { 53 | return &Error{code: ERROR_PathNotExist, message: path} 54 | } 55 | 56 | func newPathConflictError(path string) error { 57 | return &Error{code: ERROR_PathConflict, message: path} 58 | } 59 | 60 | func newGabsError(gabsError error) error { 61 | return &Error{code: ERROR_GabsError, message: gabsError.Error()} 62 | } 63 | -------------------------------------------------------------------------------- /slib/statestore/object.go: -------------------------------------------------------------------------------- 1 | package statestore 2 | 3 | import ( 4 | "cs.utexas.edu/zjia/faas/slib/common" 5 | 6 | "cs.utexas.edu/zjia/faas/protocol" 7 | 8 | gabs "github.com/Jeffail/gabs/v2" 9 | ) 10 | 11 | type ObjectView struct { 12 | name string 13 | nextSeqNum uint64 14 | contents *gabs.Container 15 | } 16 | 17 | type ObjectRef struct { 18 | env *envImpl 19 | name string 20 | nameHash uint64 21 | view *ObjectView 22 | multiCtx *multiContext 23 | txnCtx *txnContext 24 | } 25 | 26 | func (env *envImpl) Object(name string) *ObjectRef { 27 | if env.txnCtx != nil && !env.txnCtx.active { 28 | panic("Cannot create object within inactive transaction!") 29 | } 30 | if obj, exists := env.objs[name]; exists { 31 | return obj 32 | } else { 33 | obj := &ObjectRef{ 34 | env: env, 35 | name: name, 36 | nameHash: common.NameHash(name), 37 | view: nil, 38 | multiCtx: nil, 39 | txnCtx: env.txnCtx, 40 | } 41 | env.objs[name] = obj 42 | return obj 43 | } 44 | } 45 | 46 | func (objView *ObjectView) Clone() *ObjectView { 47 | return &ObjectView{ 48 | name: objView.name, 49 | nextSeqNum: objView.nextSeqNum, 50 | contents: gabs.Wrap(common.DeepCopy(objView.contents.Data())), 51 | } 52 | } 53 | 54 | func (obj *ObjectRef) ensureView() error { 55 | if obj.view == nil { 56 | tailSeqNum := protocol.MaxLogSeqnum 57 | if obj.txnCtx != nil { 58 | tailSeqNum = obj.txnCtx.id 59 | } 60 | return obj.syncTo(tailSeqNum) 61 | } else { 62 | return nil 63 | } 64 | } 65 | 66 | func (obj *ObjectRef) Sync() error { 67 | if obj.txnCtx != nil { 68 | panic("Cannot Sync() objects within a transaction context") 69 | } 70 | return obj.syncTo(protocol.MaxLogSeqnum) 71 | } 72 | 73 | func (obj *ObjectRef) Get(path string) (Value, error) { 74 | if obj.multiCtx != nil { 75 | panic("Cannot call Get within multi context") 76 | } 77 | if obj.txnCtx != nil && !obj.txnCtx.active { 78 | panic("Cannot call Get within inactive transaction!") 79 | } 80 | if err := obj.ensureView(); err != nil { 81 | return NullValue(), err 82 | } 83 | resolved := obj.view.contents.Path(path) 84 | if resolved == nil { 85 | return NullValue(), newPathNotExistError(path) 86 | } 87 | return valueFromInterface(resolved.Data()), nil 88 | } 89 | -------------------------------------------------------------------------------- /slib/sync/sharded_queue.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "time" 9 | 10 | "cs.utexas.edu/zjia/faas/types" 11 | ) 12 | 13 | type ShardedQueue struct { 14 | subQueues []*Queue 15 | 16 | shards []int 17 | nextPush int 18 | nextPop int 19 | } 20 | 21 | func NewShardedQueue(ctx context.Context, env types.Environment, name string, shards int) (*ShardedQueue, error) { 22 | if shards <= 0 { 23 | log.Fatalf("[FATAL] Shards must be positive!") 24 | } 25 | queues := make([]*Queue, 0, 16) 26 | for i := 0; i < shards; i++ { 27 | q, err := NewQueue(ctx, env, fmt.Sprintf("%s-%d", name, i)) 28 | if err != nil { 29 | return nil, err 30 | } 31 | queues = append(queues, q) 32 | } 33 | shardArr := make([]int, shards) 34 | for i := 0; i < shards; i++ { 35 | shardArr[i] = i 36 | } 37 | rand.Seed(time.Now().UnixNano()) 38 | rand.Shuffle(shards, func(i, j int) { 39 | shardArr[i], shardArr[j] = shardArr[j], shardArr[i] 40 | }) 41 | return &ShardedQueue{ 42 | subQueues: queues, 43 | shards: shardArr, 44 | nextPush: rand.Intn(shards), 45 | nextPop: rand.Intn(shards), 46 | }, nil 47 | } 48 | 49 | func (q *ShardedQueue) Push(payload string) error { 50 | shard := q.shards[q.nextPush] 51 | q.nextPush = (q.nextPush + 1) % len(q.shards) 52 | queue := q.subQueues[shard] 53 | return queue.Push(payload) 54 | } 55 | 56 | func (q *ShardedQueue) Pop() (string /* payload */, error) { 57 | shard := q.shards[q.nextPop] 58 | q.nextPop = (q.nextPop + 1) % len(q.shards) 59 | queue := q.subQueues[shard] 60 | return queue.Pop() 61 | } 62 | 63 | func (q *ShardedQueue) PopBlocking() (string /* payload */, error) { 64 | shard := q.shards[q.nextPop] 65 | q.nextPop = (q.nextPop + 1) % len(q.shards) 66 | queue := q.subQueues[shard] 67 | return queue.PopBlocking() 68 | } 69 | 70 | func (q *ShardedQueue) PopFromShard(shard int) (string /* payload */, error) { 71 | if 0 <= shard && shard < len(q.subQueues) { 72 | return q.subQueues[shard].Pop() 73 | } else { 74 | return "", fmt.Errorf("Invalid shard number: %d", shard) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/base/asm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace faas { 4 | 5 | inline void asm_volatile_memory() { 6 | asm volatile("" : : : "memory"); 7 | } 8 | 9 | inline void asm_volatile_pause() { 10 | asm volatile("pause"); 11 | } 12 | 13 | } // namespace faas 14 | -------------------------------------------------------------------------------- /src/base/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __linux__ 4 | #error We only support Linux 5 | #endif 6 | 7 | #ifndef _GNU_SOURCE 8 | #define _GNU_SOURCE 9 | #endif 10 | 11 | #ifdef __FAAS_CPP_WORKER 12 | #if !defined(__FAAS_USED_IN_BINDING) && !defined(__FAAS_CPP_WORKER_SRC) 13 | #error Need the source file to have __FAAS_USED_IN_BINDING defined 14 | #endif 15 | #endif 16 | 17 | #ifdef __FAAS_NODE_ADDON 18 | #if !defined(__FAAS_USED_IN_BINDING) && !defined(__FAAS_NODE_ADDON_SRC) 19 | #error Need the source file to have __FAAS_USED_IN_BINDING defined 20 | #endif 21 | #define __FAAS_CXX_NO_EXCEPTIONS 22 | #endif 23 | 24 | #ifdef __FAAS_PYTHON_BINDING 25 | #if !defined(__FAAS_USED_IN_BINDING) && !defined(__FAAS_PYTHON_BINDING_SRC) 26 | #error Need the source file to have __FAAS_USED_IN_BINDING defined 27 | #endif 28 | #include 29 | #if PY_VERSION_HEX < 0x03070000 30 | #error FaaS Python binding requires Python 3.7+ 31 | #endif 32 | #endif 33 | 34 | // C includes 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | // C++ includes 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | // STL containers 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #ifdef __FAAS_CPP_WORKER 62 | #include 63 | #endif 64 | 65 | // fmtlib 66 | #define FMT_HEADER_ONLY 67 | #include 68 | #include 69 | 70 | // Guidelines Support Library (GSL) 71 | #include 72 | 73 | #include "base/diagnostic.h" 74 | 75 | #ifdef __FAAS_SRC 76 | #define __FAAS_HAVE_ABSL 77 | #endif 78 | 79 | #if defined(__FAAS_HAVE_ABSL) && !defined(__FAAS_USED_IN_BINDING) 80 | 81 | // Will not include common absl headers in source files 82 | // with __FAAS_USED_IN_BINDING defined 83 | 84 | __BEGIN_THIRD_PARTY_HEADERS 85 | 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | 107 | __END_THIRD_PARTY_HEADERS 108 | 109 | #endif // defined(__FAAS_HAVE_ABSL) && !defined(__FAAS_USED_IN_BINDING) 110 | 111 | #include "base/macro.h" 112 | #include "base/logging.h" 113 | #include "base/std_span.h" 114 | -------------------------------------------------------------------------------- /src/base/diagnostic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __FAAS_SRC 4 | 5 | #ifdef __clang__ 6 | 7 | #pragma clang diagnostic error "-Wimplicit-fallthrough" 8 | 9 | #if !defined(__FAAS_NOWARN_CONVERSION) 10 | #define __CLANG_CONVERSION_DIAGNOSTIC_ENABLED 11 | #pragma clang diagnostic error "-Wconversion" 12 | #pragma clang diagnostic ignored "-Wimplicit-float-conversion" 13 | #endif // !defined(__FAAS_NOWARN_CONVERSION) 14 | 15 | #ifdef __FAAS_NOWARN_SIGN_CONVERSION 16 | #pragma clang diagnostic ignored "-Wsign-conversion" 17 | #endif // __FAAS_NOWARN_SIGN_CONVERSION 18 | 19 | #define __BEGIN_THIRD_PARTY_HEADERS \ 20 | _Pragma("clang diagnostic push") \ 21 | _Pragma("clang diagnostic ignored \"-Wimplicit-fallthrough\"") \ 22 | _Pragma("clang diagnostic ignored \"-Wconversion\"") 23 | 24 | #define __END_THIRD_PARTY_HEADERS \ 25 | _Pragma("clang diagnostic pop") 26 | 27 | #endif // __clang__ 28 | 29 | #endif // __FAAS_SRC 30 | 31 | #ifndef __BEGIN_THIRD_PARTY_HEADERS 32 | #define __BEGIN_THIRD_PARTY_HEADERS 33 | #endif 34 | 35 | #ifndef __END_THIRD_PARTY_HEADERS 36 | #define __END_THIRD_PARTY_HEADERS 37 | #endif 38 | -------------------------------------------------------------------------------- /src/base/init.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error base/init.h cannot be included outside 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | namespace faas { 11 | namespace base { 12 | 13 | void InitMain(int argc, char* argv[], 14 | std::vector* positional_args = nullptr); 15 | 16 | // When SIGABRT, SIGTERM signals are received, clean-up functions will 17 | // be invoked before exit. 18 | void ChainCleanupFn(std::function fn); 19 | 20 | // When SIGINT signal is received, this function will be invoked. 21 | void SetInterruptHandler(std::function fn); 22 | 23 | } // namespace base 24 | } // namespace faas 25 | -------------------------------------------------------------------------------- /src/base/macro.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // for PIPE_BUF 4 | 5 | // Put this in the declarations for a class to be uncopyable. 6 | #define DISALLOW_COPY(TypeName) \ 7 | TypeName(const TypeName&) = delete 8 | 9 | // Put this in the declarations for a class to be unassignable. 10 | #define DISALLOW_ASSIGN(TypeName) \ 11 | TypeName& operator=(const TypeName&) = delete 12 | 13 | // Put this in the declarations for a class to be uncopyable and unassignable. 14 | #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ 15 | DISALLOW_COPY(TypeName); \ 16 | DISALLOW_ASSIGN(TypeName) 17 | 18 | // A macro to disallow all the implicit constructors, namely the 19 | // default constructor, copy constructor and operator= functions. 20 | // This is especially useful for classes containing only static methods. 21 | #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ 22 | TypeName() = delete; \ 23 | DISALLOW_COPY_AND_ASSIGN(TypeName) 24 | 25 | #define __FAAS_PREDICT_FALSE(x) __builtin_expect(x, 0) 26 | #define __FAAS_PREDICT_TRUE(x) __builtin_expect(false || (x), true) 27 | 28 | // We're always on x86_64 29 | #define __FAAS_CACHE_LINE_SIZE 64 30 | #define __FAAS_PAGE_SIZE 4096 31 | #define __FAAS_PTR_SIZE 8 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 2560 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 | #define NOT_IMPLEMENTED() LOG(FATAL) << "Not implemented" 50 | #define UNREACHABLE() LOG(FATAL) << "Unreachable" 51 | -------------------------------------------------------------------------------- /src/base/std_span.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace std { 7 | using gsl::span; 8 | } // namespace std 9 | 10 | #define EMPTY_CHAR_SPAN std::span() 11 | 12 | #define STRING_AS_SPAN(STR_VAR) \ 13 | std::span((STR_VAR).data(), (STR_VAR).length()) 14 | 15 | #define VECTOR_AS_SPAN(VEC_VAR) \ 16 | std::span( \ 17 | (VEC_VAR).data(), (VEC_VAR).size()) 18 | 19 | #define VECTOR_AS_CHAR_SPAN(VEC_VAR) \ 20 | std::span( \ 21 | reinterpret_cast((VEC_VAR).data()), \ 22 | sizeof(decltype(VEC_VAR)::value_type) * (VEC_VAR).size()) 23 | -------------------------------------------------------------------------------- /src/base/thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error base/thread.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | #include 9 | 10 | namespace faas { 11 | namespace base { 12 | 13 | class Thread { 14 | public: 15 | static constexpr const char* kMainThreadName = "Main"; 16 | 17 | Thread(std::string_view name, std::function fn); 18 | ~Thread(); 19 | 20 | void Start(); 21 | void Join(); 22 | 23 | void MarkThreadCategory(std::string_view category); 24 | 25 | const char* name() const { return name_.c_str(); } 26 | int tid() const { return tid_; } 27 | 28 | static Thread* current() { return DCHECK_NOTNULL(current_); } 29 | 30 | static void RegisterMainThread(); 31 | 32 | private: 33 | enum State { kCreated, kStarting, kRunning, kFinished }; 34 | 35 | std::atomic state_; 36 | std::string name_; 37 | std::function fn_; 38 | int tid_; 39 | 40 | absl::Notification started_; 41 | pthread_t pthread_; 42 | 43 | static thread_local Thread* current_; 44 | 45 | void Run(); 46 | static void* StartRoutine(void* arg); 47 | 48 | DISALLOW_COPY_AND_ASSIGN(Thread); 49 | }; 50 | 51 | } // namespace base 52 | } // namespace faas 53 | -------------------------------------------------------------------------------- /src/bin/bench_ipc_shm.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "common/time.h" 4 | #include "utils/fs.h" 5 | #include "utils/bench.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | ABSL_FLAG(int, cpu, 0, "Pin benchmark thread to this CPU"); 12 | ABSL_FLAG(size_t, shm_size, 4096, "Shared memory size"); 13 | ABSL_FLAG(absl::Duration, duration, absl::Seconds(30), "Duration to run"); 14 | 15 | using namespace faas; 16 | 17 | static constexpr size_t kBufferSizeForSamples = 1<<24; 18 | 19 | int main(int argc, char* argv[]) { 20 | base::InitMain(argc, argv); 21 | 22 | bench_utils::Samples shmget_delay(kBufferSizeForSamples); 23 | bench_utils::Samples shmat_delay(kBufferSizeForSamples); 24 | bench_utils::Samples shmdt_delay(kBufferSizeForSamples); 25 | bench_utils::Samples shm_remove_delay(kBufferSizeForSamples); 26 | 27 | size_t shm_size = absl::GetFlag(FLAGS_shm_size); 28 | int cpu = absl::GetFlag(FLAGS_cpu); 29 | if (cpu != -1) { 30 | bench_utils::PinCurrentThreadToCpu(cpu); 31 | } 32 | auto perf_event_group = bench_utils::SetupCpuRelatedPerfEvents(cpu); 33 | perf_event_group->ResetAndEnable(); 34 | 35 | bench_utils::BenchLoop bench_loop(absl::GetFlag(FLAGS_duration), [&] () -> bool { 36 | int64_t start_timestamp; 37 | // shmget 38 | start_timestamp = GetMonotonicNanoTimestamp(); 39 | int shmid = shmget(IPC_PRIVATE, shm_size, IPC_CREAT | 0600); 40 | shmget_delay.Add( 41 | gsl::narrow_cast(GetMonotonicNanoTimestamp() - start_timestamp)); 42 | PCHECK(shmid != -1); 43 | // shmat 44 | start_timestamp = GetMonotonicNanoTimestamp(); 45 | void* ptr = shmat(shmid, nullptr, 0); 46 | shmat_delay.Add( 47 | gsl::narrow_cast(GetMonotonicNanoTimestamp() - start_timestamp)); 48 | PCHECK(ptr != (void*)-1); 49 | // Write data 50 | memset(ptr, 0, shm_size); 51 | // shmdt 52 | start_timestamp = GetMonotonicNanoTimestamp(); 53 | PCHECK(shmdt(ptr) == 0); 54 | shmdt_delay.Add( 55 | gsl::narrow_cast(GetMonotonicNanoTimestamp() - start_timestamp)); 56 | // shmctl remove 57 | start_timestamp = GetMonotonicNanoTimestamp(); 58 | PCHECK(shmctl(shmid, IPC_RMID, nullptr) == 0); 59 | shm_remove_delay.Add( 60 | gsl::narrow_cast(GetMonotonicNanoTimestamp() - start_timestamp)); 61 | return true; 62 | }); 63 | 64 | perf_event_group->Disable(); 65 | 66 | LOG(INFO) << "Elapsed milliseconds: " 67 | << absl::ToInt64Milliseconds(bench_loop.elapsed_time()); 68 | LOG(INFO) << "Loop rate: " 69 | << bench_loop.loop_count() / absl::ToDoubleMilliseconds(bench_loop.elapsed_time()) 70 | << " loops per millisecond"; 71 | shmget_delay.ReportStatistics("shmget delay"); 72 | shmat_delay.ReportStatistics("shmat delay"); 73 | shmdt_delay.ReportStatistics("shmdt delay"); 74 | shm_remove_delay.ReportStatistics("shm_remove delay"); 75 | bench_utils::ReportCpuRelatedPerfEventValues("Shm", perf_event_group.get(), 76 | bench_loop.elapsed_time(), 77 | bench_loop.loop_count()); 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /src/bin/controller.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "common/flags.h" 4 | #include "log/controller.h" 5 | 6 | #include 7 | 8 | ABSL_FLAG(uint32_t, random_seed, 23333, "Random seed"); 9 | ABSL_FLAG(size_t, metalog_replicas, 3, "Replicas for meta logs"); 10 | ABSL_FLAG(size_t, userlog_replicas, 3, "Replicas for users' logs"); 11 | ABSL_FLAG(size_t, index_replicas, 3, "Replicas for log index"); 12 | ABSL_FLAG(size_t, num_phylogs, 1, "Number of physical logs"); 13 | 14 | namespace faas { 15 | 16 | static std::atomic controller_ptr{nullptr}; 17 | static void StopControllerHandler() { 18 | log::Controller* controller = controller_ptr.exchange(nullptr); 19 | if (controller != nullptr) { 20 | controller->ScheduleStop(); 21 | } 22 | } 23 | 24 | void ControllerMain(int argc, char* argv[]) { 25 | base::InitMain(argc, argv); 26 | base::SetInterruptHandler(StopControllerHandler); 27 | 28 | auto controller = std::make_unique( 29 | absl::GetFlag(FLAGS_random_seed)); 30 | 31 | controller->set_metalog_replicas(absl::GetFlag(FLAGS_metalog_replicas)); 32 | controller->set_userlog_replicas(absl::GetFlag(FLAGS_userlog_replicas)); 33 | controller->set_index_replicas(absl::GetFlag(FLAGS_index_replicas)); 34 | controller->set_num_phylogs(absl::GetFlag(FLAGS_num_phylogs)); 35 | 36 | controller->Start(); 37 | controller_ptr.store(controller.get()); 38 | controller->WaitForFinish(); 39 | } 40 | 41 | } // namespace faas 42 | 43 | int main(int argc, char* argv[]) { 44 | faas::ControllerMain(argc, argv); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /src/bin/engine.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/procfs.h" 7 | #include "utils/env_variables.h" 8 | #include "engine/engine.h" 9 | 10 | ABSL_FLAG(int, engine_tcp_port, -1, 11 | "If set, Launcher and FuncWorker will communicate with engine via localhost TCP socket"); 12 | ABSL_FLAG(int, node_id, -1, 13 | "My node ID. If -1 is set, node ID will be automatically generated based on " 14 | "/proc/sys/kernel/hostname"); 15 | ABSL_FLAG(std::string, root_path_for_ipc, "/dev/shm/faas_ipc", 16 | "Root directory for IPCs used by FaaS"); 17 | ABSL_FLAG(std::string, func_config_file, "", "Path to function config file"); 18 | ABSL_FLAG(bool, enable_shared_log, false, "If to enable shared log."); 19 | 20 | namespace faas { 21 | 22 | static std::atomic server_ptr{nullptr}; 23 | static void StopServerHandler() { 24 | server::ServerBase* server = server_ptr.exchange(nullptr); 25 | if (server != nullptr) { 26 | server->ScheduleStop(); 27 | } 28 | } 29 | 30 | static uint16_t GenerateNodeId() { 31 | std::string hostname = procfs_utils::ReadHostname(); 32 | int result = 0; 33 | for (const char ch : hostname) { 34 | // Let overflow happens freely here 35 | result = result * 177 + ch; 36 | } 37 | return gsl::narrow_cast(result); 38 | } 39 | 40 | void EngineMain(int argc, char* argv[]) { 41 | base::InitMain(argc, argv); 42 | base::SetInterruptHandler(StopServerHandler); 43 | ipc::SetRootPathForIpc(absl::GetFlag(FLAGS_root_path_for_ipc), /* create= */ true); 44 | 45 | std::string cgroup_fs_root(utils::GetEnvVariable("FAAS_CGROUP_FS_ROOT", "")); 46 | if (cgroup_fs_root.length() > 0) { 47 | docker_utils::SetCgroupFsRoot(cgroup_fs_root); 48 | } 49 | 50 | int node_id = absl::GetFlag(FLAGS_node_id); 51 | if (node_id == -1) { 52 | node_id = utils::GetEnvVariableAsInt("FAAS_NODE_ID", -1); 53 | } 54 | if (node_id == -1) { 55 | node_id = GenerateNodeId(); 56 | } 57 | auto engine = std::make_unique(node_id); 58 | engine->set_engine_tcp_port(absl::GetFlag(FLAGS_engine_tcp_port)); 59 | engine->set_func_config_file(absl::GetFlag(FLAGS_func_config_file)); 60 | 61 | if (absl::GetFlag(FLAGS_enable_shared_log)) { 62 | engine->enable_shared_log(); 63 | } 64 | 65 | engine->Start(); 66 | server_ptr.store(engine.get()); 67 | engine->WaitForFinish(); 68 | } 69 | 70 | } // namespace faas 71 | 72 | int main(int argc, char* argv[]) { 73 | faas::EngineMain(argc, argv); 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /src/bin/gateway.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "gateway/server.h" 4 | 5 | ABSL_FLAG(int, http_port, 8080, "Port for HTTP connections"); 6 | ABSL_FLAG(int, grpc_port, 50051, "Port for gRPC connections"); 7 | ABSL_FLAG(std::string, func_config_file, "", "Path to function config file"); 8 | 9 | namespace faas { 10 | 11 | static std::atomic server_ptr{nullptr}; 12 | static void StopServerHandler() { 13 | server::ServerBase* server = server_ptr.exchange(nullptr); 14 | if (server != nullptr) { 15 | server->ScheduleStop(); 16 | } 17 | } 18 | 19 | void GatewayMain(int argc, char* argv[]) { 20 | base::InitMain(argc, argv); 21 | base::SetInterruptHandler(StopServerHandler); 22 | 23 | auto server = std::make_unique(); 24 | server->set_http_port(absl::GetFlag(FLAGS_http_port)); 25 | server->set_grpc_port(absl::GetFlag(FLAGS_grpc_port)); 26 | server->set_func_config_file(absl::GetFlag(FLAGS_func_config_file)); 27 | 28 | server->Start(); 29 | server_ptr.store(server.get()); 30 | server->WaitForFinish(); 31 | } 32 | 33 | } // namespace faas 34 | 35 | int main(int argc, char* argv[]) { 36 | faas::GatewayMain(argc, argv); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /src/bin/launcher.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "ipc/base.h" 4 | #include "launcher/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_working_dir, "", 11 | "Working directory of function processes"); 12 | ABSL_FLAG(std::string, fprocess_output_dir, "", 13 | "If not empty, stdout and stderr of function processes will be saved " 14 | "in the given directory"); 15 | ABSL_FLAG(std::string, fprocess_mode, "cpp", 16 | "Operating mode of fprocess. Valid options are cpp, go, nodejs, and python."); 17 | ABSL_FLAG(int, engine_tcp_port, -1, "If set, will connect to engine via localhost TCP socket"); 18 | 19 | namespace faas { 20 | 21 | static std::atomic launcher_ptr{nullptr}; 22 | static void StopLauncherHandler() { 23 | launcher::Launcher* launcher = launcher_ptr.exchange(nullptr); 24 | if (launcher != nullptr) { 25 | launcher->ScheduleStop(); 26 | } 27 | } 28 | 29 | void LauncherMain(int argc, char* argv[]) { 30 | base::InitMain(argc, argv); 31 | base::SetInterruptHandler(StopLauncherHandler); 32 | ipc::SetRootPathForIpc(absl::GetFlag(FLAGS_root_path_for_ipc)); 33 | 34 | auto launcher = std::make_unique(); 35 | launcher->set_func_id(absl::GetFlag(FLAGS_func_id)); 36 | launcher->set_fprocess(absl::GetFlag(FLAGS_fprocess)); 37 | launcher->set_fprocess_working_dir(absl::GetFlag(FLAGS_fprocess_working_dir)); 38 | launcher->set_fprocess_output_dir(absl::GetFlag(FLAGS_fprocess_output_dir)); 39 | launcher->set_engine_tcp_port(absl::GetFlag(FLAGS_engine_tcp_port)); 40 | 41 | std::string fprocess_mode = absl::GetFlag(FLAGS_fprocess_mode); 42 | if (fprocess_mode == "cpp") { 43 | launcher->set_fprocess_mode(launcher::Launcher::kCppMode); 44 | } else if (fprocess_mode == "go") { 45 | launcher->set_fprocess_mode(launcher::Launcher::kGoMode); 46 | } else if (fprocess_mode == "nodejs") { 47 | launcher->set_fprocess_mode(launcher::Launcher::kNodeJsMode); 48 | } else if (fprocess_mode == "python") { 49 | launcher->set_fprocess_mode(launcher::Launcher::kPythonMode); 50 | } else { 51 | LOG(FATAL) << "Invalid fprocess_mode: " << fprocess_mode; 52 | } 53 | 54 | launcher->Start(); 55 | launcher_ptr.store(launcher.get()); 56 | launcher->WaitForFinish(); 57 | } 58 | 59 | } // namespace faas 60 | 61 | int main(int argc, char* argv[]) { 62 | faas::LauncherMain(argc, argv); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /src/bin/sequencer.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "utils/env_variables.h" 4 | #include "log/sequencer.h" 5 | 6 | ABSL_FLAG(int, node_id, -1, 7 | "My node ID. Also settable through environment variable FAAS_NODE_ID."); 8 | 9 | namespace faas { 10 | 11 | static std::atomic server_ptr{nullptr}; 12 | static void StopServerHandler() { 13 | server::ServerBase* server = server_ptr.exchange(nullptr); 14 | if (server != nullptr) { 15 | server->ScheduleStop(); 16 | } 17 | } 18 | 19 | void SequencerMain(int argc, char* argv[]) { 20 | base::InitMain(argc, argv); 21 | base::SetInterruptHandler(StopServerHandler); 22 | 23 | int node_id = absl::GetFlag(FLAGS_node_id); 24 | if (node_id == -1) { 25 | node_id = utils::GetEnvVariableAsInt("FAAS_NODE_ID", -1); 26 | } 27 | if (node_id == -1) { 28 | LOG(FATAL) << "Node ID not set!"; 29 | } 30 | auto sequencer = std::make_unique(node_id); 31 | 32 | sequencer->Start(); 33 | server_ptr.store(sequencer.get()); 34 | sequencer->WaitForFinish(); 35 | } 36 | 37 | } // namespace faas 38 | 39 | int main(int argc, char* argv[]) { 40 | faas::SequencerMain(argc, argv); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/bin/storage.cpp: -------------------------------------------------------------------------------- 1 | #include "base/init.h" 2 | #include "base/common.h" 3 | #include "utils/env_variables.h" 4 | #include "log/storage.h" 5 | 6 | ABSL_FLAG(int, node_id, -1, 7 | "My node ID. Also settable through environment variable FAAS_NODE_ID."); 8 | ABSL_FLAG(std::string, db_path, "", "Path for RocksDB database storage."); 9 | 10 | namespace faas { 11 | 12 | static std::atomic server_ptr{nullptr}; 13 | static void StopServerHandler() { 14 | server::ServerBase* server = server_ptr.exchange(nullptr); 15 | if (server != nullptr) { 16 | server->ScheduleStop(); 17 | } 18 | } 19 | 20 | void StorageMain(int argc, char* argv[]) { 21 | base::InitMain(argc, argv); 22 | base::SetInterruptHandler(StopServerHandler); 23 | 24 | int node_id = absl::GetFlag(FLAGS_node_id); 25 | if (node_id == -1) { 26 | node_id = utils::GetEnvVariableAsInt("FAAS_NODE_ID", -1); 27 | } 28 | if (node_id == -1) { 29 | LOG(FATAL) << "Node ID not set!"; 30 | } 31 | auto storage = std::make_unique(node_id); 32 | 33 | storage->set_db_path(absl::GetFlag(FLAGS_db_path)); 34 | 35 | storage->Start(); 36 | server_ptr.store(storage.get()); 37 | storage->WaitForFinish(); 38 | } 39 | 40 | } // namespace faas 41 | 42 | int main(int argc, char* argv[]) { 43 | faas::StorageMain(argc, argv); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/common/flags.cpp: -------------------------------------------------------------------------------- 1 | #include "common/flags.h" 2 | 3 | ABSL_FLAG(std::string, listen_addr, "0.0.0.0", 4 | "Address to listen for external TCP connections"); 5 | ABSL_FLAG(std::string, listen_iface, "lo", 6 | "Interface to listen for message connections"); 7 | ABSL_FLAG(int, num_io_workers, 1, "Number of IO workers."); 8 | ABSL_FLAG(int, message_conn_per_worker, 8, 9 | "Number of connections for message passing per IO worker."); 10 | ABSL_FLAG(int, socket_listen_backlog, 64, "Backlog for listen"); 11 | ABSL_FLAG(bool, tcp_enable_reuseport, false, "Enable SO_REUSEPORT"); 12 | ABSL_FLAG(bool, tcp_enable_nodelay, true, "Enable TCP_NODELAY"); 13 | ABSL_FLAG(bool, tcp_enable_keepalive, true, "Enable TCP keep-alive"); 14 | 15 | ABSL_FLAG(std::string, zookeeper_host, "localhost:2181", "ZooKeeper host"); 16 | ABSL_FLAG(std::string, zookeeper_root_path, "/faas", "Root path for all znodes"); 17 | -------------------------------------------------------------------------------- /src/common/flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error common/flags.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | 9 | __BEGIN_THIRD_PARTY_HEADERS 10 | #include 11 | #include 12 | __END_THIRD_PARTY_HEADERS 13 | 14 | ABSL_DECLARE_FLAG(std::string, listen_addr); 15 | ABSL_DECLARE_FLAG(std::string, listen_iface); 16 | ABSL_DECLARE_FLAG(int, num_io_workers); 17 | ABSL_DECLARE_FLAG(int, message_conn_per_worker); 18 | ABSL_DECLARE_FLAG(int, socket_listen_backlog); 19 | ABSL_DECLARE_FLAG(bool, tcp_enable_reuseport); 20 | ABSL_DECLARE_FLAG(bool, tcp_enable_nodelay); 21 | ABSL_DECLARE_FLAG(bool, tcp_enable_keepalive); 22 | 23 | ABSL_DECLARE_FLAG(std::string, zookeeper_host); 24 | ABSL_DECLARE_FLAG(std::string, zookeeper_root_path); 25 | -------------------------------------------------------------------------------- /src/common/func_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | 6 | namespace faas { 7 | 8 | class FuncConfig { 9 | public: 10 | FuncConfig() {} 11 | ~FuncConfig() {} 12 | 13 | static constexpr int kMaxFuncId = (1 << protocol::kFuncIdBits) - 1; 14 | static constexpr int kMaxMethodId = (1 << protocol::kMethodIdBits) - 1; 15 | 16 | struct Entry { 17 | std::string func_name; 18 | int func_id; 19 | int min_workers; 20 | int max_workers; 21 | uint32_t default_logspace; 22 | bool allow_http_get; 23 | bool qs_as_input; 24 | bool is_grpc_service; 25 | std::string grpc_service_name; 26 | std::vector grpc_methods; 27 | std::unordered_map grpc_method_ids; 28 | }; 29 | 30 | bool Load(std::string_view json_contents); 31 | 32 | const Entry* find_by_func_name(std::string_view func_name) const { 33 | if (entires_by_func_name_.count(std::string(func_name)) > 0) { 34 | return entires_by_func_name_.at(std::string(func_name)); 35 | } else { 36 | return nullptr; 37 | } 38 | } 39 | 40 | const Entry* find_by_func_id(int func_id) const { 41 | if (entries_by_func_id_.count(func_id) > 0) { 42 | return entries_by_func_id_.at(func_id); 43 | } else { 44 | return nullptr; 45 | } 46 | } 47 | 48 | private: 49 | std::vector> entries_; 50 | std::unordered_map entires_by_func_name_; 51 | std::unordered_map entries_by_func_id_; 52 | 53 | static bool ValidateFuncId(int func_id); 54 | static bool ValidateFuncName(std::string_view func_name); 55 | 56 | DISALLOW_COPY_AND_ASSIGN(FuncConfig); 57 | }; 58 | 59 | } // namespace faas 60 | -------------------------------------------------------------------------------- /src/common/http_status.cpp: -------------------------------------------------------------------------------- 1 | #include "common/http_status.h" 2 | 3 | namespace faas { 4 | 5 | std::string_view GetHttpStatusString(HttpStatus status) { 6 | switch (status) { 7 | case HttpStatus::OK: 8 | return "200 OK"; 9 | case HttpStatus::BAD_REQUEST: 10 | return "400 Bad Request"; 11 | case HttpStatus::NOT_FOUND: 12 | return "404 Not Found"; 13 | case HttpStatus::INTERNAL_SERVER_ERROR: 14 | return "500 Internal Server Error"; 15 | default: 16 | LOG(FATAL) << "Unknown HTTP status: " << static_cast(status); 17 | } 18 | } 19 | 20 | } // namespace faas 21 | -------------------------------------------------------------------------------- /src/common/http_status.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | 7 | enum class HttpStatus : uint16_t { 8 | OK = 200, 9 | BAD_REQUEST = 400, 10 | NOT_FOUND = 404, 11 | INTERNAL_SERVER_ERROR = 500 12 | }; 13 | 14 | std::string_view GetHttpStatusString(HttpStatus status); 15 | 16 | } // namespace faas 17 | -------------------------------------------------------------------------------- /src/common/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | #include 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 GetRealtimeMicroTimestamp() { 23 | struct timespec tp; 24 | PCHECK(clock_gettime(CLOCK_REALTIME, &tp) == 0) << "clock_gettime failed"; 25 | return TimeSpecToMicro(&tp); 26 | } 27 | 28 | inline int64_t TimeSpecToNano(struct timespec* tp) { 29 | int64_t ret = 0; 30 | ret += int64_t{tp->tv_sec} * 1000000000; 31 | ret += int64_t{tp->tv_nsec}; 32 | return ret; 33 | } 34 | 35 | inline int64_t GetMonotonicNanoTimestamp() { 36 | struct timespec tp; 37 | PCHECK(clock_gettime(CLOCK_MONOTONIC, &tp) == 0) << "clock_gettime failed"; 38 | return TimeSpecToNano(&tp); 39 | } 40 | 41 | inline int64_t GetRealtimeNanoTimestamp() { 42 | struct timespec tp; 43 | PCHECK(clock_gettime(CLOCK_REALTIME, &tp) == 0) << "clock_gettime failed"; 44 | return TimeSpecToNano(&tp); 45 | } 46 | 47 | } // namespace faas 48 | -------------------------------------------------------------------------------- /src/common/uv.cpp: -------------------------------------------------------------------------------- 1 | #include "common/uv.h" 2 | 3 | #include "utils/io.h" 4 | #include "utils/timerfd.h" 5 | #include "utils/random.h" 6 | 7 | namespace faas { 8 | namespace uv { 9 | 10 | void HandleFreeCallback(uv_handle_t* handle) { 11 | free(handle); 12 | } 13 | 14 | HandleScope::HandleScope() 15 | : loop_(nullptr), num_handles_on_closing_(0) {} 16 | 17 | HandleScope::~HandleScope() { 18 | DCHECK(handles_.empty()); 19 | } 20 | 21 | void HandleScope::Init(uv_loop_t* loop, std::function finish_callback) { 22 | DCHECK(loop_ == nullptr); 23 | loop_ = loop; 24 | finish_callback_ = finish_callback; 25 | } 26 | 27 | void HandleScope::AddHandle(uv_handle_t* handle) { 28 | DCHECK(handle->loop == loop_); 29 | handles_.insert(handle); 30 | } 31 | 32 | void HandleScope::CloseHandle(uv_handle_t* handle) { 33 | DCHECK_IN_EVENT_LOOP_THREAD(loop_); 34 | DCHECK(handles_.contains(handle)); 35 | handles_.erase(handle); 36 | handle->data = this; 37 | num_handles_on_closing_++; 38 | uv_close(handle, &HandleScope::HandleCloseCallback); 39 | } 40 | 41 | UV_CLOSE_CB_FOR_CLASS(HandleScope, HandleClose) { 42 | num_handles_on_closing_--; 43 | if (num_handles_on_closing_ == 0 && handles_.empty()) { 44 | finish_callback_(); 45 | } 46 | } 47 | 48 | Timer::Timer() 49 | : timerfd_(-1) {} 50 | 51 | Timer::~Timer() {} 52 | 53 | void Timer::Init(uv_loop_t* loop, std::function callback) { 54 | timerfd_ = io_utils::CreateTimerFd(); 55 | CHECK(timerfd_ != -1); 56 | UV_CHECK_OK(uv_poll_init(loop, &uv_handle_, timerfd_)); 57 | uv_handle_.data = this; 58 | UV_CHECK_OK(uv_poll_start(&uv_handle_, UV_READABLE, &Timer::ExpiredCallback)); 59 | cb_ = callback; 60 | } 61 | 62 | void Timer::Close() { 63 | if (timerfd_ != -1) { 64 | uv_close(UV_AS_HANDLE(&uv_handle_), nullptr); 65 | } 66 | } 67 | 68 | void Timer::ExpireIn(absl::Duration duration) { 69 | CHECK(timerfd_ != -1); 70 | CHECK(io_utils::SetupTimerFdOneTime(timerfd_, duration)); 71 | } 72 | 73 | void Timer::StochasticExpireIn(absl::Duration duration) { 74 | CHECK(timerfd_ != -1); 75 | double x = 1.0 - utils::GetRandomDouble(0.0, 1.0); // x ends in (0, 1] 76 | double timeout_us = absl::ToDoubleMicroseconds(duration) * (-log(x)); 77 | CHECK(io_utils::SetupTimerFdOneTime(timerfd_, absl::Microseconds(timeout_us))); 78 | } 79 | 80 | void Timer::PeriodicExpire(absl::Duration interval) { 81 | CHECK(timerfd_ != -1); 82 | CHECK(io_utils::SetupTimerFdPeriodic(timerfd_, absl::Microseconds(1), interval)); 83 | } 84 | 85 | UV_POLL_CB_FOR_CLASS(Timer, Expired) { 86 | uint64_t n_expired; 87 | ssize_t nread = read(timerfd_, &n_expired, sizeof(uint64_t)); 88 | if (nread != sizeof(uint64_t)) { 89 | PLOG(ERROR) << "Failed to read on timerfd"; 90 | return; 91 | } 92 | if (n_expired > 1) { 93 | LOG(WARNING) << "Expired more than once: " << n_expired; 94 | } 95 | cb_(this); 96 | } 97 | 98 | } // namespace uv 99 | } // namespace faas 100 | -------------------------------------------------------------------------------- /src/common/zk_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error common/zk_utils.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | #include "common/zk.h" 9 | 10 | namespace faas { 11 | namespace zk_utils { 12 | 13 | // Helper functions 14 | std::string_view EventName(zk::ZKEvent event); 15 | bool ParseSequenceNumber(std::string_view created_path, uint32_t* parsed); 16 | 17 | // Synchronous helper APIs 18 | // They cannot be called from callbacks of ZKSession's asynchronous APIs 19 | // 20 | // Synchronously create a new node 21 | zk::ZKStatus CreateSync(zk::ZKSession* session, std::string_view path, 22 | std::span value, 23 | zk::ZKCreateMode mode, std::string* created_path); 24 | // Synchronously delete a node. 25 | zk::ZKStatus DeleteSync(zk::ZKSession* session, std::string_view path); 26 | // Get the value of `path` 27 | // If `path` not exists, will wait for its creation 28 | zk::ZKStatus GetOrWaitSync(zk::ZKSession* session, std::string_view path, 29 | std::string* value); 30 | 31 | class DirWatcher { 32 | public: 33 | // The directory being watched MUST be flat 34 | DirWatcher(zk::ZKSession* session, std::string_view directory, 35 | bool sequential_znodes = false); 36 | ~DirWatcher(); 37 | 38 | using NodeCreatedCallback = 39 | std::function /* contents */)>; 41 | void SetNodeCreatedCallback(NodeCreatedCallback cb); 42 | 43 | using NodeChangedCallback = 44 | std::function /* contents */)>; 46 | void SetNodeChangedCallback(NodeChangedCallback cb); 47 | 48 | using NodeDeletedCallback = std::function; 49 | void SetNodeDeletedCallback(NodeDeletedCallback cb); 50 | 51 | void Start(); 52 | 53 | private: 54 | zk::ZKSession* session_; 55 | std::string dir_full_path_; 56 | bool sequential_znodes_; 57 | 58 | absl::flat_hash_set nodes_; 59 | 60 | NodeCreatedCallback node_created_cb_; 61 | NodeChangedCallback node_changed_cb_; 62 | NodeDeletedCallback node_deleted_cb_; 63 | 64 | std::string GetNodePath(std::string_view full_path) { 65 | std::string_view base = absl::StripPrefix(full_path, dir_full_path_); 66 | return std::string(absl::StripPrefix(base, "/")); 67 | } 68 | 69 | void InvokeGet(std::string path, bool from_get_watch); 70 | void InvokeGetChildren(); 71 | 72 | void GetWatchCallback(zk::ZKEvent event, std::string_view path); 73 | void GetCallback(std::string path, bool from_get_watch, 74 | zk::ZKStatus status, 75 | const zk::ZKResult& result, bool* remove_watch); 76 | void GetChildrenWatchCallback(zk::ZKEvent event, std::string_view path); 77 | void GetChildrenCallback(zk::ZKStatus status, 78 | const zk::ZKResult& result, bool* remove_watch); 79 | 80 | DISALLOW_COPY_AND_ASSIGN(DirWatcher); 81 | }; 82 | 83 | } // namespace zk_utils 84 | } // namespace faas 85 | -------------------------------------------------------------------------------- /src/engine/flags.cpp: -------------------------------------------------------------------------------- 1 | #include "engine/flags.h" 2 | 3 | ABSL_FLAG(size_t, sequencer_conn_per_worker, 2, ""); 4 | ABSL_FLAG(size_t, shared_log_conn_per_worker, 2, ""); 5 | 6 | ABSL_FLAG(bool, enable_monitor, false, ""); 7 | ABSL_FLAG(bool, func_worker_use_engine_socket, false, ""); 8 | ABSL_FLAG(bool, use_fifo_for_nested_call, false, ""); 9 | ABSL_FLAG(bool, func_worker_pipe_direct_write, false, ""); 10 | 11 | ABSL_FLAG(double, max_relative_queueing_delay, 0.0, ""); 12 | ABSL_FLAG(double, concurrency_limit_coef, 1.0, ""); 13 | ABSL_FLAG(double, expected_concurrency_coef, 1.0, ""); 14 | ABSL_FLAG(int, min_worker_request_interval_ms, 200, ""); 15 | ABSL_FLAG(bool, always_request_worker_if_possible, false, ""); 16 | ABSL_FLAG(bool, disable_concurrency_limiter, false, ""); 17 | 18 | ABSL_FLAG(double, instant_rps_p_norm, 1.0, ""); 19 | ABSL_FLAG(double, instant_rps_ema_alpha, 0.001, ""); 20 | ABSL_FLAG(double, instant_rps_ema_tau_ms, 0, ""); 21 | -------------------------------------------------------------------------------- /src/engine/flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/flags.h" 4 | 5 | ABSL_DECLARE_FLAG(size_t, sequencer_conn_per_worker); 6 | ABSL_DECLARE_FLAG(size_t, shared_log_conn_per_worker); 7 | 8 | ABSL_DECLARE_FLAG(bool, enable_monitor); 9 | ABSL_DECLARE_FLAG(bool, func_worker_use_engine_socket); 10 | ABSL_DECLARE_FLAG(bool, use_fifo_for_nested_call); 11 | ABSL_DECLARE_FLAG(bool, func_worker_pipe_direct_write); 12 | 13 | ABSL_DECLARE_FLAG(double, max_relative_queueing_delay); 14 | ABSL_DECLARE_FLAG(double, concurrency_limit_coef); 15 | ABSL_DECLARE_FLAG(double, expected_concurrency_coef); 16 | ABSL_DECLARE_FLAG(int, min_worker_request_interval_ms); 17 | ABSL_DECLARE_FLAG(bool, always_request_worker_if_possible); 18 | ABSL_DECLARE_FLAG(bool, disable_concurrency_limiter); 19 | 20 | ABSL_DECLARE_FLAG(double, instant_rps_p_norm); 21 | ABSL_DECLARE_FLAG(double, instant_rps_ema_alpha); 22 | ABSL_DECLARE_FLAG(double, instant_rps_ema_tau_ms); 23 | -------------------------------------------------------------------------------- /src/engine/message_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "common/stat.h" 6 | #include "utils/appendable_buffer.h" 7 | #include "utils/object_pool.h" 8 | #include "server/io_worker.h" 9 | 10 | namespace faas { 11 | namespace engine { 12 | 13 | class Engine; 14 | 15 | class MessageConnection final : public server::ConnectionBase { 16 | public: 17 | static constexpr size_t kBufSize = __FAAS_MESSAGE_SIZE * 4; 18 | 19 | explicit MessageConnection(Engine* engine, int sockfd); 20 | ~MessageConnection(); 21 | 22 | uint16_t func_id() const { return func_id_; } 23 | uint16_t client_id() const { return client_id_; } 24 | bool handshake_done() const { return handshake_done_; } 25 | bool is_launcher_connection() const { return client_id_ == 0; } 26 | bool is_func_worker_connection() const { return client_id_ > 0; } 27 | 28 | void Start(server::IOWorker* io_worker) override; 29 | void ScheduleClose() override; 30 | 31 | // Must be thread-safe 32 | void WriteMessage(const protocol::Message& message); 33 | 34 | private: 35 | enum State { kCreated, kHandshake, kRunning, kClosing, kClosed }; 36 | 37 | Engine* engine_; 38 | server::IOWorker* io_worker_; 39 | State state_; 40 | uint16_t func_id_; 41 | uint16_t client_id_; 42 | bool handshake_done_; 43 | 44 | std::optional sockfd_; 45 | std::optional in_fifo_fd_; 46 | std::optional out_fifo_fd_; 47 | std::atomic pipe_for_write_fd_; 48 | 49 | std::string log_header_; 50 | 51 | utils::AppendableBuffer message_buffer_; 52 | protocol::Message handshake_response_; 53 | utils::AppendableBuffer write_message_buffer_; 54 | 55 | absl::Mutex write_message_mu_; 56 | absl::InlinedVector 57 | pending_messages_ ABSL_GUARDED_BY(write_message_mu_); 58 | 59 | void RecvHandshakeMessage(); 60 | void SendPendingMessages(); 61 | bool OnRecvSockData(int status, std::span data); 62 | bool OnRecvData(int status, std::span data); 63 | void OnFdClosed(); 64 | 65 | bool WriteMessageWithFifo(const protocol::Message& message); 66 | 67 | DISALLOW_COPY_AND_ASSIGN(MessageConnection); 68 | }; 69 | 70 | } // namespace engine 71 | } // namespace faas 72 | -------------------------------------------------------------------------------- /src/engine/monitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "base/thread.h" 5 | 6 | namespace faas { 7 | namespace engine { 8 | 9 | class Engine; 10 | class MessageConnection; 11 | 12 | class Monitor { 13 | public: 14 | static constexpr float kDefaultFrequencyHz = 0.3f; 15 | 16 | explicit Monitor(Engine* engine); 17 | ~Monitor(); 18 | 19 | void set_frequency(float hz); 20 | 21 | void Start(); 22 | void ScheduleStop(); 23 | void WaitForFinish(); 24 | 25 | void OnIOWorkerCreated(std::string_view worker_name, int event_loop_thread_tid); 26 | void OnNewFuncContainer(uint16_t func_id, std::string_view container_id); 27 | 28 | private: 29 | enum State { kCreated, kRunning, kStopping, kStopped }; 30 | std::atomic state_; 31 | Engine* engine_; 32 | float frequency_hz_; 33 | base::Thread background_thread_; 34 | 35 | absl::Mutex mu_; 36 | std::string self_container_id_; 37 | absl::flat_hash_map 38 | io_workers_ ABSL_GUARDED_BY(mu_); 39 | absl::flat_hash_map 40 | func_container_ids_ ABSL_GUARDED_BY(mu_); 41 | 42 | void BackgroundThreadMain(); 43 | 44 | DISALLOW_COPY_AND_ASSIGN(Monitor); 45 | }; 46 | 47 | } // namespace engine 48 | } // namespace faas 49 | -------------------------------------------------------------------------------- /src/engine/worker_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "engine/message_connection.h" 6 | 7 | namespace faas { 8 | namespace engine { 9 | 10 | class Engine; 11 | class FuncWorker; 12 | 13 | class WorkerManager { 14 | public: 15 | static constexpr int kDefaultMinWorkersPerFunc = 4; 16 | 17 | explicit WorkerManager(Engine* engine); 18 | ~WorkerManager(); 19 | 20 | // All must be thread-safe 21 | bool OnLauncherConnected(MessageConnection* launcher_connection); 22 | void OnLauncherDisconnected(MessageConnection* launcher_connection); 23 | bool OnFuncWorkerConnected(MessageConnection* worker_connection); 24 | void OnFuncWorkerDisconnected(MessageConnection* worker_connection); 25 | bool RequestNewFuncWorker(uint16_t func_id, uint16_t* client_id); 26 | std::shared_ptr GetFuncWorker(uint16_t client_id); 27 | 28 | private: 29 | Engine* engine_; 30 | std::atomic next_client_id_; 31 | 32 | absl::Mutex mu_; 33 | absl::flat_hash_map> 34 | launcher_connections_ ABSL_GUARDED_BY(mu_); 35 | absl::flat_hash_map> 36 | func_workers_ ABSL_GUARDED_BY(mu_); 37 | 38 | bool RequestNewFuncWorkerInternal(MessageConnection* launcher_connection, uint16_t* client_id); 39 | 40 | DISALLOW_COPY_AND_ASSIGN(WorkerManager); 41 | }; 42 | 43 | class FuncWorker { 44 | public: 45 | explicit FuncWorker(MessageConnection* message_connection); 46 | ~FuncWorker(); 47 | 48 | uint16_t func_id() const { return func_id_; } 49 | uint16_t client_id() const { return client_id_; } 50 | 51 | // Must be thread-safe 52 | void SendMessage(protocol::Message* message); 53 | 54 | private: 55 | uint16_t func_id_; 56 | uint16_t client_id_; 57 | std::shared_ptr message_connection_; 58 | 59 | DISALLOW_COPY_AND_ASSIGN(FuncWorker); 60 | }; 61 | 62 | } // namespace engine 63 | } // namespace faas 64 | -------------------------------------------------------------------------------- /src/gateway/flags.cpp: -------------------------------------------------------------------------------- 1 | #include "gateway/flags.h" 2 | 3 | ABSL_FLAG(size_t, max_running_requests, 0, ""); 4 | ABSL_FLAG(bool, lb_per_fn_round_robin, false, ""); 5 | ABSL_FLAG(bool, lb_pick_least_load, false, ""); 6 | 7 | ABSL_FLAG(std::string, async_call_result_path, "", ""); 8 | -------------------------------------------------------------------------------- /src/gateway/flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/flags.h" 4 | 5 | ABSL_DECLARE_FLAG(size_t, max_running_requests); 6 | ABSL_DECLARE_FLAG(bool, lb_per_fn_round_robin); 7 | ABSL_DECLARE_FLAG(bool, lb_pick_least_load); 8 | 9 | ABSL_DECLARE_FLAG(std::string, async_call_result_path); 10 | -------------------------------------------------------------------------------- /src/gateway/func_call_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "utils/appendable_buffer.h" 6 | #include "server/io_worker.h" 7 | 8 | namespace faas { 9 | namespace 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 | ~FuncCallContext() {} 24 | 25 | void set_func_name(std::string_view func_name) { func_name_.assign(func_name); } 26 | void set_method_name(std::string_view method_name) { method_name_.assign(method_name); } 27 | void set_async(bool async) { async_ = async; } 28 | void set_logspace(uint32_t logspace) { logspace_ = logspace; } 29 | void set_h2_stream_id(int32_t h2_stream_id) { h2_stream_id_ = h2_stream_id; } 30 | void set_func_call(const protocol::FuncCall& func_call) { func_call_ = func_call; } 31 | void append_input(std::span input) { input_.AppendData(input); } 32 | void append_output(std::span output) { output_.AppendData(output); } 33 | void set_status(Status status) { status_ = status; } 34 | 35 | std::string_view func_name() const { return func_name_; } 36 | std::string_view method_name() const { return method_name_; } 37 | bool is_async() const { return async_; } 38 | uint32_t logspace() const { return logspace_; } 39 | int32_t h2_stream_id() const { return h2_stream_id_; } 40 | protocol::FuncCall func_call() const { return func_call_; } 41 | std::span input() const { return input_.to_span(); } 42 | std::span output() const { return output_.to_span(); } 43 | Status status() const { return status_; } 44 | 45 | void Reset() { 46 | status_ = kCreated; 47 | func_name_.clear(); 48 | method_name_.clear(); 49 | async_ = false; 50 | logspace_ = 0; 51 | func_call_ = protocol::kInvalidFuncCall; 52 | input_.Reset(); 53 | output_.Reset(); 54 | } 55 | 56 | private: 57 | Status status_; 58 | std::string func_name_; 59 | std::string method_name_; 60 | bool async_; 61 | uint32_t logspace_; 62 | int32_t h2_stream_id_; 63 | protocol::FuncCall func_call_; 64 | utils::AppendableBuffer input_; 65 | utils::AppendableBuffer output_; 66 | 67 | DISALLOW_COPY_AND_ASSIGN(FuncCallContext); 68 | }; 69 | 70 | } // namespace gateway 71 | } // namespace faas 72 | -------------------------------------------------------------------------------- /src/gateway/http_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/http_status.h" 5 | #include "utils/appendable_buffer.h" 6 | #include "server/io_worker.h" 7 | #include "gateway/func_call_context.h" 8 | 9 | #include 10 | 11 | namespace faas { 12 | namespace gateway { 13 | 14 | class Server; 15 | 16 | class HttpConnection final : public server::ConnectionBase { 17 | public: 18 | static constexpr size_t kBufSize = 65536; 19 | 20 | static constexpr const char* kServerString = "FaaS/0.1"; 21 | static constexpr const char* kResponseContentType = "text/plain"; 22 | 23 | HttpConnection(Server* server, int connection_id, int sockfd); 24 | ~HttpConnection(); 25 | 26 | void Start(server::IOWorker* io_worker) override; 27 | void ScheduleClose() override; 28 | 29 | void OnFuncCallFinished(FuncCallContext* func_call_context); 30 | 31 | private: 32 | enum State { kCreated, kRunning, kClosing, kClosed }; 33 | 34 | Server* server_; 35 | State state_; 36 | int sockfd_; 37 | server::IOWorker* io_worker_; 38 | 39 | std::string log_header_; 40 | 41 | FuncCallContext func_call_context_; 42 | http_parser_settings http_parser_settings_; 43 | http_parser http_parser_; 44 | int header_field_value_flag_; 45 | bool keep_recv_data_; 46 | 47 | // For request 48 | utils::AppendableBuffer url_buffer_; 49 | utils::AppendableBuffer header_field_buffer_; 50 | utils::AppendableBuffer header_value_buffer_; 51 | utils::AppendableBuffer body_buffer_; 52 | size_t header_field_buffer_pos_; 53 | size_t header_value_buffer_pos_; 54 | absl::flat_hash_map headers_; 55 | 56 | // For response 57 | std::string response_header_; 58 | 59 | void StartRecvData(); 60 | void StopRecvData(); 61 | bool OnRecvData(int status, std::span data); 62 | 63 | void HttpParserOnMessageBegin(); 64 | void HttpParserOnUrl(const char* data, size_t length); 65 | void HttpParserOnHeaderField(const char* data, size_t length); 66 | void HttpParserOnHeaderValue(const char* data, size_t length); 67 | void HttpParserOnHeadersComplete(); 68 | void HttpParserOnBody(const char* data, size_t length); 69 | void HttpParserOnMessageComplete(); 70 | 71 | void HttpParserOnNewHeader(); 72 | void ResetHttpParser(); 73 | void OnNewHttpRequest(std::string_view method, std::string_view path, 74 | std::string_view qs = std::string_view{}); 75 | void SendHttpResponse(HttpStatus status, std::span body = EMPTY_CHAR_SPAN); 76 | void OnFuncCallFinishedInternal(); 77 | 78 | static int HttpParserOnMessageBeginCallback(http_parser* http_parser); 79 | static int HttpParserOnUrlCallback(http_parser* http_parser, const char* data, size_t length); 80 | static int HttpParserOnHeaderFieldCallback(http_parser* http_parser, const char* data, size_t length); 81 | static int HttpParserOnHeaderValueCallback(http_parser* http_parser, const char* data, size_t length); 82 | static int HttpParserOnHeadersCompleteCallback(http_parser* http_parser); 83 | static int HttpParserOnBodyCallback(http_parser* http_parser, const char* data, size_t length); 84 | static int HttpParserOnMessageCompleteCallback(http_parser* http_parser); 85 | 86 | DISALLOW_COPY_AND_ASSIGN(HttpConnection); 87 | }; 88 | 89 | } // namespace gateway 90 | } // namespace faas 91 | -------------------------------------------------------------------------------- /src/gateway/node_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "common/stat.h" 6 | #include "server/node_watcher.h" 7 | 8 | namespace faas { 9 | namespace gateway { 10 | 11 | class Server; 12 | 13 | class NodeManager { 14 | public: 15 | explicit NodeManager(Server* server); 16 | ~NodeManager(); 17 | 18 | bool PickNodeForNewFuncCall(const protocol::FuncCall& func_call, uint16_t* node_id); 19 | void FuncCallFinished(const protocol::FuncCall& func_call, uint16_t node_id); 20 | 21 | void OnNodeOnline(server::NodeWatcher::NodeType node_type, uint16_t node_id); 22 | void OnNodeOffline(server::NodeWatcher::NodeType node_type, uint16_t node_id); 23 | 24 | private: 25 | Server* server_; 26 | 27 | absl::Mutex mu_; 28 | 29 | size_t max_running_requests_ ABSL_GUARDED_BY(mu_); 30 | absl::flat_hash_set running_requests_ ABSL_GUARDED_BY(mu_); 31 | 32 | struct Node { 33 | uint16_t node_id; 34 | size_t inflight_requests; 35 | stat::Counter dispatched_requests_stat; 36 | explicit Node(uint16_t node_id); 37 | }; 38 | 39 | absl::flat_hash_map> 40 | connected_nodes_ ABSL_GUARDED_BY(mu_); 41 | std::vector connected_node_list_ ABSL_GUARDED_BY(mu_); 42 | 43 | absl::BitGen random_bit_gen_ ABSL_GUARDED_BY(mu_); 44 | absl::flat_hash_map 45 | next_dispatch_node_idx_ ABSL_GUARDED_BY(mu_); 46 | 47 | DISALLOW_COPY_AND_ASSIGN(NodeManager); 48 | }; 49 | 50 | } // namespace gateway 51 | } // namespace faas 52 | -------------------------------------------------------------------------------- /src/ipc/base.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_USED_IN_BINDING 2 | #include "ipc/base.h" 3 | 4 | #include "utils/fs.h" 5 | 6 | namespace faas { 7 | namespace ipc { 8 | 9 | namespace { 10 | std::string root_path_for_ipc = "NOT_SET"; 11 | std::string engine_unix_ipc_path = "NOT_SET"; 12 | std::string root_path_for_shm = "NOT_SET"; 13 | std::string root_path_for_fifo = "NOT_SET"; 14 | 15 | static constexpr const char* kEngineUnixSocket = "engine.sock"; 16 | static constexpr const char* kShmSubPath = "shm"; 17 | static constexpr const char* kFifoSubPath = "fifo"; 18 | } 19 | 20 | void SetRootPathForIpc(std::string_view path, bool create) { 21 | root_path_for_ipc = std::string(path); 22 | engine_unix_ipc_path = fs_utils::JoinPath(root_path_for_ipc, kEngineUnixSocket); 23 | root_path_for_shm = fs_utils::JoinPath(root_path_for_ipc, kShmSubPath); 24 | root_path_for_fifo = fs_utils::JoinPath(root_path_for_ipc, kFifoSubPath); 25 | if (create) { 26 | if (fs_utils::IsDirectory(root_path_for_ipc)) { 27 | PCHECK(fs_utils::RemoveDirectoryRecursively(root_path_for_ipc)); 28 | } else if (fs_utils::Exists(root_path_for_ipc)) { 29 | PCHECK(fs_utils::Remove(root_path_for_ipc)); 30 | } 31 | PCHECK(fs_utils::MakeDirectory(root_path_for_ipc)); 32 | PCHECK(fs_utils::MakeDirectory(root_path_for_shm)); 33 | PCHECK(fs_utils::MakeDirectory(root_path_for_fifo)); 34 | } else { 35 | CHECK(fs_utils::IsDirectory(root_path_for_ipc)) << root_path_for_ipc << " does not exist"; 36 | CHECK(fs_utils::IsDirectory(root_path_for_shm)) << root_path_for_shm << " does not exist"; 37 | CHECK(fs_utils::IsDirectory(root_path_for_fifo)) << root_path_for_fifo << " does not exist"; 38 | } 39 | } 40 | 41 | std::string_view GetRootPathForIpc() { 42 | CHECK_NE(root_path_for_ipc, "NOT_SET"); 43 | return root_path_for_ipc; 44 | } 45 | 46 | std::string_view GetEngineUnixSocketPath() { 47 | CHECK_NE(engine_unix_ipc_path, "NOT_SET"); 48 | return engine_unix_ipc_path; 49 | } 50 | 51 | std::string_view GetRootPathForShm() { 52 | CHECK_NE(root_path_for_shm, "NOT_SET"); 53 | return root_path_for_shm; 54 | } 55 | 56 | std::string_view GetRootPathForFifo() { 57 | CHECK_NE(root_path_for_fifo, "NOT_SET"); 58 | return root_path_for_fifo; 59 | } 60 | 61 | std::string GetFuncWorkerInputFifoName(uint16_t client_id) { 62 | return fmt::format("worker_{}_input", client_id); 63 | } 64 | 65 | std::string GetFuncWorkerOutputFifoName(uint16_t client_id) { 66 | return fmt::format("worker_{}_output", client_id); 67 | } 68 | 69 | std::string GetFuncCallInputShmName(uint64_t full_call_id) { 70 | return fmt::format("{}.i", full_call_id); 71 | } 72 | 73 | std::string GetFuncCallOutputShmName(uint64_t full_call_id) { 74 | return fmt::format("{}.o", full_call_id); 75 | } 76 | 77 | std::string GetFuncCallOutputFifoName(uint64_t full_call_id) { 78 | return fmt::format("{}.o", full_call_id); 79 | } 80 | 81 | } // namespace ipc 82 | } // namespace faas 83 | -------------------------------------------------------------------------------- /src/ipc/base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace ipc { 7 | 8 | void SetRootPathForIpc(std::string_view path, bool create = false); 9 | 10 | std::string_view GetRootPathForIpc(); 11 | std::string_view GetEngineUnixSocketPath(); 12 | std::string_view GetRootPathForShm(); 13 | std::string_view GetRootPathForFifo(); 14 | 15 | std::string GetFuncWorkerInputFifoName(uint16_t client_id); 16 | std::string GetFuncWorkerOutputFifoName(uint16_t client_id); 17 | 18 | std::string GetFuncCallInputShmName(uint64_t full_call_id); 19 | std::string GetFuncCallOutputShmName(uint64_t full_call_id); 20 | std::string GetFuncCallOutputFifoName(uint64_t full_call_id); 21 | 22 | } // namespace ipc 23 | } // namespace faas 24 | -------------------------------------------------------------------------------- /src/ipc/fifo.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_USED_IN_BINDING 2 | #include "ipc/fifo.h" 3 | 4 | #include "ipc/base.h" 5 | #include "utils/fs.h" 6 | #include "utils/io.h" 7 | #include "common/time.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace faas { 14 | namespace ipc { 15 | 16 | bool FifoCreate(std::string_view name) { 17 | std::string full_path = fs_utils::JoinPath(GetRootPathForFifo(), name); 18 | if (mkfifo(full_path.c_str(), __FAAS_FILE_CREAT_MODE) != 0) { 19 | PLOG(ERROR) << "mkfifo " << full_path << " failed"; 20 | return false; 21 | } 22 | return true; 23 | } 24 | 25 | void FifoRemove(std::string_view name) { 26 | std::string full_path = fs_utils::JoinPath(GetRootPathForFifo(), name); 27 | if (!fs_utils::Remove(full_path)) { 28 | PLOG(ERROR) << "Failed to remove " << full_path; 29 | } 30 | } 31 | 32 | std::optional FifoOpenForRead(std::string_view name, bool nonblocking) { 33 | return fs_utils::Open( 34 | fs_utils::JoinPath(GetRootPathForFifo(), name), 35 | /* flags= */ O_RDONLY | (nonblocking ? O_NONBLOCK : 0)); 36 | } 37 | 38 | std::optional FifoOpenForWrite(std::string_view name, bool nonblocking) { 39 | return fs_utils::Open( 40 | fs_utils::JoinPath(GetRootPathForFifo(), name), 41 | /* flags= */ O_WRONLY | (nonblocking ? O_NONBLOCK : 0)); 42 | } 43 | 44 | std::optional FifoOpenForReadWrite(std::string_view name, bool nonblocking) { 45 | return fs_utils::Open( 46 | fs_utils::JoinPath(GetRootPathForFifo(), name), 47 | /* flags= */ O_RDWR | (nonblocking ? O_NONBLOCK : 0)); 48 | } 49 | 50 | } // namespace ipc 51 | } // namespace faas 52 | -------------------------------------------------------------------------------- /src/ipc/fifo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace ipc { 7 | 8 | bool FifoCreate(std::string_view name); 9 | void FifoRemove(std::string_view name); 10 | // FifoOpenFor{Read, Write, ReadWrite} returns fd on success 11 | std::optional FifoOpenForRead(std::string_view name, bool nonblocking = true); 12 | std::optional FifoOpenForWrite(std::string_view name, bool nonblocking = true); 13 | std::optional FifoOpenForReadWrite(std::string_view name, bool nonblocking = true); 14 | 15 | } // namespace ipc 16 | } // namespace faas 17 | -------------------------------------------------------------------------------- /src/ipc/shm_region.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_USED_IN_BINDING 2 | #include "ipc/shm_region.h" 3 | 4 | #include "ipc/base.h" 5 | #include "common/stat.h" 6 | #include "common/time.h" 7 | #include "utils/fs.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace faas { 15 | namespace ipc { 16 | 17 | std::unique_ptr ShmCreate(std::string_view name, size_t size) { 18 | std::string full_path = fs_utils::JoinPath(GetRootPathForShm(), name); 19 | int fd = open(full_path.c_str(), O_CREAT|O_EXCL|O_RDWR, __FAAS_FILE_CREAT_MODE); 20 | if (fd == -1) { 21 | PLOG(ERROR) << "open " << full_path << " failed"; 22 | return nullptr; 23 | } 24 | PCHECK(ftruncate(fd, static_cast(size)) == 0) << "ftruncate failed"; 25 | void* ptr = nullptr; 26 | if (size > 0) { 27 | ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 28 | PCHECK(ptr != MAP_FAILED) << "mmap failed"; 29 | memset(ptr, 0, size); 30 | } 31 | PCHECK(close(fd) == 0) << "close failed"; 32 | return std::unique_ptr(new ShmRegion(name, reinterpret_cast(ptr), size)); 33 | } 34 | 35 | std::unique_ptr ShmOpen(std::string_view name, bool readonly) { 36 | std::string full_path = fs_utils::JoinPath(GetRootPathForShm(), name); 37 | int fd = open(full_path.c_str(), readonly ? O_RDONLY : O_RDWR); 38 | if (fd == -1) { 39 | PLOG(ERROR) << "open " << full_path << " failed"; 40 | return nullptr; 41 | } 42 | struct stat statbuf; 43 | PCHECK(fstat(fd, &statbuf) == 0) << "fstat failed"; 44 | size_t size = gsl::narrow_cast(statbuf.st_size); 45 | void* ptr = nullptr; 46 | if (size > 0) { 47 | ptr = mmap(0, size, readonly ? PROT_READ : (PROT_READ|PROT_WRITE), MAP_SHARED, fd, 0); 48 | PCHECK(ptr != MAP_FAILED) << "mmap failed"; 49 | } 50 | PCHECK(close(fd) == 0) << "close failed"; 51 | return std::unique_ptr(new ShmRegion(name, reinterpret_cast(ptr), size)); 52 | } 53 | 54 | ShmRegion::~ShmRegion() { 55 | if (size_ > 0) { 56 | PCHECK(munmap(base_, size_) == 0); 57 | } 58 | if (remove_on_destruction_) { 59 | std::string full_path = fs_utils::JoinPath(GetRootPathForShm(), name_); 60 | if (!fs_utils::Remove(full_path)) { 61 | PLOG(ERROR) << "Failed to remove " << full_path; 62 | } 63 | } 64 | } 65 | 66 | } // namespace ipc 67 | } // namespace faas 68 | -------------------------------------------------------------------------------- /src/ipc/shm_region.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "ipc/base.h" 5 | 6 | namespace faas { 7 | namespace ipc { 8 | 9 | class ShmRegion; 10 | 11 | // Shm{Create, Open} returns nullptr on failure 12 | std::unique_ptr ShmCreate(std::string_view name, size_t size); 13 | std::unique_ptr ShmOpen(std::string_view name, bool readonly = true); 14 | 15 | class ShmRegion { 16 | public: 17 | ~ShmRegion(); 18 | 19 | void EnableRemoveOnDestruction() { remove_on_destruction_ = true; } 20 | void DisableRemoveOnDestruction() { remove_on_destruction_ = false; } 21 | 22 | char* base() { return base_; } 23 | const char* base() const { return base_; } 24 | size_t size() const { return size_; } 25 | 26 | std::span to_span() const { 27 | return std::span(base_, size_); 28 | } 29 | 30 | private: 31 | ShmRegion(std::string_view name, char* base, size_t size) 32 | : name_(name), base_(base), size_(size), remove_on_destruction_(false) {} 33 | 34 | std::string name_; 35 | char* base_; 36 | size_t size_; 37 | bool remove_on_destruction_; 38 | 39 | friend std::unique_ptr ShmCreate(std::string_view name, size_t size); 40 | friend std::unique_ptr ShmOpen(std::string_view name, bool readonly); 41 | 42 | DISALLOW_COPY_AND_ASSIGN(ShmRegion); 43 | }; 44 | 45 | } // namespace ipc 46 | } // namespace faas 47 | -------------------------------------------------------------------------------- /src/ipc/spsc_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error ipc/spsc_queue.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | #include "ipc/shm_region.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 | // Called by the producer 23 | static std::unique_ptr> Open(std::string_view name); 24 | 25 | // Methods called by the producer 26 | void SetWakeupConsumerFn(std::function fn); 27 | bool Push(const T& message); // Return false if queue is full 28 | 29 | // Methods called by the consumer 30 | void ConsumerEnterSleep(); 31 | bool Pop(T* message); // Return false if queue is empty 32 | 33 | private: 34 | bool consumer_; 35 | std::unique_ptr shm_region_; 36 | size_t queue_size_; 37 | size_t* head_; 38 | size_t* tail_; 39 | char* cell_base_; 40 | 41 | std::function wakeup_consumer_fn_; 42 | bool wakeup_consumer_flag_; 43 | 44 | SPSCQueue(bool producer, std::unique_ptr shm_region); 45 | static size_t compute_total_bytesize(size_t queue_size); 46 | static void BuildMemoryLayout(char* base_ptr, size_t queue_size); 47 | void* cell(size_t idx) { return cell_base_ + idx * sizeof(T); } 48 | 49 | DISALLOW_COPY_AND_ASSIGN(SPSCQueue); 50 | }; 51 | 52 | } // namespace ipc 53 | } // namespace faas 54 | 55 | #include "ipc/spsc_queue-inl.h" 56 | -------------------------------------------------------------------------------- /src/launcher/engine_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "common/uv.h" 6 | #include "utils/appendable_buffer.h" 7 | #include "utils/buffer_pool.h" 8 | #include "utils/object_pool.h" 9 | 10 | namespace faas { 11 | namespace launcher { 12 | 13 | class Launcher; 14 | 15 | class EngineConnection : public uv::Base { 16 | public: 17 | explicit EngineConnection(Launcher* launcher); 18 | ~EngineConnection(); 19 | 20 | void Start(uv_loop_t* uv_loop, int engine_tcp_port, 21 | const protocol::Message& handshake_message); 22 | void ScheduleClose(); 23 | 24 | void WriteMessage(const protocol::Message& message); 25 | 26 | private: 27 | enum State { kCreated, kHandshake, kRunning, kClosing, kClosed }; 28 | 29 | Launcher* launcher_; 30 | State state_; 31 | 32 | uv_connect_t connect_req_; 33 | uv_loop_t* uv_loop_; 34 | uv_stream_t* engine_conn_; 35 | 36 | utils::AppendableBuffer message_buffer_; 37 | protocol::Message handshake_message_; 38 | 39 | void RecvHandshakeResponse(); 40 | 41 | DECLARE_UV_CONNECT_CB_FOR_CLASS(Connect); 42 | DECLARE_UV_ALLOC_CB_FOR_CLASS(BufferAlloc); 43 | DECLARE_UV_READ_CB_FOR_CLASS(ReadHandshakeResponse); 44 | DECLARE_UV_WRITE_CB_FOR_CLASS(WriteHandshake); 45 | DECLARE_UV_READ_CB_FOR_CLASS(ReadMessage); 46 | DECLARE_UV_WRITE_CB_FOR_CLASS(WriteMessage); 47 | DECLARE_UV_CLOSE_CB_FOR_CLASS(Close); 48 | 49 | DISALLOW_COPY_AND_ASSIGN(EngineConnection); 50 | }; 51 | 52 | } // namespace launcher 53 | } // namespace faas 54 | -------------------------------------------------------------------------------- /src/launcher/func_process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "common/uv.h" 6 | #include "common/subprocess.h" 7 | #include "utils/buffer_pool.h" 8 | #include "utils/object_pool.h" 9 | 10 | namespace faas { 11 | namespace 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 | ~FuncProcess(); 19 | 20 | int id() const { return id_; } 21 | 22 | bool Start(uv_loop_t* uv_loop, utils::BufferPool* read_buffer_pool); 23 | void SendMessage(const protocol::Message& message); 24 | void ScheduleClose(); 25 | 26 | private: 27 | enum State { kCreated, kRunning, kClosing, kClosed }; 28 | 29 | State state_; 30 | Launcher* launcher_; 31 | int id_; 32 | int initial_client_id_; 33 | uint32_t initial_payload_size_; 34 | 35 | std::string log_header_; 36 | 37 | uv_loop_t* uv_loop_; 38 | uv::Subprocess subprocess_; 39 | utils::BufferPool* read_buffer_pool_; 40 | int message_pipe_fd_; 41 | uv_pipe_t* message_pipe_; 42 | 43 | void OnSubprocessExit(int exit_status, std::span stdout, 44 | std::span stderr); 45 | 46 | DECLARE_UV_WRITE_CB_FOR_CLASS(SendMessage); 47 | 48 | DISALLOW_COPY_AND_ASSIGN(FuncProcess); 49 | }; 50 | 51 | } // namespace launcher 52 | } // namespace faas 53 | -------------------------------------------------------------------------------- /src/log/cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "log/common.h" 4 | 5 | // Forward declarations 6 | namespace tkrzw { class CacheDBM; } 7 | 8 | namespace faas { 9 | namespace log { 10 | 11 | class LRUCache { 12 | public: 13 | explicit LRUCache(int mem_cap_mb); 14 | ~LRUCache(); 15 | 16 | void Put(const LogMetaData& log_metadata, std::span user_tags, 17 | std::span log_data); 18 | std::optional Get(uint64_t seqnum); 19 | 20 | void PutAuxData(uint64_t seqnum, std::span data); 21 | std::optional GetAuxData(uint64_t seqnum); 22 | 23 | private: 24 | std::unique_ptr dbm_; 25 | 26 | DISALLOW_COPY_AND_ASSIGN(LRUCache); 27 | }; 28 | 29 | } // namespace log 30 | } // namespace faas 31 | -------------------------------------------------------------------------------- /src/log/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | 6 | __BEGIN_THIRD_PARTY_HEADERS 7 | #include "proto/shared_log.pb.h" 8 | __END_THIRD_PARTY_HEADERS 9 | 10 | namespace faas { 11 | namespace log { 12 | 13 | constexpr uint64_t kEmptyLogTag = 0; 14 | constexpr uint64_t kMaxLogSeqNum = 0xffff000000000000ULL; 15 | constexpr uint64_t kInvalidLogSeqNum = protocol::kInvalidLogSeqNum; 16 | constexpr uint64_t kInvalidLogTag = protocol::kInvalidLogTag; 17 | 18 | struct SharedLogRequest { 19 | protocol::SharedLogMessage message; 20 | std::string payload; 21 | void* local_op; 22 | 23 | explicit SharedLogRequest(void* local_op) 24 | : local_op(local_op) {} 25 | 26 | explicit SharedLogRequest(const protocol::SharedLogMessage& message, 27 | std::span payload = EMPTY_CHAR_SPAN) 28 | : message(message), 29 | payload(), 30 | local_op(nullptr) { 31 | if (payload.size() > 0) { 32 | this->payload.assign(payload.data(), payload.size()); 33 | } 34 | } 35 | }; 36 | 37 | using UserTagVec = absl::InlinedVector; 38 | 39 | struct LogMetaData { 40 | uint32_t user_logspace; 41 | uint64_t seqnum; 42 | uint64_t localid; 43 | size_t num_tags; 44 | size_t data_size; 45 | }; 46 | 47 | struct LogEntry { 48 | LogMetaData metadata; 49 | UserTagVec user_tags; 50 | std::string data; 51 | }; 52 | 53 | } // namespace log 54 | } // namespace faas 55 | -------------------------------------------------------------------------------- /src/log/db.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "log/common.h" 4 | 5 | // Forward declarations 6 | namespace rocksdb { class DB; class ColumnFamilyHandle; } 7 | namespace tkrzw { class DBM; } 8 | 9 | namespace faas { 10 | namespace log { 11 | 12 | class DBInterface { 13 | public: 14 | virtual ~DBInterface() {} 15 | 16 | virtual void InstallLogSpace(uint32_t logspace_id) = 0; 17 | virtual std::optional Get(uint32_t logspace_id, uint32_t key) = 0; 18 | virtual void Put(uint32_t logspace_id, uint32_t key, std::span data) = 0; 19 | }; 20 | 21 | class RocksDBBackend final : public DBInterface { 22 | public: 23 | explicit RocksDBBackend(std::string_view db_path); 24 | ~RocksDBBackend(); 25 | 26 | void InstallLogSpace(uint32_t logspace_id) override; 27 | std::optional Get(uint32_t logspace_id, uint32_t key) override; 28 | void Put(uint32_t logspace_id, uint32_t key, std::span data) override; 29 | 30 | private: 31 | std::unique_ptr db_; 32 | absl::Mutex mu_; 33 | absl::flat_hash_map> 35 | column_families_ ABSL_GUARDED_BY(mu_); 36 | 37 | rocksdb::ColumnFamilyHandle* GetCFHandle(uint32_t logspace_id); 38 | 39 | DISALLOW_COPY_AND_ASSIGN(RocksDBBackend); 40 | }; 41 | 42 | class TkrzwDBMBackend final : public DBInterface { 43 | public: 44 | enum Type { kHashDBM, kTreeDBM, kSkipDBM }; 45 | TkrzwDBMBackend(Type type, std::string_view db_path); 46 | ~TkrzwDBMBackend(); 47 | 48 | void InstallLogSpace(uint32_t logspace_id) override; 49 | std::optional Get(uint32_t logspace_id, uint32_t key) override; 50 | void Put(uint32_t logspace_id, uint32_t key, std::span data) override; 51 | 52 | private: 53 | Type type_; 54 | std::string db_path_; 55 | 56 | absl::Mutex mu_; 57 | absl::flat_hash_map> 59 | dbs_ ABSL_GUARDED_BY(mu_); 60 | 61 | tkrzw::DBM* GetDBM(uint32_t logspace_id); 62 | 63 | DISALLOW_COPY_AND_ASSIGN(TkrzwDBMBackend); 64 | }; 65 | 66 | } // namespace log 67 | } // namespace faas 68 | -------------------------------------------------------------------------------- /src/log/engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "log/engine_base.h" 4 | #include "log/log_space.h" 5 | #include "log/index.h" 6 | #include "log/utils.h" 7 | 8 | namespace faas { 9 | 10 | // Forward declaration 11 | namespace engine { class Engine; } 12 | 13 | namespace log { 14 | 15 | class Engine final : public EngineBase { 16 | public: 17 | explicit Engine(engine::Engine* engine); 18 | ~Engine(); 19 | 20 | private: 21 | std::string log_header_; 22 | 23 | absl::Mutex view_mu_; 24 | const View* current_view_ ABSL_GUARDED_BY(view_mu_); 25 | bool current_view_active_ ABSL_GUARDED_BY(view_mu_); 26 | std::vector views_ ABSL_GUARDED_BY(view_mu_); 27 | LogSpaceCollection 28 | producer_collection_ ABSL_GUARDED_BY(view_mu_); 29 | LogSpaceCollection 30 | index_collection_ ABSL_GUARDED_BY(view_mu_); 31 | 32 | log_utils::FutureRequests future_requests_; 33 | log_utils::ThreadedMap onging_reads_; 34 | 35 | void OnViewCreated(const View* view) override; 36 | void OnViewFrozen(const View* view) override; 37 | void OnViewFinalized(const FinalizedView* finalized_view) override; 38 | 39 | void HandleLocalAppend(LocalOp* op) override; 40 | void HandleLocalTrim(LocalOp* op) override; 41 | void HandleLocalRead(LocalOp* op) override; 42 | void HandleLocalSetAuxData(LocalOp* op) override; 43 | 44 | void HandleRemoteRead(const protocol::SharedLogMessage& request) override; 45 | void OnRecvNewMetaLogs(const protocol::SharedLogMessage& message, 46 | std::span payload) override; 47 | void OnRecvNewIndexData(const protocol::SharedLogMessage& message, 48 | std::span payload) override; 49 | void OnRecvResponse(const protocol::SharedLogMessage& message, 50 | std::span payload) override; 51 | 52 | void ProcessAppendResults(const LogProducer::AppendResultVec& results); 53 | void ProcessIndexQueryResults(const Index::QueryResultVec& results); 54 | void ProcessRequests(const std::vector& requests); 55 | 56 | void ProcessIndexFoundResult(const IndexQueryResult& query_result); 57 | void ProcessIndexContinueResult(const IndexQueryResult& query_result, 58 | Index::QueryResultVec* more_results); 59 | 60 | inline LogMetaData MetaDataFromAppendOp(LocalOp* op) { 61 | DCHECK(op->type == protocol::SharedLogOpType::APPEND); 62 | return LogMetaData { 63 | .user_logspace = op->user_logspace, 64 | .seqnum = kInvalidLogSeqNum, 65 | .localid = 0, 66 | .num_tags = op->user_tags.size(), 67 | .data_size = op->data.length() 68 | }; 69 | } 70 | 71 | protocol::SharedLogMessage BuildReadRequestMessage(LocalOp* op); 72 | protocol::SharedLogMessage BuildReadRequestMessage(const IndexQueryResult& result); 73 | 74 | IndexQuery BuildIndexQuery(LocalOp* op); 75 | IndexQuery BuildIndexQuery(const protocol::SharedLogMessage& message); 76 | IndexQuery BuildIndexQuery(const IndexQueryResult& result); 77 | 78 | DISALLOW_COPY_AND_ASSIGN(Engine); 79 | }; 80 | 81 | } // namespace log 82 | } // namespace faas 83 | -------------------------------------------------------------------------------- /src/log/flags.cpp: -------------------------------------------------------------------------------- 1 | #include "log/flags.h" 2 | 3 | ABSL_FLAG(int, slog_local_cut_interval_us, 1000, ""); 4 | ABSL_FLAG(int, slog_global_cut_interval_us, 1000, ""); 5 | ABSL_FLAG(size_t, slog_log_space_hash_tokens, 128, ""); 6 | ABSL_FLAG(size_t, slog_num_tail_metalog_entries, 32, ""); 7 | 8 | ABSL_FLAG(bool, slog_enable_statecheck, false, ""); 9 | ABSL_FLAG(int, slog_statecheck_interval_sec, 10, ""); 10 | 11 | ABSL_FLAG(bool, slog_engine_force_remote_index, false, ""); 12 | ABSL_FLAG(float, slog_engine_prob_remote_index, 0.0f, ""); 13 | ABSL_FLAG(bool, slog_engine_enable_cache, false, ""); 14 | ABSL_FLAG(int, slog_engine_cache_cap_mb, 1024, ""); 15 | ABSL_FLAG(bool, slog_engine_propagate_auxdata, false, ""); 16 | 17 | ABSL_FLAG(int, slog_storage_cache_cap_mb, 1024, ""); 18 | ABSL_FLAG(std::string, slog_storage_backend, "rocksdb", 19 | "rocskdb, tkrzw_hash, tkrzw_tree, or tkrzw_skip"); 20 | ABSL_FLAG(int, slog_storage_bgthread_interval_ms, 1, ""); 21 | ABSL_FLAG(size_t, slog_storage_max_live_entries, 65536, ""); 22 | -------------------------------------------------------------------------------- /src/log/flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/flags.h" 4 | 5 | ABSL_DECLARE_FLAG(int, slog_local_cut_interval_us); 6 | ABSL_DECLARE_FLAG(int, slog_global_cut_interval_us); 7 | ABSL_DECLARE_FLAG(size_t, slog_log_space_hash_tokens); 8 | ABSL_DECLARE_FLAG(size_t, slog_num_tail_metalog_entries); 9 | 10 | ABSL_DECLARE_FLAG(bool, slog_enable_statecheck); 11 | ABSL_DECLARE_FLAG(int, slog_statecheck_interval_sec); 12 | 13 | ABSL_DECLARE_FLAG(bool, slog_engine_force_remote_index); 14 | ABSL_DECLARE_FLAG(float, slog_engine_prob_remote_index); 15 | ABSL_DECLARE_FLAG(bool, slog_engine_enable_cache); 16 | ABSL_DECLARE_FLAG(int, slog_engine_cache_cap_mb); 17 | ABSL_DECLARE_FLAG(bool, slog_engine_propagate_auxdata); 18 | 19 | ABSL_DECLARE_FLAG(int, slog_storage_cache_cap_mb); 20 | ABSL_DECLARE_FLAG(std::string, slog_storage_backend); 21 | ABSL_DECLARE_FLAG(int, slog_storage_bgthread_interval_ms); 22 | ABSL_DECLARE_FLAG(size_t, slog_storage_max_live_entries); 23 | -------------------------------------------------------------------------------- /src/log/sequencer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "log/sequencer_base.h" 4 | #include "log/log_space.h" 5 | #include "log/utils.h" 6 | 7 | namespace faas { 8 | namespace log { 9 | 10 | class Sequencer final : public SequencerBase { 11 | public: 12 | explicit Sequencer(uint16_t node_id); 13 | ~Sequencer(); 14 | 15 | private: 16 | std::string log_header_; 17 | 18 | absl::Mutex view_mu_; 19 | const View* current_view_ ABSL_GUARDED_BY(view_mu_); 20 | LockablePtr 21 | current_primary_ ABSL_GUARDED_BY(view_mu_); 22 | LogSpaceCollection 23 | primary_collection_ ABSL_GUARDED_BY(view_mu_); 24 | LogSpaceCollection 25 | backup_collection_ ABSL_GUARDED_BY(view_mu_); 26 | 27 | log_utils::FutureRequests future_requests_; 28 | 29 | void OnViewCreated(const View* view) override; 30 | void OnViewFrozen(const View* view) override; 31 | void OnViewFinalized(const FinalizedView* finalized_view) override; 32 | 33 | void HandleTrimRequest(const protocol::SharedLogMessage& request) override; 34 | void OnRecvMetaLogProgress(const protocol::SharedLogMessage& message) override; 35 | void OnRecvShardProgress(const protocol::SharedLogMessage& message, 36 | std::span payload) override; 37 | void OnRecvNewMetaLogs(const protocol::SharedLogMessage& message, 38 | std::span payload) override; 39 | 40 | void ProcessRequests(const std::vector& requests); 41 | 42 | void MarkNextCutIfDoable() override; 43 | 44 | DISALLOW_COPY_AND_ASSIGN(Sequencer); 45 | }; 46 | 47 | } // namespace log 48 | } // namespace faas 49 | -------------------------------------------------------------------------------- /src/log/storage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "log/storage_base.h" 4 | #include "log/log_space.h" 5 | #include "log/utils.h" 6 | 7 | namespace faas { 8 | namespace log { 9 | 10 | class Storage final : public StorageBase { 11 | public: 12 | explicit Storage(uint16_t node_id); 13 | ~Storage(); 14 | 15 | private: 16 | std::string log_header_; 17 | 18 | absl::Mutex view_mu_; 19 | const View* current_view_ ABSL_GUARDED_BY(view_mu_); 20 | bool view_finalized_ ABSL_GUARDED_BY(view_mu_); 21 | LogSpaceCollection 22 | storage_collection_ ABSL_GUARDED_BY(view_mu_); 23 | 24 | log_utils::FutureRequests future_requests_; 25 | 26 | void OnViewCreated(const View* view) override; 27 | void OnViewFinalized(const FinalizedView* finalized_view) override; 28 | 29 | void HandleReadAtRequest(const protocol::SharedLogMessage& request) override; 30 | void HandleReplicateRequest(const protocol::SharedLogMessage& message, 31 | std::span payload) override; 32 | void OnRecvNewMetaLogs(const protocol::SharedLogMessage& message, 33 | std::span payload) override; 34 | void OnRecvLogAuxData(const protocol::SharedLogMessage& message, 35 | std::span payload) override; 36 | 37 | void ProcessReadResults(const LogStorage::ReadResultVec& results); 38 | void ProcessReadFromDB(const protocol::SharedLogMessage& request); 39 | void ProcessRequests(const std::vector& requests); 40 | 41 | void SendEngineLogResult(const protocol::SharedLogMessage& request, 42 | protocol::SharedLogMessage* response, 43 | std::span tags_data, 44 | std::span log_data); 45 | 46 | void BackgroundThreadMain() override; 47 | void SendShardProgressIfNeeded() override; 48 | void FlushLogEntries(); 49 | 50 | DISALLOW_COPY_AND_ASSIGN(Storage); 51 | }; 52 | 53 | } // namespace log 54 | } // namespace faas 55 | -------------------------------------------------------------------------------- /src/log/view_watcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/zk.h" 5 | #include "common/zk_utils.h" 6 | #include "log/view.h" 7 | 8 | __BEGIN_THIRD_PARTY_HEADERS 9 | #include "proto/shared_log.pb.h" 10 | __END_THIRD_PARTY_HEADERS 11 | 12 | namespace faas { 13 | namespace log { 14 | 15 | class FinalizedView; 16 | 17 | class ViewWatcher { 18 | public: 19 | ViewWatcher(); 20 | ~ViewWatcher(); 21 | 22 | void StartWatching(zk::ZKSession* session); 23 | 24 | using ViewCallback = std::function; 25 | void SetViewCreatedCallback(ViewCallback cb); 26 | void SetViewFrozenCallback(ViewCallback cb); 27 | 28 | using ViewFinalizedCallback = std::function; 29 | void SetViewFinalizedCallback(ViewFinalizedCallback cb); 30 | 31 | private: 32 | std::optional watcher_; 33 | 34 | std::vector> views_; 35 | std::vector> finalized_views_; 36 | 37 | ViewCallback view_created_cb_; 38 | ViewCallback view_frozen_cb_; 39 | ViewFinalizedCallback view_finalized_cb_; 40 | 41 | inline uint16_t next_view_id() const { 42 | return gsl::narrow_cast(views_.size()); 43 | } 44 | 45 | inline const View* current_view() const { 46 | return views_.empty() ? nullptr : views_.back().get(); 47 | } 48 | 49 | inline const View* view_with_id(uint16_t view_id) const { 50 | return view_id < views_.size() ? views_.at(view_id).get() : nullptr; 51 | } 52 | 53 | void InstallNextView(const ViewProto& view_proto); 54 | void FinalizeCurrentView(const FinalizedViewProto& finalized_view_proto); 55 | void OnZNodeCreated(std::string_view path, std::span contents); 56 | 57 | DISALLOW_COPY_AND_ASSIGN(ViewWatcher); 58 | }; 59 | 60 | class FinalizedView { 61 | public: 62 | FinalizedView(const View* view, const FinalizedViewProto& finalized_view_proto); 63 | ~FinalizedView() {} 64 | 65 | const View* view() const { return view_; } 66 | 67 | uint32_t final_metalog_position(uint32_t logspace_id) const { 68 | DCHECK(final_metalog_positions_.contains(logspace_id)); 69 | return final_metalog_positions_.at(logspace_id); 70 | } 71 | 72 | using MetaLogVec = std::vector; 73 | const MetaLogVec& tail_metalogs(uint32_t logspace_id) const { 74 | DCHECK(tail_metalogs_.contains(logspace_id)); 75 | return tail_metalogs_.at(logspace_id); 76 | } 77 | 78 | private: 79 | const View* view_; 80 | absl::flat_hash_map final_metalog_positions_; 81 | absl::flat_hash_map tail_metalogs_; 82 | 83 | DISALLOW_COPY_AND_ASSIGN(FinalizedView); 84 | }; 85 | 86 | } // namespace log 87 | } // namespace faas 88 | -------------------------------------------------------------------------------- /src/server/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace faas { 4 | 5 | constexpr int kConnectionTypeMask = 0x7fff0000; 6 | 7 | // ===== Connection type IDs ===== 8 | 9 | constexpr int kGatewayIngressTypeId = 0x00 << 16; 10 | constexpr int kGatewayEgressHubTypeId = 0x01 << 16; 11 | constexpr int kEngineIngressTypeId = 0x02 << 16; 12 | constexpr int kEngineEgressHubTypeId = 0x03 << 16; 13 | constexpr int kSequencerIngressTypeId = 0x04 << 16; 14 | constexpr int kSequencerEgressHubTypeId = 0x05 << 16; 15 | constexpr int kStorageIngressTypeId = 0x06 << 16; 16 | constexpr int kStorageEgressHubTypeId = 0x07 << 16; 17 | 18 | // Timers 19 | constexpr int kTimerTypeId = 0x10 << 16; 20 | constexpr int kSLogLocalCutTimerTypeId = kTimerTypeId + 1; 21 | constexpr int kSLogStateCheckTimerTypeId = kTimerTypeId + 2; 22 | constexpr int kSendShardProgressTimerId = kTimerTypeId + 3; 23 | constexpr int kMetaLogCutTimerId = kTimerTypeId + 3; 24 | 25 | // Used by Gateway 26 | constexpr int kHttpConnectionTypeId = 0x20 << 16; 27 | constexpr int kGrpcConnectionTypeId = 0x21 << 16; 28 | 29 | // Used by Engine 30 | constexpr int kMessageConnectionTypeId = 0x30 << 16; 31 | constexpr int kSequencerConnectionTypeId = 0x31 << 16; 32 | constexpr int kIncomingSLogConnectionTypeId = 0x32 << 16; 33 | constexpr int kSLogMessageHubTypeId = 0x33 << 16; 34 | 35 | // ===== Buffer groups ===== 36 | 37 | // 8-byte buffer group 38 | constexpr uint16_t kOctaBufGroup = 0x00; 39 | // Default buffer group for IngressConnection 40 | // Default buffer size is 64KB 41 | constexpr uint16_t kDefaultIngressBufGroup = 0x01; 42 | 43 | // Used by Gateway 44 | constexpr uint16_t kHttpConnectionBufGroup = 0x10; 45 | constexpr uint16_t kGrpcConnectionBufGroup = 0x11; 46 | 47 | // Used by Engine 48 | constexpr uint16_t kMessageConnectionBufGroup = 0x20; 49 | constexpr uint16_t kSequencerConnectionBufGroup = 0x21; 50 | constexpr uint16_t kIncomingSLogConnectionBufGroup = 0x22; 51 | 52 | } // namespace faas 53 | -------------------------------------------------------------------------------- /src/server/egress_hub.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "utils/socket.h" 5 | #include "utils/appendable_buffer.h" 6 | #include "utils/round_robin_set.h" 7 | #include "server/io_worker.h" 8 | 9 | namespace faas { 10 | namespace server { 11 | 12 | class EgressHub : public ConnectionBase { 13 | public: 14 | EgressHub(int type, const struct sockaddr_in* addr, size_t num_conn); 15 | virtual ~EgressHub(); 16 | 17 | void Start(IOWorker* io_worker) override; 18 | void ScheduleClose() override; 19 | 20 | using HandshakeMessageCallback = std::function; 21 | void SetHandshakeMessageCallback(HandshakeMessageCallback cb); 22 | 23 | void SendMessage(std::span part1, 24 | std::span part2 = EMPTY_CHAR_SPAN, 25 | std::span part3 = EMPTY_CHAR_SPAN, 26 | std::span part4 = EMPTY_CHAR_SPAN); 27 | 28 | private: 29 | enum State { kCreated, kRunning, kClosing, kClosed }; 30 | 31 | IOWorker* io_worker_; 32 | State state_; 33 | struct sockaddr_in addr_; 34 | 35 | std::vector sockfds_; 36 | utils::RoundRobinSet connections_for_pick_; 37 | 38 | HandshakeMessageCallback handshake_message_cb_; 39 | 40 | std::string log_header_; 41 | utils::AppendableBuffer write_buffer_; 42 | bool send_fn_scheduled_; 43 | 44 | void OnSocketConnected(int sockfd, int status); 45 | void SocketReady(int sockfd); 46 | void RemoveSocket(int sockfd); 47 | void ScheduleSendFunction(); 48 | void SendPendingMessages(); 49 | 50 | static std::string GetLogHeader(int type); 51 | 52 | DISALLOW_COPY_AND_ASSIGN(EgressHub); 53 | }; 54 | 55 | } // namespace server 56 | } // namespace faas 57 | -------------------------------------------------------------------------------- /src/server/ingress_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "utils/appendable_buffer.h" 6 | #include "server/io_worker.h" 7 | 8 | namespace faas { 9 | namespace server { 10 | 11 | class IngressConnection : public ConnectionBase { 12 | public: 13 | IngressConnection(int type, int sockfd, size_t msghdr_size); 14 | virtual ~IngressConnection(); 15 | 16 | static constexpr size_t kDefaultBufSize = 65536; 17 | 18 | void Start(IOWorker* io_worker) override; 19 | void ScheduleClose() override; 20 | 21 | void set_buffer_group(uint16_t buf_group, size_t buf_size) { 22 | buf_group_ = buf_group; 23 | buf_size_ = buf_size; 24 | } 25 | 26 | using MessageFullSizeCallback = std::function /* header */)>; 27 | void SetMessageFullSizeCallback(MessageFullSizeCallback cb); 28 | 29 | using NewMessageCallback = std::function /* message */)>; 30 | void SetNewMessageCallback(NewMessageCallback cb); 31 | 32 | static size_t GatewayMessageFullSizeCallback(std::span header); 33 | static NewMessageCallback BuildNewGatewayMessageCallback( 34 | std::function /* payload */)> cb); 36 | 37 | static size_t SharedLogMessageFullSizeCallback(std::span header); 38 | static NewMessageCallback BuildNewSharedLogMessageCallback( 39 | std::function /* payload */)> cb); 41 | 42 | private: 43 | enum State { kCreated, kRunning, kClosing, kClosed }; 44 | 45 | IOWorker* io_worker_; 46 | State state_; 47 | int sockfd_; 48 | size_t msghdr_size_; 49 | 50 | uint16_t buf_group_; 51 | size_t buf_size_; 52 | 53 | MessageFullSizeCallback message_full_size_cb_; 54 | NewMessageCallback new_message_cb_; 55 | 56 | std::string log_header_; 57 | utils::AppendableBuffer read_buffer_; 58 | 59 | void ProcessMessages(); 60 | bool OnRecvData(int status, std::span data); 61 | 62 | static std::string GetLogHeader(int type, int sockfd); 63 | 64 | DISALLOW_COPY_AND_ASSIGN(IngressConnection); 65 | }; 66 | 67 | } // namespace server 68 | } // namespace faas 69 | -------------------------------------------------------------------------------- /src/server/node_watcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "common/zk.h" 6 | #include "common/zk_utils.h" 7 | #include "utils/socket.h" 8 | 9 | namespace faas { 10 | namespace server { 11 | 12 | class NodeWatcher final { 13 | public: 14 | NodeWatcher(); 15 | ~NodeWatcher(); 16 | 17 | void StartWatching(zk::ZKSession* session); 18 | 19 | enum NodeType { 20 | kGatewayNode = 0, 21 | kEngineNode = 1, 22 | kSequencerNode = 2, 23 | kStorageNode = 3, 24 | kTotalNodeType = 4 25 | }; 26 | 27 | using NodeEventCallback = std::function; 28 | void SetNodeOnlineCallback(NodeEventCallback cb); 29 | void SetNodeOfflineCallback(NodeEventCallback cb); 30 | 31 | bool GetNodeAddr(NodeType node_type, uint16_t node_id, struct sockaddr_in* addr); 32 | 33 | static NodeType GetSrcNodeType(protocol::ConnType conn_type); 34 | static NodeType GetDstNodeType(protocol::ConnType conn_type); 35 | 36 | private: 37 | static constexpr const char* kNodeTypeStr[] = { 38 | "GatewayNode", 39 | "EngineNode", 40 | "SequencerNode", 41 | "StorageNode" 42 | }; 43 | 44 | std::optional watcher_; 45 | 46 | NodeEventCallback node_online_cb_; 47 | NodeEventCallback node_offline_cb_; 48 | 49 | absl::Mutex mu_; 50 | absl::flat_hash_map 51 | node_addr_[kTotalNodeType] ABSL_GUARDED_BY(mu_); 52 | 53 | bool ParseNodePath(std::string_view path, NodeType* node_type, uint16_t* node_id); 54 | 55 | void OnZNodeCreated(std::string_view path, std::span contents); 56 | void OnZNodeChanged(std::string_view path, std::span contents); 57 | void OnZNodeDeleted(std::string_view path); 58 | 59 | DISALLOW_COPY_AND_ASSIGN(NodeWatcher); 60 | }; 61 | 62 | } // namespace server 63 | } // namespace faas 64 | -------------------------------------------------------------------------------- /src/server/server_base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/zk.h" 5 | #include "common/protocol.h" 6 | #include "utils/appendable_buffer.h" 7 | #include "server/io_worker.h" 8 | #include "server/node_watcher.h" 9 | #include "server/timer.h" 10 | 11 | namespace faas { 12 | namespace server { 13 | 14 | class ServerBase { 15 | public: 16 | static constexpr size_t kDefaultIOWorkerBufferSize = 65536; 17 | 18 | explicit ServerBase(std::string_view node_name); 19 | virtual ~ServerBase(); 20 | 21 | void Start(); 22 | void ScheduleStop(); 23 | void WaitForFinish(); 24 | 25 | protected: 26 | enum State { kCreated, kBootstrapping, kRunning, kStopping, kStopped }; 27 | std::atomic state_; 28 | 29 | zk::ZKSession* zk_session() { return &zk_session_; } 30 | NodeWatcher* node_watcher() { return &node_watcher_; } 31 | 32 | bool WithinMyEventLoopThread() const; 33 | 34 | void ForEachIOWorker(std::function cb) const; 35 | IOWorker* PickIOWorkerForConnType(int conn_type); 36 | IOWorker* SomeIOWorker() const; 37 | 38 | static IOWorker* CurrentIOWorker() { return IOWorker::current(); } 39 | static IOWorker* CurrentIOWorkerChecked() { return DCHECK_NOTNULL(IOWorker::current()); } 40 | 41 | void RegisterConnection(IOWorker* io_worker, ConnectionBase* connection); 42 | 43 | using ConnectionCallback = std::function; 44 | void ListenForNewConnections(int server_sockfd, ConnectionCallback cb); 45 | 46 | Timer* CreateTimer(int timer_type, IOWorker* io_worker, Timer::Callback cb); 47 | void CreatePeriodicTimer(int timer_type, absl::Duration interval, Timer::Callback cb); 48 | 49 | // Supposed to be implemented by sub-class 50 | virtual void StartInternal() = 0; 51 | virtual void StopInternal() = 0; 52 | virtual void OnConnectionClose(ConnectionBase* connection) = 0; 53 | virtual void OnRemoteMessageConn(const protocol::HandshakeMessage& handshake, 54 | int sockfd) = 0; 55 | 56 | static int GetIngressConnTypeId(protocol::ConnType conn_type, uint16_t node_id); 57 | static int GetEgressHubTypeId(protocol::ConnType conn_type, uint16_t node_id); 58 | 59 | private: 60 | std::string node_name_; 61 | 62 | int stop_eventfd_; 63 | int message_sockfd_; 64 | base::Thread event_loop_thread_; 65 | zk::ZKSession zk_session_; 66 | NodeWatcher node_watcher_; 67 | 68 | mutable std::atomic next_io_worker_for_pick_; 69 | 70 | std::vector> io_workers_; 71 | absl::flat_hash_map pipes_to_io_worker_; 72 | absl::flat_hash_map connection_cbs_; 73 | absl::flat_hash_map next_io_worker_id_; 74 | std::atomic next_connection_id_; 75 | absl::flat_hash_set> timers_; 76 | 77 | void SetupIOWorkers(); 78 | void SetupMessageServer(); 79 | void OnNewMessageConnection(int sockfd); 80 | 81 | void EventLoopThreadMain(); 82 | void DoStop(); 83 | void DoReadClosedConnection(int pipefd); 84 | void DoAcceptConnection(int server_sockfd); 85 | 86 | DISALLOW_COPY_AND_ASSIGN(ServerBase); 87 | }; 88 | 89 | } // namespace server 90 | } // namespace faas 91 | -------------------------------------------------------------------------------- /src/server/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "server/timer.h" 2 | 3 | #include "utils/io.h" 4 | #include "utils/timerfd.h" 5 | #include "server/constants.h" 6 | 7 | namespace faas { 8 | namespace server { 9 | 10 | Timer::Timer(int timer_type, Callback cb) 11 | : ConnectionBase(timer_type), 12 | periodic_(false), 13 | cb_(cb), 14 | io_worker_(nullptr), 15 | state_(kCreated), 16 | timerfd_(-1) {} 17 | 18 | Timer::~Timer() { 19 | DCHECK(state_ == kCreated || state_ == kClosed); 20 | DCHECK(timerfd_ == -1); 21 | } 22 | 23 | void Timer::SetPeriodic(absl::Time initial, absl::Duration interval) { 24 | DCHECK(!periodic_ && state_ == kCreated); 25 | periodic_ = true; 26 | initial_ = initial; 27 | interval_ = interval; 28 | } 29 | 30 | void Timer::Start(server::IOWorker* io_worker) { 31 | DCHECK(io_worker->WithinMyEventLoopThread()); 32 | io_worker_ = io_worker; 33 | timerfd_ = io_utils::CreateTimerFd(); 34 | CHECK(timerfd_ != -1); 35 | io_utils::FdUnsetNonblocking(timerfd_); 36 | URING_DCHECK_OK(current_io_uring()->RegisterFd(timerfd_)); 37 | state_ = kIdle; 38 | if (periodic_) { 39 | absl::Duration initial_duration = initial_ - absl::Now(); 40 | if (initial_duration < absl::ZeroDuration()) { 41 | LOG(WARNING) << "Has past the initial duration"; 42 | initial_duration = absl::Microseconds(1); 43 | } 44 | CHECK(io_utils::SetupTimerFdPeriodic(timerfd_, initial_duration, interval_)); 45 | state_ = kScheduled; 46 | } 47 | URING_DCHECK_OK(current_io_uring()->StartRead( 48 | timerfd_, kOctaBufGroup, 49 | [this] (int status, std::span data) -> bool { 50 | if (state_ != kScheduled) { 51 | return false; 52 | } 53 | if (!periodic_) { 54 | state_ = kIdle; 55 | } 56 | cb_(); 57 | return true; 58 | } 59 | )); 60 | } 61 | 62 | void Timer::ScheduleClose() { 63 | DCHECK(io_worker_->WithinMyEventLoopThread()); 64 | state_ = kClosing; 65 | URING_DCHECK_OK(current_io_uring()->Close(timerfd_, [this] () { 66 | timerfd_ = -1; 67 | state_ = kClosed; 68 | io_worker_->OnConnectionClose(this); 69 | })); 70 | } 71 | 72 | bool Timer::TriggerIn(absl::Duration d) { 73 | DCHECK(!periodic_); 74 | DCHECK(io_worker_->WithinMyEventLoopThread()); 75 | if (state_ != kIdle) { 76 | LOG(WARNING) << "Not in idle state, cannot schedule trigger of this timer!"; 77 | return false; 78 | } 79 | state_ = kScheduled; 80 | CHECK(io_utils::SetupTimerFdOneTime(timerfd_, d)); 81 | return true; 82 | } 83 | 84 | } // namespace server 85 | } // namespace faas 86 | -------------------------------------------------------------------------------- /src/server/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "server/io_worker.h" 5 | 6 | namespace faas { 7 | namespace server { 8 | 9 | class Timer final : public server::ConnectionBase { 10 | public: 11 | using Callback = std::function; 12 | Timer(int timer_type, Callback cb); 13 | ~Timer(); 14 | 15 | void SetPeriodic(absl::Time initial, absl::Duration interval); 16 | 17 | void Start(IOWorker* io_worker) override; 18 | void ScheduleClose() override; 19 | 20 | bool TriggerIn(absl::Duration d); 21 | 22 | private: 23 | enum State { kCreated, kIdle, kScheduled, kClosing, kClosed }; 24 | 25 | bool periodic_; 26 | absl::Time initial_; 27 | absl::Duration interval_; 28 | 29 | Callback cb_; 30 | IOWorker* io_worker_; 31 | State state_; 32 | int timerfd_; 33 | 34 | DISALLOW_COPY_AND_ASSIGN(Timer); 35 | }; 36 | 37 | } // namespace server 38 | } // namespace faas 39 | -------------------------------------------------------------------------------- /src/utils/base64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace utils { 7 | 8 | std::string Base64Encode(std::span data) { 9 | static constexpr const char* kBase64Chars = 10 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 11 | "abcdefghijklmnopqrstuvwxyz" 12 | "0123456789" 13 | "+/"; 14 | static constexpr const char kTrailingChar = '='; 15 | 16 | size_t in_len = data.size(); 17 | size_t len_encoded = (in_len + 2) / 3 * 4; 18 | std::string ret; 19 | ret.reserve(len_encoded); 20 | 21 | const unsigned char* bytes_to_encode = (const unsigned char*) data.data(); 22 | size_t pos = 0; 23 | 24 | while (pos < in_len) { 25 | ret.push_back(kBase64Chars[(bytes_to_encode[pos + 0] & 0xfc) >> 2]); 26 | 27 | if (pos+1 < in_len) { 28 | ret.push_back(kBase64Chars[ ((bytes_to_encode[pos + 0] & 0x03) << 4) 29 | + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]); 30 | 31 | if (pos+2 < in_len) { 32 | ret.push_back(kBase64Chars[ ((bytes_to_encode[pos + 1] & 0x0f) << 2) 33 | + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]); 34 | ret.push_back(kBase64Chars[ bytes_to_encode[pos + 2] & 0x3f]); 35 | } else { 36 | ret.push_back(kBase64Chars[(bytes_to_encode[pos + 1] & 0x0f) << 2]); 37 | ret.push_back(kTrailingChar); 38 | } 39 | } else { 40 | ret.push_back(kBase64Chars[(bytes_to_encode[pos + 0] & 0x03) << 4]); 41 | ret.push_back(kTrailingChar); 42 | ret.push_back(kTrailingChar); 43 | } 44 | 45 | pos += 3; 46 | } 47 | 48 | return ret; 49 | } 50 | 51 | } // namespace utils 52 | } // namespace faas 53 | -------------------------------------------------------------------------------- /src/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 | void ReportCpuRelatedPerfEventValues(std::string_view header, 17 | utils::PerfEventGroup* perf_event_group, 18 | absl::Duration duration, size_t loop_count); 19 | 20 | class BenchLoop { 21 | public: 22 | using LoopFn = std::function; 23 | 24 | explicit BenchLoop(LoopFn fn); 25 | BenchLoop(absl::Duration max_duration, LoopFn fn); 26 | BenchLoop(size_t max_loop_count, LoopFn fn); 27 | ~BenchLoop(); 28 | 29 | absl::Duration elapsed_time() const; 30 | size_t loop_count() const; 31 | 32 | private: 33 | LoopFn fn_; 34 | absl::Duration max_duration_; 35 | size_t max_loop_count_; 36 | 37 | bool finished_; 38 | absl::Duration duration_; 39 | size_t loop_count_; 40 | 41 | void Run(); 42 | DISALLOW_COPY_AND_ASSIGN(BenchLoop); 43 | }; 44 | 45 | template 46 | class Samples { 47 | public: 48 | explicit Samples(size_t buffer_size) 49 | : buffer_size_(buffer_size), 50 | buffer_(new T[buffer_size]), 51 | count_(0), pos_(0) {} 52 | 53 | ~Samples() { delete[] buffer_; } 54 | 55 | void Add(T value) { 56 | count_++; 57 | pos_++; 58 | if (pos_ == buffer_size_) { 59 | LOG(WARNING) << "Internal buffer of Samples not big enough"; 60 | pos_ = 0; 61 | } 62 | buffer_[pos_] = value; 63 | } 64 | 65 | size_t count() const { return count_; } 66 | 67 | void ReportStatistics(std::string_view header) { 68 | size_t size = std::min(count_, buffer_size_); 69 | std::sort(buffer_, buffer_ + size); 70 | LOG(INFO) << header << ": count=" << count_ << ", " 71 | << "p50=" << buffer_[percentile(size, 0.5)] << ", " 72 | << "p70=" << buffer_[percentile(size, 0.7)] << ", " 73 | << "p90=" << buffer_[percentile(size, 0.9)] << ", " 74 | << "p99=" << buffer_[percentile(size, 0.99)] << ", " 75 | << "p99.9=" << buffer_[percentile(size, 0.999)]; 76 | } 77 | 78 | private: 79 | size_t buffer_size_; 80 | T* buffer_; 81 | size_t count_; 82 | size_t pos_; 83 | 84 | size_t percentile(size_t size, double p) { 85 | size_t idx = gsl::narrow_cast(size * p + 0.5); 86 | if (idx < 0) { 87 | return 0; 88 | } else if (idx >= size) { 89 | return size - 1; 90 | } else { 91 | return idx; 92 | } 93 | } 94 | 95 | DISALLOW_COPY_AND_ASSIGN(Samples); 96 | }; 97 | 98 | } // namespace bench_utils 99 | } // namespace faas 100 | -------------------------------------------------------------------------------- /src/utils/bits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace bits { 7 | 8 | inline uint64_t JoinTwo32(uint32_t high, uint32_t low) { 9 | return uint64_t{low} + (uint64_t{high} << 32); 10 | } 11 | 12 | inline uint32_t JoinTwo16(uint16_t high, uint16_t low) { 13 | return uint32_t{low} + (uint32_t{high} << 16); 14 | } 15 | 16 | inline uint32_t LowHalf64(uint64_t x) { 17 | return gsl::narrow_cast(x); 18 | } 19 | 20 | inline uint16_t LowHalf32(uint32_t x) { 21 | return gsl::narrow_cast(x); 22 | } 23 | 24 | inline uint32_t HighHalf64(uint64_t x) { 25 | return gsl::narrow_cast(x >> 32); 26 | } 27 | 28 | inline uint16_t HighHalf32(uint32_t x) { 29 | return gsl::narrow_cast(x >> 16); 30 | } 31 | 32 | inline std::string HexStr(uint64_t x) { 33 | return fmt::format("{:016x}", x); 34 | } 35 | 36 | inline std::string HexStr(uint32_t x) { 37 | return fmt::format("{:08x}", x); 38 | } 39 | 40 | inline std::string HexStr(uint16_t x) { 41 | return fmt::format("{:04x}", x); 42 | } 43 | 44 | inline std::string HexStr0x(uint64_t x) { 45 | return fmt::format("{:#018x}", x); 46 | } 47 | 48 | inline std::string HexStr0x(uint32_t x) { 49 | return fmt::format("{:#010x}", x); 50 | } 51 | 52 | inline std::string HexStr0x(uint16_t x) { 53 | return fmt::format("{:#06x}", x); 54 | } 55 | 56 | } // namespace bits 57 | } // namespace faas 58 | -------------------------------------------------------------------------------- /src/utils/blocking_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error utils/blocking_queue.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | 9 | namespace faas { 10 | namespace utils { 11 | 12 | template 13 | class BlockingQueue { 14 | public: 15 | // A straightforward BlockingQueue implemented with mutex and condition variable 16 | BlockingQueue(); 17 | ~BlockingQueue(); 18 | 19 | void Stop(); 20 | 21 | void Push(const T& value); 22 | void Push(T&& value); 23 | 24 | bool Pop(T* value); 25 | 26 | private: 27 | absl::Mutex mu_; 28 | absl::CondVar cv_; 29 | 30 | std::deque queue_ ABSL_GUARDED_BY(mu_); 31 | bool stopped_ ABSL_GUARDED_BY(mu_); 32 | 33 | DISALLOW_COPY_AND_ASSIGN(BlockingQueue); 34 | }; 35 | 36 | // Start implementation of BlockingQueue 37 | 38 | template 39 | BlockingQueue::BlockingQueue() 40 | : stopped_(false) {} 41 | 42 | template 43 | BlockingQueue::~BlockingQueue() { 44 | DCHECK(stopped_); 45 | if (!queue_.empty()) { 46 | LOG_F(WARNING, "There are {} elements left in the queue", queue_.size()); 47 | } 48 | } 49 | 50 | template 51 | void BlockingQueue::Stop() { 52 | absl::MutexLock lk(&mu_); 53 | stopped_ = true; 54 | cv_.SignalAll(); 55 | } 56 | 57 | template 58 | void BlockingQueue::Push(const T& value) { 59 | absl::MutexLock lk(&mu_); 60 | if (stopped_) { 61 | LOG(INFO) << "This queue has stopped, will ignore this push"; 62 | return; 63 | } 64 | queue_.push_back(value); 65 | cv_.Signal(); 66 | } 67 | 68 | template 69 | void BlockingQueue::Push(T&& value) { 70 | absl::MutexLock lk(&mu_); 71 | if (stopped_) { 72 | LOG(INFO) << "This queue has stopped, will ignore this push"; 73 | return; 74 | } 75 | queue_.push_back(value); 76 | cv_.Signal(); 77 | } 78 | 79 | template 80 | bool BlockingQueue::Pop(T* value) { 81 | absl::MutexLock lk(&mu_); 82 | while (queue_.empty() && !stopped_) { 83 | cv_.Wait(&mu_); 84 | } 85 | if (stopped_) { 86 | return false; 87 | } 88 | DCHECK(!queue_.empty()); 89 | *value = std::move(queue_.front()); 90 | queue_.pop_front(); 91 | return true; 92 | } 93 | 94 | } // namespace utils 95 | } // namespace faas 96 | -------------------------------------------------------------------------------- /src/utils/buffer_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error utils/buffer_pool.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | 9 | namespace faas { 10 | namespace utils { 11 | 12 | // BufferPool is NOT thread-safe 13 | class BufferPool { 14 | public: 15 | BufferPool(std::string_view pool_name, size_t buffer_size) 16 | : pool_name_(std::string(pool_name)), buffer_size_(buffer_size) {} 17 | ~BufferPool() {} 18 | 19 | size_t buffer_size() const { return buffer_size_; } 20 | 21 | void Get(char** buf, size_t* size) { 22 | if (available_buffers_.empty()) { 23 | std::unique_ptr new_buffer(new char[buffer_size_]); 24 | available_buffers_.push_back(new_buffer.get()); 25 | all_buffers_.push_back(std::move(new_buffer)); 26 | LOG(INFO) << "BufferPool[" << pool_name_ << "]: Allocate new buffer, " 27 | << "current buffer count is " << all_buffers_.size(); 28 | } 29 | *buf = available_buffers_.back(); 30 | available_buffers_.pop_back(); 31 | *size = buffer_size_; 32 | } 33 | 34 | void Get(std::span* buf) { 35 | char* base; 36 | size_t len; 37 | Get(&base, &len); 38 | *buf = std::span(base, len); 39 | } 40 | 41 | void Return(char* buf) { 42 | available_buffers_.push_back(buf); 43 | } 44 | 45 | void Return(std::span buf) { 46 | DCHECK_EQ(buf.size(), buffer_size_); 47 | Return(buf.data()); 48 | } 49 | 50 | private: 51 | std::string pool_name_; 52 | size_t buffer_size_; 53 | absl::InlinedVector available_buffers_; 54 | absl::InlinedVector, 16> all_buffers_; 55 | 56 | DISALLOW_COPY_AND_ASSIGN(BufferPool); 57 | }; 58 | 59 | } // namespace utils 60 | } // namespace faas 61 | -------------------------------------------------------------------------------- /src/utils/docker.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/docker.h" 2 | 3 | #include "common/time.h" 4 | #include "utils/fs.h" 5 | 6 | namespace faas { 7 | namespace docker_utils { 8 | 9 | namespace { 10 | std::string cgroupfs_root = "/sys/fs/cgroup"; 11 | 12 | template 13 | bool ReadIntegerFromFile(std::string_view path, T* value) { 14 | std::string contents; 15 | if (!fs_utils::ReadContents(path, &contents)) { 16 | return false; 17 | } 18 | return absl::SimpleAtoi(contents, value); 19 | } 20 | 21 | bool ReadCpuAcctStat(std::string_view container_id, int32_t* user, int32_t* system) { 22 | std::string full_path(fmt::format( 23 | "{}/cpuacct/docker/{}/cpuacct.stat", cgroupfs_root, container_id)); 24 | std::string contents; 25 | if (!fs_utils::ReadContents(full_path, &contents)) { 26 | return false; 27 | } 28 | for (const auto& line : absl::StrSplit(contents, '\n', absl::SkipWhitespace())) { 29 | if (absl::StartsWith(line, "user ")) { 30 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "user "), user)) { 31 | return false; 32 | } 33 | } 34 | if (absl::StartsWith(line, "system ")) { 35 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "system "), system)) { 36 | return false; 37 | } 38 | } 39 | } 40 | return true; 41 | } 42 | } 43 | 44 | void SetCgroupFsRoot(std::string_view path) { 45 | cgroupfs_root = std::string(path); 46 | } 47 | 48 | const std::string kInvalidContainerId(kContainerIdLength, '0'); 49 | 50 | std::string GetSelfContainerId() { 51 | std::string contents; 52 | if (!fs_utils::ReadContents("/proc/self/cgroup", &contents)) { 53 | LOG(ERROR) << "Failed to read /proc/self/cgroup"; 54 | return kInvalidContainerId; 55 | } 56 | size_t pos = contents.find("/docker/"); 57 | if (pos == std::string::npos 58 | || pos + strlen("/docker/") + kContainerIdLength >= contents.length()) { 59 | LOG(ERROR) << "Cannot find docker's cgroup in /proc/self/cgroup"; 60 | return kInvalidContainerId; 61 | } 62 | return contents.substr(pos + strlen("/docker/"), kContainerIdLength); 63 | } 64 | 65 | bool ReadContainerStat(std::string_view container_id, ContainerStat* stat) { 66 | stat->timestamp = GetMonotonicNanoTimestamp(); 67 | if (!ReadIntegerFromFile(fmt::format("{}/cpuacct/docker/{}/cpuacct.usage", 68 | cgroupfs_root, container_id), 69 | &stat->cpu_usage)) { 70 | return false; 71 | } 72 | if (!ReadCpuAcctStat(container_id, &stat->cpu_stat_user, &stat->cpu_stat_sys)) { 73 | return false; 74 | } 75 | return true; 76 | } 77 | 78 | } // namespace docker_utils 79 | } // namespace faas 80 | -------------------------------------------------------------------------------- /src/utils/docker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error utils/docker.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | 9 | namespace faas { 10 | namespace docker_utils { 11 | 12 | // cgroup_fs root by default is /sys/fs/cgroup 13 | void SetCgroupFsRoot(std::string_view path); 14 | 15 | constexpr size_t kContainerIdLength = 64; 16 | extern const std::string kInvalidContainerId; 17 | 18 | // Get container ID of the running process 19 | // Will return kInvalidContainerId if failed 20 | std::string GetSelfContainerId(); 21 | 22 | struct ContainerStat { 23 | int64_t timestamp; // in ns 24 | int64_t cpu_usage; // in ns, from cpuacct.usage 25 | int32_t cpu_stat_user; // in tick, from cpuacct.stat 26 | int32_t cpu_stat_sys; // in tick, from cpuacct.stat 27 | }; 28 | 29 | bool ReadContainerStat(std::string_view container_id, ContainerStat* stat); 30 | 31 | } // namespace docker_utils 32 | } // namespace faas 33 | -------------------------------------------------------------------------------- /src/utils/env_variables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | #ifdef __FAAS_HAVE_ABSL 6 | __BEGIN_THIRD_PARTY_HEADERS 7 | #include 8 | __END_THIRD_PARTY_HEADERS 9 | #endif 10 | 11 | namespace faas { 12 | namespace utils { 13 | 14 | inline std::string_view GetEnvVariable(std::string_view name, 15 | std::string_view default_value = "") { 16 | char* value = getenv(std::string(name).c_str()); 17 | return value != nullptr ? value : default_value; 18 | } 19 | 20 | #ifdef __FAAS_HAVE_ABSL 21 | 22 | template 23 | IntType GetEnvVariableAsInt(std::string_view name, 24 | IntType default_value = 0) { 25 | char* value = getenv(std::string(name).c_str()); 26 | if (value == nullptr) { 27 | return default_value; 28 | } 29 | IntType result; 30 | if (!absl::SimpleAtoi(value, &result)) { 31 | return default_value; 32 | } 33 | return result; 34 | } 35 | 36 | #else 37 | 38 | inline int GetEnvVariableAsInt(std::string_view name, int default_value = 0) { 39 | char* value = getenv(std::string(name).c_str()); 40 | if (value == nullptr) { 41 | return default_value; 42 | } 43 | return atoi(value); 44 | } 45 | 46 | #endif 47 | 48 | } // namespace utils 49 | } // namespace faas 50 | -------------------------------------------------------------------------------- /src/utils/fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace fs_utils { 7 | 8 | bool Exists(std::string_view path); 9 | bool IsFile(std::string_view path); 10 | bool IsDirectory(std::string_view path); 11 | std::string GetRealPath(std::string_view path); 12 | bool MakeDirectory(std::string_view path); 13 | bool Remove(std::string_view path); 14 | bool RemoveDirectoryRecursively(std::string_view path); 15 | bool ReadContents(std::string_view path, std::string* contents); 16 | 17 | // Return fd on success 18 | std::optional Open(std::string_view full_path, int flags); 19 | std::optional Create(std::string_view full_path); 20 | 21 | std::string JoinPath(std::string_view path1, std::string_view path2); 22 | std::string JoinPath(std::string_view path1, std::string_view path2, std::string_view path3); 23 | 24 | } // namespace fs_utils 25 | } // namespace faas 26 | -------------------------------------------------------------------------------- /src/utils/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define XXH_INLINE_ALL 4 | __BEGIN_THIRD_PARTY_HEADERS 5 | #include 6 | __END_THIRD_PARTY_HEADERS 7 | 8 | namespace faas { 9 | namespace hash { 10 | 11 | constexpr uint64_t kDefaultHashSeed64 = 0xecae064502f9bedcULL; 12 | 13 | template 14 | uint64_t xxHash64(IntType value, uint64_t seed = kDefaultHashSeed64) { 15 | return XXH64(&value, sizeof(IntType), seed); 16 | } 17 | 18 | } // namespace hash 19 | } // namespace faas 20 | -------------------------------------------------------------------------------- /src/utils/io.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_USED_IN_BINDING 2 | #include "utils/io.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace faas { 9 | namespace io_utils { 10 | 11 | void FdSetNonblocking(int fd) { 12 | int flags = fcntl(fd, F_GETFL, 0); 13 | PCHECK(flags != -1) << "fcntl F_GETFL failed"; 14 | PCHECK(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0) 15 | << "fcntl F_SETFL failed"; 16 | } 17 | 18 | void FdUnsetNonblocking(int fd) { 19 | int flags = fcntl(fd, F_GETFL, 0); 20 | PCHECK(flags != -1) << "fcntl F_GETFL failed"; 21 | PCHECK(fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == 0) 22 | << "fcntl F_SETFL failed"; 23 | } 24 | 25 | bool FdPollForRead(int fd, int timeout_ms) { 26 | struct pollfd pfd; 27 | pfd.fd = fd; 28 | pfd.events = POLLIN; 29 | int ret = poll(&pfd, 1, timeout_ms); 30 | if (ret == -1) { 31 | PLOG(ERROR) << "poll failed"; 32 | return false; 33 | } 34 | if (ret == 0) { 35 | LOG(ERROR) << "poll on given fifo timeout"; 36 | return false; 37 | } 38 | if ((pfd.revents & POLLIN) == 0) { 39 | LOG(ERROR) << "Error happens on given fifo: revents=" << pfd.revents; 40 | return false; 41 | } 42 | return true; 43 | } 44 | 45 | } // namespace io_utils 46 | } // namespace faas 47 | -------------------------------------------------------------------------------- /src/utils/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | #include 6 | 7 | namespace faas { 8 | namespace io_utils { 9 | 10 | void FdSetNonblocking(int fd); 11 | void FdUnsetNonblocking(int fd); 12 | bool FdPollForRead(int fd, int timeout_ms); 13 | 14 | template 15 | bool SendMessage(int fd, const T& message) { 16 | const char* buffer = reinterpret_cast(&message); 17 | size_t pos = 0; 18 | while (pos < sizeof(T)) { 19 | ssize_t nwrite = write(fd, buffer + pos, sizeof(T) - pos); 20 | DCHECK(nwrite != 0) << "write() returns 0"; 21 | if (nwrite < 0) { 22 | if (errno == EAGAIN || errno == EINTR) { 23 | continue; 24 | } 25 | return false; 26 | } 27 | pos += static_cast(nwrite); 28 | } 29 | return true; 30 | } 31 | 32 | template 33 | bool RecvMessage(int fd, T* message, bool* eof) { 34 | char* buffer = reinterpret_cast(message); 35 | size_t pos = 0; 36 | if (eof != nullptr) { 37 | *eof = false; 38 | } 39 | while (pos < sizeof(T)) { 40 | ssize_t nread = read(fd, buffer + pos, sizeof(T) - pos); 41 | if (nread == 0) { 42 | if (eof != nullptr) { 43 | *eof = true; 44 | } 45 | return false; 46 | } 47 | if (nread < 0) { 48 | if (errno == EAGAIN || errno == EINTR) { 49 | continue; 50 | } 51 | return false; 52 | } 53 | pos += static_cast(nread); 54 | } 55 | return true; 56 | } 57 | 58 | inline bool SendData(int fd, const char* data, size_t size) { 59 | size_t pos = 0; 60 | while (pos < size) { 61 | ssize_t nwrite = write(fd, data + pos, size - pos); 62 | DCHECK(nwrite != 0) << "write() returns 0"; 63 | if (nwrite < 0) { 64 | if (errno == EAGAIN || errno == EINTR) { 65 | continue; 66 | } 67 | return false; 68 | } 69 | pos += static_cast(nwrite); 70 | } 71 | return true; 72 | } 73 | 74 | inline bool SendData(int fd, std::span data) { 75 | return SendData(fd, data.data(), data.size()); 76 | } 77 | 78 | inline bool WriteData(int fd, std::span data) { 79 | return SendData(fd, data.data(), data.size()); 80 | } 81 | 82 | inline bool RecvData(int fd, char* buffer, size_t size, bool* eof) { 83 | size_t pos = 0; 84 | if (eof != nullptr) { 85 | *eof = false; 86 | } 87 | while (pos < size) { 88 | ssize_t nread = read(fd, buffer + pos, size - pos); 89 | if (nread == 0) { 90 | if (eof != nullptr) { 91 | *eof = true; 92 | } 93 | return false; 94 | } 95 | if (nread < 0) { 96 | if (errno == EAGAIN || errno == EINTR) { 97 | continue; 98 | } 99 | return false; 100 | } 101 | pos += static_cast(nread); 102 | } 103 | return true; 104 | } 105 | 106 | } // namespace io_utils 107 | } // namespace faas 108 | -------------------------------------------------------------------------------- /src/utils/perf_event.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_NOWARN_CONVERSION 2 | #include "utils/perf_event.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace faas { 8 | namespace utils { 9 | 10 | namespace { 11 | long perf_event_open(struct perf_event_attr* hw_event, pid_t pid, int cpu, 12 | int group_fd, unsigned long flags) { 13 | return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); 14 | } 15 | } 16 | 17 | PerfEventGroup::PerfEventGroup() 18 | : cpu_(-1), exclude_user_(false), exclude_kernel_(false), group_fd_(-1) {} 19 | 20 | PerfEventGroup::~PerfEventGroup() { 21 | for (int fd : event_fds_) { 22 | PCHECK(close(fd) == 0); 23 | } 24 | } 25 | 26 | bool PerfEventGroup::AddEvent(uint32_t type, uint64_t config) { 27 | struct perf_event_attr pe; 28 | memset(&pe, 0, sizeof(pe)); 29 | pe.type = type; 30 | pe.size = sizeof(pe); 31 | pe.config = config; 32 | pe.read_format = PERF_FORMAT_GROUP; 33 | pe.disabled = 1; 34 | pe.exclude_kernel = exclude_kernel_; 35 | pe.exclude_user = exclude_user_; 36 | int fd = perf_event_open(&pe, 0, cpu_, group_fd_, 0); 37 | if (fd == -1) { 38 | return false; 39 | } 40 | if (group_fd_ == -1) { 41 | group_fd_ = fd; 42 | } 43 | event_fds_.push_back(fd); 44 | return true; 45 | } 46 | 47 | void PerfEventGroup::Reset() { 48 | CHECK(group_fd_ != -1) << "No event has been added yet"; 49 | for (int fd : event_fds_) { 50 | PCHECK(ioctl(fd, PERF_EVENT_IOC_RESET, 0) == 0) << "ioctl (reset) failed"; 51 | } 52 | } 53 | 54 | void PerfEventGroup::Enable() { 55 | CHECK(group_fd_ != -1) << "No event has been added yet"; 56 | for (int fd : event_fds_) { 57 | PCHECK(ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) == 0) << "ioctl (reset) failed"; 58 | } 59 | } 60 | 61 | void PerfEventGroup::Disable() { 62 | CHECK(group_fd_ != -1) << "No event has been added yet"; 63 | for (int fd : event_fds_) { 64 | PCHECK(ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) == 0) << "ioctl (reset) failed"; 65 | } 66 | } 67 | 68 | std::vector PerfEventGroup::ReadValues() { 69 | CHECK(group_fd_ != -1) << "No event has been added yet"; 70 | uint64_t* values = new uint64_t[event_fds_.size() + 1]; 71 | ssize_t read_size = sizeof(uint64_t) * (event_fds_.size() + 1); 72 | PCHECK(read(group_fd_, values, read_size) == read_size); 73 | CHECK_EQ(static_cast(values[0]), event_fds_.size()); 74 | std::vector ret; 75 | for (size_t i = 0; i < event_fds_.size(); i++) { 76 | ret.push_back(values[1 + i]); 77 | } 78 | delete[] values; 79 | return ret; 80 | } 81 | 82 | } // namespace utils 83 | } // namespace faas 84 | -------------------------------------------------------------------------------- /src/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 | ~PerfEventGroup(); 18 | 19 | void set_cpu(int cpu) { cpu_ = cpu; } 20 | void set_exclude_user(bool value) { exclude_user_ = value; } 21 | void set_exclude_kernel(bool value) { exclude_kernel_ = value; } 22 | 23 | bool AddEvent(uint32_t type, uint64_t config); 24 | 25 | void Reset(); 26 | void Enable(); 27 | void Disable(); 28 | std::vector ReadValues(); 29 | 30 | void ResetAndEnable() { 31 | Reset(); 32 | Enable(); 33 | } 34 | 35 | private: 36 | int cpu_; 37 | bool exclude_user_; 38 | bool exclude_kernel_; 39 | 40 | int group_fd_; 41 | std::vector event_fds_; 42 | 43 | DISALLOW_COPY_AND_ASSIGN(PerfEventGroup); 44 | }; 45 | 46 | } // namespace utils 47 | } // namespace faas 48 | -------------------------------------------------------------------------------- /src/utils/procfs.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/procfs.h" 2 | 3 | #include "common/time.h" 4 | #include "utils/fs.h" 5 | 6 | namespace faas { 7 | namespace procfs_utils { 8 | 9 | bool ReadThreadStat(int tid, ThreadStat* stat) { 10 | stat->timestamp = GetMonotonicNanoTimestamp(); 11 | 12 | std::string procfs_stat_path(fmt::format("/proc/self/task/{}/stat", tid)); 13 | std::string stat_contents; 14 | if (!fs_utils::ReadContents(procfs_stat_path, &stat_contents)) { 15 | LOG(ERROR) << "Failed to read " << procfs_stat_path; 16 | return false; 17 | } 18 | size_t first_parentheses_pos = stat_contents.find('('); 19 | size_t last_parentheses_pos = stat_contents.find_last_of(')'); 20 | if (first_parentheses_pos == std::string::npos 21 | || last_parentheses_pos == std::string::npos) { 22 | LOG(ERROR) << "Invalid /proc/[tid]/stat contents"; 23 | return false; 24 | } 25 | std::vector parts = absl::StrSplit( 26 | stat_contents.substr(last_parentheses_pos + 1), ' ', absl::SkipWhitespace()); 27 | if (parts.size() != 50) { 28 | LOG(ERROR) << "Invalid /proc/[tid]/stat contents"; 29 | return false; 30 | } 31 | if (!absl::SimpleAtoi(parts[11], &stat->cpu_stat_user)) { 32 | return false; 33 | } 34 | if (!absl::SimpleAtoi(parts[12], &stat->cpu_stat_sys)) { 35 | return false; 36 | } 37 | 38 | std::string procfs_status_path(fmt::format("/proc/self/task/{}/status", tid)); 39 | std::string status_contents; 40 | if (!fs_utils::ReadContents(procfs_status_path, &status_contents)) { 41 | LOG(ERROR) << "Failed to read " << procfs_status_path; 42 | return false; 43 | } 44 | stat->voluntary_ctxt_switches = -1; 45 | stat->nonvoluntary_ctxt_switches = -1; 46 | for (const auto& line : absl::StrSplit(status_contents, '\n', absl::SkipWhitespace())) { 47 | if (absl::StartsWith(line, "voluntary_ctxt_switches:")) { 48 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "voluntary_ctxt_switches:"), 49 | &stat->voluntary_ctxt_switches)) { 50 | return false; 51 | } 52 | } 53 | if (absl::StartsWith(line, "nonvoluntary_ctxt_switches:")) { 54 | if (!absl::SimpleAtoi(absl::StripPrefix(line, "nonvoluntary_ctxt_switches:"), 55 | &stat->nonvoluntary_ctxt_switches)) { 56 | return false; 57 | } 58 | } 59 | } 60 | if (stat->voluntary_ctxt_switches == -1 || stat->nonvoluntary_ctxt_switches == -1) { 61 | LOG(ERROR) << "Invalid /proc/[tid]/status contents"; 62 | return false; 63 | } 64 | 65 | return true; 66 | } 67 | 68 | std::string ReadHostname() { 69 | std::string hostname; 70 | if (!fs_utils::ReadContents("/proc/sys/kernel/hostname", &hostname)) { 71 | LOG(FATAL) << "Failed to read /proc/sys/kernel/hostname"; 72 | } 73 | hostname = absl::StripSuffix(hostname, "\n"); 74 | return hostname; 75 | } 76 | 77 | } // namespace procfs_utils 78 | } // namespace faas 79 | -------------------------------------------------------------------------------- /src/utils/procfs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __FAAS_SRC 4 | #error utils/protfs.h cannot be included outside 5 | #endif 6 | 7 | #include "base/common.h" 8 | 9 | namespace faas { 10 | namespace procfs_utils { 11 | 12 | struct ThreadStat { 13 | int64_t timestamp; // in ns 14 | int32_t cpu_stat_user; // in tick, from /proc/[tid]/stat utime 15 | int32_t cpu_stat_sys; // in tick, from /proc/[tid]/stat stime 16 | int32_t voluntary_ctxt_switches; // from /proc/[tid]/status 17 | int32_t nonvoluntary_ctxt_switches; // from /proc/[tid]/status 18 | }; 19 | 20 | bool ReadThreadStat(int tid, ThreadStat* stat); 21 | 22 | // Return contents of /proc/sys/kernel/hostname 23 | std::string ReadHostname(); 24 | 25 | } // namespace procfs_utils 26 | } // namespace faas 27 | -------------------------------------------------------------------------------- /src/utils/random.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_USED_IN_BINDING 2 | #include "utils/random.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace faas { 8 | namespace utils { 9 | 10 | namespace { 11 | using RndGen = std::mt19937_64; 12 | 13 | static constexpr size_t kNumRndGens = 64; 14 | static RndGen rd_gens[kNumRndGens]; 15 | static std::atomic next_rd_gen{0}; 16 | static thread_local RndGen* rd_gen{nullptr}; 17 | 18 | static RndGen& GetThreadLocalRndGen() { 19 | if (rd_gen == nullptr) { 20 | size_t rd_gen_idx = next_rd_gen.fetch_add(1); 21 | if (rd_gen_idx >= kNumRndGens) { 22 | LOG(FATAL) << "Not enough statically allocated random generators, " 23 | "consider enlarge kNumRndGens"; 24 | } 25 | rd_gen = &rd_gens[rd_gen_idx]; 26 | uint64_t seed; 27 | while (true) { 28 | ssize_t ret = getrandom(&seed, sizeof(uint64_t), 0); 29 | if (ret == -1 && errno != EINTR) { 30 | PLOG(FATAL) << "getrandom failed"; 31 | } 32 | if (gsl::narrow_cast(ret) == sizeof(uint64_t)) { 33 | break; 34 | } 35 | } 36 | rd_gen->seed(seed); 37 | } 38 | return *rd_gen; 39 | } 40 | } // namespace 41 | 42 | int GetRandomInt(int a, int b) { 43 | DCHECK_LT(a, b); 44 | std::uniform_int_distribution distribution(a, b - 1); 45 | return distribution(GetThreadLocalRndGen()); 46 | } 47 | 48 | float GetRandomFloat(float a, float b) { 49 | DCHECK_LE(a, b); 50 | std::uniform_real_distribution distribution(a, b); 51 | return distribution(GetThreadLocalRndGen()); 52 | } 53 | 54 | double GetRandomDouble(double a, double b) { 55 | DCHECK_LE(a, b); 56 | std::uniform_real_distribution distribution(a, b); 57 | return distribution(GetThreadLocalRndGen()); 58 | } 59 | 60 | } // namespace utils 61 | } // namespace faas 62 | -------------------------------------------------------------------------------- /src/utils/random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace utils { 7 | 8 | // Thread-safe 9 | int GetRandomInt(int a, int b); // In [a, b) 10 | float GetRandomFloat(float a = 0.0f, float b = 1.0f); // In [a, b) 11 | double GetRandomDouble(double a = 0.0, double b = 1.0); // In [a, b) 12 | 13 | } // namespace utils 14 | } // namespace faas 15 | -------------------------------------------------------------------------------- /src/utils/round_robin_set.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace utils { 7 | 8 | template 9 | class RoundRobinSet { 10 | public: 11 | RoundRobinSet(); 12 | ~RoundRobinSet() {} 13 | 14 | size_t size() const { return elems_.size(); } 15 | 16 | bool Add(const T& elem); 17 | bool Remove(const T& elem); 18 | bool PickNext(T* elem); 19 | 20 | private: 21 | std::set elems_; 22 | typename std::set::iterator current_; 23 | DISALLOW_COPY_AND_ASSIGN(RoundRobinSet); 24 | }; 25 | 26 | template 27 | RoundRobinSet::RoundRobinSet() 28 | : current_(elems_.begin()) {} 29 | 30 | template 31 | bool RoundRobinSet::Add(const T& elem) { 32 | auto ret = elems_.insert(elem); 33 | return ret.second; 34 | } 35 | 36 | template 37 | bool RoundRobinSet::Remove(const T& elem) { 38 | auto iter = elems_.find(elem); 39 | if (iter == elems_.end()) { 40 | return false; 41 | } 42 | if (iter == current_) { 43 | current_ = elems_.erase(iter); 44 | } else { 45 | elems_.erase(iter); 46 | } 47 | return true; 48 | } 49 | 50 | template 51 | bool RoundRobinSet::PickNext(T* elem) { 52 | if (elems_.empty()) { 53 | return false; 54 | } 55 | if (current_ == elems_.end()) { 56 | current_ = elems_.begin(); 57 | } 58 | *elem = *current_; 59 | current_++; 60 | return true; 61 | } 62 | 63 | } // namespace utils 64 | } // namespace faas 65 | -------------------------------------------------------------------------------- /src/utils/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | #include 6 | 7 | namespace faas { 8 | namespace utils { 9 | 10 | // Listen on `sockfd` with `backlog` 11 | bool SocketListen(int sockfd, int backlog); 12 | 13 | // Return sockfd on success, and return -1 on error 14 | int UnixSocketBindAndListen(std::string_view path, int backlog = 4); 15 | int UnixSocketConnect(std::string_view path); 16 | int TcpSocketBindAndListen(std::string_view ip, uint16_t port, int backlog = 4); 17 | int TcpSocketConnect(std::string_view ip, uint16_t port); 18 | int Tcp6SocketBindAndListen(std::string_view ip, uint16_t port, int backlog = 4); 19 | int Tcp6SocketConnect(std::string_view ip, uint16_t port); 20 | 21 | // Bind an arbitrary port that is available 22 | int TcpSocketBindArbitraryPort(std::string_view ip, uint16_t* port); 23 | 24 | bool SetTcpSocketNoDelay(int sockfd); 25 | bool SetTcpSocketKeepAlive(int sockfd); 26 | 27 | // `host_or_ip` can be hostname or IP address 28 | bool ResolveHost(std::string_view host_or_ip, std::string* ip); 29 | // `addr_str` assumed to be "[host]:[port]" 30 | bool ResolveTcpAddr(struct sockaddr_in* addr, std::string_view addr_str); 31 | 32 | // Resolve the IP address of a network interface 33 | bool ResolveInterfaceIp(std::string_view interface, std::string* ip); 34 | 35 | bool NetworkOpWithRetry(int max_retry, int sleep_sec, std::function fn); 36 | 37 | } // namespace utils 38 | } // namespace faas 39 | -------------------------------------------------------------------------------- /src/utils/timerfd.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/timerfd.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace faas { 7 | namespace io_utils { 8 | 9 | int CreateTimerFd() { 10 | int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); 11 | if (fd == -1) { 12 | PLOG(ERROR) << "Failed to create timerfd"; 13 | return -1; 14 | } 15 | return fd; 16 | } 17 | 18 | bool SetupTimerFdOneTime(int fd, absl::Duration duration) { 19 | struct itimerspec spec; 20 | memset(&spec, 0, sizeof(spec)); 21 | spec.it_value = absl::ToTimespec(duration); 22 | if (timerfd_settime(fd, 0, &spec, nullptr) != 0) { 23 | PLOG(ERROR) << "timerfd_settime failed"; 24 | return false; 25 | } 26 | return true; 27 | } 28 | 29 | bool SetupTimerFdPeriodic(int fd, absl::Duration initial, absl::Duration duration) { 30 | struct itimerspec spec; 31 | spec.it_value = absl::ToTimespec(initial); 32 | spec.it_interval = absl::ToTimespec(duration); 33 | if (timerfd_settime(fd, 0, &spec, nullptr) != 0) { 34 | PLOG(ERROR) << "timerfd_settime failed"; 35 | return false; 36 | } 37 | return true; 38 | } 39 | 40 | } // namespace io_utils 41 | } // namespace faas 42 | -------------------------------------------------------------------------------- /src/utils/timerfd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | 5 | namespace faas { 6 | namespace io_utils { 7 | 8 | int CreateTimerFd(); 9 | 10 | bool SetupTimerFdOneTime(int fd, absl::Duration duration); 11 | bool SetupTimerFdPeriodic(int fd, absl::Duration initial, absl::Duration duration); 12 | 13 | } // namespace io_utils 14 | } // namespace faas 15 | -------------------------------------------------------------------------------- /src/worker/worker_lib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base/common.h" 4 | #include "common/protocol.h" 5 | #include "ipc/shm_region.h" 6 | 7 | namespace faas { 8 | namespace worker_lib { 9 | 10 | bool GetFuncCallInput(const protocol::Message& dispatch_func_call_message, 11 | std::span* input, 12 | std::unique_ptr* shm_region); 13 | 14 | void FuncCallFinished(const protocol::FuncCall& func_call, 15 | bool success, std::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, std::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 | std::span input, 25 | std::unique_ptr* shm_region, 26 | protocol::Message* invoke_func_message); 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, std::span* output, 32 | std::unique_ptr* shm_region, 33 | bool* pipe_buf_used); 34 | 35 | } // namespace worker_lib 36 | } // namespace faas 37 | -------------------------------------------------------------------------------- /worker/cpp/Makefile: -------------------------------------------------------------------------------- 1 | #### PROJECT SETTINGS #### 2 | # Compiler used 3 | CXX ?= g++ 4 | # Extension of source files used in the project 5 | SRC_EXT = cpp 6 | # General compiler flags 7 | COMPILE_FLAGS = -std=c++17 -Wall -Werror -D__FAAS_CPP_WORKER \ 8 | -DNDEBUG -O3 -DDCHECK_ALWAYS_ON 9 | # Add additional include paths 10 | INCLUDES = -I. -I./src -I./include \ 11 | -I./deps/fmt/include \ 12 | -I./deps/GSL/include \ 13 | -I./deps/json/single_include 14 | # General linker settings 15 | LINK_FLAGS = -ldl 16 | #### END PROJECT SETTINGS #### 17 | 18 | # Function used to check variables. Use on the command line: 19 | # make print-VARNAME 20 | # Useful for debugging and adding features 21 | print-%: ; @echo $*=$($*) 22 | 23 | # Shell used in this makefile 24 | # bash is used for 'echo -en' 25 | SHELL = /bin/bash 26 | # Clear built-in rules 27 | .SUFFIXES: 28 | 29 | # Verbose option, to output compile and link commands 30 | export V := 0 31 | export CMD_PREFIX := @ 32 | ifeq ($(V),1) 33 | CMD_PREFIX := 34 | endif 35 | 36 | BUILD_PATH := build 37 | BIN_PATH := bin 38 | MAIN_BIN := $(BIN_PATH)/func_worker_v1 39 | 40 | CXXFLAGS := $(CXXFLAGS) $(COMPILE_FLAGS) 41 | LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) 42 | 43 | SOURCES = main_v1.cpp \ 44 | worker/v1/func_worker.cpp \ 45 | src/base/logging.cpp \ 46 | src/common/func_config.cpp \ 47 | src/ipc/base.cpp \ 48 | src/ipc/fifo.cpp \ 49 | src/ipc/shm_region.cpp \ 50 | src/utils/fs.cpp \ 51 | src/utils/io.cpp \ 52 | src/utils/random.cpp \ 53 | src/utils/socket.cpp \ 54 | src/worker/worker_lib.cpp 55 | 56 | # Set the object file names, with the source directory stripped 57 | # from the path, and the build path prepended in its place 58 | OBJECTS = $(SOURCES:%.$(SRC_EXT)=$(BUILD_PATH)/%.o) 59 | # Set the dependency files that will be used to add header dependencies 60 | DEPS = $(OBJECTS:.o=.d) 61 | 62 | TIME_FILE = $(dir $@).$(notdir $@)_time 63 | START_TIME = date '+%s' > $(TIME_FILE) 64 | END_TIME = read st < $(TIME_FILE) ; \ 65 | $(RM) $(TIME_FILE) ; \ 66 | st=$$((`date '+%s'` - $$st - 86400)) ; \ 67 | echo `date -u -d @$$st '+%H:%M:%S'` 68 | 69 | # Standard, non-optimized release build 70 | .PHONY: release 71 | release: dirs 72 | @echo "Beginning build" 73 | @$(START_TIME) 74 | @$(MAKE) all --no-print-directory 75 | @echo -n "Total build time: " 76 | @$(END_TIME) 77 | 78 | # Create the directories used in the build 79 | .PHONY: dirs 80 | dirs: 81 | @mkdir -p $(dir $(OBJECTS)) 82 | @mkdir -p $(BIN_PATH) 83 | 84 | # Removes all build files 85 | .PHONY: clean 86 | clean: 87 | @echo "Deleting directories" 88 | @$(RM) -r build bin 89 | 90 | # Main rule, checks the executable and symlinks to the output 91 | all: $(MAIN_BIN) 92 | 93 | # Link the executable 94 | $(MAIN_BIN): $(OBJECTS) 95 | @echo "Linking: $@" 96 | $(CMD_PREFIX)$(CXX) $^ $(LDFLAGS) -o $@ 97 | 98 | .SECONDARY: $(OBJECTS) 99 | 100 | # Add dependency files, if they exist 101 | -include $(DEPS) 102 | 103 | # Source file rules 104 | # After the first compilation they will be joined with the rules from the 105 | # dependency files to provide header dependencies 106 | $(BUILD_PATH)/%.o: %.$(SRC_EXT) 107 | @echo "Compiling: $< -> $@" 108 | $(CMD_PREFIX)$(CXX) $(CXXFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@ 109 | -------------------------------------------------------------------------------- /worker/cpp/deps/GSL: -------------------------------------------------------------------------------- 1 | ../../../deps/GSL -------------------------------------------------------------------------------- /worker/cpp/deps/fmt: -------------------------------------------------------------------------------- 1 | ../../../deps/fmt -------------------------------------------------------------------------------- /worker/cpp/deps/json/single_include: -------------------------------------------------------------------------------- 1 | ../../../../deps/json/single_include -------------------------------------------------------------------------------- /worker/cpp/include/faas/worker_v1_interface.h: -------------------------------------------------------------------------------- 1 | #ifndef _FAAS_WORKER_V1_INTERFACE_H_ 2 | #define _FAAS_WORKER_V1_INTERFACE_H_ 3 | 4 | #include 5 | 6 | #ifdef __FAAS_CPP_WORKER_SRC 7 | #define API_EXPORT 8 | #else 9 | #define API_EXPORT __attribute__ ((visibility ("default"))) 10 | #endif 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif // __cplusplus 15 | 16 | // ================== INTERFACE START ================== 17 | 18 | typedef void (*faas_append_output_fn_t)( 19 | void* caller_context, const char* data, size_t length); 20 | 21 | // Return 0 on success. 22 | typedef int (*faas_invoke_func_fn_t)( 23 | void* caller_context, const char* func_name, 24 | const char* input_data, size_t input_length, 25 | const char** output_data, size_t* output_length); 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 | API_EXPORT int faas_init(); 33 | // Create a new function worker. 34 | // When calling `invoke_func_fn` and `append_output_fn`, caller_context 35 | // received in `faas_create_func_worker` should be passed unchanged. 36 | API_EXPORT int faas_create_func_worker( 37 | void* caller_context, 38 | faas_invoke_func_fn_t invoke_func_fn, 39 | faas_append_output_fn_t append_output_fn, 40 | void** worker_handle); 41 | // Destroy a function worker. 42 | API_EXPORT int faas_destroy_func_worker(void* worker_handle); 43 | // Execute the function. `append_output_fn` can be called multiple 44 | // times to append new data to the output buffer. invoke_func_fn 45 | // can be used to invoke other functions in the system. 46 | // For the same worker_handle, faas_func_call will never be called 47 | // concurrently from different threads, i.e. the implementation 48 | // does not need to be thread-safe for a single function worker. 49 | API_EXPORT int faas_func_call( 50 | void* worker_handle, 51 | const char* input, size_t input_length); 52 | 53 | // =================== INTERFACE END =================== 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif // __cplusplus 58 | 59 | #ifdef __FAAS_CPP_WORKER_SRC 60 | #ifdef __cplusplus 61 | 62 | typedef decltype(faas_init)* faas_init_fn_t; 63 | typedef decltype(faas_create_func_worker)* faas_create_func_worker_fn_t; 64 | typedef decltype(faas_destroy_func_worker)* faas_destroy_func_worker_fn_t; 65 | typedef decltype(faas_func_call)* faas_func_call_fn_t; 66 | 67 | #endif // __cplusplus 68 | #endif // __FAAS_CPP_WORKER_SRC 69 | 70 | #undef API_EXPORT 71 | 72 | #endif // _FAAS_WORKER_V1_INTERFACE_H_ 73 | -------------------------------------------------------------------------------- /worker/cpp/main_v1.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_CPP_WORKER_SRC 2 | #include "base/common.h" 3 | #include "base/logging.h" 4 | #include "ipc/base.h" 5 | #include "utils/env_variables.h" 6 | #include "worker/v1/func_worker.h" 7 | 8 | namespace faas { 9 | 10 | void FuncWorkerV1Main(int argc, char* argv[]) { 11 | if (argc != 2) { 12 | fprintf(stderr, "The only argument should be path to the function library\n"); 13 | exit(EXIT_FAILURE); 14 | } 15 | 16 | logging::Init(utils::GetEnvVariableAsInt("FAAS_VLOG_LEVEL", 0)); 17 | ipc::SetRootPathForIpc( 18 | utils::GetEnvVariable("FAAS_ROOT_PATH_FOR_IPC", "/dev/shm/faas_ipc")); 19 | 20 | auto func_worker = std::make_unique(); 21 | func_worker->set_func_id( 22 | utils::GetEnvVariableAsInt("FAAS_FUNC_ID", -1)); 23 | func_worker->set_fprocess_id( 24 | utils::GetEnvVariableAsInt("FAAS_FPROCESS_ID", -1)); 25 | func_worker->set_client_id( 26 | utils::GetEnvVariableAsInt("FAAS_CLIENT_ID", 0)); 27 | func_worker->set_message_pipe_fd( 28 | utils::GetEnvVariableAsInt("FAAS_MSG_PIPE_FD", -1)); 29 | if (utils::GetEnvVariableAsInt("FAAS_USE_ENGINE_SOCKET", 0) == 1) { 30 | func_worker->enable_use_engine_socket(); 31 | } 32 | func_worker->set_engine_tcp_port( 33 | utils::GetEnvVariableAsInt("FAAS_ENGINE_TCP_PORT", -1)); 34 | func_worker->set_func_library_path(argv[1]); 35 | func_worker->Serve(); 36 | } 37 | 38 | } // namespace faas 39 | 40 | int main(int argc, char* argv[]) { 41 | faas::FuncWorkerV1Main(argc, argv); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /worker/cpp/src: -------------------------------------------------------------------------------- 1 | ../../src -------------------------------------------------------------------------------- /worker/golang/common/time.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | _ "unsafe" // required to use //go:linkname 5 | ) 6 | 7 | //go:noescape 8 | //go:linkname nanotime runtime.nanotime 9 | func nanotime() int64 10 | 11 | func GetMonotonicMicroTimestamp() int64 { 12 | return nanotime() / 1000 13 | } 14 | -------------------------------------------------------------------------------- /worker/golang/config/func_config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "strings" 8 | ) 9 | 10 | type FuncConfigEntry struct { 11 | FuncName string `json:"funcName"` 12 | FuncId uint16 `json:"funcId"` 13 | GrpcMethods []string `json:"grpcMethods"` 14 | } 15 | 16 | var entries []*FuncConfigEntry 17 | var entriesByFuncId map[uint16]*FuncConfigEntry 18 | var entriesByFuncName map[string]*FuncConfigEntry 19 | 20 | func InitFuncConfig(jsonContents []byte) error { 21 | entries = make([]*FuncConfigEntry, 0) 22 | err := json.Unmarshal(jsonContents, &entries) 23 | if err != nil { 24 | return fmt.Errorf("Failed to unmarshal json: %v", err) 25 | } 26 | entriesByFuncId = make(map[uint16]*FuncConfigEntry) 27 | entriesByFuncName = make(map[string]*FuncConfigEntry) 28 | for _, entry := range entries { 29 | if entriesByFuncId[entry.FuncId] != nil { 30 | return fmt.Errorf("Duplicate func_id %d", entry.FuncId) 31 | } 32 | entriesByFuncId[entry.FuncId] = entry 33 | if entriesByFuncName[entry.FuncName] != nil { 34 | return fmt.Errorf("Duplicate func_name %d", entry.FuncName) 35 | } 36 | if strings.HasPrefix(entry.FuncName, "grpc:") { 37 | serviceName := strings.TrimPrefix(entry.FuncName, "grpc:") 38 | log.Printf("[INFO] Load configuration for gRPC service %s", serviceName) 39 | for _, methodName := range entry.GrpcMethods { 40 | log.Printf("[INFO] Register method %s for gRPC service %s", methodName, serviceName) 41 | } 42 | } else { 43 | log.Printf("[INFO] Load configuration for function %s[%d]", entry.FuncName, entry.FuncId) 44 | } 45 | entriesByFuncName[entry.FuncName] = entry 46 | } 47 | return nil 48 | } 49 | 50 | func FindByFuncName(funcName string) *FuncConfigEntry { 51 | return entriesByFuncName[funcName] 52 | } 53 | 54 | func FindByFuncId(funcId uint16) *FuncConfigEntry { 55 | return entriesByFuncId[funcId] 56 | } 57 | 58 | func (fcEntry *FuncConfigEntry) FindGrpcMethod(method string) int { 59 | for idx, methodName := range fcEntry.GrpcMethods { 60 | if methodName == method { 61 | return idx 62 | } 63 | } 64 | return -1 65 | } 66 | -------------------------------------------------------------------------------- /worker/golang/go.mod: -------------------------------------------------------------------------------- 1 | module cs.utexas.edu/zjia/faas 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /worker/golang/ipc/base.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | var rootPathForIpc string 8 | 9 | const fileCreatMode = 0664 10 | 11 | func SetRootPathForIpc(path string) { 12 | rootPathForIpc = path 13 | } 14 | 15 | func GetEngineUnixSocketPath() string { 16 | return fmt.Sprintf("%s/engine.sock", rootPathForIpc) 17 | } 18 | 19 | func GetFuncWorkerInputFifoName(clientId uint16) string { 20 | return fmt.Sprintf("worker_%d_input", clientId) 21 | } 22 | 23 | func GetFuncWorkerOutputFifoName(clientId uint16) string { 24 | return fmt.Sprintf("worker_%d_output", clientId) 25 | } 26 | 27 | func GetFuncCallInputShmName(fullCallId uint64) string { 28 | return fmt.Sprintf("%d.i", fullCallId) 29 | } 30 | 31 | func GetFuncCallOutputShmName(fullCallId uint64) string { 32 | return fmt.Sprintf("%d.o", fullCallId) 33 | } 34 | 35 | func GetFuncCallOutputFifoName(fullCallId uint64) string { 36 | return fmt.Sprintf("%d.o", fullCallId) 37 | } 38 | -------------------------------------------------------------------------------- /worker/golang/ipc/fifo.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "syscall" 7 | ) 8 | 9 | func FifoCreate(name string) error { 10 | return syscall.Mkfifo(fmt.Sprintf("%s/fifo/%s", rootPathForIpc, name), fileCreatMode) 11 | } 12 | 13 | func FifoRemove(name string) { 14 | os.Remove(fmt.Sprintf("%s/fifo/%s", rootPathForIpc, name)) 15 | } 16 | 17 | func FifoOpenForRead(name string, nonblocking bool) (*os.File, error) { 18 | fifoPath := fmt.Sprintf("%s/fifo/%s", rootPathForIpc, name) 19 | flags := syscall.O_RDONLY 20 | if nonblocking { 21 | flags |= syscall.O_NONBLOCK 22 | } 23 | fd, err := syscall.Open(fifoPath, flags, 0) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return os.NewFile(uintptr(fd), fifoPath), nil 28 | } 29 | 30 | func FifoOpenForWrite(name string, nonblocking bool) (*os.File, error) { 31 | fifoPath := fmt.Sprintf("%s/fifo/%s", rootPathForIpc, name) 32 | flags := syscall.O_WRONLY 33 | if nonblocking { 34 | flags |= syscall.O_NONBLOCK 35 | } 36 | fd, err := syscall.Open(fifoPath, flags, 0) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return os.NewFile(uintptr(fd), fifoPath), nil 41 | } 42 | 43 | func FifoOpenForReadWrite(name string, nonblocking bool) (*os.File, error) { 44 | fifoPath := fmt.Sprintf("%s/fifo/%s", rootPathForIpc, name) 45 | flags := syscall.O_RDWR 46 | if nonblocking { 47 | flags |= syscall.O_NONBLOCK 48 | } 49 | fd, err := syscall.Open(fifoPath, flags, 0) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return os.NewFile(uintptr(fd), fifoPath), nil 54 | } 55 | -------------------------------------------------------------------------------- /worker/golang/ipc/shm_region.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "syscall" 7 | ) 8 | 9 | type ShmRegion struct { 10 | Name string 11 | Data []byte 12 | Size int 13 | } 14 | 15 | func ShmCreate(name string, size int) (*ShmRegion, error) { 16 | flags := syscall.O_CREAT | syscall.O_EXCL | syscall.O_RDWR 17 | fd, err := syscall.Open(shmFullPath(name), flags, fileCreatMode) 18 | if err != nil { 19 | return nil, fmt.Errorf("open failed: %v", err) 20 | } 21 | defer syscall.Close(fd) 22 | err = syscall.Ftruncate(fd, int64(size)) 23 | if err != nil { 24 | return nil, fmt.Errorf("ftruncate failed: %v", err) 25 | } 26 | if size > 0 { 27 | flags = syscall.PROT_READ | syscall.PROT_WRITE 28 | data, err := syscall.Mmap(fd, 0, size, flags, syscall.MAP_SHARED) 29 | if err != nil { 30 | return nil, fmt.Errorf("mmap failed: %v", err) 31 | } 32 | return &ShmRegion{ 33 | Name: name, 34 | Data: data, 35 | Size: size, 36 | }, nil 37 | } else { 38 | return &ShmRegion{ 39 | Name: name, 40 | Data: nil, 41 | Size: 0, 42 | }, nil 43 | } 44 | } 45 | 46 | func ShmOpen(name string, readonly bool) (*ShmRegion, error) { 47 | flags := syscall.O_RDWR 48 | if readonly { 49 | flags = syscall.O_RDONLY 50 | } 51 | fd, err := syscall.Open(shmFullPath(name), flags, 0) 52 | if err != nil { 53 | return nil, fmt.Errorf("open failed: %v", err) 54 | } 55 | defer syscall.Close(fd) 56 | var stat syscall.Stat_t 57 | err = syscall.Fstat(fd, &stat) 58 | if err != nil { 59 | return nil, fmt.Errorf("fstat failed: %v", err) 60 | } 61 | if stat.Size > 0 { 62 | size := int(stat.Size) 63 | flags = syscall.PROT_READ 64 | if !readonly { 65 | flags |= syscall.PROT_WRITE 66 | } 67 | data, err := syscall.Mmap(fd, 0, size, flags, syscall.MAP_SHARED) 68 | if err != nil { 69 | return nil, fmt.Errorf("mmap failed: %v", err) 70 | } 71 | return &ShmRegion{ 72 | Name: name, 73 | Data: data, 74 | Size: size, 75 | }, nil 76 | } else { 77 | return &ShmRegion{ 78 | Name: name, 79 | Data: nil, 80 | Size: 0, 81 | }, nil 82 | } 83 | } 84 | 85 | func (r *ShmRegion) Close() { 86 | if r.Size > 0 { 87 | syscall.Munmap(r.Data) 88 | r.Data = nil 89 | } 90 | } 91 | 92 | func (r *ShmRegion) Remove() { 93 | os.Remove(shmFullPath(r.Name)) 94 | } 95 | 96 | func shmFullPath(shmName string) string { 97 | return fmt.Sprintf("%s/shm/%s", rootPathForIpc, shmName) 98 | } 99 | -------------------------------------------------------------------------------- /worker/golang/lib.go: -------------------------------------------------------------------------------- 1 | package faas 2 | 3 | import ( 4 | "encoding/binary" 5 | "log" 6 | "os" 7 | "runtime" 8 | "strconv" 9 | 10 | config "cs.utexas.edu/zjia/faas/config" 11 | ipc "cs.utexas.edu/zjia/faas/ipc" 12 | protocol "cs.utexas.edu/zjia/faas/protocol" 13 | types "cs.utexas.edu/zjia/faas/types" 14 | worker "cs.utexas.edu/zjia/faas/worker" 15 | ) 16 | 17 | func Serve(factory types.FuncHandlerFactory) { 18 | runtime.GOMAXPROCS(1) 19 | ipc.SetRootPathForIpc(os.Getenv("FAAS_ROOT_PATH_FOR_IPC")) 20 | funcId, err := strconv.Atoi(os.Getenv("FAAS_FUNC_ID")) 21 | if err != nil { 22 | log.Fatal("[FATAL] Failed to parse FAAS_FUNC_ID") 23 | } 24 | clientId, err := strconv.Atoi(os.Getenv("FAAS_CLIENT_ID")) 25 | if err != nil { 26 | log.Fatal("[FATAL] Failed to parse FAAS_CLIENT_ID") 27 | } 28 | msgPipeFd, err := strconv.Atoi(os.Getenv("FAAS_MSG_PIPE_FD")) 29 | if err != nil { 30 | log.Fatal("[FATAL] Failed to parse FAAS_MSG_PIPE_FD") 31 | } 32 | 33 | msgPipe := os.NewFile(uintptr(msgPipeFd), "msg_pipe") 34 | payloadSizeBuf := make([]byte, 4) 35 | nread, err := msgPipe.Read(payloadSizeBuf) 36 | if err != nil || nread != len(payloadSizeBuf) { 37 | log.Fatal("[FATAL] Failed to read payload size") 38 | } 39 | payloadSize := binary.LittleEndian.Uint32(payloadSizeBuf) 40 | payload := make([]byte, payloadSize) 41 | nread, err = msgPipe.Read(payload) 42 | if err != nil || nread != len(payload) { 43 | log.Fatal("[FATAL] Failed to read payload") 44 | } 45 | err = config.InitFuncConfig(payload) 46 | if err != nil { 47 | log.Fatal("[FATAL] InitFuncConfig failed: %s", err) 48 | } 49 | w, err := worker.NewFuncWorker(uint16(funcId), uint16(clientId), factory) 50 | if err != nil { 51 | log.Fatal("[FATAL] Failed to create FuncWorker: ", err) 52 | } 53 | numWorkers := 1 54 | go func(w *worker.FuncWorker) { 55 | w.Run() 56 | }(w) 57 | 58 | maxProcFactor, err := strconv.Atoi(os.Getenv("FAAS_GO_MAX_PROC_FACTOR")) 59 | if err != nil { 60 | maxProcFactor = 8 61 | } 62 | 63 | for { 64 | message := protocol.NewEmptyMessage() 65 | nread, err := msgPipe.Read(message) 66 | if err != nil || nread != protocol.MessageFullByteSize { 67 | log.Fatal("[FATAL] Failed to read launcher message") 68 | } 69 | if protocol.IsCreateFuncWorkerMessage(message) { 70 | clientId := protocol.GetClientIdFromMessage(message) 71 | w, err := worker.NewFuncWorker(uint16(funcId), clientId, factory) 72 | if err != nil { 73 | log.Fatal("[FATAL] Failed to create FuncWorker: ", err) 74 | } 75 | numWorkers += 1 76 | planedMaxProcs := (numWorkers-1)/maxProcFactor + 1 77 | currentMaxProcs := runtime.GOMAXPROCS(0) 78 | if planedMaxProcs > currentMaxProcs { 79 | runtime.GOMAXPROCS(planedMaxProcs) 80 | log.Printf("[INFO] Current GOMAXPROCS is %d", planedMaxProcs) 81 | } 82 | go func(w *worker.FuncWorker) { 83 | w.Run() 84 | }(w) 85 | } else { 86 | log.Fatal("[FATAL] Unknown message type") 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /worker/golang/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type LogEntry struct { 8 | SeqNum uint64 9 | Tags []uint64 10 | Data []byte 11 | AuxData []byte 12 | } 13 | 14 | type Environment interface { 15 | InvokeFunc(ctx context.Context, funcName string, input []byte) ( /* output */ []byte, error) 16 | InvokeFuncAsync(ctx context.Context, funcName string, input []byte) error 17 | GrpcCall(ctx context.Context, service string, method string, request []byte) ( /* reply */ []byte, error) 18 | 19 | GenerateUniqueID() uint64 20 | 21 | // Shared log operations 22 | // Append a new log entry, tags must be non-zero 23 | SharedLogAppend(ctx context.Context, tags []uint64, data []byte) ( /* seqnum */ uint64, error) 24 | // Read the first log with `tag` whose seqnum >= given `seqNum` 25 | // `tag`==0 means considering log with any tag, including empty tag 26 | SharedLogReadNext(ctx context.Context, tag uint64, seqNum uint64) (*LogEntry, error) 27 | SharedLogReadNextBlock(ctx context.Context, tag uint64, seqNum uint64) (*LogEntry, error) 28 | // Read the last log with `tag` whose seqnum <= given `seqNum` 29 | // `tag`==0 means considering log with any tag, including empty tag 30 | SharedLogReadPrev(ctx context.Context, tag uint64, seqNum uint64) (*LogEntry, error) 31 | // Alias for ReadPrev(tag, MaxSeqNum) 32 | SharedLogCheckTail(ctx context.Context, tag uint64) (*LogEntry, error) 33 | // Set auxiliary data for log entry of given `seqNum` 34 | SharedLogSetAuxData(ctx context.Context, seqNum uint64, auxData []byte) error 35 | } 36 | 37 | type FuncHandler interface { 38 | Call(ctx context.Context, input []byte) ( /* output */ []byte, error) 39 | } 40 | 41 | type GrpcFuncHandler interface { 42 | Call(ctx context.Context, method string, request []byte) ( /* reply */ []byte, error) 43 | } 44 | 45 | type FuncHandlerFactory interface { 46 | New(env Environment, funcName string) (FuncHandler, error) 47 | GrpcNew(env Environment, service string) (GrpcFuncHandler, error) 48 | } 49 | -------------------------------------------------------------------------------- /worker/nodejs/addon.cpp: -------------------------------------------------------------------------------- 1 | #define __FAAS_NODE_ADDON_SRC 2 | #include "base/logging.h" 3 | #include "utils/env_variables.h" 4 | #include "engine.h" 5 | 6 | #include 7 | 8 | namespace faas { 9 | namespace nodejs { 10 | 11 | Napi::Object InitModule(Napi::Env env, Napi::Object exports) { 12 | logging::Init(utils::GetEnvVariableAsInt("FAAS_VLOG_LEVEL", 0)); 13 | return Engine::Init(env, exports); 14 | } 15 | 16 | } // namespace nodejs 17 | } // namespace faas 18 | 19 | Napi::Object InitAll(Napi::Env env, Napi::Object exports) { 20 | return faas::nodejs::InitModule(env, exports); 21 | } 22 | 23 | NODE_API_MODULE(addon, InitAll) 24 | -------------------------------------------------------------------------------- /worker/nodejs/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ 6 | "addon.cpp", 7 | "engine.cpp", 8 | "src/base/logging.cpp", 9 | "src/common/func_config.cpp", 10 | "src/ipc/base.cpp", 11 | "src/ipc/fifo.cpp", 12 | "src/ipc/shm_region.cpp", 13 | "src/utils/fs.cpp", 14 | "src/utils/io.cpp", 15 | "src/utils/random.cpp", 16 | "src/utils/socket.cpp", 17 | "src/worker/worker_lib.cpp", 18 | "src/worker/event_driven_worker.cpp" 19 | ], 20 | "include_dirs": [ 21 | " 9 | 10 | namespace faas { 11 | namespace nodejs { 12 | 13 | class Engine : public Napi::ObjectWrap { 14 | public: 15 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 16 | explicit Engine(const Napi::CallbackInfo& info); 17 | ~Engine(); 18 | 19 | private: 20 | Napi::Env env_; 21 | std::unique_ptr worker_; 22 | Napi::FunctionReference handler_; 23 | 24 | uv_loop_t* uv_loop_; 25 | utils::SimpleObjectPool uv_poll_pool_; 26 | std::unordered_map uv_poll_to_fds_; 27 | std::unordered_map fd_to_uv_polls_; 28 | 29 | std::unordered_map 30 | outgoing_func_call_cbs_; 31 | 32 | void StartInternal(Napi::Function handler); 33 | 34 | Napi::Value IsGrpcService(const Napi::CallbackInfo& info); 35 | Napi::Value GetFuncName(const Napi::CallbackInfo& info); 36 | Napi::Value Start(const Napi::CallbackInfo& info); 37 | Napi::Value InvokeFunc(const Napi::CallbackInfo& info); 38 | Napi::Value GrpcCall(const Napi::CallbackInfo& info); 39 | static Napi::Value IncomingFuncCallFinished(const Napi::CallbackInfo& info); 40 | 41 | void AddWatchFdReadable(int fd); 42 | void RemoveWatchFdReadable(int fd); 43 | void OnIncomingFuncCall(int64_t handle, std::string_view method, std::span request); 44 | void OnOutgoingFuncCallComplete(int64_t handle, bool success, std::span output); 45 | 46 | void RemovePoll(uv_poll_t* uv_poll); 47 | 48 | DECLARE_UV_POLL_CB_FOR_CLASS(FdEvent); 49 | DECLARE_UV_CLOSE_CB_FOR_CLASS(PollClose); 50 | 51 | DISALLOW_COPY_AND_ASSIGN(Engine); 52 | }; 53 | 54 | } // namespace nodejs 55 | } // namespace faas 56 | -------------------------------------------------------------------------------- /worker/nodejs/index.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon') 2 | 3 | class Context { 4 | constructor (engine, handle) { 5 | this.engine = engine 6 | this.handle = handle 7 | } 8 | 9 | invokeFunc (funcName, input, cb) { 10 | this.engine.invokeFunc(this.handle, funcName, input, cb) 11 | } 12 | 13 | grpcCall (service, method, request, cb) { 14 | this.engine.grpcCall(this.handle, service, method, request, cb) 15 | } 16 | } 17 | 18 | exports.serveForever = function (handlerFactory) { 19 | const engine = new addon.Engine() 20 | if (engine.isGrpcService()) { 21 | throw new Error('Can only call serveForever for normal function') 22 | } 23 | const handler = handlerFactory(engine.getFuncName()) 24 | engine.start(function (handle, input, callback) { 25 | handler(new Context(engine, handle), input, function (err, output) { 26 | if (err) { 27 | callback(handle, false) 28 | } else { 29 | callback(handle, true, output) 30 | } 31 | }) 32 | }) 33 | } 34 | 35 | exports.serveGrpcService = function (service, implementation) { 36 | const engine = new addon.Engine() 37 | if (!engine.isGrpcService()) { 38 | throw new Error('Can only call serveGrpcService for gRPC service') 39 | } 40 | engine.start(function (handle, method, request, callback) { 41 | const methodDef = service[method] 42 | if (methodDef) { 43 | const handler = implementation[methodDef.originalName] 44 | if (handler) { 45 | handler({ 46 | request: methodDef.requestDeserialize(request), 47 | faasContext: new Context(engine, handle), 48 | }, function (err, reply) { 49 | if (err) { 50 | callback(handle, false) 51 | } else { 52 | callback(handle, true, methodDef.responseSerialize(reply)) 53 | } 54 | }) 55 | } else { 56 | callback(new Error('Cannot find handler function for method ' + method)) 57 | } 58 | } else { 59 | callback(new Error('Cannot process method ' + method)) 60 | } 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /worker/nodejs/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "faas", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "bindings": { 8 | "version": "1.5.0", 9 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 10 | "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 11 | "requires": { 12 | "file-uri-to-path": "1.0.0" 13 | } 14 | }, 15 | "file-uri-to-path": { 16 | "version": "1.0.0", 17 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 18 | "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" 19 | }, 20 | "node-addon-api": { 21 | "version": "3.0.0", 22 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz", 23 | "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /worker/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "faas", 3 | "version": "0.0.1", 4 | "description": "FaaS worker library", 5 | "main": "index.js", 6 | "private": true, 7 | "gypfile": true, 8 | "engines": { 9 | "node": "~10 >=10.20 || >=12.17" 10 | }, 11 | "dependencies": { 12 | "bindings": "~1.5.0", 13 | "node-addon-api": "^3.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /worker/nodejs/src: -------------------------------------------------------------------------------- 1 | ../../src -------------------------------------------------------------------------------- /worker/python/deps/GSL: -------------------------------------------------------------------------------- 1 | ../../../deps/GSL -------------------------------------------------------------------------------- /worker/python/deps/fmt: -------------------------------------------------------------------------------- 1 | ../../../deps/fmt -------------------------------------------------------------------------------- /worker/python/deps/json/single_include: -------------------------------------------------------------------------------- 1 | ../../../../deps/json/single_include -------------------------------------------------------------------------------- /worker/python/deps/pybind11: -------------------------------------------------------------------------------- 1 | ../../../deps/pybind11 -------------------------------------------------------------------------------- /worker/python/src: -------------------------------------------------------------------------------- 1 | ../../src --------------------------------------------------------------------------------