diff --git a/.gitmodules b/.gitmodules index 49853b8d2f..c5857050fa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "dpdk"] - path = dpdk - url = ../dpdk +[submodule "seastar"] + path = seastar + url = ../seastar [submodule "swagger-ui"] path = swagger-ui url = ../urchin-swagger-ui diff --git a/LICENSE.seastar b/LICENSE.seastar deleted file mode 100644 index f433b1a53f..0000000000 --- a/LICENSE.seastar +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/NOTICE b/NOTICE deleted file mode 100644 index ef3d318a60..0000000000 --- a/NOTICE +++ /dev/null @@ -1,11 +0,0 @@ -Seastar Framework -Copyright 2015 Cloudius Systems - -This works contains software from the OSv project (http://osv.io), licensed -under the BSD license. - -This work contains software from the DPDK project (http://dpdk.org), licensed -under the BSD license. The software is under the dpdk/ directory. - -This works contains software from https://github.com/hideo55/cpp-HyperLogLog, licensed -under the MIT license. diff --git a/README-OSv.md b/README-OSv.md deleted file mode 100644 index 55fe7fdca5..0000000000 --- a/README-OSv.md +++ /dev/null @@ -1,108 +0,0 @@ -Running Seastar on OSv -====================== - -1. Compiling Seastar for OSv ----------------------------- - -Before compiling Seastar, configure it with the following command: - -./configure.py --so --disable-hwloc \ - --cflags="-DDEFAULT_ALLOCATOR -fvisibility=default -DHAVE_OSV -I../osv/include" \ - --mode release - -Or more easily, use the "--with-osv=..." shortcut for all the above settings: - -./configure.py --mode release --with-osv=../osv - - -Explanation of these configuration options: - * The "--so" option is needed so that the Seastar applications, such as - httpd, are built as shared objects instead of ordinary executables. - Note the "--pie" option also exists, but because of bug #352 in OSv, - and the fact that Seastar uses thread_local in one place, these PIEs - cannot be run on OSv. - * The "--disable-hwloc" option is needed so that Seastar does not attempt - to use the complex NUMA-discovery library, which isn't supported on OSv - (and isn't relevant anyway, because VMs with NUMA are not (yet) common. - * The "-DEFAULT_ALLOCATOR" uses in Seastar the system's regular malloc() - and free(), instead of redefining them. Without this flag, what seems - to happen is that some code compiled into the OSv kernel (notably, - libboost_program_options.a) uses the standard malloc(), while inline - code compiled into Seastar uses the Seastar free() to try and free that - memory, resulting in a spectacular crash. - * The "-fvisibility=default" option disables the "-fvisibility=hidden" - option which is hard-coded into Seastar's build file. Supposedly - "-fvisibility=hidden" provides some better optimization in some cases, - but it also means OSv can't find main() in the generated shared object! - So either we use "-fvisibility=default", as suggested here, or - alternatively, make *only* main() visible - for example, to make httpd's - main() visible add in apps/httpd/httpd.cc, before the main() definition, - the chant: [[gnu::visibility("default")]] - * The "-DHAVE_OSV" conditionally compiles code in Seastar that relies - on OSv APIs (currently, this enables virtio device assignment). - This OSv-specific code relies on some OSv header files, which is - why the "-I../osv/include" is needed (where "../osv" is where the - OSv source tree is open). - * The "--mode release" is to compile only the release build. You'll - usually not want to debug Seastar on OSv (it's easier to debug on Linux). - - -2. Building a Seastar-httpd module for OSv ------------------------------------------- - -As an example, we'll build a "seastar" module in OSv running Seastar's -httpd application. - -In the OSv working directory, create a directory apps/seastar in it put: - -* a link to the httpd binary in the Seastar working directory. I.e., - ln -s ../../../seastar/build/release/apps/httpd/httpd httpd - -* A usr.manifest file, adding only this single "httpd" executable to the image: - /httpd: ${MODULE_DIR}/httpd - -* A module.py file with a default command line: - - from osv.modules import api - default = api.run(cmdline="/httpd --no-handle-interrupt") - -The "--no-handle-interrupt" is needed so that Seastar does not attempt to -use signalfd() to capture ^C. signalfd is not yet available on OSv, and -the ^C handling is not a very important feature of Seastar anyway. - -Also note that currently we cannot specify "--network-stack=native", because -neither vhost nor a more efficient mechanism for "virtio assignment" is yet -complete on OSv. So we must keep the default (which is "--network-stack=posix") -which uses the OS's Posix networking APIs, which OSv fully supports. - - -3. Running the seastar module on OSv -------------------------------------- - -To run the Seastar httpd application, using the module defined above, do, -as usual, in the OSv working directory: - -$ make image=seastar -j4 -$ sudo scripts/run.py -nvV - -This will open an HTTP server on port 10000 of the VM. For example, if the -above creates a VM with an IP address of 192.168.122.89, we can test it as -following: - -$ curl 192.168.122.89:10000 -this is the future

Future!!

- -4. Debugging OSv with the Seastar application ---------------------------------------------- - -If you want to debug OSv (not the Seastar application) in relation to the -way it runs Seastar, you'll want the "httpd" shared object to be available -to gdb. -Unfortunately, the object lookup code in "osv syms" (translate() in loader.py) -does not seem to look for objects in apps/, so until we fix this, we need -to put a link to httpd in a well-known place, such as build/release. So -do this in the OSv top directory: - ln -s ../../apps/seastar/httpd build/release/httpd - -Note you'll need to repeat this if you do "make clean" (as "make clean" -removes everything in build/release). diff --git a/README.md b/README.md deleted file mode 100644 index 861f2655e0..0000000000 --- a/README.md +++ /dev/null @@ -1,348 +0,0 @@ -Seastar -======= - -Introduction ------------- - -SeaStar is an event-driven framework allowing you to write non-blocking, -asynchronous code in a relatively straightforward manner (once understood). -It is based on [futures](http://en.wikipedia.org/wiki/Futures_and_promises). - -Building Seastar --------------------- - - -### Building seastar on Fedora 21 - -Installing required packages: -``` -yum install gcc-c++ libaio-devel ninja-build ragel hwloc-devel numactl-devel libpciaccess-devel cryptopp-devel xen-devel boost-devel -``` - -You then need to run the following to create the "build.ninja" file: -``` -./configure.py -``` -Note it is enough to run this once, and you don't need to repeat it before -every build. build.ninja includes a rule which will automatically re-run -./configure.py if it changes. - -Then finally: -``` -ninja-build -``` - -### Building seastar on Fedora 20 - -Installing GCC 4.9 for gnu++1y: -* Beware that this installation will replace your current GCC version. -``` -yum install fedora-release-rawhide -yum --enablerepo rawhide update gcc-c++ -yum --enablerepo rawhide install libubsan libasan -``` - -Installing required packages: -``` -yum install libaio-devel ninja-build ragel hwloc-devel numactl-devel libpciaccess-devel cryptopp-devel -``` - -You then need to run the following to create the "build.ninja" file: -``` -./configure.py -``` -Note it is enough to run this once, and you don't need to repeat it before -every build. build.ninja includes a rule which will automatically re-run -./configure.py if it changes. - -Then finally: -``` -ninja-build -``` - -### Building seastar on Ubuntu 14.04 - -Installing required packages: -``` -sudo apt-get install libaio-dev ninja-build ragel libhwloc-dev libnuma-dev libpciaccess-dev libcrypto++-dev libboost-all-dev -``` - -Installing GCC 4.9 for gnu++1y. Unlike the Fedora case above, this will -not harm the existing installation of GCC 4.8, and will install an -additional set of compilers, and additional commands named gcc-4.9, -g++-4.9, etc., that need to be used explicitly, while the "gcc", "g++", -etc., commands continue to point to the 4.8 versions. - -``` -# Install add-apt-repository -sudo apt-get install software-properties-common python-software-properties -# Use it to add Ubuntu's testing compiler repository -sudo add-apt-repository ppa:ubuntu-toolchain-r/test -sudo apt-get update -# Install gcc 4.9 and relatives -sudo apt-get install g++-4.9 -# Also set up necessary header file links and stuff (?) -sudo apt-get install gcc-4.9-multilib g++-4.9-multilib -``` - -To compile Seastar explicitly using gcc 4.9, use: -``` -./configure.py --compiler=g++-4.9 -``` - -To compile OSv explicitly using gcc 4.9, use: -``` -make CC=gcc-4.9 CXX=g++-4.9 -j 24 -``` - -### Building seastar in Docker container - -To build a Docker image: - -``` -docker build -t seastar-dev docker/dev -``` - -Create an shell function for building insider the container (bash syntax given): - -``` -$ seabuild() { docker run -v $HOME/seastar/:/seastar -u $(id -u):$(id -g) -w /seastar -t seastar-dev "$@"; } -``` - -(it is recommended to put this inside your .bashrc or similar) - -To build inside a container: - -``` -$ seabuild ./configure.py -$ seabuild ninja-build -``` - -### Building with a DPDK network backend - - 1. Setup host to compile DPDK: - - Ubuntu - `sudo apt-get install -y build-essential linux-image-extra-$(uname -r$)` - 2. Run a configure.py: `./configure.py --enable-dpdk`. - 3. Run `ninja-build`. - -To run with the DPDK backend for a native stack give the seastar application `--dpdk-pmd 1` parameter. - -You can also configure DPDK as an [external package](README-DPDK.md). - -Futures and promises --------------------- - -A *future* is a result of a computation that may not be available yet. -Examples include: - - * a data buffer that we are reading from the network - * the expiration of a timer - * the completion of a disk write - * the result computation that requires the values from - one or more other futures. - -a *promise* is an object or function that provides you with a future, -with the expectation that it will fulfill the future. - -Promises and futures simplify asynchronous programming since they decouple -the event producer (the promise) and the event consumer (whoever uses the -future). Whether the promise is fulfilled before the future is consumed, -or vice versa, does not change the outcome of the code. - -Consuming a future ------------------- - -You consume a future by using its *then()* method, providing it with a -callback (typically a lambda). For example, consider the following -operation: - -```C++ -future get(); // promises an int will be produced eventually -future<> put(int) // promises to store an int - -void f() { - get().then([] (int value) { - put(value + 1).then([] { - std::cout << "value stored successfully\n"; - }); - }); -} -``` - -Here, we initiate a *get()* operation, requesting that when it completes, a -*put()* operation will be scheduled with an incremented value. We also -request that when the *put()* completes, some text will be printed out. - -Chaining futures ----------------- - -If a *then()* lambda returns a future (call it x), then that *then()* -will return a future (call it y) that will receive the same value. This -removes the need for nesting lambda blocks; for example the code above -could be rewritten as: - -```C++ -future get(); // promises an int will be produced eventually -future<> put(int) // promises to store an int - -void f() { - get().then([] (int value) { - return put(value + 1); - }).then([] { - std::cout << "value stored successfully\n"; - }); -} -``` - -Loops ------ - -Loops are achieved with a tail call; for example: - -```C++ -future get(); // promises an int will be produced eventually -future<> put(int) // promises to store an int - -future<> loop_to(int end) { - if (value == end) { - return make_ready_future<>(); - } - get().then([end] (int value) { - return put(value + 1); - }).then([end] { - return loop_to(end); - }); -} -``` - -The *make_ready_future()* function returns a future that is already -available --- corresponding to the loop termination condition, where -no further I/O needs to take place. - -Under the hood --------------- - -When the loop above runs, both *then* method calls execute immediately ---- but without executing the bodies. What happens is the following: - -1. `get()` is called, initiates the I/O operation, and allocates a - temporary structure (call it `f1`). -2. The first `then()` call chains its body to `f1` and allocates - another temporary structure, `f2`. -3. The second `then()` call chains its body to `f2`. - -Again, all this runs immediately without waiting for anything. - -After the I/O operation initiated by `get()` completes, it calls the -continuation stored in `f1`, calls it, and frees `f1`. The continuation -calls `put()`, which initiates the I/O operation required to perform -the store, and allocates a temporary object `f12`, and chains some glue -code to it. - -After the I/O operation initiated by `put()` completes, it calls the -continuation associated with `f12`, which simply tells it to call the -continuation associated with `f2`. This continuation simply calls -`loop_to()`. Both `f12` and `f2` are freed. `loop_to()` then calls -`get()`, which starts the process all over again, allocating new versions -of `f1` and `f2`. - -Handling exceptions -------------------- - -If a `.then()` clause throws an exception, the scheduler will catch it -and cancel any dependent `.then()` clauses. If you want to trap the -exception, add a `.then_wrapped()` clause at the end: - -```C++ -future receive(); -request parse(buffer buf); -future process(request req); -future<> send(response resp); - -void f() { - receive().then([] (buffer buf) { - return process(parse(std::move(buf)); - }).then([] (response resp) { - return send(std::move(resp)); - }).then([] { - f(); - }).then_wrapped([] (auto&& f) { - try { - f.get(); - } catch (std::exception& e) { - // your handler goes here - } - }); -} -``` - -The previous future is passed as a parameter to the lambda, and its value can -be inspected with `f.get()`. When the `get()` variable is called as a -function, it will re-throw the exception that aborted processing, and you can -then apply any needed error handling. It is essentially a transformation of - -```C++ -buffer receive(); -request parse(buffer buf); -response process(request req); -void send(response resp); - -void f() { - try { - while (true) { - auto req = parse(receive()); - auto resp = process(std::move(req)); - send(std::move(resp)); - } - } catch (std::exception& e) { - // your handler goes here - } -} -``` - -Note, however, that the `.then_wrapped()` clause will be scheduled both when -exception occurs or not. Therefore, the mere fact that `.then_wrapped()` is -executed does not mean that an exception was thrown. Only the execution of the -catch block can guarantee that. - - -This is shown below: - -```C++ - -future my_future(); - -void f() { - receive().then_wrapped([] (future f) { - try { - my_type x = f.get(); - return do_something(x); - } catch (std::exception& e) { - // your handler goes here - } - }); -} -``` -### Setup notes - -SeaStar is a high performance framework and tuned to get the best -performance by default. As such, we're tuned towards polling vs interrupt -driven. Our assumption is that applications written for SeaStar will be -busy handling 100,000 IOPS and beyond. Polling means that each of our -cores will consume 100% cpu even when no work is given to it. - - -Recommended hardware configuration for SeaStar ----------------------------------------------- - -* CPUs - As much as you need. SeaStar is highly friendly for multi-core and NUMA -* NICs - As fast as possible, we recommend 10G or 40G cards. It's possible to use - 1G to but you may be limited by their capacity. - In addition, the more hardware queue per cpu the better for SeaStar. - Otherwise we have to emulate that in software. -* Disks - Fast SSDs with high number of IOPS. -* Client machines - Usually a single client machine can't load our servers. - Both memaslap (memcached) and WRK (httpd) cannot over load their matching - server counter parts. We recommend running the client on different machine - than the servers and use several of them. diff --git a/apps/httpd/demo.json b/apps/httpd/demo.json deleted file mode 100644 index 12261c453f..0000000000 --- a/apps/httpd/demo.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "apiVersion": "0.0.1", - "swaggerVersion": "1.2", - "basePath": "{{Protocol}}://{{Host}}", - "resourcePath": "/hello", - "produces": [ - "application/json" - ], - "apis": [ - { - "path": "/hello/world/{var1}/{var2}", - "operations": [ - { - "method": "GET", - "summary": "Returns the number of seconds since the system was booted", - "type": "long", - "nickname": "hello_world", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name":"var2", - "description":"Full path of file or directory", - "required":true, - "allowMultiple":true, - "type":"string", - "paramType":"path" - }, - { - "name":"var1", - "description":"Full path of file or directory", - "required":true, - "allowMultiple":false, - "type":"string", - "paramType":"path" - }, - { - "name":"query_enum", - "description":"The operation to perform", - "required":true, - "allowMultiple":false, - "type":"string", - "paramType":"query", - "enum":["VAL1", "VAL2", "VAL3"] - } - ] - } - ] - } - ], - "models" : { - "my_object": { - "id": "my_object", - "description": "Demonstrate an object", - "properties": { - "var1": { - "type": "string", - "description": "The first parameter in the path" - }, - "var2": { - "type": "string", - "description": "The second parameter in the path" - }, - "enum_var" : { - "type": "string", - "description": "Demonstrate an enum returned, note this is not the same enum type of the request", - "enum":["VAL1", "VAL2", "VAL3"] - } - } - } - } -} diff --git a/apps/httpd/main.cc b/apps/httpd/main.cc deleted file mode 100644 index 27bebbcb40..0000000000 --- a/apps/httpd/main.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "http/httpd.hh" -#include "http/handlers.hh" -#include "http/function_handlers.hh" -#include "http/file_handler.hh" -#include "apps/httpd/demo.json.hh" -#include "http/api_docs.hh" - -namespace bpo = boost::program_options; - -using namespace httpd; - -class handl : public httpd::handler_base { -public: - virtual future > handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) { - rep->_content = "hello"; - rep->done("html"); - return make_ready_future>(std::move(rep)); - } -}; - -void set_routes(routes& r) { - function_handler* h1 = new function_handler([](const_req req) { - return "hello"; - }); - function_handler* h2 = new function_handler([](std::unique_ptr req) { - return make_ready_future("json-future"); - }); - r.add(operation_type::GET, url("/"), h1); - r.add(operation_type::GET, url("/jf"), h2); - r.add(operation_type::GET, url("/file").remainder("path"), - new directory_handler("/")); - demo_json::hello_world.set(r, [] (const_req req) { - demo_json::my_object obj; - obj.var1 = req.param.at("var1"); - obj.var2 = req.param.at("var2"); - demo_json::ns_hello_world::query_enum v = demo_json::ns_hello_world::str2query_enum(req.query_parameters.at("query_enum")); - // This demonstrate enum conversion - obj.enum_var = v; - return obj; - }); -} - -int main(int ac, char** av) { - app_template app; - app.add_options()("port", bpo::value()->default_value(10000), - "HTTP Server port"); - return app.run(ac, av, [&] { - auto&& config = app.configuration(); - uint16_t port = config["port"].as(); - auto server = new http_server_control(); - auto rb = make_shared("apps/httpd/"); - server->start().then([server] { - return server->set_routes(set_routes); - }).then([server, rb]{ - return server->set_routes([rb](routes& r){rb->set_api_doc(r);}); - }).then([server, rb]{ - return server->set_routes([rb](routes& r) {rb->register_function(r, "demo", "hello world application");}); - }).then([server, port] { - return server->listen(port); - }).then([server, port] { - std::cout << "Seastar HTTP server listening on port " << port << " ...\n"; - engine().at_exit([server] { - return server->stop(); - }); - }); - - }); -} diff --git a/apps/memcached/ascii.rl b/apps/memcached/ascii.rl deleted file mode 100644 index f6f577c412..0000000000 --- a/apps/memcached/ascii.rl +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "core/ragel.hh" -#include "apps/memcached/memcached.hh" -#include -#include -#include - -%%{ - -machine memcache_ascii_protocol; - -access _fsm_; - -action mark { - g.mark_start(p); -} - -action start_blob { - g.mark_start(p); - _size_left = _size; -} - -action advance_blob { - auto len = std::min((uint32_t)(pe - p), _size_left); - _size_left -= len; - p += len; - if (_size_left == 0) { - _blob = str(); - p--; - fret; - } - p--; -} - -crlf = '\r\n'; -sp = ' '; -u32 = digit+ >{ _u32 = 0; } ${ _u32 *= 10; _u32 += fc - '0'; }; -u64 = digit+ >{ _u64 = 0; } ${ _u64 *= 10; _u64 += fc - '0'; }; -key = [^ ]+ >mark %{ _key = memcache::item_key(str()); }; -flags = digit+ >mark %{ _flags_str = str(); }; -expiration = u32 %{ _expiration = _u32; }; -size = u32 >mark %{ _size = _u32; _size_str = str(); }; -blob := any+ >start_blob $advance_blob; -maybe_noreply = (sp "noreply" @{ _noreply = true; })? >{ _noreply = false; }; -maybe_expiration = (sp expiration)? >{ _expiration = 0; }; -version_field = u64 %{ _version = _u64; }; - -insertion_params = sp key sp flags sp expiration sp size maybe_noreply (crlf @{ fcall blob; } ) crlf; -set = "set" insertion_params @{ _state = state::cmd_set; }; -add = "add" insertion_params @{ _state = state::cmd_add; }; -replace = "replace" insertion_params @{ _state = state::cmd_replace; }; -cas = "cas" sp key sp flags sp expiration sp size sp version_field maybe_noreply (crlf @{ fcall blob; } ) crlf @{ _state = state::cmd_cas; }; -get = "get" (sp key %{ _keys.emplace_back(std::move(_key)); })+ crlf @{ _state = state::cmd_get; }; -gets = "gets" (sp key %{ _keys.emplace_back(std::move(_key)); })+ crlf @{ _state = state::cmd_gets; }; -delete = "delete" sp key maybe_noreply crlf @{ _state = state::cmd_delete; }; -flush = "flush_all" maybe_expiration maybe_noreply crlf @{ _state = state::cmd_flush_all; }; -version = "version" crlf @{ _state = state::cmd_version; }; -stats = "stats" crlf @{ _state = state::cmd_stats; }; -stats_hash = "stats hash" crlf @{ _state = state::cmd_stats_hash; }; -incr = "incr" sp key sp u64 maybe_noreply crlf @{ _state = state::cmd_incr; }; -decr = "decr" sp key sp u64 maybe_noreply crlf @{ _state = state::cmd_decr; }; -main := (add | replace | set | get | gets | delete | flush | version | cas | stats | incr | decr - | stats_hash) >eof{ _state = state::eof; }; - -prepush { - prepush(); -} - -postpop { - postpop(); -} - -}%% - -class memcache_ascii_parser : public ragel_parser_base { - %% write data nofinal noprefix; -public: - enum class state { - error, - eof, - cmd_set, - cmd_cas, - cmd_add, - cmd_replace, - cmd_get, - cmd_gets, - cmd_delete, - cmd_flush_all, - cmd_version, - cmd_stats, - cmd_stats_hash, - cmd_incr, - cmd_decr, - }; - state _state; - uint32_t _u32; - uint64_t _u64; - memcache::item_key _key; - sstring _flags_str; - uint32_t _expiration; - uint32_t _size; - sstring _size_str; - uint32_t _size_left; - uint64_t _version; - sstring _blob; - bool _noreply; - std::vector _keys; -public: - void init() { - init_base(); - _state = state::error; - _keys.clear(); - %% write init; - } - - char* parse(char* p, char* pe, char* eof) { - sstring_builder::guard g(_builder, p, pe); - auto str = [this, &g, &p] { g.mark_end(p); return get_str(); }; - %% write exec; - if (_state != state::error) { - return p; - } - if (p != pe) { - p = pe; - return p; - } - return nullptr; - } - bool eof() const { - return _state == state::eof; - } -}; diff --git a/apps/memcached/memcache.cc b/apps/memcached/memcache.cc deleted file mode 100644 index d9f1b1a72e..0000000000 --- a/apps/memcached/memcache.cc +++ /dev/null @@ -1,1405 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014-2015 Cloudius Systems - */ - -#include -#include -#include -#include -#include -#include -#include -#include "core/app-template.hh" -#include "core/future-util.hh" -#include "core/timer-set.hh" -#include "core/shared_ptr.hh" -#include "core/stream.hh" -#include "core/memory.hh" -#include "core/units.hh" -#include "core/distributed.hh" -#include "core/vector-data-sink.hh" -#include "core/bitops.hh" -#include "core/slab.hh" -#include "core/align.hh" -#include "net/api.hh" -#include "net/packet-data-source.hh" -#include "apps/memcached/ascii.hh" -#include "memcached.hh" -#include - -#define PLATFORM "seastar" -#define VERSION "v1.0" -#define VERSION_STRING PLATFORM " " VERSION - -using namespace net; - -namespace bi = boost::intrusive; - -namespace memcache { - -static constexpr double default_slab_growth_factor = 1.25; -static constexpr uint64_t default_slab_page_size = 1UL*MB; -static constexpr uint64_t default_per_cpu_slab_size = 0UL; // zero means reclaimer is enabled. -static __thread slab_allocator* slab; - -template -using optional = boost::optional; - -struct expiration { - static constexpr uint32_t seconds_in_a_month = 60U * 60 * 24 * 30; - uint32_t _time; - - expiration() : _time(0U) {} - - expiration(uint32_t seconds) { - if (seconds == 0U) { - _time = 0U; // means never expire. - } else if (seconds <= seconds_in_a_month) { - _time = seconds + time(0); // from delta - } else { - _time = seconds; // from real time - } - } - - bool ever_expires() { - return _time; - } - - clock_type::time_point to_time_point() { - return clock_type::time_point(std::chrono::seconds(_time)); - } -}; - -class item : public slab_item_base { -public: - using version_type = uint64_t; - using time_point = clock_type::time_point; - using duration = clock_type::duration; - static constexpr uint8_t field_alignment = alignof(void*); -private: - using hook_type = bi::unordered_set_member_hook<>; - // TODO: align shared data to cache line boundary - version_type _version; - hook_type _cache_link; - bi::list_member_hook<> _timer_link; - size_t _key_hash; - expiration _expiry; - uint32_t _value_size; - uint32_t _slab_page_index; - uint16_t _ref_count; - uint8_t _key_size; - uint8_t _ascii_prefix_size; - char _data[]; // layout: data=key, (data+key_size)=ascii_prefix, (data+key_size+ascii_prefix_size)=value. - friend class cache; -public: - item(uint32_t slab_page_index, item_key&& key, sstring&& ascii_prefix, - sstring&& value, expiration expiry, version_type version = 1) - : _version(version) - , _key_hash(key.hash()) - , _expiry(expiry) - , _value_size(value.size()) - , _slab_page_index(slab_page_index) - , _ref_count(0U) - , _key_size(key.key().size()) - , _ascii_prefix_size(ascii_prefix.size()) - { - assert(_key_size <= std::numeric_limits::max()); - assert(_ascii_prefix_size <= std::numeric_limits::max()); - // storing key - memcpy(_data, key.key().c_str(), _key_size); - // storing ascii_prefix - memcpy(_data + align_up(_key_size, field_alignment), ascii_prefix.c_str(), _ascii_prefix_size); - // storing value - memcpy(_data + align_up(_key_size, field_alignment) + align_up(_ascii_prefix_size, field_alignment), - value.c_str(), _value_size); - } - - item(const item&) = delete; - item(item&&) = delete; - - clock_type::time_point get_timeout() { - return _expiry.to_time_point(); - } - - version_type version() { - return _version; - } - - const std::experimental::string_view key() const { - return std::experimental::string_view(_data, _key_size); - } - - const std::experimental::string_view ascii_prefix() const { - const char *p = _data + align_up(_key_size, field_alignment); - return std::experimental::string_view(p, _ascii_prefix_size); - } - - const std::experimental::string_view value() const { - const char *p = _data + align_up(_key_size, field_alignment) + - align_up(_ascii_prefix_size, field_alignment); - return std::experimental::string_view(p, _value_size); - } - - size_t key_size() const { - return _key_size; - } - - size_t ascii_prefix_size() const { - return _ascii_prefix_size; - } - - size_t value_size() const { - return _value_size; - } - - optional data_as_integral() { - auto str = value().data(); - if (str[0] == '-') { - return {}; - } - - auto len = _value_size; - - // Strip trailing space - while (len && str[len - 1] == ' ') { - len--; - } - - try { - return {boost::lexical_cast(str, len)}; - } catch (const boost::bad_lexical_cast& e) { - return {}; - } - } - - // needed by timer_set - bool cancel() { - return false; - } - - // Methods required by slab allocator. - uint32_t get_slab_page_index() const { - return _slab_page_index; - } - bool is_unlocked() const { - return _ref_count == 1; - } - - friend bool operator==(const item &a, const item &b) { - return (a._key_hash == b._key_hash) && - (a._key_size == b._key_size) && - (memcmp(a._data, b._data, a._key_size) == 0); - } - - friend std::size_t hash_value(const item &i) { - return i._key_hash; - } - - friend inline void intrusive_ptr_add_ref(item* it) { - assert(it->_ref_count >= 0); - ++it->_ref_count; - if (it->_ref_count == 2) { - slab->lock_item(it); - } - } - - friend inline void intrusive_ptr_release(item* it) { - --it->_ref_count; - if (it->_ref_count == 1) { - slab->unlock_item(it); - } else if (it->_ref_count == 0) { - slab->free(it); - } - assert(it->_ref_count >= 0); - } - - friend class item_key_cmp; -}; - -struct item_key_cmp -{ -private: - bool compare(const item_key& key, const item& it) const { - return (it._key_hash == key.hash()) && - (it._key_size == key.key().size()) && - (memcmp(it._data, key.key().c_str(), it._key_size) == 0); - } -public: - bool operator()(const item_key& key, const item& it) const { - return compare(key, it); - } - - bool operator()(const item& it, const item_key& key) const { - return compare(key, it); - } -}; - -using item_ptr = foreign_ptr>; - -struct cache_stats { - size_t _get_hits {}; - size_t _get_misses {}; - size_t _set_adds {}; - size_t _set_replaces {}; - size_t _cas_hits {}; - size_t _cas_misses {}; - size_t _cas_badval {}; - size_t _delete_misses {}; - size_t _delete_hits {}; - size_t _incr_misses {}; - size_t _incr_hits {}; - size_t _decr_misses {}; - size_t _decr_hits {}; - size_t _expired {}; - size_t _evicted {}; - size_t _bytes {}; - size_t _resize_failure {}; - size_t _size {}; - size_t _reclaims{}; - - void operator+=(const cache_stats& o) { - _get_hits += o._get_hits; - _get_misses += o._get_misses; - _set_adds += o._set_adds; - _set_replaces += o._set_replaces; - _cas_hits += o._cas_hits; - _cas_misses += o._cas_misses; - _cas_badval += o._cas_badval; - _delete_misses += o._delete_misses; - _delete_hits += o._delete_hits; - _incr_misses += o._incr_misses; - _incr_hits += o._incr_hits; - _decr_misses += o._decr_misses; - _decr_hits += o._decr_hits; - _expired += o._expired; - _evicted += o._evicted; - _bytes += o._bytes; - _resize_failure += o._resize_failure; - _size += o._size; - _reclaims += o._reclaims; - } -}; - -enum class cas_result { - not_found, stored, bad_version -}; - -struct remote_origin_tag { - template - static inline - T move_if_local(T& ref) { - return ref; - } -}; - -struct local_origin_tag { - template - static inline - T move_if_local(T& ref) { - return std::move(ref); - } -}; - -struct item_insertion_data { - item_key key; - sstring ascii_prefix; - sstring data; - expiration expiry; -}; - -class cache { -private: - using cache_type = bi::unordered_set, - bi::power_2_buckets, - bi::constant_time_size>; - using cache_iterator = typename cache_type::iterator; - static constexpr size_t initial_bucket_count = 1 << 10; - static constexpr float load_factor = 0.75f; - size_t _resize_up_threshold = load_factor * initial_bucket_count; - cache_type::bucket_type* _buckets; - cache_type _cache; - seastar::timer_set _alive; - timer<> _timer; - cache_stats _stats; - timer<> _flush_timer; -private: - size_t item_size(item& item_ref) { - constexpr size_t field_alignment = alignof(void*); - return sizeof(item) + - align_up(item_ref.key_size(), field_alignment) + - align_up(item_ref.ascii_prefix_size(), field_alignment) + - item_ref.value_size(); - } - - size_t item_size(item_insertion_data& insertion) { - constexpr size_t field_alignment = alignof(void*); - auto size = sizeof(item) + - align_up(insertion.key.key().size(), field_alignment) + - align_up(insertion.ascii_prefix.size(), field_alignment) + - insertion.data.size(); -#ifdef __DEBUG__ - static bool print_item_footprint = true; - if (print_item_footprint) { - print_item_footprint = false; - std::cout << __FUNCTION__ << ": " << size << "\n"; - std::cout << "sizeof(item) " << sizeof(item) << "\n"; - std::cout << "key.size " << insertion.key.key().size() << "\n"; - std::cout << "value.size " << insertion.data.size() << "\n"; - std::cout << "ascii_prefix.size " << insertion.ascii_prefix.size() << "\n"; - } -#endif - return size; - } - - template - void erase(item& item_ref) { - if (IsInCache) { - _cache.erase(_cache.iterator_to(item_ref)); - } - if (IsInTimerList) { - if (item_ref._expiry.ever_expires()) { - _alive.remove(item_ref); - } - } - _stats._bytes -= item_size(item_ref); - if (Release) { - // memory used by item shouldn't be freed when slab is replacing it with another item. - intrusive_ptr_release(&item_ref); - } - } - - void expire() { - auto exp = _alive.expire(clock_type::now()); - while (!exp.empty()) { - auto item = &*exp.begin(); - exp.pop_front(); - erase(*item); - _stats._expired++; - } - _timer.arm(_alive.get_next_timeout()); - } - - inline - cache_iterator find(const item_key& key) { - return _cache.find(key, std::hash(), item_key_cmp()); - } - - template - inline - cache_iterator add_overriding(cache_iterator i, item_insertion_data& insertion) { - auto& old_item = *i; - uint64_t old_item_version = old_item._version; - - erase(old_item); - - size_t size = item_size(insertion); - auto new_item = slab->create(size, Origin::move_if_local(insertion.key), Origin::move_if_local(insertion.ascii_prefix), - Origin::move_if_local(insertion.data), insertion.expiry, old_item_version + 1); - intrusive_ptr_add_ref(new_item); - - auto insert_result = _cache.insert(*new_item); - assert(insert_result.second); - if (insertion.expiry.ever_expires() && _alive.insert(*new_item)) { - _timer.rearm(new_item->get_timeout()); - } - _stats._bytes += size; - return insert_result.first; - } - - template - inline - void add_new(item_insertion_data& insertion) { - size_t size = item_size(insertion); - auto new_item = slab->create(size, Origin::move_if_local(insertion.key), Origin::move_if_local(insertion.ascii_prefix), - Origin::move_if_local(insertion.data), insertion.expiry); - intrusive_ptr_add_ref(new_item); - auto& item_ref = *new_item; - _cache.insert(item_ref); - if (insertion.expiry.ever_expires() && _alive.insert(item_ref)) { - _timer.rearm(item_ref.get_timeout()); - } - _stats._bytes += size; - maybe_rehash(); - } - - void maybe_rehash() { - if (_cache.size() >= _resize_up_threshold) { - auto new_size = _cache.bucket_count() * 2; - auto old_buckets = _buckets; - try { - _buckets = new cache_type::bucket_type[new_size]; - } catch (const std::bad_alloc& e) { - _stats._resize_failure++; - return; - } - _cache.rehash(typename cache_type::bucket_traits(_buckets, new_size)); - delete[] old_buckets; - _resize_up_threshold = _cache.bucket_count() * load_factor; - } - } -public: - cache(uint64_t per_cpu_slab_size, uint64_t slab_page_size) - : _buckets(new cache_type::bucket_type[initial_bucket_count]) - , _cache(cache_type::bucket_traits(_buckets, initial_bucket_count)) - { - _timer.set_callback([this] { expire(); }); - _flush_timer.set_callback([this] { flush_all(); }); - - // initialize per-thread slab allocator. - slab = new slab_allocator(default_slab_growth_factor, per_cpu_slab_size, slab_page_size, - [this](item& item_ref) { erase(item_ref); _stats._evicted++; }); -#ifdef __DEBUG__ - static bool print_slab_classes = true; - if (print_slab_classes) { - print_slab_classes = false; - slab->print_slab_classes(); - } -#endif - } - - ~cache() { - flush_all(); - } - - void flush_all() { - _flush_timer.cancel(); - _cache.erase_and_dispose(_cache.begin(), _cache.end(), [this] (item* it) { - erase(*it); - }); - } - - void flush_at(clock_type::time_point time_point) { - _flush_timer.rearm(time_point); - } - - template - bool set(item_insertion_data& insertion) { - auto i = find(insertion.key); - if (i != _cache.end()) { - add_overriding(i, insertion); - _stats._set_replaces++; - return true; - } else { - add_new(insertion); - _stats._set_adds++; - return false; - } - } - - template - bool add(item_insertion_data& insertion) { - auto i = find(insertion.key); - if (i != _cache.end()) { - return false; - } - - _stats._set_adds++; - add_new(insertion); - return true; - } - - template - bool replace(item_insertion_data& insertion) { - auto i = find(insertion.key); - if (i == _cache.end()) { - return false; - } - - _stats._set_replaces++; - add_overriding(i, insertion); - return true; - } - - bool remove(const item_key& key) { - auto i = find(key); - if (i == _cache.end()) { - _stats._delete_misses++; - return false; - } - _stats._delete_hits++; - auto& item_ref = *i; - erase(item_ref); - return true; - } - - item_ptr get(const item_key& key) { - auto i = find(key); - if (i == _cache.end()) { - _stats._get_misses++; - return nullptr; - } - _stats._get_hits++; - auto& item_ref = *i; - return item_ptr(&item_ref); - } - - template - cas_result cas(item_insertion_data& insertion, item::version_type version) { - auto i = find(insertion.key); - if (i == _cache.end()) { - _stats._cas_misses++; - return cas_result::not_found; - } - auto& item_ref = *i; - if (item_ref._version != version) { - _stats._cas_badval++; - return cas_result::bad_version; - } - _stats._cas_hits++; - add_overriding(i, insertion); - return cas_result::stored; - } - - size_t size() { - return _cache.size(); - } - - size_t bucket_count() { - return _cache.bucket_count(); - } - - cache_stats stats() { - _stats._size = size(); - return _stats; - } - - template - std::pair incr(item_key& key, uint64_t delta) { - auto i = find(key); - if (i == _cache.end()) { - _stats._incr_misses++; - return {item_ptr{}, false}; - } - auto& item_ref = *i; - _stats._incr_hits++; - auto value = item_ref.data_as_integral(); - if (!value) { - return {boost::intrusive_ptr(&item_ref), false}; - } - item_insertion_data insertion { - .key = Origin::move_if_local(key), - .ascii_prefix = sstring(item_ref.ascii_prefix().data(), item_ref.ascii_prefix_size()), - .data = to_sstring(*value + delta), - .expiry = item_ref._expiry - }; - i = add_overriding(i, insertion); - return {boost::intrusive_ptr(&*i), true}; - } - - template - std::pair decr(item_key& key, uint64_t delta) { - auto i = find(key); - if (i == _cache.end()) { - _stats._decr_misses++; - return {item_ptr{}, false}; - } - auto& item_ref = *i; - _stats._decr_hits++; - auto value = item_ref.data_as_integral(); - if (!value) { - return {boost::intrusive_ptr(&item_ref), false}; - } - item_insertion_data insertion { - .key = Origin::move_if_local(key), - .ascii_prefix = sstring(item_ref.ascii_prefix().data(), item_ref.ascii_prefix_size()), - .data = to_sstring(*value - std::min(*value, delta)), - .expiry = item_ref._expiry - }; - i = add_overriding(i, insertion); - return {boost::intrusive_ptr(&*i), true}; - } - - std::pair>> print_hash_stats() { - static constexpr unsigned bits = sizeof(size_t) * 8; - size_t histo[bits + 1] {}; - size_t max_size = 0; - unsigned max_bucket = 0; - - for (size_t i = 0; i < _cache.bucket_count(); i++) { - size_t size = _cache.bucket_size(i); - unsigned bucket; - if (size == 0) { - bucket = 0; - } else { - bucket = bits - count_leading_zeros(size); - } - max_bucket = std::max(max_bucket, bucket); - max_size = std::max(max_size, size); - histo[bucket]++; - } - - std::stringstream ss; - - ss << "size: " << _cache.size() << "\n"; - ss << "buckets: " << _cache.bucket_count() << "\n"; - ss << "load: " << to_sstring_sprintf((double)_cache.size() / _cache.bucket_count(), "%.2lf") << "\n"; - ss << "max bucket occupancy: " << max_size << "\n"; - ss << "bucket occupancy histogram:\n"; - - for (unsigned i = 0; i < (max_bucket + 2); i++) { - ss << " "; - if (i == 0) { - ss << "0: "; - } else if (i == 1) { - ss << "1: "; - } else { - ss << (1 << (i - 1)) << "+: "; - } - ss << histo[i] << "\n"; - } - return {engine().cpu_id(), make_foreign(make_lw_shared(ss.str()))}; - } - - future<> stop() { return make_ready_future<>(); } -}; - -class sharded_cache { -private: - distributed& _peers; - - inline - unsigned get_cpu(const item_key& key) { - return std::hash()(key) % smp::count; - } -public: - sharded_cache(distributed& peers) : _peers(peers) {} - - future<> flush_all() { - return _peers.invoke_on_all(&cache::flush_all); - } - - future<> flush_at(clock_type::time_point time_point) { - return _peers.invoke_on_all(&cache::flush_at, time_point); - } - - // The caller must keep @insertion live until the resulting future resolves. - future set(item_insertion_data& insertion) { - auto cpu = get_cpu(insertion.key); - if (engine().cpu_id() == cpu) { - return make_ready_future(_peers.local().set(insertion)); - } - return _peers.invoke_on(cpu, &cache::set, std::ref(insertion)); - } - - // The caller must keep @insertion live until the resulting future resolves. - future add(item_insertion_data& insertion) { - auto cpu = get_cpu(insertion.key); - if (engine().cpu_id() == cpu) { - return make_ready_future(_peers.local().add(insertion)); - } - return _peers.invoke_on(cpu, &cache::add, std::ref(insertion)); - } - - // The caller must keep @insertion live until the resulting future resolves. - future replace(item_insertion_data& insertion) { - auto cpu = get_cpu(insertion.key); - if (engine().cpu_id() == cpu) { - return make_ready_future(_peers.local().replace(insertion)); - } - return _peers.invoke_on(cpu, &cache::replace, std::ref(insertion)); - } - - // The caller must keep @key live until the resulting future resolves. - future remove(const item_key& key) { - auto cpu = get_cpu(key); - return _peers.invoke_on(cpu, &cache::remove, std::ref(key)); - } - - // The caller must keep @key live until the resulting future resolves. - future get(const item_key& key) { - auto cpu = get_cpu(key); - return _peers.invoke_on(cpu, &cache::get, std::ref(key)); - } - - // The caller must keep @insertion live until the resulting future resolves. - future cas(item_insertion_data& insertion, item::version_type version) { - auto cpu = get_cpu(insertion.key); - if (engine().cpu_id() == cpu) { - return make_ready_future(_peers.local().cas(insertion, version)); - } - return _peers.invoke_on(cpu, &cache::cas, std::ref(insertion), std::move(version)); - } - - future stats() { - return _peers.map_reduce(adder(), &cache::stats); - } - - // The caller must keep @key live until the resulting future resolves. - future> incr(item_key& key, uint64_t delta) { - auto cpu = get_cpu(key); - if (engine().cpu_id() == cpu) { - return make_ready_future>( - _peers.local().incr(key, delta)); - } - return _peers.invoke_on(cpu, &cache::incr, std::ref(key), std::move(delta)); - } - - // The caller must keep @key live until the resulting future resolves. - future> decr(item_key& key, uint64_t delta) { - auto cpu = get_cpu(key); - if (engine().cpu_id() == cpu) { - return make_ready_future>( - _peers.local().decr(key, delta)); - } - return _peers.invoke_on(cpu, &cache::decr, std::ref(key), std::move(delta)); - } - - future<> print_hash_stats(output_stream& out) { - return _peers.map_reduce([&out] (std::pair>> data) mutable { - return out.write("=== CPU " + std::to_string(data.first) + " ===\r\n") - .then([&out, str = std::move(data.second)] { - return out.write(*str); - }); - }, &cache::print_hash_stats); - } -}; - -struct system_stats { - uint32_t _curr_connections {}; - uint32_t _total_connections {}; - uint64_t _cmd_get {}; - uint64_t _cmd_set {}; - uint64_t _cmd_flush {}; - clock_type::time_point _start_time; -public: - system_stats() { - _start_time = clock_type::time_point::max(); - } - system_stats(clock_type::time_point start_time) - : _start_time(start_time) { - } - system_stats self() { - return *this; - } - void operator+=(const system_stats& other) { - _curr_connections += other._curr_connections; - _total_connections += other._total_connections; - _cmd_get += other._cmd_get; - _cmd_set += other._cmd_set; - _cmd_flush += other._cmd_flush; - _start_time = std::min(_start_time, other._start_time); - } - future<> stop() { return make_ready_future<>(); } -}; - -class ascii_protocol { -private: - using this_type = ascii_protocol; - sharded_cache& _cache; - distributed& _system_stats; - memcache_ascii_parser _parser; - item_key _item_key; - item_insertion_data _insertion; - std::vector _items; -private: - static constexpr const char *msg_crlf = "\r\n"; - static constexpr const char *msg_error = "ERROR\r\n"; - static constexpr const char *msg_stored = "STORED\r\n"; - static constexpr const char *msg_not_stored = "NOT_STORED\r\n"; - static constexpr const char *msg_end = "END\r\n"; - static constexpr const char *msg_value = "VALUE "; - static constexpr const char *msg_deleted = "DELETED\r\n"; - static constexpr const char *msg_not_found = "NOT_FOUND\r\n"; - static constexpr const char *msg_ok = "OK\r\n"; - static constexpr const char *msg_version = "VERSION " VERSION_STRING "\r\n"; - static constexpr const char *msg_exists = "EXISTS\r\n"; - static constexpr const char *msg_stat = "STAT "; - static constexpr const char *msg_out_of_memory = "SERVER_ERROR Out of memory allocating new item\r\n"; - static constexpr const char *msg_error_non_numeric_value = "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n"; -private: - template - static void append_item(scattered_message& msg, item_ptr item) { - if (!item) { - return; - } - - msg.append_static("VALUE "); - msg.append_static(item->key()); - msg.append_static(item->ascii_prefix()); - - if (WithVersion) { - msg.append_static(" "); - msg.append(to_sstring(item->version())); - } - - msg.append_static(msg_crlf); - msg.append_static(item->value()); - msg.append_static(msg_crlf); - msg.on_delete([item = std::move(item)] {}); - } - - template - future<> handle_get(output_stream& out) { - _system_stats.local()._cmd_get++; - if (_parser._keys.size() == 1) { - return _cache.get(_parser._keys[0]).then([&out] (auto item) -> future<> { - scattered_message msg; - this_type::append_item(msg, std::move(item)); - msg.append_static(msg_end); - return out.write(std::move(msg)); - }); - } else { - _items.clear(); - return parallel_for_each(_parser._keys.begin(), _parser._keys.end(), [this] (const auto& key) { - return _cache.get(key).then([this] (auto item) { - _items.emplace_back(std::move(item)); - }); - }).then([this, &out] () { - scattered_message msg; - for (auto& item : _items) { - append_item(msg, std::move(item)); - } - msg.append_static(msg_end); - return out.write(std::move(msg)); - }); - } - } - - template - static future<> print_stat(output_stream& out, const char* key, Value value) { - return out.write(msg_stat) - .then([&out, key] { return out.write(key); }) - .then([&out] { return out.write(" "); }) - .then([&out, value] { return out.write(to_sstring(value)); }) - .then([&out] { return out.write(msg_crlf); }); - } - - future<> print_stats(output_stream& out) { - return _cache.stats().then([this, &out] (auto stats) { - return _system_stats.map_reduce(adder(), &system_stats::self) - .then([this, &out, all_cache_stats = std::move(stats)] (auto all_system_stats) -> future<> { - auto now = clock_type::now(); - auto total_items = all_cache_stats._set_replaces + all_cache_stats._set_adds - + all_cache_stats._cas_hits; - return print_stat(out, "pid", getpid()) - .then([this, now, &out, uptime = now - all_system_stats._start_time] { - return print_stat(out, "uptime", - std::chrono::duration_cast(uptime).count()); - }).then([this, now, &out] { - return print_stat(out, "time", - std::chrono::duration_cast(now.time_since_epoch()).count()); - }).then([this, &out] { - return print_stat(out, "version", VERSION_STRING); - }).then([this, &out] { - return print_stat(out, "pointer_size", sizeof(void*)*8); - }).then([this, &out, v = all_system_stats._curr_connections] { - return print_stat(out, "curr_connections", v); - }).then([this, &out, v = all_system_stats._total_connections] { - return print_stat(out, "total_connections", v); - }).then([this, &out, v = all_system_stats._curr_connections] { - return print_stat(out, "connection_structures", v); - }).then([this, &out, v = all_system_stats._cmd_get] { - return print_stat(out, "cmd_get", v); - }).then([this, &out, v = all_system_stats._cmd_set] { - return print_stat(out, "cmd_set", v); - }).then([this, &out, v = all_system_stats._cmd_flush] { - return print_stat(out, "cmd_flush", v); - }).then([this, &out] { - return print_stat(out, "cmd_touch", 0); - }).then([this, &out, v = all_cache_stats._get_hits] { - return print_stat(out, "get_hits", v); - }).then([this, &out, v = all_cache_stats._get_misses] { - return print_stat(out, "get_misses", v); - }).then([this, &out, v = all_cache_stats._delete_misses] { - return print_stat(out, "delete_misses", v); - }).then([this, &out, v = all_cache_stats._delete_hits] { - return print_stat(out, "delete_hits", v); - }).then([this, &out, v = all_cache_stats._incr_misses] { - return print_stat(out, "incr_misses", v); - }).then([this, &out, v = all_cache_stats._incr_hits] { - return print_stat(out, "incr_hits", v); - }).then([this, &out, v = all_cache_stats._decr_misses] { - return print_stat(out, "decr_misses", v); - }).then([this, &out, v = all_cache_stats._decr_hits] { - return print_stat(out, "decr_hits", v); - }).then([this, &out, v = all_cache_stats._cas_misses] { - return print_stat(out, "cas_misses", v); - }).then([this, &out, v = all_cache_stats._cas_hits] { - return print_stat(out, "cas_hits", v); - }).then([this, &out, v = all_cache_stats._cas_badval] { - return print_stat(out, "cas_badval", v); - }).then([this, &out] { - return print_stat(out, "touch_hits", 0); - }).then([this, &out] { - return print_stat(out, "touch_misses", 0); - }).then([this, &out] { - return print_stat(out, "auth_cmds", 0); - }).then([this, &out] { - return print_stat(out, "auth_errors", 0); - }).then([this, &out] { - return print_stat(out, "threads", smp::count); - }).then([this, &out, v = all_cache_stats._size] { - return print_stat(out, "curr_items", v); - }).then([this, &out, v = total_items] { - return print_stat(out, "total_items", v); - }).then([this, &out, v = all_cache_stats._expired] { - return print_stat(out, "seastar.expired", v); - }).then([this, &out, v = all_cache_stats._resize_failure] { - return print_stat(out, "seastar.resize_failure", v); - }).then([this, &out, v = all_cache_stats._evicted] { - return print_stat(out, "evictions", v); - }).then([this, &out, v = all_cache_stats._bytes] { - return print_stat(out, "bytes", v); - }).then([&out] { - return out.write(msg_end); - }); - }); - }); - } -public: - ascii_protocol(sharded_cache& cache, distributed& system_stats) - : _cache(cache) - , _system_stats(system_stats) - {} - - void prepare_insertion() { - _insertion = item_insertion_data{ - .key = std::move(_parser._key), - .ascii_prefix = make_sstring(" ", _parser._flags_str, " ", _parser._size_str), - .data = std::move(_parser._blob), - .expiry = expiration(_parser._expiration) - }; - } - - future<> handle(input_stream& in, output_stream& out) { - _parser.init(); - return in.consume(_parser).then([this, &out] () -> future<> { - switch (_parser._state) { - case memcache_ascii_parser::state::eof: - return make_ready_future<>(); - - case memcache_ascii_parser::state::error: - return out.write(msg_error); - - case memcache_ascii_parser::state::cmd_set: - { - _system_stats.local()._cmd_set++; - prepare_insertion(); - auto f = _cache.set(_insertion); - if (_parser._noreply) { - return std::move(f).discard_result(); - } - return std::move(f).then([&out] (...) { - return out.write(msg_stored); - }); - } - - case memcache_ascii_parser::state::cmd_cas: - { - _system_stats.local()._cmd_set++; - prepare_insertion(); - auto f = _cache.cas(_insertion, _parser._version); - if (_parser._noreply) { - return std::move(f).discard_result(); - } - return std::move(f).then([&out] (auto result) { - switch (result) { - case cas_result::stored: - return out.write(msg_stored); - case cas_result::not_found: - return out.write(msg_not_found); - case cas_result::bad_version: - return out.write(msg_exists); - default: - std::abort(); - } - }); - } - - case memcache_ascii_parser::state::cmd_add: - { - _system_stats.local()._cmd_set++; - prepare_insertion(); - auto f = _cache.add(_insertion); - if (_parser._noreply) { - return std::move(f).discard_result(); - } - return std::move(f).then([&out] (bool added) { - return out.write(added ? msg_stored : msg_not_stored); - }); - } - - case memcache_ascii_parser::state::cmd_replace: - { - _system_stats.local()._cmd_set++; - prepare_insertion(); - auto f = _cache.replace(_insertion); - if (_parser._noreply) { - return std::move(f).discard_result(); - } - return std::move(f).then([&out] (auto replaced) { - return out.write(replaced ? msg_stored : msg_not_stored); - }); - } - - case memcache_ascii_parser::state::cmd_get: - return handle_get(out); - - case memcache_ascii_parser::state::cmd_gets: - return handle_get(out); - - case memcache_ascii_parser::state::cmd_delete: - { - auto f = _cache.remove(_parser._key); - if (_parser._noreply) { - return std::move(f).discard_result(); - } - return std::move(f).then([&out] (bool removed) { - return out.write(removed ? msg_deleted : msg_not_found); - }); - } - - case memcache_ascii_parser::state::cmd_flush_all: - { - _system_stats.local()._cmd_flush++; - if (_parser._expiration) { - auto expiry = expiration(_parser._expiration); - auto f = _cache.flush_at(expiry.to_time_point()); - if (_parser._noreply) { - return f; - } - return std::move(f).then([&out] { - return out.write(msg_ok); - }); - } else { - auto f = _cache.flush_all(); - if (_parser._noreply) { - return f; - } - return std::move(f).then([&out] { - return out.write(msg_ok); - }); - } - } - - case memcache_ascii_parser::state::cmd_version: - return out.write(msg_version); - - case memcache_ascii_parser::state::cmd_stats: - return print_stats(out); - - case memcache_ascii_parser::state::cmd_stats_hash: - return _cache.print_hash_stats(out); - - case memcache_ascii_parser::state::cmd_incr: - { - auto f = _cache.incr(_parser._key, _parser._u64); - if (_parser._noreply) { - return std::move(f).discard_result(); - } - return std::move(f).then([&out] (auto result) { - auto item = std::move(result.first); - if (!item) { - return out.write(msg_not_found); - } - auto incremented = result.second; - if (!incremented) { - return out.write(msg_error_non_numeric_value); - } - return out.write(item->value().data(), item->value_size()).then([&out] { - return out.write(msg_crlf); - }); - }); - } - - case memcache_ascii_parser::state::cmd_decr: - { - auto f = _cache.decr(_parser._key, _parser._u64); - if (_parser._noreply) { - return std::move(f).discard_result(); - } - return std::move(f).then([&out] (auto result) { - auto item = std::move(result.first); - if (!item) { - return out.write(msg_not_found); - } - auto decremented = result.second; - if (!decremented) { - return out.write(msg_error_non_numeric_value); - } - return out.write(item->value().data(), item->value_size()).then([&out] { - return out.write(msg_crlf); - }); - }); - } - }; - std::abort(); - }).then_wrapped([this, &out] (auto&& f) -> future<> { - // FIXME: then_wrapped() being scheduled even though no exception was triggered has a - // performance cost of about 2.6%. Not using it means maintainability penalty. - try { - f.get(); - } catch (std::bad_alloc& e) { - if (_parser._noreply) { - return make_ready_future<>(); - } - return out.write(msg_out_of_memory); - } - return make_ready_future<>(); - }); - }; -}; - -class udp_server { -public: - static const size_t default_max_datagram_size = 1400; -private: - sharded_cache& _cache; - distributed& _system_stats; - udp_channel _chan; - uint16_t _port; - size_t _max_datagram_size = default_max_datagram_size; - - struct header { - packed _request_id; - packed _sequence_number; - packed _n; - packed _reserved; - - template - auto adjust_endianness(Adjuster a) { - return a(_request_id, _sequence_number, _n); - } - } __attribute__((packed)); - - struct connection { - ipv4_addr _src; - uint16_t _request_id; - input_stream _in; - output_stream _out; - std::vector _out_bufs; - ascii_protocol _proto; - - connection(ipv4_addr src, uint16_t request_id, input_stream&& in, size_t out_size, - sharded_cache& c, distributed& system_stats) - : _src(src) - , _request_id(request_id) - , _in(std::move(in)) - , _out(output_stream(data_sink(std::make_unique(_out_bufs)), out_size, true)) - , _proto(c, system_stats) - {} - - future<> respond(udp_channel& chan) { - int i = 0; - return do_for_each(_out_bufs.begin(), _out_bufs.end(), [this, i, &chan] (packet& p) mutable { - header* out_hdr = p.prepend_header
(0); - out_hdr->_request_id = _request_id; - out_hdr->_sequence_number = i++; - out_hdr->_n = _out_bufs.size(); - *out_hdr = hton(*out_hdr); - return chan.send(_src, std::move(p)); - }); - } - }; - -public: - udp_server(sharded_cache& c, distributed& system_stats, uint16_t port = 11211) - : _cache(c) - , _system_stats(system_stats) - , _port(port) - {} - - void set_max_datagram_size(size_t max_datagram_size) { - _max_datagram_size = max_datagram_size; - } - - void start() { - _chan = engine().net().make_udp_channel({_port}); - keep_doing([this] { - return _chan.receive().then([this](udp_datagram dgram) { - packet& p = dgram.get_data(); - if (p.len() < sizeof(header)) { - // dropping invalid packet - return make_ready_future<>(); - } - - header hdr = ntoh(*p.get_header
()); - p.trim_front(sizeof(hdr)); - - auto request_id = hdr._request_id; - auto in = as_input_stream(std::move(p)); - auto conn = make_lw_shared(dgram.get_src(), request_id, std::move(in), - _max_datagram_size - sizeof(header), _cache, _system_stats); - - if (hdr._n != 1 || hdr._sequence_number != 0) { - return conn->_out.write("CLIENT_ERROR only single-datagram requests supported\r\n").then([this, conn] { - return conn->_out.flush().then([this, conn] { - return conn->respond(_chan).then([conn] {}); - }); - }); - } - - return conn->_proto.handle(conn->_in, conn->_out).then([this, conn]() mutable { - return conn->_out.flush().then([this, conn] { - return conn->respond(_chan).then([conn] {}); - }); - }); - }); - }).or_terminate(); - }; - - future<> stop() { return make_ready_future<>(); } -}; - -class tcp_server { -private: - lw_shared_ptr _listener; - sharded_cache& _cache; - distributed& _system_stats; - uint16_t _port; - struct connection { - connected_socket _socket; - socket_address _addr; - input_stream _in; - output_stream _out; - ascii_protocol _proto; - distributed& _system_stats; - connection(connected_socket&& socket, socket_address addr, sharded_cache& c, distributed& system_stats) - : _socket(std::move(socket)) - , _addr(addr) - , _in(_socket.input()) - , _out(_socket.output()) - , _proto(c, system_stats) - , _system_stats(system_stats) - { - _system_stats.local()._curr_connections++; - _system_stats.local()._total_connections++; - } - ~connection() { - _system_stats.local()._curr_connections--; - } - }; -public: - tcp_server(sharded_cache& cache, distributed& system_stats, uint16_t port = 11211) - : _cache(cache) - , _system_stats(system_stats) - , _port(port) - {} - - void start() { - listen_options lo; - lo.reuse_address = true; - _listener = engine().listen(make_ipv4_address({_port}), lo); - keep_doing([this] { - return _listener->accept().then([this] (connected_socket fd, socket_address addr) mutable { - auto conn = make_lw_shared(std::move(fd), addr, _cache, _system_stats); - do_until([conn] { return conn->_in.eof(); }, [this, conn] { - return conn->_proto.handle(conn->_in, conn->_out).then([conn] { - return conn->_out.flush(); - }); - }); - }); - }).or_terminate(); - } - - future<> stop() { return make_ready_future<>(); } -}; - -class stats_printer { -private: - timer<> _timer; - sharded_cache& _cache; -public: - stats_printer(sharded_cache& cache) - : _cache(cache) {} - - void start() { - _timer.set_callback([this] { - _cache.stats().then([this] (auto stats) { - auto gets_total = stats._get_hits + stats._get_misses; - auto get_hit_rate = gets_total ? ((double)stats._get_hits * 100 / gets_total) : 0; - auto sets_total = stats._set_adds + stats._set_replaces; - auto set_replace_rate = sets_total ? ((double)stats._set_replaces * 100/ sets_total) : 0; - std::cout << "items: " << stats._size << " " - << std::setprecision(2) << std::fixed - << "get: " << stats._get_hits << "/" << gets_total << " (" << get_hit_rate << "%) " - << "set: " << stats._set_replaces << "/" << sets_total << " (" << set_replace_rate << "%)"; - std::cout << std::endl; - }); - }); - _timer.arm_periodic(std::chrono::seconds(1)); - } - - future<> stop() { return make_ready_future<>(); } -}; - -} /* namespace memcache */ - -int main(int ac, char** av) { - distributed cache_peers; - memcache::sharded_cache cache(cache_peers); - distributed system_stats; - distributed udp_server; - distributed tcp_server; - memcache::stats_printer stats(cache); - - namespace bpo = boost::program_options; - app_template app; - app.add_options() - ("max-datagram-size", bpo::value()->default_value(memcache::udp_server::default_max_datagram_size), - "Maximum size of UDP datagram") - ("max-slab-size", bpo::value()->default_value(memcache::default_per_cpu_slab_size/MB), - "Maximum memory to be used for items (value in megabytes) (reclaimer is disabled if set)") - ("slab-page-size", bpo::value()->default_value(memcache::default_slab_page_size/MB), - "Size of slab page (value in megabytes)") - ("stats", - "Print basic statistics periodically (every second)") - ("port", bpo::value()->default_value(11211), - "Specify UDP and TCP ports for memcached server to listen on") - ; - - return app.run(ac, av, [&] { - engine().at_exit([&] { return tcp_server.stop(); }); - engine().at_exit([&] { return udp_server.stop(); }); - engine().at_exit([&] { return cache_peers.stop(); }); - engine().at_exit([&] { return system_stats.stop(); }); - - auto&& config = app.configuration(); - uint16_t port = config["port"].as(); - uint64_t per_cpu_slab_size = config["max-slab-size"].as() * MB; - uint64_t slab_page_size = config["slab-page-size"].as() * MB; - return cache_peers.start(std::move(per_cpu_slab_size), std::move(slab_page_size)).then([&system_stats] { - return system_stats.start(clock_type::now()); - }).then([&] { - std::cout << PLATFORM << " memcached " << VERSION << "\n"; - return make_ready_future<>(); - }).then([&, port] { - return tcp_server.start(std::ref(cache), std::ref(system_stats), port); - }).then([&tcp_server] { - return tcp_server.invoke_on_all(&memcache::tcp_server::start); - }).then([&, port] { - if (engine().net().has_per_core_namespace()) { - return udp_server.start(std::ref(cache), std::ref(system_stats), port); - } else { - return udp_server.start_single(std::ref(cache), std::ref(system_stats), port); - } - }).then([&] { - return udp_server.invoke_on_all(&memcache::udp_server::set_max_datagram_size, - (size_t)config["max-datagram-size"].as()); - }).then([&] { - return udp_server.invoke_on_all(&memcache::udp_server::start); - }).then([&stats, start_stats = config.count("stats")] { - if (start_stats) { - stats.start(); - } - }); - }); -} diff --git a/apps/memcached/memcached.hh b/apps/memcached/memcached.hh deleted file mode 100644 index 54fa217fcc..0000000000 --- a/apps/memcached/memcached.hh +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#ifndef _MEMCACHED_HH -#define _MEMCACHED_HH - -#include "core/sstring.hh" - -namespace memcache { - -class item; -class cache; - -class item_key { -private: - sstring _key; - size_t _hash; -public: - item_key() = default; - item_key(item_key&) = default; - item_key(sstring key) - : _key(key) - , _hash(std::hash()(key)) - {} - item_key(item_key&& other) - : _key(std::move(other._key)) - , _hash(other._hash) - { - other._hash = 0; - } - size_t hash() const { - return _hash; - } - const sstring& key() const { - return _key; - } - bool operator==(const item_key& other) const { - return other._hash == _hash && other._key == _key; - } - void operator=(item_key&& other) { - _key = std::move(other._key); - _hash = other._hash; - other._hash = 0; - } -}; - -} - -namespace std { - -template <> -struct hash { - size_t operator()(const memcache::item_key& key) { - return key.hash(); - } -}; - -} /* namespace std */ - -#endif diff --git a/apps/seawreck/http_response_parser.rl b/apps/seawreck/http_response_parser.rl deleted file mode 100644 index 832223eeee..0000000000 --- a/apps/seawreck/http_response_parser.rl +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include "core/ragel.hh" -#include -#include - -struct http_response { - sstring _version; - std::unordered_map _headers; -}; - -%% machine http_response; - -%%{ - -access _fsm_; - -action mark { - g.mark_start(p); -} - -action store_version { - _rsp->_version = str(); -} - -action store_field_name { - _field_name = str(); -} - -action store_value { - _value = str(); -} - -action assign_field { - _rsp->_headers[_field_name] = std::move(_value); -} - -action extend_field { - _rsp->_headers[_field_name] += sstring(" ") + std::move(_value); -} - -action done { - done = true; - fbreak; -} - -cr = '\r'; -lf = '\n'; -crlf = '\r\n'; -tchar = alpha | digit | '-' | '!' | '#' | '$' | '%' | '&' | '\'' | '*' - | '+' | '.' | '^' | '_' | '`' | '|' | '~'; - -sp = ' '; -ht = '\t'; - -sp_ht = sp | ht; - -http_version = 'HTTP/' (digit '.' digit) >mark %store_version; - -field = tchar+ >mark %store_field_name; -value = any* >mark %store_value; -start_line = http_version space digit digit digit space (any - cr - lf)* crlf; -header_1st = (field sp_ht* ':' value :> crlf) %assign_field; -header_cont = (sp_ht+ value sp_ht* crlf) %extend_field; -header = header_1st header_cont*; -main := start_line header* :> (crlf @done); - -}%% - -class http_response_parser : public ragel_parser_base { - %% write data nofinal noprefix; -public: - enum class state { - error, - eof, - done, - }; - std::unique_ptr _rsp; - sstring _field_name; - sstring _value; - state _state; -public: - void init() { - init_base(); - _rsp.reset(new http_response()); - _state = state::eof; - %% write init; - } - char* parse(char* p, char* pe, char* eof) { - sstring_builder::guard g(_builder, p, pe); - auto str = [this, &g, &p] { g.mark_end(p); return get_str(); }; - bool done = false; - if (p != pe) { - _state = state::error; - } - %% write exec; - if (!done) { - p = nullptr; - } else { - _state = state::done; - } - return p; - } - auto get_parsed_response() { - return std::move(_rsp); - } - bool eof() const { - return _state == state::eof; - } -}; diff --git a/apps/seawreck/seawreck.cc b/apps/seawreck/seawreck.cc deleted file mode 100644 index 28ed128c3b..0000000000 --- a/apps/seawreck/seawreck.cc +++ /dev/null @@ -1,223 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include "apps/seawreck/http_response_parser.hh" -#include "core/print.hh" -#include "core/reactor.hh" -#include "core/app-template.hh" -#include "core/future-util.hh" -#include "core/distributed.hh" -#include "core/semaphore.hh" -#include "core/future-util.hh" -#include - -template -void http_debug(const char* fmt, Args&&... args) { -#if HTTP_DEBUG - print(fmt, std::forward(args)...); -#endif -} - -class http_client { -private: - unsigned _duration; - unsigned _conn_per_core; - unsigned _reqs_per_conn; - std::vector _sockets; - semaphore _conn_connected{0}; - semaphore _conn_finished{0}; - timer<> _run_timer; - bool _timer_based; - bool _timer_done{false}; - uint64_t _total_reqs{0}; -public: - http_client(unsigned duration, unsigned total_conn, unsigned reqs_per_conn) - : _duration(duration) - , _conn_per_core(total_conn / smp::count) - , _reqs_per_conn(reqs_per_conn) - , _run_timer([this] { _timer_done = true; }) - , _timer_based(reqs_per_conn == 0) { - } - - class connection { - private: - connected_socket _fd; - input_stream _read_buf; - output_stream _write_buf; - http_response_parser _parser; - http_client* _http_client; - uint64_t _nr_done{0}; - public: - connection(connected_socket&& fd, http_client* client) - : _fd(std::move(fd)) - , _read_buf(_fd.input()) - , _write_buf(_fd.output()) - , _http_client(client){ - } - - uint64_t nr_done() { - return _nr_done; - } - - future<> do_req() { - return _write_buf.write("GET / HTTP/1.1\r\nHost: 127.0.0.1:10000\r\n\r\n").then([this] { - return _write_buf.flush(); - }).then([this] { - _parser.init(); - return _read_buf.consume(_parser).then([this] { - // Read HTTP response header first - if (_parser.eof()) { - return make_ready_future<>(); - } - auto _rsp = _parser.get_parsed_response(); - auto it = _rsp->_headers.find("Content-Length"); - if (it == _rsp->_headers.end()) { - print("Error: HTTP response does not contain: Content-Length\n"); - return make_ready_future<>(); - } - auto content_len = std::stoi(it->second); - http_debug("Content-Length = %d\n", content_len); - // Read HTTP response body - return _read_buf.read_exactly(content_len).then([this] (temporary_buffer buf) { - _nr_done++; - http_debug("%s\n", buf.get()); - if (_http_client->done(_nr_done)) { - return make_ready_future(); - } else { - return do_req(); - } - }); - }); - }); - } - }; - - future total_reqs() { - print("Requests on cpu %2d: %ld\n", engine().cpu_id(), _total_reqs); - return make_ready_future(_total_reqs); - } - - bool done(uint64_t nr_done) { - if (_timer_based) { - return _timer_done; - } else { - return nr_done >= _reqs_per_conn; - } - } - - future<> connect(ipv4_addr server_addr) { - // Establish all the TCP connections first - for (unsigned i = 0; i < _conn_per_core; i++) { - engine().net().connect(make_ipv4_address(server_addr)).then([this] (connected_socket fd) { - _sockets.push_back(std::move(fd)); - http_debug("Established connection %6d on cpu %3d\n", _conn_connected.current(), engine().cpu_id()); - _conn_connected.signal(); - }).or_terminate(); - } - return _conn_connected.wait(_conn_per_core); - } - - future<> run() { - // All connected, start HTTP request - http_debug("Established all %6d tcp connections on cpu %3d\n", _conn_per_core, engine().cpu_id()); - if (_timer_based) { - _run_timer.arm(std::chrono::seconds(_duration)); - } - for (auto&& fd : _sockets) { - auto conn = new connection(std::move(fd), this); - conn->do_req().then_wrapped([this, conn] (auto&& f) { - http_debug("Finished connection %6d on cpu %3d\n", _conn_finished.current(), engine().cpu_id()); - _total_reqs += conn->nr_done(); - _conn_finished.signal(); - delete conn; - try { - f.get(); - } catch (std::exception& ex) { - print("http request error: %s\n", ex.what()); - } - }); - } - - // All finished - return _conn_finished.wait(_conn_per_core); - } - future<> stop() { - return make_ready_future(); - } -}; - -namespace bpo = boost::program_options; - -int main(int ac, char** av) { - app_template app; - app.add_options() - ("server,s", bpo::value()->default_value("192.168.66.100:10000"), "Server address") - ("conn,c", bpo::value()->default_value(100), "total connections") - ("reqs,r", bpo::value()->default_value(0), "reqs per connection") - ("duration,d", bpo::value()->default_value(10), "duration of the test in seconds)"); - - return app.run(ac, av, [&app] { - auto& config = app.configuration(); - auto server = config["server"].as(); - auto reqs_per_conn = config["reqs"].as(); - auto total_conn= config["conn"].as(); - auto duration = config["duration"].as(); - - if (total_conn % smp::count != 0) { - print("Error: conn needs to be n * cpu_nr\n"); - return engine().exit(0); - } - - auto http_clients = new distributed; - - // Start http requests on all the cores - auto started = std::chrono::high_resolution_clock::now(); - print("========== http_client ============\n"); - print("Server: %s\n", server); - print("Connections: %u\n", total_conn); - print("Requests/connection: %s\n", reqs_per_conn == 0 ? "dynamic (timer based)" : std::to_string(reqs_per_conn)); - http_clients->start(std::ref(duration), std::ref(total_conn), std::ref(reqs_per_conn)).then([http_clients, started, server] { - return http_clients->invoke_on_all(&http_client::connect, ipv4_addr{server}); - }).then([http_clients] { - return http_clients->invoke_on_all(&http_client::run); - }).then([http_clients] { - return http_clients->map_reduce(adder(), &http_client::total_reqs); - }).then([http_clients, started] (auto total_reqs) { - // All the http requests are finished - auto finished = std::chrono::high_resolution_clock::now(); - auto elapsed = finished - started; - auto secs = static_cast(elapsed.count() / 1000000000.0); - print("Total cpus: %u\n", smp::count); - print("Total requests: %u\n", total_reqs); - print("Total time: %f\n", secs); - print("Requests/sec: %f\n", static_cast(total_reqs) / secs); - print("========== done ============\n"); - http_clients->stop().then([http_clients] { - // FIXME: If we call engine().exit(0) here to exit when - // requests are done. The tcp connection will not be closed - // properly, becasue we exit too earily and the FIN packets are - // not exchanged. - delete http_clients; - engine().exit(0); - }); - }); - }); -} diff --git a/configure.py b/configure.py index 5e01829890..8b5a6703dc 100755 --- a/configure.py +++ b/configure.py @@ -1,21 +1,4 @@ #!/usr/bin/python3 -# -# This file is open source software, licensed to you under the terms -# of the Apache License, Version 2.0 (the "License"). See the NOTICE file -# distributed with this work for additional information regarding copyright -# ownership. You may not use this file except in compliance with the License. -# -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# import os, os.path, textwrap, argparse, sys, shlex, subprocess, tempfile, re configure_args = str.join(' ', [shlex.quote(x) for x in sys.argv[1:]]) @@ -195,58 +178,18 @@ urchin_tests = [ 'tests/urchin/network_topology_strategy_test', 'tests/urchin/query_processor_test', 'tests/urchin/batchlog_manager_test', -] - -tests = [ - 'tests/fileiotest', - 'tests/directory_test', - 'tests/linecount', - 'tests/echotest', - 'tests/l3_test', - 'tests/ip_test', - 'tests/timertest', - 'tests/tcp_test', - 'tests/futures_test', - 'tests/foreign_ptr_test', - 'tests/smp_test', - 'tests/thread_test', - 'tests/thread_context_switch', - 'tests/udp_server', - 'tests/udp_client', - 'tests/blkdiscard_test', - 'tests/sstring_test', - 'tests/httpd', - 'tests/memcached/test_ascii_parser', - 'tests/tcp_server', - 'tests/tcp_client', - 'tests/allocator_test', - 'tests/output_stream_test', - 'tests/udp_zero_copy', - 'tests/shared_ptr_test', - 'tests/slab_test', - 'tests/fstream_test', - 'tests/distributed_test', - 'tests/rpc', - 'tests/semaphore_test', - ] - -# urchin -tests += [ 'tests/urchin/bytes_ostream_test', 'tests/urchin/UUID_test', 'tests/urchin/murmur_hash_test', ] apps = [ - 'apps/httpd/httpd', - 'seastar', - 'apps/seawreck/seawreck', - 'apps/memcached/memcached', + 'scylla', ] -tests += urchin_tests +tests = urchin_tests -all_artifacts = apps + tests + ['libseastar.a', 'seastar.pc'] +all_artifacts = apps + tests arg_parser = argparse.ArgumentParser('Configure seastar') arg_parser.add_argument('--static', dest = 'static', action = 'store_const', default = '', @@ -276,121 +219,8 @@ add_tristate(arg_parser, name = 'hwloc', dest = 'hwloc', help = 'hwloc support') add_tristate(arg_parser, name = 'xen', dest = 'xen', help = 'Xen support') args = arg_parser.parse_args() -libnet = [ - 'net/proxy.cc', - 'net/virtio.cc', - 'net/dpdk.cc', - 'net/ip.cc', - 'net/ethernet.cc', - 'net/arp.cc', - 'net/native-stack.cc', - 'net/ip_checksum.cc', - 'net/udp.cc', - 'net/tcp.cc', - 'net/dhcp.cc', - ] - -core = [ - 'core/reactor.cc', - 'core/fstream.cc', - 'core/posix.cc', - 'core/memory.cc', - 'core/resource.cc', - 'core/scollectd.cc', - 'core/app-template.cc', - 'core/thread.cc', - 'core/dpdk_rte.cc', - 'util/conversions.cc', - 'net/packet.cc', - 'net/posix-stack.cc', - 'net/net.cc', - 'rpc/rpc.cc', - ] - -http = ['http/transformers.cc', - 'http/json_path.cc', - 'http/file_handler.cc', - 'http/common.cc', - 'http/routes.cc', - 'json/json_elements.cc', - 'json/formatter.cc', - 'http/matcher.cc', - 'http/mime_types.cc', - 'http/httpd.cc', - 'http/reply.cc', - 'http/request_parser.rl', - 'http/api_docs.cc', - ] - -api = ['api/api.cc', - 'api/api-doc/storage_service.json', - 'api/storage_service.cc', - 'api/api-doc/commitlog.json', - 'api/commitlog.cc', - 'api/api-doc/gossiper.json', - 'api/gossiper.cc', - 'api/api-doc/failure_detector.json', - 'api/failure_detector.cc', - 'api/api-doc/column_family.json', - 'api/column_family.cc', - 'api/messaging_service.cc', - 'api/api-doc/messaging_service.json', - 'api/api-doc/storage_proxy.json', - 'api/storage_proxy.cc', - 'api/api-doc/cache_service.json', - 'api/cache_service.cc', - 'api/api-doc/collectd.json', - 'api/collectd.cc', - 'api/api-doc/endpoint_snitch_info.json', - 'api/endpoint_snitch.cc', - 'api/api-doc/compaction_manager.json', - 'api/compaction_manager.cc', - 'api/api-doc/hinted_handoff.json', - 'api/hinted_handoff.cc', - ] - -boost_test_lib = [ - 'tests/test-utils.cc', - 'tests/test_runner.cc', -] - defines = [] -libs = '-laio -lboost_program_options -lboost_system -lboost_filesystem -lstdc++ -lm -lboost_unit_test_framework -lboost_thread -lcryptopp -lrt -lyaml-cpp -lboost_date_time' -hwloc_libs = '-lhwloc -lnuma -lpciaccess -lxml2 -lz' -urchin_libs = '-llz4 -lsnappy -lz' - -libs = urchin_libs + ' ' + libs - -xen_used = False -def have_xen(): - source = '#include \n' - source += '#include \n' - source += '#include \n' - source += '#include \n' - source += '#include \n' - - return try_compile(compiler = args.cxx, source = source) - -if apply_tristate(args.xen, test = have_xen, - note = 'Note: xen-devel not installed. No Xen support.', - missing = 'Error: required package xen-devel not installed.'): - libs += ' -lxenstore' - defines.append("HAVE_XEN") - libnet += [ 'net/xenfront.cc' ] - core += [ - 'core/xen/xenstore.cc', - 'core/xen/gntalloc.cc', - 'core/xen/evtchn.cc', - ] - xen_used=True - -if xen_used and args.dpdk_target: - print("Error: only xen or dpdk can be used, not both.") - sys.exit(1) - -memcache_base = [ - 'apps/memcached/ascii.rl' -] + libnet + core +urchin_libs = '-llz4 -lsnappy -lz -lboost_thread -lcryptopp -lrt -lyaml-cpp -lboost_date_time' cassandra_interface = Thrift(source = 'interface/cassandra.thrift', service = 'Cassandra') @@ -532,74 +362,56 @@ urchin_core = (['database.cc', ] + [Antlr3Grammar('cql3/Cql.g')] + [Thrift('interface/cassandra.thrift', 'Cassandra')] - + core + libnet) + ) -urchin_tests_dependencies = urchin_core + http + api + [ +api = ['api/api.cc', + 'api/api-doc/storage_service.json', + 'api/storage_service.cc', + 'api/api-doc/commitlog.json', + 'api/commitlog.cc', + 'api/api-doc/gossiper.json', + 'api/gossiper.cc', + 'api/api-doc/failure_detector.json', + 'api/failure_detector.cc', + 'api/api-doc/column_family.json', + 'api/column_family.cc', + 'api/messaging_service.cc', + 'api/api-doc/messaging_service.json', + 'api/api-doc/storage_proxy.json', + 'api/storage_proxy.cc', + 'api/api-doc/cache_service.json', + 'api/cache_service.cc', + 'api/api-doc/collectd.json', + 'api/collectd.cc', + 'api/api-doc/endpoint_snitch_info.json', + 'api/endpoint_snitch.cc', + 'api/api-doc/compaction_manager.json', + 'api/compaction_manager.cc', + 'api/api-doc/hinted_handoff.json', + 'api/hinted_handoff.cc', + ] + +urchin_tests_dependencies = urchin_core + [ 'tests/urchin/cql_test_env.cc', 'tests/urchin/cql_assertions.cc', 'tests/urchin/result_set_assertions.cc', ] +urchin_tests_seastar_deps = [ + 'seastar/tests/test-utils.cc', + 'seastar/tests/test_runner.cc', +] + deps = { - 'libseastar.a' : core + libnet, - 'seastar.pc': [], - 'seastar': ['main.cc'] + http + api + urchin_core, - 'apps/httpd/httpd': ['apps/httpd/demo.json', 'apps/httpd/main.cc'] + http + libnet + core, - 'apps/memcached/memcached': ['apps/memcached/memcache.cc'] + memcache_base, - 'tests/memcached/test_ascii_parser': ['tests/memcached/test_ascii_parser.cc'] + memcache_base + boost_test_lib, - 'tests/fileiotest': ['tests/fileiotest.cc'] + core + boost_test_lib, - 'tests/directory_test': ['tests/directory_test.cc'] + core, - 'tests/linecount': ['tests/linecount.cc'] + core, - 'tests/echotest': ['tests/echotest.cc'] + core + libnet, - 'tests/l3_test': ['tests/l3_test.cc'] + core + libnet, - 'tests/ip_test': ['tests/ip_test.cc'] + core + libnet, - 'tests/tcp_test': ['tests/tcp_test.cc'] + core + libnet, - 'tests/timertest': ['tests/timertest.cc'] + core, - 'tests/futures_test': ['tests/futures_test.cc'] + core + boost_test_lib, - 'tests/foreign_ptr_test': ['tests/foreign_ptr_test.cc'] + core + boost_test_lib, - 'tests/semaphore_test': ['tests/semaphore_test.cc'] + core + boost_test_lib, - 'tests/smp_test': ['tests/smp_test.cc'] + core, - 'tests/thread_test': ['tests/thread_test.cc'] + core + boost_test_lib, - 'tests/thread_context_switch': ['tests/thread_context_switch.cc'] + core, - 'tests/udp_server': ['tests/udp_server.cc'] + core + libnet, - 'tests/udp_client': ['tests/udp_client.cc'] + core + libnet, - 'tests/tcp_server': ['tests/tcp_server.cc'] + core + libnet, - 'tests/tcp_client': ['tests/tcp_client.cc'] + core + libnet, - 'apps/seawreck/seawreck': ['apps/seawreck/seawreck.cc', 'apps/seawreck/http_response_parser.rl'] + core + libnet, - 'tests/blkdiscard_test': ['tests/blkdiscard_test.cc'] + core, - 'tests/sstring_test': ['tests/sstring_test.cc'] + core, - 'tests/httpd': ['tests/httpd.cc'] + http + core + boost_test_lib, - 'tests/allocator_test': ['tests/allocator_test.cc', 'core/memory.cc', 'core/posix.cc'], - 'tests/output_stream_test': ['tests/output_stream_test.cc'] + core + libnet + boost_test_lib, - 'tests/udp_zero_copy': ['tests/udp_zero_copy.cc'] + core + libnet, - 'tests/shared_ptr_test': ['tests/shared_ptr_test.cc'] + core, - 'tests/slab_test': ['tests/slab_test.cc'] + core, - 'tests/fstream_test': ['tests/fstream_test.cc'] + core + boost_test_lib, - 'tests/distributed_test': ['tests/distributed_test.cc'] + core, - 'tests/rpc': ['tests/rpc.cc'] + core + libnet, - 'tests/urchin/gossiping_property_file_snitch_test': ['tests/urchin/gossiping_property_file_snitch_test.cc'] + urchin_core, - 'tests/urchin/network_topology_strategy_test': ['tests/urchin/network_topology_strategy_test.cc'] + urchin_core, + 'scylla': ['main.cc'] + urchin_core + api, } for t in urchin_tests: deps[t] = urchin_tests_dependencies + [t + '.cc'] + if 'types_test' not in t and 'keys_test' not in t and 'partitioner_test' not in t and 'map_difference_test' not in t and 'frozen_mutation_test' not in t and 'perf_mutation' not in t and 'cartesian_product_test' not in t and 'perf_hash' not in t and 'perf_cql_parser' not in t and 'message' not in t and 'perf_simple_query' not in t and 'serialization' not in t and t != 'tests/urchin/gossip' and 'compound_test' not in t: + deps[t] += urchin_tests_seastar_deps -deps['tests/urchin/mutation_test'] += boost_test_lib -deps['tests/urchin/cql_query_test'] += boost_test_lib -deps['tests/urchin/mutation_reader_test'] += boost_test_lib -deps['tests/urchin/mutation_query_test'] += boost_test_lib -deps['tests/urchin/commitlog_test'] += boost_test_lib -deps['tests/urchin/config_test'] += boost_test_lib -deps['tests/urchin/sstable_test'] += boost_test_lib + ['tests/urchin/sstable_datafile_test.cc'] -deps['tests/urchin/sstable_mutation_test'] += boost_test_lib -deps['tests/urchin/hash_test'] += boost_test_lib -deps['tests/urchin/serializer_test'] += boost_test_lib -deps['tests/urchin/gossip_test'] += boost_test_lib -deps['tests/urchin/gossiping_property_file_snitch_test'] += boost_test_lib -deps['tests/urchin/network_topology_strategy_test'] += boost_test_lib -deps['tests/urchin/row_cache_test'] += boost_test_lib -deps['tests/urchin/query_processor_test'] += boost_test_lib -deps['tests/urchin/batchlog_manager_test'] += boost_test_lib +deps['tests/urchin/sstable_test'] += ['tests/urchin/sstable_datafile_test.cc'] deps['tests/urchin/bytes_ostream_test'] = ['tests/urchin/bytes_ostream_test.cc'] deps['tests/urchin/UUID_test'] = ['utils/UUID_gen.cc', 'tests/urchin/UUID_test.cc'] @@ -610,55 +422,6 @@ warnings = [ '-Wno-maybe-uninitialized', # false positives on gcc 5 ] -# The "--with-osv=" parameter is a shortcut for a bunch of other -# settings: -if args.with_osv: - args.so = True - args.hwloc = False - args.user_cflags = (args.user_cflags + - ' -DDEFAULT_ALLOCATOR -fvisibility=default -DHAVE_OSV -I' + - args.with_osv + ' -I' + args.with_osv + '/include -I' + - args.with_osv + '/arch/x64') - -if args.dpdk: - subprocess.check_call('make -C dpdk RTE_OUTPUT=$PWD/build/dpdk/ config T=x86_64-native-linuxapp-gcc', - shell = True) - # adjust configutation to taste - dotconfig = 'build/dpdk/.config' - lines = open(dotconfig).readlines() - def update(lines, vars): - ret = [] - for line in lines: - for var, val in vars.items(): - if line.startswith(var + '='): - line = var + '=' + val + '\n' - ret.append(line) - return ret - lines = update(lines, {'CONFIG_RTE_LIBRTE_PMD_BOND': 'n', - 'CONFIG_RTE_MBUF_SCATTER_GATHER': 'n', - 'CONFIG_RTE_LIBRTE_IP_FRAG': 'n', - 'CONFIG_RTE_APP_TEST': 'n', - 'CONFIG_RTE_TEST_PMD': 'n', - 'CONFIG_RTE_MBUF_REFCNT_ATOMIC': 'n', - 'CONFIG_RTE_MAX_MEMSEG': '8192', - }) - open(dotconfig, 'w').writelines(lines) - args.dpdk_target = 'build/dpdk' - -if args.dpdk_target: - args.user_cflags = (args.user_cflags + - ' -DHAVE_DPDK -I' + args.dpdk_target + '/include ' + - dpdk_cflags(args.dpdk_target) + - ' -Wno-error=literal-suffix -Wno-literal-suffix -Wno-invalid-offsetof') - libs += (' -L' + args.dpdk_target + '/lib ') - if args.with_osv: - libs += '-lintel_dpdk -lrt -lm -ldl' - else: - libs += '-Wl,--whole-archive -lrte_pmd_vmxnet3_uio -lrte_pmd_i40e -lrte_pmd_ixgbe -lrte_pmd_e1000 -lrte_pmd_ring -Wl,--no-whole-archive -lrte_distributor -lrte_pipeline -lrte_table -lrte_port -lrte_timer -lrte_hash -lrte_lpm -lrte_power -lrte_acl -lrte_meter -lrte_sched -lrte_kvargs -lrte_mbuf -lethdev -lrte_eal -lrte_malloc -lrte_mempool -lrte_ring -lrte_cmdline -lrte_cfgfile -lrt -lm -ldl' - -args.user_cflags += " " + pkg_config("--cflags", "jsoncpp") -libs += " " + pkg_config("--libs", "jsoncpp") - warnings = [w for w in warnings if warning_supported(warning = w, compiler = args.cxx)] @@ -667,16 +430,6 @@ warnings = ' '.join(warnings) dbgflag = debug_flag(args.cxx) if args.debuginfo else '' -def have_hwloc(): - return try_compile(compiler = args.cxx, source = '#include \n#include ') - -if apply_tristate(args.hwloc, test = have_hwloc, - note = 'Note: hwloc-devel/numactl-devel not installed. No NUMA support.', - missing = 'Error: required packages hwloc-devel/numactl-devel not installed.'): - libs += ' ' + hwloc_libs - defines.append('HAVE_HWLOC') - defines.append('HAVE_NUMA') - if args.so: args.pie = '-shared' args.fpie = '-fpic' @@ -697,13 +450,43 @@ link_pool_depth = max(int(total_memory / 7e9), 1) build_modes = modes if args.mode == 'all' else [args.mode] build_artifacts = all_artifacts if not args.artifacts else args.artifacts -dpdk_sources = [] -if args.dpdk: - for root, dirs, files in os.walk('dpdk'): - dpdk_sources += [os.path.join(root, file) - for file in files - if file.endswith('.h') or file.endswith('.c')] -dpdk_sources = ' '.join(dpdk_sources) +status = subprocess.call('./configure.py', cwd = 'seastar') + +if status != 0: + print('Seastar configuration failed') + sys.exit(1) + + +pc = { mode : 'build/{}/seastar.pc'.format(mode) for mode in build_modes } +for ninja in ['ninja', 'ninja-build', 'true']: + try: + status = subprocess.call([ninja] + list(pc.values()), cwd = 'seastar') + if status == 0: + break + except OSError as e: + pass +if ninja == 'true': + print('Unable to create {}'.format(pc)) + sys.exit(1) + +for mode in build_modes: + cfg = dict([line.strip().split(': ', 1) + for line in open('seastar/' + pc[mode]) + if ': ' in line]) + modes[mode]['seastar_cflags'] = cfg['Cflags'] + modes[mode]['seastar_libs'] = cfg['Libs'] + +def gen_seastar_deps(): + for root, dir, files in os.walk('seastar'): + for f in files: + if f.endswith('.cc') or f.endswith('.hh'): + yield root + '/' + f + +seastar_deps = ' '.join(gen_seastar_deps()) + +args.user_cflags += " " + pkg_config("--cflags", "jsoncpp") +libs = "-lyaml-cpp -llz4 -lz -lsnappy " + pkg_config("--libs", "jsoncpp") + ' -lboost_filesystem' +user_cflags = args.user_cflags outdir = 'build' buildfile = 'build.ninja' @@ -720,9 +503,8 @@ with open(buildfile, 'w') as f: configure_args = {configure_args} builddir = {outdir} cxx = {cxx} - # we disable _FORTIFY_SOURCE because it generates false positives with longjmp() (core/thread.cc) - cxxflags = -std=gnu++1y {dbgflag} {fpie} -Wall -Werror -fvisibility=hidden -pthread -I. -U_FORTIFY_SOURCE {user_cflags} {warnings} {defines} - ldflags = {dbgflag} -Wl,--no-as-needed {static} {pie} -fvisibility=hidden -pthread {user_ldflags} + cxxflags = {user_cflags} {warnings} {defines} + ldflags = {user_ldflags} libs = {libs} pool link_pool depth = {link_pool_depth} @@ -733,8 +515,11 @@ with open(buildfile, 'w') as f: command = echo -e $text > $out description = GEN $out rule swagger - command = json/json2code.py -f $in -o $out + command = seastar/json/json2code.py -f $in -o $out description = SWAGGER $out + rule ninja + command = {ninja} -C $subdir $target + description = NINJA $out ''').format(**globals())) if args.dpdk: f.write(textwrap.dedent('''\ @@ -744,19 +529,14 @@ with open(buildfile, 'w') as f: ''').format(**globals())) for mode in build_modes: modeval = modes[mode] - if modeval['sanitize'] and not do_sanitize: - print('Note: --static disables debug mode sanitizers') - modeval['sanitize'] = '' - modeval['sanitize_libs'] = '' f.write(textwrap.dedent('''\ - cxxflags_{mode} = {sanitize} {opt} -I $builddir/{mode}/gen - libs_{mode} = {libs} {sanitize_libs} + cxxflags_{mode} = -I. -I $builddir/{mode}/gen -I seastar rule cxx.{mode} - command = $cxx -MMD -MT $out -MF $out.d $cxxflags $cxxflags_{mode} -c -o $out $in + command = $cxx -MMD -MT $out -MF $out.d {seastar_cflags} $cxxflags $cxxflags_{mode} -c -o $out $in description = CXX $out depfile = $out.d rule link.{mode} - command = $cxx $cxxflags_{mode} $ldflags -o $out $in $libs $libs_{mode} + command = $cxx $cxxflags_{mode} $ldflags {seastar_libs} -o $out $in $libs $libs_{mode} description = LINK $out pool = link_pool rule link_stripped.{mode} @@ -807,18 +587,8 @@ with open(buildfile, 'w') as f: elif binary.endswith('.a'): f.write('build $builddir/{}/{}: ar.{} {}\n'.format(mode, binary, mode, str.join(' ', objs))) else: - if binary.startswith('tests/'): - # Our code's debugging information is huge, and multiplied - # by many tests yields ridiculous amounts of disk space. - # So we strip the tests by default; The user can very - # quickly re-link the test unstripped by adding a "_g" - # to the test name, e.g., "ninja build/release/testname_g" - f.write('build $builddir/{}/{}: link_stripped.{} {}\n'.format(mode, binary, mode, str.join(' ', objs))) - if has_thrift: - f.write(' libs = -lthrift -lboost_system $libs\n') - f.write('build $builddir/{}/{}_g: link.{} {}\n'.format(mode, binary, mode, str.join(' ', objs))) - else: - f.write('build $builddir/{}/{}: link.{} {} | {}\n'.format(mode, binary, mode, str.join(' ', objs), dpdk_deps)) + f.write('build $builddir/{}/{}: link.{} {} {}\n'.format(mode, binary, mode, str.join(' ', objs), + 'seastar/build/{}/libseastar.a'.format(mode))) if has_thrift: f.write(' libs = -lthrift -lboost_system $libs\n') for src in srcs: @@ -865,6 +635,9 @@ with open(buildfile, 'w') as f: for cc in grammar.sources('$builddir/{}/gen'.format(mode)): obj = cc.replace('.cpp', '.o') f.write('build {}: cxx.{} {}\n'.format(obj, mode, cc)) + f.write('build seastar/build/{}/libseastar.a: ninja {}\n'.format(mode, seastar_deps)) + f.write(' subdir = seastar\n') + f.write(' target = build/{}/libseastar.a\n'.format(mode)) f.write(textwrap.dedent('''\ rule configure command = python3 configure.py $configure_args diff --git a/core/align.hh b/core/align.hh deleted file mode 100644 index e53c5dd91e..0000000000 --- a/core/align.hh +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef ALIGN_HH_ -#define ALIGN_HH_ - -#include -#include - -template -inline constexpr -T align_up(T v, T align) { - return (v + align - 1) & ~(align - 1); -} - -template -inline constexpr -T* align_up(T* v, size_t align) { - static_assert(sizeof(T) == 1, "align byte pointers only"); - return reinterpret_cast(align_up(reinterpret_cast(v), align)); -} - -template -inline constexpr -T align_down(T v, T align) { - return v & ~(align - 1); -} - -template -inline constexpr -T* align_down(T* v, size_t align) { - static_assert(sizeof(T) == 1, "align byte pointers only"); - return reinterpret_cast(align_down(reinterpret_cast(v), align)); -} - -#endif /* ALIGN_HH_ */ diff --git a/core/app-template.cc b/core/app-template.cc deleted file mode 100644 index f40a04d2a9..0000000000 --- a/core/app-template.cc +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "app-template.hh" -#include "core/reactor.hh" -#include "core/scollectd.hh" -#include "core/print.hh" -#include -#include -#include -#include - -namespace bpo = boost::program_options; - -app_template::app_template() - : _opts("App options") { - _opts.add_options() - ("help,h", "show help message") - ; - _opts.add(reactor::get_options_description()); - _opts.add(smp::get_options_description()); - _opts.add(scollectd::get_options_description()); -} - -boost::program_options::options_description_easy_init -app_template::add_options() { - return _opts.add_options(); -} - -void -app_template::add_positional_options(std::initializer_list options) { - for (auto&& o : options) { - _opts.add(boost::make_shared(o.name, o.value_semantic, o.help)); - _pos_opts.add(o.name, o.max_count); - } -} - - -bpo::variables_map& -app_template::configuration() { - return *_configuration; -} - -int -app_template::run(int ac, char ** av, std::function&& func) { -#ifdef DEBUG - print("WARNING: debug mode. Not for benchmarking or production\n"); -#endif - bpo::variables_map configuration; - try { - bpo::store(bpo::command_line_parser(ac, av) - .options(_opts) - .positional(_pos_opts) - .run() - , configuration); - auto home = std::getenv("HOME"); - if (home) { - std::ifstream ifs(std::string(home) + "/.config/seastar/seastar.conf"); - if (ifs) { - bpo::store(bpo::parse_config_file(ifs, _opts), configuration); - } - } - } catch (bpo::error& e) { - print("error: %s\n\nTry --help.\n", e.what()); - return 2; - } - bpo::notify(configuration); - if (configuration.count("help")) { - std::cout << _opts << "\n"; - return 1; - } - smp::configure(configuration); - _configuration = {std::move(configuration)}; - engine().when_started().then([this] { - scollectd::configure( this->configuration()); - }).then( - std::move(func) - ).then_wrapped([] (auto&& f) { - try { - f.get(); - } catch (std::exception& ex) { - std::cout << "program failed with uncaught exception: " << ex.what() << "\n"; - engine().exit(1); - } - }); - auto exit_code = engine().run(); - smp::cleanup(); - return exit_code; -} diff --git a/core/app-template.hh b/core/app-template.hh deleted file mode 100644 index bd357a3ee5..0000000000 --- a/core/app-template.hh +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ -#ifndef _APP_TEMPLATE_HH -#define _APP_TEMPLATE_HH - -#include -#include -#include - -class app_template { -private: - boost::program_options::options_description _opts; - boost::program_options::positional_options_description _pos_opts; - boost::optional _configuration; -public: - struct positional_option { - const char* name; - const boost::program_options::value_semantic* value_semantic; - const char* help; - int max_count; - }; -public: - app_template(); - boost::program_options::options_description_easy_init add_options(); - void add_positional_options(std::initializer_list options); - boost::program_options::variables_map& configuration(); - int run(int ac, char ** av, std::function&& func); -}; - -#endif diff --git a/core/apply.hh b/core/apply.hh deleted file mode 100644 index c015274fde..0000000000 --- a/core/apply.hh +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef APPLY_HH_ -#define APPLY_HH_ - -#include -#include - -template -struct apply_helper; - -template -struct apply_helper> { - static auto apply(Func&& func, Tuple args) { - return func(std::get(std::forward(args))...); - } -}; - -template -inline -auto apply(Func&& func, std::tuple&& args) { - using helper = apply_helper&&, std::index_sequence_for>; - return helper::apply(std::forward(func), std::move(args)); -} - -template -inline -auto apply(Func&& func, std::tuple& args) { - using helper = apply_helper&, std::index_sequence_for>; - return helper::apply(std::forward(func), args); -} - -template -inline -auto apply(Func&& func, const std::tuple& args) { - using helper = apply_helper&, std::index_sequence_for>; - return helper::apply(std::forward(func), args); -} - -#endif /* APPLY_HH_ */ diff --git a/core/array_map.hh b/core/array_map.hh deleted file mode 100644 index 9e0a1f60d1..0000000000 --- a/core/array_map.hh +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef ARRAY_MAP_HH_ -#define ARRAY_MAP_HH_ - -#include - -// unordered_map implemented as a simple array - -template -class array_map { - std::array _a {}; -public: - array_map(std::initializer_list> i) { - for (auto kv : i) { - _a[kv.first] = kv.second; - } - } - Value& operator[](size_t key) { return _a[key]; } - const Value& operator[](size_t key) const { return _a[key]; } - - Value& at(size_t key) { - if (key >= Max) { - throw std::out_of_range(std::to_string(key) + " >= " + std::to_string(Max)); - } - return _a[key]; - } -}; - - - -#endif /* ARRAY_MAP_HH_ */ diff --git a/core/bitops.hh b/core/bitops.hh deleted file mode 100644 index cece53676a..0000000000 --- a/core/bitops.hh +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef BITOPS_HH_ -#define BITOPS_HH_ - -inline -constexpr unsigned count_leading_zeros(unsigned x) { - return __builtin_clz(x); -} - -inline -constexpr unsigned count_leading_zeros(unsigned long x) { - return __builtin_clzl(x); -} - -inline -constexpr unsigned count_leading_zeros(unsigned long long x) { - return __builtin_clzll(x); -} - -inline -constexpr unsigned count_trailing_zeros(unsigned x) { - return __builtin_ctz(x); -} - -inline -constexpr unsigned count_trailing_zeros(unsigned long x) { - return __builtin_ctzl(x); -} - -inline -constexpr unsigned count_trailing_zeros(unsigned long long x) { - return __builtin_ctzll(x); -} - -#endif /* BITOPS_HH_ */ diff --git a/core/bitset-iter.hh b/core/bitset-iter.hh deleted file mode 100644 index 5a1cf09ae6..0000000000 --- a/core/bitset-iter.hh +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -/* - * Imported from OSv: - * - * Copyright (C) 2014 Cloudius Systems, Ltd. - * - * This work is open source software, licensed under the terms of the - * BSD license as described in the LICENSE file in the top-level directory. - */ - -#ifndef __OSV_BITSET_ITER -#define __OSV_BITSET_ITER - -#include -#include - -namespace bitsets { - -static constexpr int ulong_bits = std::numeric_limits::digits; - -/** - * Returns the number of leading zeros in value's binary representation. - * - * If value == 0 the result is undefied. If T is signed and value is negative - * the result is undefined. - * - * The highest value that can be returned is std::numeric_limits::digits - 1, - * which is returned when value == 1. - */ -template -inline size_t count_leading_zeros(T value); - -/** - * Returns the number of trailing zeros in value's binary representation. - * - * If value == 0 the result is undefied. If T is signed and value is negative - * the result is undefined. - * - * The highest value that can be returned is std::numeric_limits::digits - 1. - */ -template -static inline size_t count_trailing_zeros(T value); - -template<> -inline size_t count_leading_zeros(unsigned long value) -{ - return __builtin_clzl(value); -} - -template<> -inline size_t count_leading_zeros(long value) -{ - return __builtin_clzl((unsigned long)value) - 1; -} - -template<> -inline -size_t count_trailing_zeros(unsigned long value) -{ - return __builtin_ctzl(value); -} - -template<> -inline -size_t count_trailing_zeros(long value) -{ - return __builtin_ctzl((unsigned long)value); -} - -/** - * Returns the index of the first set bit. - * Result is undefined if bitset.any() == false. - */ -template -static inline size_t get_first_set(const std::bitset& bitset) -{ - static_assert(N <= ulong_bits, "bitset too large"); - return count_trailing_zeros(bitset.to_ulong()); -} - -/** - * Returns the index of the last set bit in the bitset. - * Result is undefined if bitset.any() == false. - */ -template -static inline size_t get_last_set(const std::bitset& bitset) -{ - static_assert(N <= ulong_bits, "bitset too large"); - return ulong_bits - 1 - count_leading_zeros(bitset.to_ulong()); -} - -template -class set_iterator : public std::iterator -{ -private: - void advance() - { - if (_bitset.none()) { - _index = -1; - } else { - auto shift = get_first_set(_bitset) + 1; - _index += shift; - _bitset >>= shift; - } - } -public: - set_iterator(std::bitset bitset, int offset = 0) - : _bitset(bitset) - , _index(offset - 1) - { - static_assert(N <= ulong_bits, "This implementation is inefficient for large bitsets"); - _bitset >>= offset; - advance(); - } - - void operator++() - { - advance(); - } - - int operator*() const - { - return _index; - } - - bool operator==(const set_iterator& other) const - { - return _index == other._index; - } - - bool operator!=(const set_iterator& other) const - { - return !(*this == other); - } -private: - std::bitset _bitset; - int _index; -}; - -template -class set_range -{ -public: - using iterator = set_iterator; - using value_type = int; - - set_range(std::bitset bitset, int offset = 0) - : _bitset(bitset) - , _offset(offset) - { - } - - iterator begin() const { return iterator(_bitset, _offset); } - iterator end() const { return iterator(0); } -private: - std::bitset _bitset; - int _offset; -}; - -template -static inline set_range for_each_set(std::bitset bitset, int offset = 0) -{ - return set_range(bitset, offset); -} - - -} - -#endif diff --git a/core/circular_buffer.hh b/core/circular_buffer.hh deleted file mode 100644 index b8cc06d3c9..0000000000 --- a/core/circular_buffer.hh +++ /dev/null @@ -1,383 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef CIRCULAR_BUFFER_HH_ -#define CIRCULAR_BUFFER_HH_ - -// A growable double-ended queue container that can be efficiently -// extended (and shrunk) from both ends. Implementation is a single -// storage vector. -// -// Similar to libstdc++'s std::deque, except that it uses a single level -// store, and so is more efficient for simple stored items. -// Similar to boost::circular_buffer_space_optimized, except it uses -// uninitialized storage for unoccupied elements (and thus move/copy -// constructors instead of move/copy assignments, which are less efficient). - -#include "transfer.hh" -#include -#include - -template > -class circular_buffer { - struct impl : Alloc { - T* storage = nullptr; - // begin, end interpreted (mod capacity) - size_t begin = 0; - size_t end = 0; - size_t capacity = 0; - }; - impl _impl; -public: - using value_type = T; - using size_type = size_t; - using reference = T&; - using pointer = T*; - using const_reference = const T&; - using const_pointer = const T*; -public: - circular_buffer() = default; - circular_buffer(circular_buffer&& X); - circular_buffer(const circular_buffer& X) = delete; - ~circular_buffer(); - circular_buffer& operator=(const circular_buffer&) = delete; - circular_buffer& operator=(circular_buffer&&) = delete; - void push_front(const T& data); - void push_front(T&& data); - template - void emplace_front(A&&... args); - void push_back(const T& data); - void push_back(T&& data); - template - void emplace_back(A&&... args); - T& front(); - T& back(); - void pop_front(); - void pop_back(); - bool empty() const; - size_t size() const; - size_t capacity() const; - T& operator[](size_t idx); - template - void for_each(Func func); - // access an element, may return wrong or destroyed element - // only useful if you do not rely on data accuracy (e.g. prefetch) - T& access_element_unsafe(size_t idx); -private: - void expand(); - void maybe_expand(size_t nr = 1); - size_t mask(size_t idx) const; - - template - struct cbiterator : std::iterator { - typedef std::iterator super_t; - - ValueType& operator*() const { return cb->_impl.storage[cb->mask(idx)]; } - ValueType* operator->() const { return &cb->_impl.storage[cb->mask(idx)]; } - // prefix - cbiterator& operator++() { - idx++; - return *this; - } - // postfix - cbiterator operator++(int unused) { - auto v = *this; - idx++; - return v; - } - // prefix - cbiterator& operator--() { - idx--; - return *this; - } - // postfix - cbiterator operator--(int unused) { - auto v = *this; - idx--; - return v; - } - cbiterator operator+(typename super_t::difference_type n) const { - return cbiterator(cb, idx + n); - } - cbiterator operator-(typename super_t::difference_type n) const { - return cbiterator(cb, idx - n); - } - cbiterator& operator+=(typename super_t::difference_type n) { - idx += n; - return *this; - } - cbiterator& operator-=(typename super_t::difference_type n) { - idx -= n; - return *this; - } - bool operator==(const cbiterator& rhs) const { - return idx == rhs.idx; - } - bool operator!=(const cbiterator& rhs) const { - return idx != rhs.idx; - } - bool operator<(const cbiterator& rhs) const { - return idx < rhs.idx; - } - bool operator>(const cbiterator& rhs) const { - return idx > rhs.idx; - } - bool operator>=(const cbiterator& rhs) const { - return idx >= rhs.idx; - } - bool operator<=(const cbiterator& rhs) const { - return idx <= rhs.idx; - } - typename super_t::difference_type operator-(const cbiterator& rhs) const { - return idx - rhs.idx; - } - private: - CB* cb; - size_t idx; - cbiterator(CB* b, size_t i) : cb(b), idx(i) {} - friend class circular_buffer; - }; - friend class iterator; - -public: - typedef cbiterator iterator; - typedef cbiterator const_iterator; - - iterator begin() { - return iterator(this, _impl.begin); - } - const_iterator begin() const { - return const_iterator(this, _impl.begin); - } - iterator end() { - return iterator(this, _impl.end); - } - const_iterator end() const { - return const_iterator(this, _impl.end); - } - const_iterator cbegin() const { - return const_iterator(this, _impl.begin); - } - const_iterator cend() const { - return const_iterator(this, _impl.end); - } -}; - -template -inline -size_t -circular_buffer::mask(size_t idx) const { - return idx & (_impl.capacity - 1); -} - -template -inline -bool -circular_buffer::empty() const { - return _impl.begin == _impl.end; -} - -template -inline -size_t -circular_buffer::size() const { - return _impl.end - _impl.begin; -} - -template -inline -size_t -circular_buffer::capacity() const { - return _impl.capacity; -} - -template -inline -circular_buffer::circular_buffer(circular_buffer&& x) - : _impl(std::move(x._impl)) { - x._impl = {}; -} - -template -template -inline -void -circular_buffer::for_each(Func func) { - auto s = _impl.storage; - auto m = _impl.capacity - 1; - for (auto i = _impl.begin; i != _impl.end; ++i) { - func(s[i & m]); - } -} - -template -inline -circular_buffer::~circular_buffer() { - for_each([this] (T& obj) { - _impl.destroy(&obj); - }); - _impl.deallocate(_impl.storage, _impl.capacity); -} - -template -void -circular_buffer::expand() { - auto new_cap = std::max(_impl.capacity * 2, 1); - auto new_storage = _impl.allocate(new_cap); - auto p = new_storage; - try { - for_each([this, &p] (T& obj) { - transfer_pass1(_impl, &obj, p++); - }); - } catch (...) { - while (p != new_storage) { - _impl.destroy(--p); - } - _impl.deallocate(new_storage, new_cap); - throw; - } - p = new_storage; - for_each([this, &p] (T& obj) { - transfer_pass2(_impl, &obj, p++); - }); - std::swap(_impl.storage, new_storage); - std::swap(_impl.capacity, new_cap); - _impl.begin = 0; - _impl.end = p - _impl.storage; - _impl.deallocate(new_storage, new_cap); -} - -template -inline -void -circular_buffer::maybe_expand(size_t nr) { - if (_impl.end - _impl.begin + nr > _impl.capacity) { - expand(); - } -} - -template -inline -void -circular_buffer::push_front(const T& data) { - maybe_expand(); - auto p = &_impl.storage[mask(_impl.begin - 1)]; - _impl.construct(p, data); - --_impl.begin; -} - -template -inline -void -circular_buffer::push_front(T&& data) { - maybe_expand(); - auto p = &_impl.storage[mask(_impl.begin - 1)]; - _impl.construct(p, std::move(data)); - --_impl.begin; -} - -template -template -inline -void -circular_buffer::emplace_front(Args&&... args) { - maybe_expand(); - auto p = &_impl.storage[mask(_impl.begin - 1)]; - _impl.construct(p, std::forward(args)...); - --_impl.begin; -} - -template -inline -void -circular_buffer::push_back(const T& data) { - maybe_expand(); - auto p = &_impl.storage[mask(_impl.end)]; - _impl.construct(p, data); - ++_impl.end; -} - -template -inline -void -circular_buffer::push_back(T&& data) { - maybe_expand(); - auto p = &_impl.storage[mask(_impl.end)]; - _impl.construct(p, std::move(data)); - ++_impl.end; -} - -template -template -inline -void -circular_buffer::emplace_back(Args&&... args) { - maybe_expand(); - auto p = &_impl.storage[mask(_impl.end)]; - _impl.construct(p, std::forward(args)...); - ++_impl.end; -} - -template -inline -T& -circular_buffer::front() { - return _impl.storage[mask(_impl.begin)]; -} - -template -inline -T& -circular_buffer::back() { - return _impl.storage[mask(_impl.end - 1)]; -} - -template -inline -void -circular_buffer::pop_front() { - _impl.destroy(&front()); - ++_impl.begin; -} - -template -inline -void -circular_buffer::pop_back() { - _impl.destroy(&back()); - --_impl.end; -} - -template -inline -T& -circular_buffer::operator[](size_t idx) { - return _impl.storage[mask(_impl.begin + idx)]; -} - -template -inline -T& -circular_buffer::access_element_unsafe(size_t idx) { - return _impl.storage[mask(_impl.begin + idx)]; -} - -#endif /* CIRCULAR_BUFFER_HH_ */ diff --git a/core/deleter.hh b/core/deleter.hh deleted file mode 100644 index 250de361d1..0000000000 --- a/core/deleter.hh +++ /dev/null @@ -1,275 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef DELETER_HH_ -#define DELETER_HH_ - -#include -#include -#include -#include - -/// \addtogroup memory-module -/// @{ - -/// Provides a mechanism for managing the lifetime of a buffer. -/// -/// A \c deleter is an object that is used to inform the consumer -/// of some buffer (not referenced by the deleter itself) how to -/// delete the buffer. This can be by calling an arbitrary function -/// or destroying an object carried by the deleter. Examples of -/// a deleter's encapsulated actions are: -/// -/// - calling \c std::free(p) on some captured pointer, p -/// - calling \c delete \c p on some captured pointer, p -/// - decrementing a reference count somewhere -/// -/// A deleter performs its action from its destructor. -class deleter final { -public: - /// \cond internal - struct impl; - struct raw_object_tag {}; - /// \endcond -private: - // if bit 0 set, point to object to be freed directly. - impl* _impl = nullptr; -public: - /// Constructs an empty deleter that does nothing in its destructor. - deleter() = default; - deleter(const deleter&) = delete; - /// Moves a deleter. - deleter(deleter&& x) : _impl(x._impl) { x._impl = nullptr; } - /// \cond internal - explicit deleter(impl* i) : _impl(i) {} - deleter(raw_object_tag tag, void* object) - : _impl(from_raw_object(object)) {} - /// \endcond - /// Destroys the deleter and carries out the encapsulated action. - ~deleter(); - deleter& operator=(deleter&& x); - deleter& operator=(deleter&) = delete; - /// Performs a sharing operation. The encapsulated action will only - /// be carried out after both the original deleter and the returned - /// deleter are both destroyed. - /// - /// \return a deleter with the same encapsulated action as this one. - deleter share(); - /// Checks whether the deleter has an associated action. - explicit operator bool() const { return bool(_impl); } - /// \cond internal - void reset(impl* i) { - this->~deleter(); - new (this) deleter(i); - } - /// \endcond - /// Appends another deleter to this deleter. When this deleter is - /// destroyed, both encapsulated actions will be carried out. - void append(deleter d); -private: - static bool is_raw_object(impl* i) { - auto x = reinterpret_cast(i); - return x & 1; - } - bool is_raw_object() const { - return is_raw_object(_impl); - } - static void* to_raw_object(impl* i) { - auto x = reinterpret_cast(i); - return reinterpret_cast(x & ~uintptr_t(1)); - } - void* to_raw_object() const { - return to_raw_object(_impl); - } - impl* from_raw_object(void* object) { - auto x = reinterpret_cast(object); - return reinterpret_cast(x | 1); - } -}; - -/// \cond internal -struct deleter::impl { - unsigned refs = 1; - deleter next; - impl(deleter next) : next(std::move(next)) {} - virtual ~impl() {} -}; -/// \endcond - -inline -deleter::~deleter() { - if (is_raw_object()) { - std::free(to_raw_object()); - return; - } - if (_impl && --_impl->refs == 0) { - delete _impl; - } -} - -inline -deleter& deleter::operator=(deleter&& x) { - if (this != &x) { - this->~deleter(); - new (this) deleter(std::move(x)); - } - return *this; -} - -/// \cond internal -template -struct lambda_deleter_impl final : deleter::impl { - Deleter del; - lambda_deleter_impl(deleter next, Deleter&& del) - : impl(std::move(next)), del(std::move(del)) {} - virtual ~lambda_deleter_impl() override { del(); } -}; - -template -struct object_deleter_impl final : deleter::impl { - Object obj; - object_deleter_impl(deleter next, Object&& obj) - : impl(std::move(next)), obj(std::move(obj)) {} -}; - -template -inline -object_deleter_impl* make_object_deleter_impl(deleter next, Object obj) { - return new object_deleter_impl(std::move(next), std::move(obj)); -} -/// \endcond - -/// Makes a \ref deleter that encapsulates the action of -/// destroying an object, as well as running another deleter. The input -/// object is moved to the deleter, and destroyed when the deleter is destroyed. -/// -/// \param d deleter that will become part of the new deleter's encapsulated action -/// \param o object whose destructor becomes part of the new deleter's encapsulated action -/// \related deleter -template -deleter -make_deleter(deleter next, Object o) { - return deleter(new lambda_deleter_impl(std::move(next), std::move(o))); -} - -/// Makes a \ref deleter that encapsulates the action of destroying an object. The input -/// object is moved to the deleter, and destroyed when the deleter is destroyed. -/// -/// \param o object whose destructor becomes the new deleter's encapsulated action -/// \related deleter -template -deleter -make_deleter(Object o) { - return make_deleter(deleter(), std::move(o)); -} - -/// \cond internal -struct free_deleter_impl final : deleter::impl { - void* obj; - free_deleter_impl(void* obj) : impl(deleter()), obj(obj) {} - virtual ~free_deleter_impl() override { std::free(obj); } -}; -/// \endcond - -inline -deleter -deleter::share() { - if (!_impl) { - return deleter(); - } - if (is_raw_object()) { - _impl = new free_deleter_impl(to_raw_object()); - } - ++_impl->refs; - return deleter(_impl); -} - -// Appends 'd' to the chain of deleters. Avoids allocation if possible. For -// performance reasons the current chain should be shorter and 'd' should be -// longer. -inline -void deleter::append(deleter d) { - if (!d._impl) { - return; - } - impl* next_impl = _impl; - deleter* next_d = this; - while (next_impl) { - assert(next_impl != d._impl); - if (is_raw_object(next_impl)) { - next_d->_impl = next_impl = new free_deleter_impl(to_raw_object(next_impl)); - } - if (next_impl->refs != 1) { - next_d->_impl = next_impl = make_object_deleter_impl(std::move(next_impl->next), deleter(next_impl)); - } - next_d = &next_impl->next; - next_impl = next_d->_impl; - } - next_d->_impl = d._impl; - d._impl = nullptr; -} - -/// Makes a deleter that calls \c std::free() when it is destroyed. -/// -/// \param obj object to free. -/// \related deleter -inline -deleter -make_free_deleter(void* obj) { - if (!obj) { - return deleter(); - } - return deleter(deleter::raw_object_tag(), obj); -} - -/// Makes a deleter that calls \c std::free() when it is destroyed, as well -/// as invoking the encapsulated action of another deleter. -/// -/// \param d deleter to invoke. -/// \param obj object to free. -/// \related deleter -inline -deleter -make_free_deleter(deleter next, void* obj) { - return make_deleter(std::move(next), [obj] () mutable { std::free(obj); }); -} - -/// \see make_deleter(Object) -/// \related deleter -template -inline -deleter -make_object_deleter(T&& obj) { - return deleter{make_object_deleter_impl(deleter(), std::move(obj))}; -} - -/// \see make_deleter(deleter, Object) -/// \related deleter -template -inline -deleter -make_object_deleter(deleter d, T&& obj) { - return deleter{make_object_deleter_impl(std::move(d), std::move(obj))}; -} - -/// @} - -#endif /* DELETER_HH_ */ diff --git a/core/distributed.hh b/core/distributed.hh deleted file mode 100644 index d95a6eea39..0000000000 --- a/core/distributed.hh +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include "sharded.hh" - -template -using distributed = seastar::sharded; diff --git a/core/do_with.hh b/core/do_with.hh deleted file mode 100644 index 911f1a8f80..0000000000 --- a/core/do_with.hh +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include "apply.hh" -#include -#include -#include - -/// \addtogroup future-util -/// @{ - -/// do_with() holds an object alive for the duration until a future -/// completes, and allow the code involved in making the future -/// complete to have easy access to this object. -/// -/// do_with() takes two arguments: The first is an temporary object (rvalue), -/// the second is a function returning a future (a so-called "promise"). -/// The function is given (a moved copy of) this temporary object, by -/// reference, and it is ensured that the object will not be destructed until -/// the completion of the future returned by the function. -/// -/// do_with() returns a future which resolves to whatever value the given future -/// (returned by the given function) resolves to. This returned value must not -/// contain references to the temporary object, as at that point the temporary -/// is destructed. -/// -/// \param rvalue a temporary value to protect while \c f is running -/// \param f a callable, accepting an lvalue reference of the same type -/// as \c rvalue, that will be accessible while \c f runs -/// \return whatever \c f returns -template -inline -auto do_with(T&& rvalue, F&& f) { - auto obj = std::make_unique(std::forward(rvalue)); - auto fut = f(*obj); - return fut.finally([obj = std::move(obj)] () {}); -} - -/// \cond internal -template -inline -auto -cherry_pick_tuple(std::index_sequence, Tuple&& tuple) { - return std::make_tuple(std::get(std::forward(tuple))...); -} -/// \endcond - -/// Multiple argument variant of \ref do_with(T&& rvalue, F&& f). -/// -/// This is the same as \ref do_with(T&& tvalue, F&& f), but accepts -/// two or more rvalue parameters, which are held in memory while -/// \c f executes. \c f will be called with all arguments as -/// reference parameters. -template -inline -auto -do_with(T1&& rv1, T2&& rv2, T3_or_F&& rv3, More&&... more) { - auto all = std::forward_as_tuple( - std::forward(rv1), - std::forward(rv2), - std::forward(rv3), - std::forward(more)...); - constexpr size_t nr = std::tuple_size::value - 1; - using idx = std::make_index_sequence; - auto&& just_values = cherry_pick_tuple(idx(), std::move(all)); - auto&& just_func = std::move(std::get(std::move(all))); - auto obj = std::make_unique>(std::move(just_values)); - auto fut = apply(just_func, *obj); - return fut.finally([obj = std::move(obj)] () {}); -} - -/// @} diff --git a/core/dpdk_rte.cc b/core/dpdk_rte.cc deleted file mode 100644 index d940c3490d..0000000000 --- a/core/dpdk_rte.cc +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#ifdef HAVE_DPDK - -#include "net/dpdk.hh" -#include "core/dpdk_rte.hh" -#include "util/conversions.hh" -#include -#include - -namespace dpdk { - -bool eal::initialized = false; - -void eal::init(cpuset cpus, boost::program_options::variables_map opts) -{ - if (initialized) { - return; - } - - std::stringstream mask; - mask << std::hex << cpus.to_ulong(); - - // TODO: Inherit these from the app parameters - "opts" - std::vector> args { - string2vector("dpdk_args"), - string2vector("-c"), string2vector(mask.str()), - string2vector("-n"), string2vector("1") - }; - - std::experimental::optional hugepages_path; - if (opts.count("hugepages")) { - hugepages_path = opts["hugepages"].as(); - } - - // If "hugepages" is not provided and DPDK PMD drivers mode is requested - - // use the default DPDK huge tables configuration. - if (hugepages_path) { - args.push_back(string2vector("--huge-dir")); - args.push_back(string2vector(hugepages_path.value())); - - // - // We don't know what is going to be our networking configuration so we - // assume there is going to be a queue per-CPU. Plus we'll give a DPDK - // 64MB for "other stuff". - // - size_t size_MB = mem_size(cpus.count()) >> 20; - std::stringstream size_MB_str; - size_MB_str << size_MB; - - args.push_back(string2vector("-m")); - args.push_back(string2vector(size_MB_str.str())); - } else if (!opts.count("dpdk-pmd")) { - args.push_back(string2vector("--no-huge")); - } -#ifdef HAVE_OSV - args.push_back(string2vector("--no-shconf")); -#endif - - std::vector cargs; - - for (auto&& a: args) { - cargs.push_back(a.data()); - } - /* initialise the EAL for all */ - int ret = rte_eal_init(cargs.size(), cargs.data()); - if (ret < 0) { - rte_exit(EXIT_FAILURE, "Cannot init EAL\n"); - } - - // For 1.8 rte_eal_pci_probe() has been moved into the rte_eal_init(). -#ifdef RTE_VERSION_1_7 - /* probe the PCI devices in case we need to use DPDK drivers */ - if (rte_eal_pci_probe() < 0) { - rte_exit(EXIT_FAILURE, "Cannot probe PCI\n"); - } -#endif - - initialized = true; -} - -uint32_t __attribute__((weak)) qp_mempool_obj_size(bool hugetlbfs_membackend) -{ - return 0; -} - -size_t eal::mem_size(int num_cpus, bool hugetlbfs_membackend) -{ - size_t memsize = 0; - // - // PMD mempool memory: - // - // We don't know what is going to be our networking configuration so we - // assume there is going to be a queue per-CPU. - // - memsize += num_cpus * qp_mempool_obj_size(hugetlbfs_membackend); - - // Plus we'll give a DPDK 64MB for "other stuff". - memsize += (64UL << 20); - - return memsize; -} - -} // namespace dpdk - -#endif // HAVE_DPDK diff --git a/core/dpdk_rte.hh b/core/dpdk_rte.hh deleted file mode 100644 index e8a063d5be..0000000000 --- a/core/dpdk_rte.hh +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#ifndef DPDK_RTE_HH_ -#define DPDK_RTE_HH_ - -#ifdef HAVE_DPDK - -#include -#include -#include -#include -#include - -/*********************** Compat section ***************************************/ -// We currently support only versions 1.7 and above. -// So, since currently the only above version is 1.8.x we will use it as "else" -// of ver. 1.7.x. -#if (RTE_VERSION >= RTE_VERSION_NUM(1,7,0,0)) && \ - (RTE_VERSION < RTE_VERSION_NUM(1,8,0,0)) -#define RTE_VERSION_1_7 -#endif - -#ifdef RTE_VERSION_1_7 -#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_MBUF_SCATTER_GATHER) || \ - defined(RTE_LIBRTE_IP_FRAG) -#error "RTE_LIBRTE_PMD_BOND, RTE_MBUF_SCATTER_GATHER," \ - "and RTE_LIBRTE_IP_FRAG should be disabled in DPDK's " \ - "config/common_linuxapp" -#endif - -#define rte_mbuf_vlan_tci(m) ((m)->pkt.vlan_macip.f.vlan_tci) -#define rte_mbuf_rss_hash(m) ((m)->pkt.hash.rss) -#define rte_mbuf_data_len(m) ((m)->pkt.data_len) -#define rte_mbuf_pkt_len(m) ((m)->pkt.pkt_len) -#define rte_mbuf_next(m) ((m)->pkt.next) -#define rte_mbuf_nb_segs(m) ((m)->pkt.nb_segs) -#define rte_mbuf_l2_len(m) ((m)->pkt.vlan_macip.f.l2_len) -#define rte_mbuf_l3_len(m) ((m)->pkt.vlan_macip.f.l3_len) -#define rte_mbuf_buf_addr(m) ((m)->buf_addr) -#define rte_mbuf_buf_physaddr(m) ((m)->buf_physaddr) -#define rte_mbuf_buf_len(m) ((m)->buf_len) -#else -#if (RTE_VERSION < RTE_VERSION_NUM(2,0,0,0)) -#if defined(RTE_MBUF_REFCNT) -#error "RTE_MBUF_REFCNT should be disabled in DPDK's config/common_linuxapp" -#endif -#endif -#define rte_mbuf_vlan_tci(m) ((m)->vlan_tci) -#define rte_mbuf_rss_hash(m) ((m)->hash.rss) -#define rte_mbuf_data_len(m) ((m)->data_len) -#define rte_mbuf_pkt_len(m) ((m)->pkt_len) -#define rte_mbuf_next(m) ((m)->next) -#define rte_mbuf_nb_segs(m) ((m)->nb_segs) -#define rte_mbuf_l2_len(m) ((m)->l2_len) -#define rte_mbuf_l3_len(m) ((m)->l3_len) -#define rte_mbuf_buf_addr(m) ((m)->buf_addr) -#define rte_mbuf_buf_physaddr(m) ((m)->buf_physaddr) -#define rte_mbuf_buf_len(m) ((m)->buf_len) - -#endif - -/******************************************************************************/ - -namespace dpdk { - -// DPDK Environment Abstraction Layer -class eal { -public: - using cpuset = std::bitset; - - static void init(cpuset cpus, boost::program_options::variables_map opts); - /** - * Returns the amount of memory needed for DPDK - * @param num_cpus Number of CPUs the application is going to use - * - * @return - */ - static size_t mem_size(int num_cpus, bool hugetlbfs_membackend = true); - static bool initialized; -}; - -} // namespace dpdk -#endif // HAVE_DPDK -#endif // DPDK_RTE_HH_ diff --git a/core/enum.hh b/core/enum.hh deleted file mode 100644 index be6c4ff984..0000000000 --- a/core/enum.hh +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -/* - * This header file defines a hash function for enum types, using the - * standard hash function of the underlying type (such as int). This makes - * it possible to inherit from this type to - */ - -#include -#include -#include - -template -class enum_hash { - static_assert(std::is_enum::value, "must be an enum"); -public: - std::size_t operator()(const T& e) const { - using utype = typename std::underlying_type::type; - return std::hash()(static_cast(e)); - } -}; diff --git a/core/file.hh b/core/file.hh deleted file mode 100644 index d15e3d6777..0000000000 --- a/core/file.hh +++ /dev/null @@ -1,555 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef FILE_HH_ -#define FILE_HH_ - -#include "stream.hh" -#include "sstring.hh" -#include "core/shared_ptr.hh" -#include "core/align.hh" -#include "core/future-util.hh" -#include -#include -#include -#include -#include -#include -#include - -/// \addtogroup fileio-module -/// @{ - -/// Enumeration describing the type of a directory entry being listed. -/// -/// \see file::list_directory() -enum class directory_entry_type { - block_device, - char_device, - directory, - fifo, - link, - regular, - socket, -}; - -/// A directory entry being listed. -struct directory_entry { - /// Name of the file in a directory entry. Will never be "." or "..". Only the last component is included. - sstring name; - /// Type of the directory entry, if known. - std::experimental::optional type; -}; - -/// \cond internal -class file_impl { -public: - virtual ~file_impl() {} - - virtual future write_dma(uint64_t pos, const void* buffer, size_t len) = 0; - virtual future write_dma(uint64_t pos, std::vector iov) = 0; - virtual future read_dma(uint64_t pos, void* buffer, size_t len) = 0; - virtual future read_dma(uint64_t pos, std::vector iov) = 0; - virtual future<> flush(void) = 0; - virtual future stat(void) = 0; - virtual future<> truncate(uint64_t length) = 0; - virtual future<> discard(uint64_t offset, uint64_t length) = 0; - virtual future<> allocate(uint64_t position, uint64_t length) = 0; - virtual future size(void) = 0; - virtual future<> close() = 0; - virtual subscription list_directory(std::function (directory_entry de)> next) = 0; - - friend class reactor; -}; - -class posix_file_impl : public file_impl { -public: - int _fd; - posix_file_impl(int fd) : _fd(fd) {} - virtual ~posix_file_impl() override; - future write_dma(uint64_t pos, const void* buffer, size_t len); - future write_dma(uint64_t pos, std::vector iov); - future read_dma(uint64_t pos, void* buffer, size_t len); - future read_dma(uint64_t pos, std::vector iov); - future<> flush(void); - future stat(void); - future<> truncate(uint64_t length); - future<> discard(uint64_t offset, uint64_t length); - virtual future<> allocate(uint64_t position, uint64_t length) override; - future size(void); - virtual future<> close() override; - virtual subscription list_directory(std::function (directory_entry de)> next) override; -}; - -class blockdev_file_impl : public posix_file_impl { -public: - blockdev_file_impl(int fd) : posix_file_impl(fd) {} - future<> truncate(uint64_t length) override; - future<> discard(uint64_t offset, uint64_t length) override; - future size(void) override; - virtual future<> allocate(uint64_t position, uint64_t length) override; -}; - -inline -shared_ptr -make_file_impl(int fd) { - auto r = ::ioctl(fd, BLKGETSIZE); - if (r != -1) { - return make_shared(fd); - } else { - return make_shared(fd); - } -} - -/// \endcond - -/// A data file on persistent storage. -/// -/// File objects represent uncached, unbuffered files. As such great care -/// must be taken to cache data at the application layer; neither seastar -/// nor the OS will cache these file. -/// -/// Data is transferred using direct memory access (DMA). This imposes -/// restrictions on file offsets and data pointers. The former must be aligned -/// on a 4096 byte boundary, while a 512 byte boundary suffices for the latter. -class file { - shared_ptr _file_impl; -private: - explicit file(int fd) : _file_impl(make_file_impl(fd)) {} - -public: - /// Copies a file object. The new and old objects refer to the - /// same underlying file. - /// - /// \param x file object to be copied - file(const file& x) = default; - /// Moves a file object. - file(file&& x) : _file_impl(std::move(x._file_impl)) {} - /// Assigns a file object. After assignent, the destination and source refer - /// to the same underlying file. - /// - /// \param x file object to assign to `this`. - file& operator=(const file& x) noexcept = default; - /// Moves assigns a file object. - file& operator=(file&& x) noexcept = default; - - // O_DIRECT reading requires that buffer, offset, and read length, are - // all aligned. Alignment of 4096 was necessary in the past, but no longer - // is - 512 is usually enough; But we'll need to use BLKSSZGET ioctl to - // be sure it is really enough on this filesystem. 4096 is always safe. - // In addition, if we start reading in things outside page boundaries, - // we will end up with various pages around, some of them with - // overlapping ranges. Those would be very challenging to cache. - - /// Alignment requirement for file offsets - static constexpr uint64_t dma_alignment = 4096; - - // Make sure alignment is a power of 2 - static_assert(dma_alignment > 0 && !(dma_alignment & (dma_alignment - 1)), - "dma_alignment must be a power of 2"); - - - /** - * Perform a single DMA read operation. - * - * @param aligned_pos offset to begin reading at (should be aligned) - * @param aligned_buffer output buffer (should be aligned) - * @param aligned_len number of bytes to read (should be aligned) - * - * Alignment is HW dependent but use 4KB alignment to be on the safe side as - * explained above. - * - * @return number of bytes actually read - * @throw exception in case of I/O error - */ - template - future - dma_read(uint64_t aligned_pos, CharType* aligned_buffer, size_t aligned_len) { - return _file_impl->read_dma(aligned_pos, aligned_buffer, aligned_len); - } - - /** - * Read the requested amount of bytes starting from the given offset. - * - * @param pos offset to begin reading from - * @param len number of bytes to read - * - * @return temporary buffer containing the requested data. - * @throw exception in case of I/O error - * - * This function doesn't require any alignment for both "pos" and "len" - * - * @note size of the returned buffer may be smaller than "len" if EOF is - * reached of in case of I/O error. - */ - template - future> dma_read(uint64_t pos, size_t len) { - return dma_read_bulk(pos, len).then( - [len] (temporary_buffer buf) { - if (len < buf.size()) { - buf.trim(len); - } - - return std::move(buf); - }); - } - - /// Error thrown when attempting to read past end-of-file - /// with \ref dma_read_exactly(). - class eof_error : public std::exception {}; - - /** - * Read the exact amount of bytes. - * - * @param pos offset in a file to begin reading from - * @param len number of bytes to read - * - * @return temporary buffer containing the read data - * @throw end_of_file_error if EOF is reached, file_io_error or - * std::system_error in case of I/O error. - */ - template - future> - dma_read_exactly(uint64_t pos, size_t len) { - return dma_read(pos, len).then( - [pos, len] (auto buf) { - if (buf.size() < len) { - throw eof_error(); - } - - return std::move(buf); - }); - } - - /// Performs a DMA read into the specified iovec. - /// - /// \param pos offset to read from. Must be aligned to \ref dma_alignment. - /// \param iov vector of address/size pairs to read into. Addresses must be - /// aligned. - /// \return a future representing the number of bytes actually read. A short - /// read may happen due to end-of-file or an I/O error. - future dma_read(uint64_t pos, std::vector iov) { - return _file_impl->read_dma(pos, std::move(iov)); - } - - /// Performs a DMA write from the specified buffer. - /// - /// \param pos offset to write into. Must be aligned to \ref dma_alignment. - /// \param buffer aligned address of buffer to read from. Buffer must exists - /// until the future is made ready. - /// \param len number of bytes to write. Must be aligned. - /// \return a future representing the number of bytes actually written. A short - /// write may happen due to an I/O error. - template - future dma_write(uint64_t pos, const CharType* buffer, size_t len) { - return _file_impl->write_dma(pos, buffer, len); - } - - /// Performs a DMA write to the specified iovec. - /// - /// \param pos offset to write into. Must be aligned to \ref dma_alignment. - /// \param iov vector of address/size pairs to write from. Addresses must be - /// aligned. - /// \return a future representing the number of bytes actually written. A short - /// write may happen due to an I/O error. - future dma_write(uint64_t pos, std::vector iov) { - return _file_impl->write_dma(pos, std::move(iov)); - } - - /// Causes any previously written data to be made stable on persistent storage. - /// - /// Prior to a flush, written data may or may not survive a power failure. After - /// a flush, data is guaranteed to be on disk. - future<> flush() { - return _file_impl->flush(); - } - - /// Returns \c stat information about the file. - future stat() { - return _file_impl->stat(); - } - - /// Truncates the file to a specified length. - future<> truncate(uint64_t length) { - return _file_impl->truncate(length); - } - - /// Preallocate disk blocks for a specified byte range. - /// - /// Requests the file system to allocate disk blocks to - /// back the specified range (\c length bytes starting at - /// \c position). The range may be outside the current file - /// size; the blocks can then be used when appending to the - /// file. - /// - /// \param position beginning of the range at which to allocate - /// blocks. - /// \parm length length of range to allocate. - /// \return future that becomes ready when the operation completes. - future<> allocate(uint64_t position, uint64_t length) { - return _file_impl->allocate(position, length); - } - - /// Discard unneeded data from the file. - /// - /// The discard operation tells the file system that a range of offsets - /// (which be aligned) is no longer needed and can be reused. - future<> discard(uint64_t offset, uint64_t length) { - return _file_impl->discard(offset, length); - } - - /// Gets the file size. - future size() { - return _file_impl->size(); - } - - /// Closes the file. - /// - /// Flushes any pending operations and release any resources associated with - /// the file (except for stable storage). - /// - /// \note - /// to ensure file data reaches stable storage, you must call \ref flush() - /// before calling \c close(). - future<> close() { - return _file_impl->close(); - } - - /// Returns a directory listing, given that this file object is a directory. - subscription list_directory(std::function (directory_entry de)> next) { - return _file_impl->list_directory(std::move(next)); - } - - /** - * Read a data bulk containing the provided addresses range that starts at - * the given offset and ends at either the address aligned to - * dma_alignment (4KB) or at the file end. - * - * @param offset starting address of the range the read bulk should contain - * @param range_size size of the addresses range - * - * @return temporary buffer containing the read data bulk. - * @throw system_error exception in case of I/O error or eof_error when - * "offset" is beyond EOF. - */ - template - future> - dma_read_bulk(uint64_t offset, size_t range_size); - -private: - template - struct read_state; - - /** - * Try to read from the given position where the previous short read has - * stopped. Check the EOF condition. - * - * The below code assumes the following: short reads due to I/O errors - * always end at address aligned to HW block boundary. Therefore if we issue - * a new read operation from the next position we are promised to get an - * error (different from EINVAL). If we've got a short read because we have - * reached EOF then the above read would either return a zero-length success - * (if the file size is aligned to HW block size) or an EINVAL error (if - * file length is not aligned to HW block size). - * - * @param pos offset to read from - * @param len number of bytes to read - * - * @return temporary buffer with read data or zero-sized temporary buffer if - * pos is at or beyond EOF. - * @throw appropriate exception in case of I/O error. - */ - template - future> - read_maybe_eof(uint64_t pos, size_t len); - - friend class reactor; -}; - -/// \cond internal - -template -struct file::read_state { - typedef temporary_buffer tmp_buf_type; - - read_state(uint64_t offset, uint64_t front, size_t to_read) - : buf(tmp_buf_type::aligned(file::dma_alignment, - align_up(to_read, file::dma_alignment))) - , _offset(offset) - , _to_read(to_read) - , _front(front) {} - - bool done() const { - return eof || pos >= _to_read; - } - - /** - * Trim the buffer to the actual number of read bytes and cut the - * bytes from offset 0 till "_front". - * - * @note this function has to be called only if we read bytes beyond - * "_front". - */ - void trim_buf_before_ret() { - if (have_good_bytes()) { - buf.trim(pos); - buf.trim_front(_front); - } else { - buf.trim(0); - } - } - - uint64_t cur_offset() const { - return _offset + pos; - } - - size_t left_space() const { - return buf.size() - pos; - } - - size_t left_to_read() const { - // positive as long as (done() == false) - return _to_read - pos; - } - - void append_new_data(tmp_buf_type& new_data) { - auto to_copy = std::min(left_space(), new_data.size()); - - std::memcpy(buf.get_write() + pos, new_data.get(), to_copy); - pos += to_copy; - } - - bool have_good_bytes() const { - return pos > _front; - } - -public: - bool eof = false; - tmp_buf_type buf; - size_t pos = 0; -private: - uint64_t _offset; - size_t _to_read; - uint64_t _front; -}; - -template -future> -file::dma_read_bulk(uint64_t offset, size_t range_size) { - using tmp_buf_type = typename read_state::tmp_buf_type; - - auto front = offset & (dma_alignment - 1); - offset -= front; - range_size += front; - - auto rstate = make_lw_shared>(offset, front, - range_size); - - // - // First, try to read directly into the buffer. Most of the reads will - // end here. - // - auto read = dma_read(offset, rstate->buf.get_write(), - rstate->buf.size()); - - return read.then([rstate, this] (size_t size) mutable { - rstate->pos = size; - - // - // If we haven't read all required data at once - - // start read-copy sequence. We can't continue with direct reads - // into the previously allocated buffer here since we have to ensure - // the aligned read length and thus the aligned destination buffer - // size. - // - // The copying will actually take place only if there was a HW glitch. - // In EOF case or in case of a persistent I/O error the only overhead is - // an extra allocation. - // - return do_until( - [rstate] { return rstate->done(); }, - [rstate, this] () mutable { - return read_maybe_eof( - rstate->cur_offset(), rstate->left_to_read()).then( - [rstate] (auto buf1) mutable { - if (buf1.size()) { - rstate->append_new_data(buf1); - } else { - rstate->eof = true; - } - - return make_ready_future<>(); - }); - }).then([rstate] () mutable { - // - // If we are here we are promised to have read some bytes beyond - // "front" so we may trim straight away. - // - rstate->trim_buf_before_ret(); - return make_ready_future(std::move(rstate->buf)); - }); - }); -} - -template -future> -file::read_maybe_eof(uint64_t pos, size_t len) { - // - // We have to allocate a new aligned buffer to make sure we don't get - // an EINVAL error due to unaligned destination buffer. - // - temporary_buffer buf = temporary_buffer::aligned( - dma_alignment, align_up(len, dma_alignment)); - - // try to read a single bulk from the given position - return dma_read(pos, buf.get_write(), buf.size()).then_wrapped( - [buf = std::move(buf)](future f) mutable { - try { - size_t size = std::get<0>(f.get()); - - buf.trim(size); - - return std::move(buf); - } catch (std::system_error& e) { - // - // TODO: implement a non-trowing file_impl::dma_read() interface to - // avoid the exceptions throwing in a good flow completely. - // Otherwise for users that don't want to care about the - // underlying file size and preventing the attempts to read - // bytes beyond EOF there will always be at least one - // exception throwing at the file end for files with unaligned - // length. - // - if (e.code().value() == EINVAL) { - buf.trim(0); - return std::move(buf); - } else { - throw; - } - } - }); -} - -/// \endcond - -/// @} - -#endif /* FILE_HH_ */ diff --git a/core/fstream.cc b/core/fstream.cc deleted file mode 100644 index 018bc2262c..0000000000 --- a/core/fstream.cc +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include "fstream.hh" -#include "align.hh" -#include -#include - -class file_data_source_impl : public data_source_impl { - lw_shared_ptr _file; - uint64_t _pos; - size_t _buffer_size; -public: - file_data_source_impl(lw_shared_ptr f, uint64_t pos, size_t buffer_size) - : _file(std::move(f)), _pos(pos), _buffer_size(buffer_size) {} - virtual future> get() override { - using buf_type = temporary_buffer; - return _file->dma_read_bulk(_pos, _buffer_size).then( - [this] (buf_type buf) { - _pos += buf.size(); - return std::move(buf); - }); - } -}; - -class file_data_source : public data_source { -public: - file_data_source(lw_shared_ptr f, uint64_t offset, size_t buffer_size) - : data_source(std::make_unique( - std::move(f), offset, buffer_size)) {} -}; - -input_stream make_file_input_stream( - lw_shared_ptr f, uint64_t offset, size_t buffer_size) { - return input_stream(file_data_source(std::move(f), offset, buffer_size)); -} - -class file_data_sink_impl : public data_sink_impl { - lw_shared_ptr _file; - file_output_stream_options _options; - uint64_t _pos = 0; - uint64_t _last_preallocation = 0; -public: - file_data_sink_impl(lw_shared_ptr f, file_output_stream_options options) - : _file(std::move(f)), _options(options) {} - future<> put(net::packet data) { abort(); } - virtual temporary_buffer allocate_buffer(size_t size) override { - return temporary_buffer::aligned(file::dma_alignment, size); - } - virtual future<> put(temporary_buffer buf) override { - // put() must usually be of chunks multiple of file::dma_alignment. - // Only the last part can have an unaligned length. If put() was - // called again with an unaligned pos, we have a bug in the caller. - assert(!(_pos & (file::dma_alignment - 1))); - bool truncate = false; - auto pos = _pos; - _pos += buf.size(); - auto p = static_cast(buf.get()); - size_t buf_size = buf.size(); - - if ((buf.size() & (file::dma_alignment - 1)) != 0) { - // If buf size isn't aligned, copy its content into a new aligned buf. - // This should only happen when the user calls output_stream::flush(). - auto tmp = allocate_buffer(align_up(buf.size(), file::dma_alignment)); - ::memcpy(tmp.get_write(), buf.get(), buf.size()); - buf = std::move(tmp); - p = buf.get(); - buf_size = buf.size(); - truncate = true; - } - auto prealloc = make_ready_future<>(); - if (pos + buf_size > _last_preallocation) { - auto old = _last_preallocation; - _last_preallocation = align_down(old + _options.preallocation_size, _options.preallocation_size); - prealloc = _file->allocate(old, _last_preallocation - old); - } - return prealloc.then([this, pos, p, buf_size, truncate, buf = std::move(buf)] () mutable { - return _file->dma_write(pos, p, buf_size).then( - [this, buf = std::move(buf), truncate] (size_t size) { - if (truncate) { - return _file->truncate(_pos); - } - return make_ready_future<>(); - }); - }); - } - future<> close() { - return _file->flush().then([this] { - return _file->close(); - }); - } -}; - -class file_data_sink : public data_sink { -public: - file_data_sink(lw_shared_ptr f, file_output_stream_options options) - : data_sink(std::make_unique( - std::move(f), options)) {} -}; - -output_stream make_file_output_stream(lw_shared_ptr f, size_t buffer_size) { - file_output_stream_options options; - options.buffer_size = buffer_size; - return make_file_output_stream(std::move(f), options); -} - -output_stream make_file_output_stream(lw_shared_ptr f, file_output_stream_options options) { - return output_stream(file_data_sink(std::move(f), options), options.buffer_size, true); -} - diff --git a/core/fstream.hh b/core/fstream.hh deleted file mode 100644 index 1fd62e49a8..0000000000 --- a/core/fstream.hh +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -// File <-> streams adapters -// -// Seastar files are block-based due to the reliance on DMA - you must read -// on sector boundaries. The adapters in this file provide a byte stream -// interface to files, while retaining the zero-copy characteristics of -// seastar files. - -#include "file.hh" -#include "iostream.hh" -#include "shared_ptr.hh" - -// Create an input_stream for reading starting at a given position of the -// given file. Multiple fibers of execution (continuations) may safely open -// multiple input streams concurrently for the same file. -input_stream make_file_input_stream( - lw_shared_ptr file, uint64_t offset = 0, - uint64_t buffer_size = 8192); - -struct file_output_stream_options { - unsigned buffer_size = 8192; - unsigned preallocation_size = 1024*1024; // 1MB -}; - -// Create an output_stream for writing starting at the position zero of a -// newly created file. -// NOTE: flush() should be the last thing to be called on a file output stream. -output_stream make_file_output_stream( - lw_shared_ptr file, - uint64_t buffer_size = 8192); - -/// Create an output_stream for writing starting at the position zero of a -/// newly created file. -/// NOTE: flush() should be the last thing to be called on a file output stream. -output_stream make_file_output_stream( - lw_shared_ptr file, - file_output_stream_options options); - diff --git a/core/function_traits.hh b/core/function_traits.hh deleted file mode 100644 index 1d278a6207..0000000000 --- a/core/function_traits.hh +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#pragma once - -#include - -template -struct function_traits; - -template -struct function_traits -{ - using return_type = Ret; - using args_as_tuple = std::tuple; - using signature = Ret (Args...); - - static constexpr std::size_t arity = sizeof...(Args); - - template - struct arg - { - static_assert(N < arity, "no such parameter index."); - using type = typename std::tuple_element>::type; - }; -}; - -template -struct function_traits : public function_traits -{}; - -template -struct function_traits : public function_traits -{}; - -template -struct function_traits : public function_traits -{}; - -template -struct function_traits : public function_traits -{}; - -template -struct function_traits : public function_traits> -{}; - diff --git a/core/future-util.hh b/core/future-util.hh deleted file mode 100644 index 76b54f22bb..0000000000 --- a/core/future-util.hh +++ /dev/null @@ -1,543 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - - -/** @file */ - -#ifndef CORE_FUTURE_UTIL_HH_ -#define CORE_FUTURE_UTIL_HH_ - -#include "task.hh" -#include "future.hh" -#include "shared_ptr.hh" -#include -#include -#include - -/// \cond internal -extern __thread size_t task_quota; -/// \endcond - - -/// \addtogroup future-util -/// @{ - -/// Run tasks in parallel (iterator version). -/// -/// Given a range [\c begin, \c end) of objects, run \c func on each \c *i in -/// the range, and return a future<> that resolves when all the functions -/// complete. \c func should return a future<> that indicates when it is -/// complete. All invocations are performed in parallel. -/// -/// \param begin an \c InputIterator designating the beginning of the range -/// \param end an \c InputIterator designating the end of the range -/// \param func Function to apply to each element in the range (returning -/// a \c future<>) -/// \return a \c future<> that resolves when all the function invocations -/// complete. If one or more return an exception, the return value -/// contains one of the exceptions. -template -inline -future<> -parallel_for_each(Iterator begin, Iterator end, Func&& func) { - auto ret = make_ready_future<>(); - while (begin != end) { - auto f = func(*begin++).then([ret = std::move(ret)] () mutable { - return std::move(ret); - }); - ret = std::move(f); - } - return ret; -} - -/// Run tasks in parallel (range version). -/// -/// Given a \c range of objects, apply \c func to each object -/// in the range, and return a future<> that resolves when all -/// the functions complete. \c func should return a future<> that indicates -/// when it is complete. All invocations are performed in parallel. -/// -/// \param range A range of objects to iterate run \c func on -/// \param func A callable, accepting reference to the range's -/// \c value_type, and returning a \c future<>. -/// \return a \c future<> that becomes ready when the entire range -/// was processed. If one or more of the invocations of -/// \c func returned an exceptional future, then the return -/// value will contain one of those exceptions. -template -inline -future<> -parallel_for_each(Range&& range, Func&& func) { - return parallel_for_each(std::begin(range), std::end(range), - std::forward(func)); -} - -// The AsyncAction concept represents an action which can complete later than -// the actual function invocation. It is represented by a function which -// returns a future which resolves when the action is done. - -/// \cond internal -template -static inline -void do_until_continued(StopCondition&& stop_cond, AsyncAction&& action, promise<> p) { - while (!stop_cond()) { - try { - auto&& f = action(); - if (!f.available()) { - f.then_wrapped([action = std::forward(action), - stop_cond = std::forward(stop_cond), p = std::move(p)](std::result_of_t fut) mutable { - try { - fut.get(); - do_until_continued(stop_cond, std::forward(action), std::move(p)); - } catch(...) { - p.set_exception(std::current_exception()); - } - }); - return; - } - - if (f.failed()) { - f.forward_to(std::move(p)); - return; - } - } catch (...) { - p.set_exception(std::current_exception()); - return; - } - } - - p.set_value(); -} -/// \endcond - -enum class stop_iteration { no, yes }; - -/// Invokes given action until it fails or the function requests iteration to stop by returning -/// \c stop_iteration::yes. -/// -/// \param action a callable taking no arguments, returning a future. Will -/// be called again as soon as the future resolves, unless the -/// future fails, action throws, or it resolves with \c stop_iteration::yes. -/// If \c action is an r-value it can be moved in the middle of iteration. -/// \return a ready future if we stopped successfully, or a failed future if -/// a call to to \c action failed. -template -static inline -future<> repeat(AsyncAction&& action) { - using futurator = futurize>; - static_assert(std::is_same, typename futurator::type>::value, "bad AsyncAction signature"); - - while (task_quota) { - try { - auto f = futurator::apply(action); - - if (!f.available()) { - return f.then([action = std::forward(action)] (stop_iteration stop) mutable { - if (stop == stop_iteration::yes) { - return make_ready_future<>(); - } else { - return repeat(std::forward(action)); - } - }); - } - - if (f.get0() == stop_iteration::yes) { - return make_ready_future<>(); - } - } catch (...) { - return make_exception_future<>(std::current_exception()); - } - - --task_quota; - } - - promise<> p; - auto f = p.get_future(); - schedule(make_task([action = std::forward(action), p = std::move(p)] () mutable { - repeat(std::forward(action)).forward_to(std::move(p)); - })); - return f; -} - -/// Invokes given action until it fails or given condition evaluates to true. -/// -/// \param stop_cond a callable taking no arguments, returning a boolean that -/// evalutes to true when you don't want to call \c action -/// any longer -/// \param action a callable taking no arguments, returning a future<>. Will -/// be called again as soon as the future resolves, unless the -/// future fails, or \c stop_cond returns \c true. -/// \return a ready future if we stopped successfully, or a failed future if -/// a call to to \c action failed. -template -static inline -future<> do_until(StopCondition&& stop_cond, AsyncAction&& action) { - promise<> p; - auto f = p.get_future(); - do_until_continued(std::forward(stop_cond), - std::forward(action), std::move(p)); - return f; -} - -/// Invoke given action until it fails. -/// -/// Calls \c action repeatedly until it returns a failed future. -/// -/// \param action a callable taking no arguments, returning a \c future<> -/// that becomes ready when you wish it to be called again. -/// \return a future<> that will resolve to the first failure of \c action -template -static inline -future<> keep_doing(AsyncAction&& action) { - return repeat([action = std::forward(action)] () mutable { - return action().then([] { - return stop_iteration::no; - }); - }); -} - -/// Call a function for each item in a range, sequentially (iterator version). -/// -/// For each item in a range, call a function, waiting for the previous -/// invocation to complete before calling the next one. -/// -/// \param begin an \c InputIterator designating the beginning of the range -/// \param end an \c InputIterator designating the endof the range -/// \param action a callable, taking a reference to objects from the range -/// as a parameter, and returning a \c future<> that resolves -/// when it is acceptable to process the next item. -/// \return a ready future on success, or the first failed future if -/// \c action failed. -template -static inline -future<> do_for_each(Iterator begin, Iterator end, AsyncAction&& action) { - if (begin == end) { - return make_ready_future<>(); - } - while (true) { - auto f = action(*begin++); - if (begin == end) { - return f; - } - if (!f.available()) { - return std::move(f).then([action = std::forward(action), - begin = std::move(begin), end = std::move(end)] () mutable { - return do_for_each(std::move(begin), std::move(end), std::forward(action)); - }); - } - if (f.failed()) { - return std::move(f); - } - } -} - -/// Call a function for each item in a range, sequentially (range version). -/// -/// For each item in a range, call a function, waiting for the previous -/// invocation to complete before calling the next one. -/// -/// \param range an \c Range object designating input values -/// \param action a callable, taking a reference to objects from the range -/// as a parameter, and returning a \c future<> that resolves -/// when it is acceptable to process the next item. -/// \return a ready future on success, or the first failed future if -/// \c action failed. -template -static inline -future<> do_for_each(Container& c, AsyncAction&& action) { - return do_for_each(std::begin(c), std::end(c), std::forward(action)); -} - -/// \cond internal -inline -future> -when_all() { - return make_ready_future>(); -} - -// gcc can't capture a parameter pack, so we need to capture -// a tuple and use apply. But apply cannot accept an overloaded -// function pointer as its first parameter, so provide this instead. -struct do_when_all { - template - future> operator()(Future&&... fut) const { - return when_all(std::move(fut)...); - } -}; -/// \endcond - -/// Wait for many futures to complete, capturing possible errors (variadic version). -/// -/// Given a variable number of futures as input, wait for all of them -/// to resolve (either successfully or with an exception), and return -/// them as a tuple so individual values or exceptions can be examined. -/// -/// \param fut the first future to wait for -/// \param rest more futures to wait for -/// \return an \c std::tuple<> of all the futures in the input; when -/// ready, all contained futures will be ready as well. -template -inline -future, Rest...>> -when_all(future&& fut, Rest&&... rest) { - using Future = future; - return fut.then_wrapped( - [rest = std::make_tuple(std::move(rest)...)] (Future&& fut) mutable { - return apply(do_when_all(), std::move(rest)).then_wrapped( - [fut = std::move(fut)] (future>&& rest) mutable { - return make_ready_future>( - std::tuple_cat(std::make_tuple(std::move(fut)), std::get<0>(rest.get()))); - }); - }); -} - -/// \cond internal -template -inline -size_t -when_all_estimate_vector_capacity(Iterator begin, Iterator end, IteratorCategory category) { - // For InputIterators we can't estimate needed capacity - return 0; -} - -template -inline -size_t -when_all_estimate_vector_capacity(Iterator begin, Iterator end, std::forward_iterator_tag category) { - // May be linear time below random_access_iterator_tag, but still better than reallocation - return std::distance(begin, end); -} - -// Internal function for when_all(). -template -inline -future> -complete_when_all(std::vector&& futures, typename std::vector::iterator pos) { - // If any futures are already ready, skip them. - while (pos != futures.end() && pos->available()) { - ++pos; - } - // Done? - if (pos == futures.end()) { - return make_ready_future>(std::move(futures)); - } - // Wait for unready future, store, and continue. - return pos->then_wrapped([futures = std::move(futures), pos] (auto fut) mutable { - *pos++ = std::move(fut); - return complete_when_all(std::move(futures), pos); - }); -} -/// \endcond - -/// Wait for many futures to complete, capturing possible errors (iterator version). -/// -/// Given a range of futures as input, wait for all of them -/// to resolve (either successfully or with an exception), and return -/// them as a \c std::vector so individual values or exceptions can be examined. -/// -/// \param begin an \c InputIterator designating the beginning of the range of futures -/// \param end an \c InputIterator designating the end of the range of futures -/// \return an \c std::vector<> of all the futures in the input; when -/// ready, all contained futures will be ready as well. -template -inline -future::value_type>> -when_all(FutureIterator begin, FutureIterator end) { - using itraits = std::iterator_traits; - std::vector ret; - ret.reserve(when_all_estimate_vector_capacity(begin, end, typename itraits::iterator_category())); - // Important to invoke the *begin here, in case it's a function iterator, - // so we launch all computation in parallel. - std::move(begin, end, std::back_inserter(ret)); - return complete_when_all(std::move(ret), ret.begin()); -} - -template -struct reducer_with_get_traits { - using result_type = decltype(std::declval().get()); - using future_type = future; - static future_type maybe_call_get(future<> f, lw_shared_ptr r) { - return f.then([r = std::move(r)] () mutable { - return make_ready_future(std::move(*r).get()); - }); - } -}; - -template -struct reducer_traits { - using future_type = future<>; - static future_type maybe_call_get(future<> f, lw_shared_ptr r) { - return f.then([r = std::move(r)] {}); - } -}; - -template -struct reducer_traits().get(), void())> : public reducer_with_get_traits {}; - -// @Mapper is a callable which transforms values from the iterator range -// into a future. @Reducer is an object which can be called with T as -// parameter and yields a future<>. It may have a get() method which returns -// a value of type U which holds the result of reduction. This value is wrapped -// in a future and returned by this function. If the reducer has no get() method -// then this function returns future<>. -// -// TODO: specialize for non-deferring reducer -template -inline -auto -map_reduce(Iterator begin, Iterator end, Mapper&& mapper, Reducer&& r) - -> typename reducer_traits::future_type -{ - auto r_ptr = make_lw_shared(std::forward(r)); - future<> ret = make_ready_future<>(); - while (begin != end) { - ret = mapper(*begin++).then([ret = std::move(ret), r_ptr] (auto value) mutable { - return ret.then([value = std::move(value), r_ptr] () mutable { - return (*r_ptr)(std::move(value)); - }); - }); - } - return reducer_traits::maybe_call_get(std::move(ret), r_ptr); -} - -/// Asynchronous map/reduce transformation. -/// -/// Given a range of objects, an asynchronous unary function -/// operating on these objects, an initial value, and a -/// binary function for reducing, map_reduce() will -/// transform each object in the range, then apply -/// the the reducing function to the result. -/// -/// Example: -/// -/// Calculate the total size of several files: -/// -/// \code -/// map_reduce(files.begin(), files.end(), -/// std::mem_fn(file::size), -/// size_t(0), -/// std::plus()) -/// \endcode -/// -/// Requirements: -/// - Iterator: an InputIterator. -/// - Mapper: unary function taking Iterator::value_type and producing a future<...>. -/// - Initial: any value type -/// - Reduce: a binary function taking two Initial values and returning an Initial -/// -/// Return type: -/// - future -/// -/// \param begin beginning of object range to operate on -/// \param end end of object range to operate on -/// \param mapper map function to call on each object, returning a future -/// \param initial initial input value to reduce function -/// \param reduce binary function for merging two result values from \c mapper -/// -/// \return equivalent to \c reduce(reduce(initial, mapper(obj0)), mapper(obj1)) ... -template -inline -future -map_reduce(Iterator begin, Iterator end, Mapper&& mapper, Initial initial, Reduce reduce) { - struct state { - Initial result; - Reduce reduce; - }; - auto s = make_lw_shared(state{std::move(initial), std::move(reduce)}); - future<> ret = make_ready_future<>(); - while (begin != end) { - ret = mapper(*begin++).then([s = s.get(), ret = std::move(ret)] (auto&& value) mutable { - s->result = s->reduce(std::move(s->result), std::move(value)); - return std::move(ret); - }); - } - return ret.then([s] { - return make_ready_future(std::move(s->result)); - }); -} - -/// Asynchronous map/reduce transformation (range version). -/// -/// Given a range of objects, an asynchronous unary function -/// operating on these objects, an initial value, and a -/// binary function for reducing, map_reduce() will -/// transform each object in the range, then apply -/// the the reducing function to the result. -/// -/// Example: -/// -/// Calculate the total size of several files: -/// -/// \code -/// std::vector files = ...; -/// map_reduce(files, -/// std::mem_fn(file::size), -/// size_t(0), -/// std::plus()) -/// \endcode -/// -/// Requirements: -/// - Iterator: an InputIterator. -/// - Mapper: unary function taking Iterator::value_type and producing a future<...>. -/// - Initial: any value type -/// - Reduce: a binary function taking two Initial values and returning an Initial -/// -/// Return type: -/// - future -/// -/// \param range object range to operate on -/// \param mapper map function to call on each object, returning a future -/// \param initial initial input value to reduce function -/// \param reduce binary function for merging two result values from \c mapper -/// -/// \return equivalent to \c reduce(reduce(initial, mapper(obj0)), mapper(obj1)) ... -template -inline -future -map_reduce(Range&& range, Mapper&& mapper, Initial initial, Reduce reduce) { - return map_reduce(std::begin(range), std::end(range), std::forward(mapper), - std::move(initial), std::move(reduce)); -} - -// Implements @Reducer concept. Calculates the result by -// adding elements to the accumulator. -template -class adder { -private: - Result _result; -public: - future<> operator()(const Addend& value) { - _result += value; - return make_ready_future<>(); - } - Result get() && { - return std::move(_result); - } -}; - -static inline -future<> now() { - return make_ready_future<>(); -} - -/// @} - -#endif /* CORE_FUTURE_UTIL_HH_ */ diff --git a/core/future.hh b/core/future.hh deleted file mode 100644 index d035793c42..0000000000 --- a/core/future.hh +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#ifndef FUTURE_HH_ -#define FUTURE_HH_ - -#include "apply.hh" -#include "task.hh" -#include -#include -#include -#include - - -/// \defgroup future-module Futures and Promises -/// -/// \brief -/// Futures and promises are the basic tools for asynchronous -/// programming in seastar. A future represents a result that -/// may not have been computed yet, for example a buffer that -/// is being read from the disk, or the result of a function -/// that is executed on another cpu. A promise object allows -/// the future to be eventually resolved by assigning it a value. -/// -/// \brief -/// Another way to look at futures and promises are as the reader -/// and writer sides, respectively, of a single-item, single use -/// queue. You read from the future, and write to the promise, -/// and the system takes care that it works no matter what the -/// order of operations is. -/// -/// \brief -/// The normal way of working with futures is to chain continuations -/// to them. A continuation is a block of code (usually a lamdba) -/// that is called when the future is assigned a value (the future -/// is resolved); the continuation can then access the actual value. -/// - -/// \defgroup future-util Future Utilities -/// -/// \brief -/// These utilities are provided to help perform operations on futures. - - -namespace seastar { - -class thread_context; - -namespace thread_impl { - -thread_context* get(); -void switch_in(thread_context* to); -void switch_out(thread_context* from); - -} - -} - - -/// \addtogroup future-module -/// @{ - -template -class promise; - -template -class future; - -/// \brief Creates a \ref future in an available, value state. -/// -/// Creates a \ref future object that is already resolved. This -/// is useful when it is determined that no I/O needs to be performed -/// to perform a computation (for example, because the data is cached -/// in some buffer). -template -future make_ready_future(A&&... value); - -/// \brief Creates a \ref future in an available, failed state. -/// -/// Creates a \ref future object that is already resolved in a failed -/// state. This is useful when no I/O needs to be performed to perform -/// a computation (for example, because the connection is closed and -/// we cannot read from it). -template -future make_exception_future(std::exception_ptr value) noexcept; - -/// \cond internal -void engine_exit(std::exception_ptr eptr = {}); - -void report_failed_future(std::exception_ptr ex); -/// \endcond - -// -// A future/promise pair maintain one logical value (a future_state). -// To minimize allocations, the value is stored in exactly one of three -// locations: -// -// - in the promise (so long as it exists, and before a .then() is called) -// -// - in the task associated with the .then() clause (after .then() is called, -// if a value was not set) -// -// - in the future (if the promise was destroyed, or if it never existed, as -// with make_ready_future()), before .then() is called -// -// Both promise and future maintain a pointer to the state, which is modified -// the the state moves to a new location due to events (such as .then() being -// called) or due to the promise or future being mobved around. -// - -/// \cond internal -template -struct future_state { - static constexpr bool move_noexcept = std::is_nothrow_move_constructible>::value; - static constexpr bool copy_noexcept = std::is_nothrow_copy_constructible>::value; - enum class state { - invalid, - future, - result, - exception, - } _state = state::future; - union any { - any() {} - ~any() {} - std::tuple value; - std::exception_ptr ex; - } _u; - future_state() noexcept {} - future_state(future_state&& x) noexcept(move_noexcept) - : _state(x._state) { - switch (_state) { - case state::future: - break; - case state::result: - new (&_u.value) std::tuple(std::move(x._u.value)); - break; - case state::exception: - new (&_u.ex) std::exception_ptr(std::move(x._u.ex)); - break; - case state::invalid: - break; - default: - abort(); - } - x._state = state::invalid; - } - __attribute__((always_inline)) - ~future_state() noexcept { - switch (_state) { - case state::invalid: - break; - case state::future: - break; - case state::result: - _u.value.~tuple(); - break; - case state::exception: - _u.ex.~exception_ptr(); - break; - default: - abort(); - } - } - future_state& operator=(future_state&& x) noexcept(move_noexcept) { - if (this != &x) { - this->~future_state(); - new (this) future_state(std::move(x)); - } - return *this; - } - bool available() const noexcept { return _state == state::result || _state == state::exception; } - bool failed() const noexcept { return _state == state::exception; } - void wait(); - void set(const std::tuple& value) noexcept(copy_noexcept) { - assert(_state == state::future); - _state = state::result; - new (&_u.value) std::tuple(value); - } - void set(std::tuple&& value) noexcept(move_noexcept) { - assert(_state == state::future); - _state = state::result; - new (&_u.value) std::tuple(std::move(value)); - } - template - void set(A&&... a) { - assert(_state == state::future); - _state = state::result; - new (&_u.value) std::tuple(std::forward(a)...); - } - void set_exception(std::exception_ptr ex) noexcept { - assert(_state == state::future); - _state = state::exception; - new (&_u.ex) std::exception_ptr(ex); - } - std::exception_ptr get_exception() noexcept { - assert(_state == state::exception); - // Move ex out so future::~future() knows we've handled it - _state = state::invalid; - return std::move(_u.ex); - } - std::tuple get() { - assert(_state != state::future); - if (_state == state::exception) { - _state = state::invalid; - // Move ex out so future::~future() knows we've handled it - std::rethrow_exception(std::move(_u.ex)); - } - return std::move(_u.value); - } - using get0_return_type = std::tuple_element_t<0, std::tuple>; - static get0_return_type get0(std::tuple&& x) { - return std::get<0>(std::move(x)); - } - void forward_to(promise& pr) noexcept { - assert(_state != state::future); - if (_state == state::exception) { - pr.set_exception(_u.ex); - } else { - pr.set_value(std::move(get())); - } - _state = state::invalid; - } -}; - -// Specialize future_state<> to overlap the state enum with the exception, as there -// is no value to hold. -// -// Assumes std::exception_ptr is really a pointer. -template <> -struct future_state<> { - static_assert(sizeof(std::exception_ptr) == sizeof(void*), "exception_ptr not a pointer"); - static constexpr bool move_noexcept = true; - static constexpr bool copy_noexcept = true; - enum class state : uintptr_t { - invalid = 0, - future = 1, - result = 2, - exception_min = 3, // or anything greater - }; - union any { - any() { st = state::future; } - ~any() {} - state st; - std::exception_ptr ex; - } _u; - future_state() noexcept {} - future_state(future_state&& x) noexcept { - if (x._u.st < state::exception_min) { - _u.st = x._u.st; - } else { - // Move ex out so future::~future() knows we've handled it - // Moving it will reset us to invalid state - new (&_u.ex) std::exception_ptr(std::move(x._u.ex)); - } - x._u.st = state::invalid; - } - ~future_state() noexcept { - if (_u.st >= state::exception_min) { - _u.ex.~exception_ptr(); - } - } - future_state& operator=(future_state&& x) noexcept { - if (this != &x) { - this->~future_state(); - new (this) future_state(std::move(x)); - } - return *this; - } - bool available() const noexcept { return _u.st == state::result || _u.st >= state::exception_min; } - bool failed() const noexcept { return _u.st >= state::exception_min; } - void set(const std::tuple<>& value) noexcept { - assert(_u.st == state::future); - _u.st = state::result; - } - void set(std::tuple<>&& value) noexcept { - assert(_u.st == state::future); - _u.st = state::result; - } - void set() { - assert(_u.st == state::future); - _u.st = state::result; - } - void set_exception(std::exception_ptr ex) noexcept { - assert(_u.st == state::future); - new (&_u.ex) std::exception_ptr(ex); - assert(_u.st >= state::exception_min); - } - std::tuple<> get() { - assert(_u.st != state::future); - if (_u.st >= state::exception_min) { - // Move ex out so future::~future() knows we've handled it - // Moving it will reset us to invalid state - std::rethrow_exception(std::move(_u.ex)); - } - return {}; - } - using get0_return_type = void; - static get0_return_type get0(std::tuple<>&&) { - return; - } - std::exception_ptr get_exception() noexcept { - assert(_u.st >= state::exception_min); - // Move ex out so future::~future() knows we've handled it - // Moving it will reset us to invalid state - return std::move(_u.ex); - } - void forward_to(promise<>& pr) noexcept; -}; - -template -struct continuation final : task { - continuation(Func&& func, future_state&& state) : _state(std::move(state)), _func(std::move(func)) {} - continuation(Func&& func) : _func(std::move(func)) {} - virtual void run() noexcept override { - _func(std::move(_state)); - } - future_state _state; - Func _func; -}; - -#ifndef DEBUG -static constexpr unsigned max_inlined_continuations = 256; -#else -static constexpr unsigned max_inlined_continuations = 1; -#endif - -/// \endcond - -/// \brief promise - allows a future value to be made available at a later time. -/// -/// -template -class promise { - future* _future = nullptr; - future_state _local_state; - future_state* _state; - std::unique_ptr _task; - static constexpr bool move_noexcept = future_state::move_noexcept; - static constexpr bool copy_noexcept = future_state::copy_noexcept; -public: - /// \brief Constructs an empty \c promise. - /// - /// Creates promise with no associated future yet (see get_future()). - promise() noexcept : _state(&_local_state) {} - - /// \brief Moves a \c promise object. - promise(promise&& x) noexcept(move_noexcept) : _future(x._future), _state(x._state), _task(std::move(x._task)) { - if (_state == &x._local_state) { - _state = &_local_state; - _local_state = std::move(x._local_state); - } - x._future = nullptr; - x._state = nullptr; - migrated(); - } - promise(const promise&) = delete; - __attribute__((always_inline)) - ~promise() noexcept { - abandoned(); - } - promise& operator=(promise&& x) noexcept(move_noexcept) { - if (this != &x) { - this->~promise(); - new (this) promise(std::move(x)); - } - return *this; - } - void operator=(const promise&) = delete; - - /// \brief Gets the promise's associated future. - /// - /// The future and promise will be remember each other, even if either or - /// both are moved. When \c set_value() or \c set_exception() are called - /// on the promise, the future will be become ready, and if a continuation - /// was attached to the future, it will run. - future get_future() noexcept; - - /// \brief Sets the promise's value (as tuple; by copying) - /// - /// Copies the tuple argument and makes it available to the associated - /// future. May be called either before or after \c get_future(). - void set_value(const std::tuple& result) noexcept(copy_noexcept) { - _state->set(result); - make_ready(); - } - - /// \brief Sets the promises value (as tuple; by moving) - /// - /// Moves the tuple argument and makes it available to the associated - /// future. May be called either before or after \c get_future(). - void set_value(std::tuple&& result) noexcept(move_noexcept) { - _state->set(std::move(result)); - make_ready(); - } - - /// \brief Sets the promises value (variadic) - /// - /// Forwards the arguments and makes them available to the associated - /// future. May be called either before or after \c get_future(). - template - void set_value(A&&... a) noexcept { - _state->set(std::forward(a)...); - make_ready(); - } - - /// \brief Marks the promise as failed - /// - /// Forwards the exception argument to the future and makes it - /// available. May be called either before or after \c get_future(). - void set_exception(std::exception_ptr ex) noexcept { - _state->set_exception(std::move(ex)); - make_ready(); - } - - /// \brief Marks the promise as failed - /// - /// Forwards the exception argument to the future and makes it - /// available. May be called either before or after \c get_future(). - template - void set_exception(Exception&& e) noexcept { - set_exception(make_exception_ptr(std::forward(e))); - } -private: - template - void schedule(Func&& func) noexcept { - auto tws = std::make_unique>(std::move(func)); - _state = &tws->_state; - _task = std::move(tws); - } - __attribute__((always_inline)) - void make_ready() noexcept; - void migrated() noexcept; - void abandoned() noexcept(move_noexcept); - - template - friend class future; -}; - -/// \brief Specialization of \c promise -/// -/// This is an alias for \c promise<>, for generic programming purposes. -/// For example, You may have a \c promise where \c T can legally be -/// \c void. -template<> -class promise : public promise<> {}; - -/// @} - -/// \addtogroup future-util -/// @{ - - -/// \brief Check whether a type is a future -/// -/// This is a type trait evaluating to \c true if the given type is a -/// future. -/// -template struct is_future : std::false_type {}; - -/// \cond internal -/// \addtogroup future-util -template struct is_future> : std::true_type {}; - -struct ready_future_marker {}; -struct ready_future_from_tuple_marker {}; -struct exception_future_marker {}; - -extern __thread size_t future_avail_count; -/// \endcond - - -/// \brief Converts a type to a future type, if it isn't already. -/// -/// \return Result in member type 'type'. -template -struct futurize; - -template -struct futurize { - /// If \c T is a future, \c T; otherwise \c future - using type = future; - /// The promise type associated with \c type. - using promise_type = promise; - - /// Apply a function to an argument list (expressed as a tuple) - /// and return the result, as a future (if it wasn't already). - template - static inline type apply(Func&& func, std::tuple&& args); - - /// Apply a function to an argument list - /// and return the result, as a future (if it wasn't already). - template - static inline type apply(Func&& func, FuncArgs&&... args); - - /// Makes an exceptional future of type \ref type. - template - static type make_exception_future(Arg&& arg); -}; - -/// \cond internal -template <> -struct futurize { - using type = future<>; - using promise_type = promise<>; - - template - static inline type apply(Func&& func, std::tuple&& args); - - template - static inline type apply(Func&& func, FuncArgs&&... args); - - template - static type make_exception_future(Arg&& arg); -}; - -template -struct futurize> { - using type = future; - using promise_type = promise; - - template - static inline type apply(Func&& func, std::tuple&& args); - - template - static inline type apply(Func&& func, FuncArgs&&... args); - - template - static type make_exception_future(Arg&& arg); -}; -/// \endcond - -// Converts a type to a future type, if it isn't already. -template -using futurize_t = typename futurize::type; - -/// @} - -/// \addtogroup future-module -/// @{ - -/// \brief A representation of a possibly not-yet-computed value. -/// -/// A \c future represents a value that has not yet been computed -/// (an asynchronous computation). It can be in one of several -/// states: -/// - unavailable: the computation has not been completed yet -/// - value: the computation has been completed successfully and a -/// value is available. -/// - failed: the computation completed with an exception. -/// -/// methods in \c future allow querying the state and, most importantly, -/// scheduling a \c continuation to be executed when the future becomes -/// available. Only one such continuation may be scheduled. -template -class future { - promise* _promise; - future_state _local_state; // valid if !_promise - static constexpr bool move_noexcept = future_state::move_noexcept; - static constexpr bool copy_noexcept = future_state::copy_noexcept; -private: - future(promise* pr) noexcept : _promise(pr) { - _promise->_future = this; - } - template - future(ready_future_marker, A&&... a) : _promise(nullptr) { - _local_state.set(std::forward(a)...); - } - template - future(ready_future_from_tuple_marker, std::tuple&& data) : _promise(nullptr) { - _local_state.set(std::move(data)); - } - future(exception_future_marker, std::exception_ptr ex) noexcept : _promise(nullptr) { - _local_state.set_exception(std::move(ex)); - } - explicit future(future_state&& state) noexcept - : _promise(nullptr), _local_state(std::move(state)) { - } - future_state* state() noexcept { - return _promise ? _promise->_state : &_local_state; - } - template - void schedule(Func&& func) noexcept { - if (state()->available()) { - ::schedule(std::make_unique>(std::move(func), std::move(*state()))); - } else { - _promise->schedule(std::move(func)); - _promise->_future = nullptr; - _promise = nullptr; - } - } - - future_state get_available_state() { - auto st = state(); - if (_promise) { - _promise->_future = nullptr; - _promise = nullptr; - } - return std::move(*st); - } - - template - futurize_t then(Func&& func, Param&& param) noexcept { - using futurator = futurize; - using P = typename futurator::promise_type; - if (state()->available() && (++future_avail_count % max_inlined_continuations)) { - try { - return futurator::apply(std::forward(func), param(get_available_state())); - } catch (...) { - P p; - p.set_exception(std::current_exception()); - return p.get_future(); - } - } - P pr; - auto fut = pr.get_future(); - schedule([pr = std::move(pr), func = std::forward(func), param = std::forward(param)] (auto&& state) mutable { - try { - futurator::apply(std::forward(func), param(std::move(state))).forward_to(std::move(pr)); - } catch (...) { - pr.set_exception(std::current_exception()); - } - }); - return fut; - } - -public: - /// \brief The data type carried by the future. - using value_type = std::tuple; - /// \brief The data type carried by the future. - using promise_type = promise; - /// \brief Moves the future into a new object. - future(future&& x) noexcept(move_noexcept) : _promise(x._promise) { - if (!_promise) { - _local_state = std::move(x._local_state); - } - x._promise = nullptr; - if (_promise) { - _promise->_future = this; - } - } - future(const future&) = delete; - future& operator=(future&& x) noexcept { - if (this != &x) { - this->~future(); - new (this) future(std::move(x)); - } - return *this; - } - void operator=(const future&) = delete; - __attribute__((always_inline)) - ~future() { - if (_promise) { - _promise->_future = nullptr; - } - if (failed()) { - report_failed_future(state()->get_exception()); - } - } - /// \brief gets the value returned by the computation - /// - /// Requires that the future be available. If the value - /// was computed successfully, it is returned (as an - /// \c std::tuple). Otherwise, an exception is thrown. - /// - /// If get() is called in a \ref seastar::thread context, - /// then it need not be available; instead, the thread will - /// be paused until the future becomes available. - std::tuple get() { - if (!state()->available()) { - wait(); - } - // detach from promise, so that promise::abandoned() doesn't trigger - if (_promise) { - _promise->_future = nullptr; - _promise = nullptr; - } - return state()->get(); - } - - /// Gets the value returned by the computation. - /// - /// Similar to \ref get(), but instead of returning a - /// tuple, returns the first value of the tuple. This is - /// useful for the common case of a \c future with exactly - /// one type parameter. - /// - /// Equivalent to: \c std::get<0>(f.get()). - typename future_state::get0_return_type get0() { - return future_state::get0(get()); - } - - /// \cond internal - void wait() { - auto thread = seastar::thread_impl::get(); - assert(thread); - schedule([this, thread] (future_state&& new_state) { - *state() = std::move(new_state); - seastar::thread_impl::switch_in(thread); - }); - seastar::thread_impl::switch_out(thread); - } - /// \endcond - - /// \brief Checks whether the future is available. - /// - /// \return \c true if the future has a value, or has failed. - bool available() noexcept { - return state()->available(); - } - - /// \brief Checks whether the future has failed. - /// - /// \return \c true if the future is availble and has failed. - bool failed() noexcept { - return state()->failed(); - } - - /// \brief Schedule a block of code to run when the future is ready. - /// - /// Schedules a function (often a lambda) to run when the future becomes - /// available. The function is called with the result of this future's - /// computation as parameters. The return value of the function becomes - /// the return value of then(), itself as a future; this allows then() - /// calls to be chained. - /// - /// If the future failed, the function is not called, and the exception - /// is propagated into the return value of then(). - /// - /// \param func - function to be called when the future becomes available, - /// unless it has failed. - /// \return a \c future representing the return value of \c func, applied - /// to the eventual value of this future. - template >> - Result then(Func&& func) noexcept { - return then>(std::forward(func), [] (future_state&& state) { return state.get(); }); - } - - /// \brief Schedule a block of code to run when the future is ready, allowing - /// for exception handling. - /// - /// Schedules a function (often a lambda) to run when the future becomes - /// available. The function is called with the this future as a parameter; - /// it will be in an available state. The return value of the function becomes - /// the return value of then_wrapped(), itself as a future; this allows - /// then_wrapped() calls to be chained. - /// - /// Unlike then(), the function will be called for both value and exceptional - /// futures. - /// - /// \param func - function to be called when the future becomes available, - /// \return a \c future representing the return value of \c func, applied - /// to the eventual value of this future. - template )>>> - Result - then_wrapped(Func&& func) noexcept { - return then)>>(std::forward(func), [] (future_state&& state) { return future(std::move(state)); }); - } - - /// \brief Satisfy some \ref promise object with this future as a result. - /// - /// Arranges so that when this future is resolve, it will be used to - /// satisfy an unrelated promise. This is similar to scheduling a - /// continuation that moves the result of this future into the promise - /// (using promise::set_value() or promise::set_exception(), except - /// that it is more efficient. - /// - /// \param pr a promise that will be fulfilled with the results of this - /// future. - void forward_to(promise&& pr) noexcept { - if (state()->available()) { - state()->forward_to(pr); - } else { - _promise->_future = nullptr; - *_promise = std::move(pr); - _promise = nullptr; - } - } - - /** - * Finally continuation for statements that require waiting for the result. I.e. you need to "finally" call - * a function that returns a possibly unavailable future. - * The returned future will be "waited for", any exception generated will be propagated, but the return value - * is ignored. I.e. the original return value (the future upon which you are making this call) will be preserved. - */ - template - future finally(Func&& func) noexcept { - return then_wrapped([func = std::forward(func)](future result) mutable { - using futurator = futurize>; - return futurator::apply(std::forward(func)).then_wrapped([result = std::move(result)](auto f_res) mutable { - try { - f_res.get(); // force excepion if one - return std::move(result); - } catch (...) { - return make_exception_future(std::current_exception()); - } - }); - }); - } - - /// \brief Terminate the program if this future fails. - /// - /// Terminates the entire program is this future resolves - /// to an exception. Use with caution. - future<> or_terminate() noexcept { - return then_wrapped([] (auto&& f) { - try { - f.get(); - } catch (...) { - engine_exit(std::current_exception()); - } - }); - } - - /// \brief Discards the value carried by this future. - /// - /// Converts the future into a no-value \c future<>, by - /// ignoring any result. Exceptions are propagated unchanged. - future<> discard_result() noexcept { - return then([] (T&&...) {}); - } - - /// \brief Handle the exception carried by this future. - /// - /// When the future resolves, if it resolves with an exception, run the - /// given function with the exception passed as an std::exception_ptr. - /// After handling the exception, it is discarded. Accordingly, we must - /// also discard the value of the future, because we cannot propagate a - /// value which would not exist in the case of an exception. - template - future<> handle_exception(Func&& func) noexcept { - return then_wrapped([func = std::forward(func)] (auto&& fut) { - try { - fut.get(); - } catch (...) { - func(std::current_exception()); - } - }); - } - - /// \cond internal - template - friend class promise; - template - friend future make_ready_future(A&&... value); - template - friend future make_exception_future(std::exception_ptr ex) noexcept; - template - friend future make_exception_future(Exception&& ex) noexcept; - /// \endcond -}; - -inline -void future_state<>::forward_to(promise<>& pr) noexcept { - assert(_u.st != state::future && _u.st != state::invalid); - if (_u.st >= state::exception_min) { - pr.set_exception(std::move(_u.ex)); - } else { - pr.set_value(std::tuple<>()); - } - _u.st = state::invalid; -} - -template -inline -future -promise::get_future() noexcept { - assert(!_future); - return future(this); -} - -template -inline -void promise::make_ready() noexcept { - if (_task) { - ::schedule(std::move(_task)); - } -} - -template -inline -void promise::migrated() noexcept { - if (_future) { - _future->_promise = this; - } -} - -template -inline -void promise::abandoned() noexcept(move_noexcept) { - if (_future) { - assert(_state); - assert(_state->available()); - _future->_local_state = std::move(*_state); - _future->_promise = nullptr; - } -} - -template -inline -future make_ready_future(A&&... value) { - return future(ready_future_marker(), std::forward(value)...); -} - -template -inline -future make_exception_future(std::exception_ptr ex) noexcept { - return future(exception_future_marker(), std::move(ex)); -} - -/// \brief Creates a \ref future in an available, failed state. -/// -/// Creates a \ref future object that is already resolved in a failed -/// state. This no I/O needs to be performed to perform a computation -/// (for example, because the connection is closed and we cannot read -/// from it). -template -inline -future make_exception_future(Exception&& ex) noexcept { - return make_exception_future(std::make_exception_ptr(std::forward(ex))); -} - -/// @} - -/// \cond internal - -template -template -typename futurize::type futurize::apply(Func&& func, std::tuple&& args) { - return make_ready_future(::apply(std::forward(func), std::move(args))); -} - -template -template -typename futurize::type futurize::apply(Func&& func, FuncArgs&&... args) { - return make_ready_future(func(std::forward(args)...)); -} - -template -typename futurize::type futurize::apply(Func&& func, std::tuple&& args) { - ::apply(std::forward(func), std::move(args)); - return make_ready_future<>(); -} - -template -typename futurize::type futurize::apply(Func&& func, FuncArgs&&... args) { - func(std::forward(args)...); - return make_ready_future<>(); -} - -template -template -typename futurize>::type futurize>::apply(Func&& func, std::tuple&& args) { - return ::apply(std::forward(func), std::move(args)); -} - -template -template -typename futurize>::type futurize>::apply(Func&& func, FuncArgs&&... args) { - return func(std::forward(args)...); -} - -template -template -inline -future -futurize::make_exception_future(Arg&& arg) { - return ::make_exception_future(std::forward(arg)); -} - -template -template -inline -future -futurize>::make_exception_future(Arg&& arg) { - return ::make_exception_future(std::forward(arg)); -} - -template -inline -future<> -futurize::make_exception_future(Arg&& arg) { - return ::make_exception_future<>(std::forward(arg)); -} - -/// \endcond - -#endif /* FUTURE_HH_ */ diff --git a/core/gate.hh b/core/gate.hh deleted file mode 100644 index c09f7f0ef5..0000000000 --- a/core/gate.hh +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#pragma once - -#include "future.hh" -#include -#include - -namespace seastar { - -/// \addtogroup fiber-module -/// @{ - -namespace stdx = std::experimental; - -/// Exception thrown when a \ref gate object has been closed -/// by the \ref gate::close() method. -class gate_closed_exception : public std::exception { -public: - virtual const char* what() const noexcept override { - return "gate closed"; - } -}; - -/// Facility to stop new requests, and to tell when existing requests are done. -/// -/// When stopping a service that serves asynchronous requests, we are faced with -/// two problems: preventing new requests from coming in, and knowing when existing -/// requests have completed. The \c gate class provides a solution. -class gate { - size_t _count = 0; - stdx::optional> _stopped; -public: - /// Registers an in-progress request. - /// - /// If the gate is not closed, the request is registered. Otherwise, - /// a \ref gate_closed_exception is thrown. - void enter() { - if (_stopped) { - throw gate_closed_exception(); - } - ++_count; - } - /// Unregisters an in-progress request. - /// - /// If the gate is closed, and there are no more in-progress requests, - /// the \ref closed() promise will be fulfilled. - void leave() { - --_count; - if (!_count && _stopped) { - _stopped->set_value(); - } - } - /// Closes the gate. - /// - /// Future calls to \ref enter() will fail with an exception, and when - /// all current requests call \ref leave(), the returned future will be - /// made ready. - future<> close() { - _stopped = stdx::make_optional(promise<>()); - if (!_count) { - _stopped->set_value(); - } - return _stopped->get_future(); - } -}; - -/// Executes the function \c func making sure the gate \c g is properly entered -/// and later on, properly left. -/// -/// \param func function to be executed -/// \param g the gate. Caller must make sure that it outlives this function. -/// \returns whatever \c func returns -/// -/// \relates gate -template -inline -auto -with_gate(gate& g, Func&& func) { - g.enter(); - return func().finally([&g] { g.leave(); }); -} -/// @} - -} diff --git a/core/iostream-impl.hh b/core/iostream-impl.hh deleted file mode 100644 index caa235af15..0000000000 --- a/core/iostream-impl.hh +++ /dev/null @@ -1,251 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - - -#pragma once - -#include "net/packet.hh" - -template -inline -future<> output_stream::write(const char_type* buf) { - return write(buf, strlen(buf)); -} - -template -template -inline -future<> output_stream::write(const basic_sstring& s) { - return write(reinterpret_cast(s.c_str()), s.size()); -} - -template -inline -future<> output_stream::write(const std::basic_string& s) { - return write(s.c_str(), s.size()); -} - -template -future<> output_stream::write(scattered_message msg) { - return write(std::move(msg).release()); -} - -template -future<> output_stream::write(net::packet p) { - static_assert(std::is_same::value, "packet works on char"); - - if (p.len() == 0) { - return make_ready_future<>(); - } - - assert(!_end && "Mixing buffered writes and zero-copy writes not supported yet"); - - if (!_trim_to_size || p.len() <= _size) { - // TODO: aggregate buffers for later coalescing. Currently we flush right - // after appending the message anyway, so it doesn't matter. - return _fd.put(std::move(p)); - } - - auto head = p.share(0, _size); - p.trim_front(_size); - return _fd.put(std::move(head)).then([this, p = std::move(p)] () mutable { - return write(std::move(p)); - }); -} - -template -future> -input_stream::read_exactly_part(size_t n, tmp_buf out, size_t completed) { - if (available()) { - auto now = std::min(n - completed, available()); - std::copy(_buf.get(), _buf.get() + now, out.get_write() + completed); - _buf.trim_front(now); - completed += now; - } - if (completed == n) { - return make_ready_future(std::move(out)); - } - - // _buf is now empty - return _fd.get().then([this, n, out = std::move(out), completed] (auto buf) mutable { - if (buf.size() == 0) { - _eof = true; - return make_ready_future(std::move(buf)); - } - _buf = std::move(buf); - return this->read_exactly_part(n, std::move(out), completed); - }); -} - -template -future> -input_stream::read_exactly(size_t n) { - if (_buf.size() == n) { - // easy case: steal buffer, return to caller - return make_ready_future(std::move(_buf)); - } else if (_buf.size() > n) { - // buffer large enough, share it with caller - auto front = _buf.share(0, n); - _buf.trim_front(n); - return make_ready_future(std::move(front)); - } else if (_buf.size() == 0) { - // buffer is empty: grab one and retry - return _fd.get().then([this, n] (auto buf) mutable { - if (buf.size() == 0) { - _eof = true; - return make_ready_future(std::move(buf)); - } - _buf = std::move(buf); - return this->read_exactly(n); - }); - } else { - // buffer too small: start copy/read loop - tmp_buf b(n); - return read_exactly_part(n, std::move(b), 0); - } -} - -template -template -future<> -input_stream::consume(Consumer& consumer) { - for (;;) { - if (_buf.empty() && !_eof) { - return _fd.get().then([this, &consumer] (tmp_buf buf) { - _buf = std::move(buf); - _eof = _buf.empty(); - return consume(consumer); - }); - } - future unconsumed = consumer(std::move(_buf)); - if (unconsumed.available()) { - unconsumed_remainder u = std::get<0>(unconsumed.get()); - if (u) { - // consumer is done - _buf = std::move(u.value()); - return make_ready_future<>(); - } - if (_eof) { - return make_ready_future<>(); - } - // If we're here, consumer consumed entire buffer and is ready for - // more now. So we do not return, and rather continue the loop. - // TODO: if we did too many iterations, schedule a call to - // consume() instead of continuing the loop. - } else { - // TODO: here we wait for the consumer to finish the previous - // buffer (fulfilling "unconsumed") before starting to read the - // next one. Consider reading ahead. - return unconsumed.then([this, &consumer] (unconsumed_remainder u) { - if (u) { - // consumer is done - _buf = std::move(u.value()); - return make_ready_future<>(); - } else { - // consumer consumed entire buffer, and is ready for more - return consume(consumer); - } - }); - } - } -} - -// Writes @buf in chunks of _size length. The last chunk is buffered if smaller. -template -future<> -output_stream::split_and_put(temporary_buffer buf) { - assert(_end == 0); - - if (buf.size() < _size) { - if (!_buf) { - _buf = _fd.allocate_buffer(_size); - } - std::copy(buf.get(), buf.get() + buf.size(), _buf.get_write()); - _end = buf.size(); - return make_ready_future<>(); - } - - auto chunk = buf.share(0, _size); - buf.trim_front(_size); - return _fd.put(std::move(chunk)).then([this, buf = std::move(buf)] () mutable { - return split_and_put(std::move(buf)); - }); -} - -template -future<> -output_stream::write(const char_type* buf, size_t n) { - auto bulk_threshold = _end ? (2 * _size - _end) : _size; - if (n >= bulk_threshold) { - if (_end) { - auto now = _size - _end; - std::copy(buf, buf + now, _buf.get_write() + _end); - _end = _size; - temporary_buffer tmp = _fd.allocate_buffer(n - now); - std::copy(buf + now, buf + n, tmp.get_write()); - return flush().then([this, tmp = std::move(tmp)]() mutable { - if (_trim_to_size) { - return split_and_put(std::move(tmp)); - } else { - return _fd.put(std::move(tmp)); - } - }); - } else { - temporary_buffer tmp = _fd.allocate_buffer(n); - std::copy(buf, buf + n, tmp.get_write()); - if (_trim_to_size) { - return split_and_put(std::move(tmp)); - } else { - return _fd.put(std::move(tmp)); - } - } - } - - if (!_buf) { - _buf = _fd.allocate_buffer(_size); - } - - auto now = std::min(n, _size - _end); - std::copy(buf, buf + now, _buf.get_write() + _end); - _end += now; - if (now == n) { - return make_ready_future<>(); - } else { - temporary_buffer next = _fd.allocate_buffer(_size); - std::copy(buf + now, buf + n, next.get_write()); - _end = n - now; - std::swap(next, _buf); - return _fd.put(std::move(next)); - } -} - -template -future<> -output_stream::flush() { - if (!_end) { - return make_ready_future<>(); - } - _buf.trim(_end); - _end = 0; - return _fd.put(std::move(_buf)); -} - diff --git a/core/iostream.hh b/core/iostream.hh deleted file mode 100644 index e9927a4a72..0000000000 --- a/core/iostream.hh +++ /dev/null @@ -1,185 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -// -// Buffered input and output streams -// -// Two abstract classes (data_source and data_sink) provide means -// to acquire bulk data from, or push bulk data to, some provider. -// These could be tied to a TCP connection, a disk file, or a memory -// buffer. -// -// Two concrete classes (input_stream and output_stream) buffer data -// from data_source and data_sink and provide easier means to process -// it. -// - -#pragma once - -#include "future.hh" -#include "temporary_buffer.hh" -#include "scattered_message.hh" - -namespace net { class packet; } - -class data_source_impl { -public: - virtual ~data_source_impl() {} - virtual future> get() = 0; -}; - -class data_source { - std::unique_ptr _dsi; -protected: - data_source_impl* impl() const { return _dsi.get(); } -public: - data_source() = default; - explicit data_source(std::unique_ptr dsi) : _dsi(std::move(dsi)) {} - data_source(data_source&& x) = default; - data_source& operator=(data_source&& x) = default; - future> get() { return _dsi->get(); } -}; - -class data_sink_impl { -public: - virtual ~data_sink_impl() {} - virtual temporary_buffer allocate_buffer(size_t size) { - return temporary_buffer(size); - } - virtual future<> put(net::packet data) = 0; - virtual future<> put(std::vector> data) { - net::packet p; - p.reserve(data.size()); - for (auto& buf : data) { - p = net::packet(std::move(p), net::fragment{buf.get_write(), buf.size()}, buf.release()); - } - return put(std::move(p)); - } - virtual future<> put(temporary_buffer buf) { - return put(net::packet(net::fragment{buf.get_write(), buf.size()}, buf.release())); - } - virtual future<> close() = 0; -}; - -class data_sink { - std::unique_ptr _dsi; -public: - data_sink() = default; - explicit data_sink(std::unique_ptr dsi) : _dsi(std::move(dsi)) {} - data_sink(data_sink&& x) = default; - data_sink& operator=(data_sink&& x) = default; - temporary_buffer allocate_buffer(size_t size) { - return _dsi->allocate_buffer(size); - } - future<> put(std::vector> data) { - return _dsi->put(std::move(data)); - } - future<> put(temporary_buffer data) { - return _dsi->put(std::move(data)); - } - future<> put(net::packet p) { - return _dsi->put(std::move(p)); - } - future<> close() { return _dsi->close(); } -}; - -template -class input_stream final { - static_assert(sizeof(CharType) == 1, "must buffer stream of bytes"); - data_source _fd; - temporary_buffer _buf; - bool _eof = false; -private: - using tmp_buf = temporary_buffer; - size_t available() const { return _buf.size(); } -protected: - void reset() { _buf = {}; } - data_source* fd() { return &_fd; } -public: - // Consumer concept, for consume() method: - using unconsumed_remainder = std::experimental::optional; - struct ConsumerConcept { - // The consumer should operate on the data given to it, and - // return a future "unconsumed remainder", which can be undefined - // if the consumer consumed all the input given to it and is ready - // for more, or defined when the consumer is done (and in that case - // the value is the unconsumed part of the last data buffer - this - // can also happen to be empty). - future operator()(tmp_buf data); - }; - using char_type = CharType; - input_stream() = default; - explicit input_stream(data_source fd) : _fd(std::move(fd)), _buf(0) {} - input_stream(input_stream&&) = default; - input_stream& operator=(input_stream&&) = default; - future> read_exactly(size_t n); - template - future<> consume(Consumer& c); - bool eof() { return _eof; } -private: - future> read_exactly_part(size_t n, tmp_buf buf, size_t completed); -}; - -// Facilitates data buffering before it's handed over to data_sink. -// -// When trim_to_size is true it's guaranteed that data sink will not receive -// chunks larger than the configured size, which could be the case when a -// single write call is made with data larger than the configured size. -// -// The data sink will not receive empty chunks. -// -template -class output_stream final { - static_assert(sizeof(CharType) == 1, "must buffer stream of bytes"); - data_sink _fd; - temporary_buffer _buf; - size_t _size = 0; - size_t _begin = 0; - size_t _end = 0; - bool _trim_to_size = false; -private: - size_t available() const { return _end - _begin; } - size_t possibly_available() const { return _size - _begin; } - future<> split_and_put(temporary_buffer buf); -public: - using char_type = CharType; - output_stream() = default; - output_stream(data_sink fd, size_t size, bool trim_to_size = false) - : _fd(std::move(fd)), _size(size), _trim_to_size(trim_to_size) {} - output_stream(output_stream&&) = default; - output_stream& operator=(output_stream&&) = default; - future<> write(const char_type* buf, size_t n); - future<> write(const char_type* buf); - - template - future<> write(const basic_sstring& s); - future<> write(const std::basic_string& s); - - future<> write(net::packet p); - future<> write(scattered_message msg); - future<> flush(); - future<> close() { return flush().then([this] { return _fd.close(); }); } -private: -}; - - -#include "iostream-impl.hh" diff --git a/core/memory.cc b/core/memory.cc deleted file mode 100644 index 6126381398..0000000000 --- a/core/memory.cc +++ /dev/null @@ -1,1221 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - - -/// \cond internal - -// -// Seastar memory allocator -// -// This is a share-nothing allocator (memory allocated on one cpu must -// be freed on the same cpu). -// -// Inspired by gperftools' tcmalloc. -// -// Memory map: -// -// 0x0000'sccc'vvvv'vvvv -// -// 0000 - required by architecture (only 48 bits of address space) -// s - chosen to satisfy system allocator (1-7) -// ccc - cpu number (0-12 bits allocated vary according to system) -// v - virtual address within cpu (32-44 bits, according to how much ccc -// leaves us -// -// Each page has a page structure that describes it. Within a cpu's -// memory pool, the page array starts at offset 0, describing all pages -// within that pool. Page 0 does not describe a valid page. -// -// Each pool can contain at most 2^32 pages (or 44 address bits), so we can -// use a 32-bit integer to identify a page. -// -// Runs of pages are organized into spans. Free spans are organized into lists, -// by size. When spans are broken up or coalesced, they may move into new lists. - -#include "memory.hh" - -#ifndef DEFAULT_ALLOCATOR - -#include "bitops.hh" -#include "align.hh" -#include "posix.hh" -#include "shared_ptr.hh" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_NUMA -#include -#endif - -namespace memory { - -static constexpr unsigned cpu_id_shift = 36; // FIXME: make dynamic -static constexpr unsigned max_cpus = 256; -static constexpr size_t cache_line_size = 64; - -using pageidx = uint32_t; - -struct page; -class page_list; - -static std::atomic live_cpus[max_cpus]; - -static thread_local uint64_t g_allocs; -static thread_local uint64_t g_frees; -static thread_local uint64_t g_cross_cpu_frees; -static thread_local uint64_t g_reclaims; - -using std::experimental::optional; - -using allocate_system_memory_fn - = std::function where, size_t how_much)>; - -namespace bi = boost::intrusive; - -inline -unsigned object_cpu_id(const void* ptr) { - return (reinterpret_cast(ptr) >> cpu_id_shift) & 0xff; -} - -class page_list_link { - uint32_t _prev; - uint32_t _next; - friend class page_list; -}; - -static char* mem_base() { - static char* known; - static std::once_flag flag; - std::call_once(flag, [] { - size_t alloc = size_t(1) << 44; - auto r = ::mmap(NULL, 2 * alloc, - PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, - -1, 0); - if (r == MAP_FAILED) { - abort(); - } - ::madvise(r, 2 * alloc, MADV_DONTDUMP); - auto cr = reinterpret_cast(r); - known = align_up(cr, alloc); - ::munmap(cr, known - cr); - ::munmap(known + alloc, cr + 2 * alloc - (known + alloc)); - }); - return known; -} - -class small_pool; - -struct free_object { - free_object* next; -}; - -struct page { - bool free; - uint8_t offset_in_span; - uint16_t nr_small_alloc; - uint32_t span_size; // in pages, if we're the head or the tail - page_list_link link; - small_pool* pool; // if used in a small_pool - free_object* freelist; -}; - -class page_list { - uint32_t _front = 0; - uint32_t _back = 0; -public: - page& front(page* ary) { return ary[_front]; } - page& back(page* ary) { return ary[_back]; } - bool empty() const { return !_front; } - void erase(page* ary, page& span) { - if (span.link._next) { - ary[span.link._next].link._prev = span.link._prev; - } else { - _back = span.link._prev; - } - if (span.link._prev) { - ary[span.link._prev].link._next = span.link._next; - } else { - _front = span.link._next; - } - } - void push_front(page* ary, page& span) { - auto idx = &span - ary; - if (_front) { - ary[_front].link._prev = idx; - } else { - _back = idx; - } - span.link._next = _front; - span.link._prev = 0; - _front = idx; - } - void pop_front(page* ary) { - if (ary[_front].link._next) { - ary[ary[_front].link._next].link._prev = 0; - } else { - _back = 0; - } - _front = ary[_front].link._next; - } -}; - -class small_pool { - unsigned _object_size; - unsigned _span_size; - free_object* _free = nullptr; - size_t _free_count = 0; - unsigned _min_free; - unsigned _max_free; - page_list _span_list; - static constexpr unsigned idx_frac_bits = 2; -private: - size_t span_bytes() const { return _span_size * page_size; } -public: - explicit small_pool(unsigned object_size) noexcept; - ~small_pool(); - void* allocate(); - void deallocate(void* object); - unsigned object_size() const { return _object_size; } - static constexpr unsigned size_to_idx(unsigned size); - static constexpr unsigned idx_to_size(unsigned idx); -private: - void add_more_objects(); - void trim_free_list(); - float waste(); -}; - -// index 0b0001'1100 -> size (1 << 4) + 0b11 << (4 - 2) - -constexpr unsigned -small_pool::idx_to_size(unsigned idx) { - return (((1 << idx_frac_bits) | (idx & ((1 << idx_frac_bits) - 1))) - << (idx >> idx_frac_bits)) - >> idx_frac_bits; -} - -static constexpr unsigned log2(unsigned n) { - return std::numeric_limits::digits - count_leading_zeros(n - 1); -} - -constexpr unsigned -small_pool::size_to_idx(unsigned size) { - return ((log2(size) << idx_frac_bits) - ((1 << idx_frac_bits) - 1)) - + ((size - 1) >> (log2(size) - idx_frac_bits)); -} - -class small_pool_array { -public: - static constexpr unsigned nr_small_pools = small_pool::size_to_idx(4 * page_size) + 1; -private: - union u { - small_pool a[nr_small_pools]; - u() { - for (unsigned i = 0; i < nr_small_pools; ++i) { - new (&a[i]) small_pool(small_pool::idx_to_size(i)); - } - } - ~u() { - // cannot really call destructor, since other - // objects may be freed after we are gone. - } - } _u; -public: - small_pool& operator[](unsigned idx) { return _u.a[idx]; } -}; - -static constexpr size_t max_small_allocation - = small_pool::idx_to_size(small_pool_array::nr_small_pools - 1); - -struct cross_cpu_free_item { - cross_cpu_free_item* next; -}; - -struct cpu_pages { - static constexpr unsigned min_free_pages = 20000000 / page_size; - char* memory; - page* pages; - uint32_t nr_pages; - uint32_t nr_free_pages; - uint32_t current_min_free_pages = 0; - unsigned cpu_id = -1U; - std::function)> reclaim_hook; - std::vector reclaimers; - static constexpr unsigned nr_span_lists = 32; - union pla { - pla() { - for (auto&& e : free_spans) { - new (&e) page_list; - } - } - ~pla() { - // no destructor -- might be freeing after we die - } - page_list free_spans[nr_span_lists]; // contains spans with span_size >= 2^idx - } fsu; - small_pool_array small_pools; - alignas(cache_line_size) std::atomic xcpu_freelist; - alignas(cache_line_size) std::vector virt_to_phys_map; - static std::atomic cpu_id_gen; - static cpu_pages* all_cpus[max_cpus]; - char* mem() { return memory; } - - void link(page_list& list, page* span); - void unlink(page_list& list, page* span); - struct trim { - unsigned offset; - unsigned nr_pages; - }; - template - void* allocate_large_and_trim(unsigned nr_pages, Trimmer trimmer); - void* allocate_large(unsigned nr_pages); - void* allocate_large_aligned(unsigned align_pages, unsigned nr_pages); - void free_large(void* ptr); - void free_span(pageidx start, uint32_t nr_pages); - void free_span_no_merge(pageidx start, uint32_t nr_pages); - void* allocate_small(unsigned size); - void free(void* ptr); - void free(void* ptr, size_t size); - void free_cross_cpu(unsigned cpu_id, void* ptr); - bool drain_cross_cpu_freelist(); - size_t object_size(void* ptr); - page* to_page(void* p) { - return &pages[(reinterpret_cast(p) - mem()) / page_size]; - } - - bool initialize(); - void reclaim(); - void set_reclaim_hook(std::function)> hook); - void resize(size_t new_size, allocate_system_memory_fn alloc_sys_mem); - void do_resize(size_t new_size, allocate_system_memory_fn alloc_sys_mem); - void replace_memory_backing(allocate_system_memory_fn alloc_sys_mem); - void init_virt_to_phys_map(); - translation translate(const void* addr, size_t size); - ~cpu_pages(); -}; - -static thread_local cpu_pages cpu_mem; -std::atomic cpu_pages::cpu_id_gen; -cpu_pages* cpu_pages::all_cpus[max_cpus]; - -// Free spans are store in the largest index i such that nr_pages >= 1 << i. -static inline -unsigned index_of(unsigned pages) { - return std::numeric_limits::digits - count_leading_zeros(pages) - 1; -} - -// Smallest index i such that all spans stored in the index are >= pages. -static inline -unsigned index_of_conservative(unsigned pages) { - if (pages == 1) { - return 0; - } - return std::numeric_limits::digits - count_leading_zeros(pages - 1); -} - -void -cpu_pages::unlink(page_list& list, page* span) { - list.erase(pages, *span); -} - -void -cpu_pages::link(page_list& list, page* span) { - list.push_front(pages, *span); -} - -void cpu_pages::free_span_no_merge(uint32_t span_start, uint32_t nr_pages) { - assert(nr_pages); - nr_free_pages += nr_pages; - auto span = &pages[span_start]; - auto span_end = &pages[span_start + nr_pages - 1]; - span->free = span_end->free = true; - span->span_size = span_end->span_size = nr_pages; - auto idx = index_of(nr_pages); - link(fsu.free_spans[idx], span); -} - -void cpu_pages::free_span(uint32_t span_start, uint32_t nr_pages) { - page* before = &pages[span_start - 1]; - if (before->free) { - auto b_size = before->span_size; - assert(b_size); - span_start -= b_size; - nr_pages += b_size; - nr_free_pages -= b_size; - unlink(fsu.free_spans[index_of(b_size)], before - (b_size - 1)); - } - page* after = &pages[span_start + nr_pages]; - if (after->free) { - auto a_size = after->span_size; - assert(a_size); - nr_pages += a_size; - nr_free_pages -= a_size; - unlink(fsu.free_spans[index_of(a_size)], after); - } - free_span_no_merge(span_start, nr_pages); -} - -template -void* -cpu_pages::allocate_large_and_trim(unsigned n_pages, Trimmer trimmer) { - auto idx = index_of_conservative(n_pages); - assert(n_pages < (2u << idx)); - while (idx < nr_span_lists && fsu.free_spans[idx].empty()) { - ++idx; - } - if (idx == nr_span_lists) { - if (initialize()) { - return allocate_large_and_trim(n_pages, trimmer); - } - // FIXME: request application to free memory - throw std::bad_alloc(); - } - auto& list = fsu.free_spans[idx]; - page* span = &list.front(pages); - auto span_size = span->span_size; - auto span_idx = span - pages; - unlink(list, span); - nr_free_pages -= span->span_size; - trim t = trimmer(span_idx, nr_pages); - if (t.offset) { - free_span_no_merge(span_idx, t.offset); - span_idx += t.offset; - span_size -= t.offset; - span = &pages[span_idx]; - - } - if (t.nr_pages < span_size) { - free_span_no_merge(span_idx + t.nr_pages, span_size - t.nr_pages); - span_size = t.nr_pages; - } - auto span_end = &pages[span_idx + t.nr_pages - 1]; - span->free = span_end->free = false; - span->span_size = span_end->span_size = t.nr_pages; - span->pool = nullptr; - if (nr_free_pages < current_min_free_pages) { - drain_cross_cpu_freelist(); - reclaim(); - } - return mem() + span_idx * page_size; -} - -void* -cpu_pages::allocate_large(unsigned n_pages) { - return allocate_large_and_trim(n_pages, [n_pages] (unsigned idx, unsigned n) { - return trim{0, std::min(n, n_pages)}; - }); -} - -void* -cpu_pages::allocate_large_aligned(unsigned align_pages, unsigned n_pages) { - return allocate_large_and_trim(n_pages + align_pages - 1, [=] (unsigned idx, unsigned n) { - return trim{align_up(idx, align_pages) - idx, n_pages}; - }); -} - -void* -cpu_pages::allocate_small(unsigned size) { - auto idx = small_pool::size_to_idx(size); - auto& pool = small_pools[idx]; - assert(size <= pool.object_size()); - return pool.allocate(); -} - -void cpu_pages::free_large(void* ptr) { - pageidx idx = (reinterpret_cast(ptr) - mem()) / page_size; - page* span = &pages[idx]; - free_span(idx, span->span_size); -} - -size_t cpu_pages::object_size(void* ptr) { - pageidx idx = (reinterpret_cast(ptr) - mem()) / page_size; - page* span = &pages[idx]; - if (span->pool) { - return span->pool->object_size(); - } else { - return size_t(span->span_size) * page_size; - } -} - -void cpu_pages::free_cross_cpu(unsigned cpu_id, void* ptr) { - if (!live_cpus[cpu_id].load(std::memory_order_relaxed)) { - // Thread was destroyed; leak object - // should only happen for boost unit-tests. - return; - } - auto p = reinterpret_cast(ptr); - auto& list = all_cpus[cpu_id]->xcpu_freelist; - auto old = list.load(std::memory_order_relaxed); - do { - p->next = old; - } while (!list.compare_exchange_weak(old, p, std::memory_order_release, std::memory_order_relaxed)); - ++g_cross_cpu_frees; -} - -bool cpu_pages::drain_cross_cpu_freelist() { - if (!xcpu_freelist.load(std::memory_order_relaxed)) { - return false; - } - auto p = xcpu_freelist.exchange(nullptr, std::memory_order_acquire); - while (p) { - auto n = p->next; - free(p); - p = n; - } - return true; -} - -void cpu_pages::free(void* ptr) { - auto obj_cpu = object_cpu_id(ptr); - if (obj_cpu != cpu_id) { - return free_cross_cpu(obj_cpu, ptr); - } - page* span = to_page(ptr); - if (span->pool) { - span->pool->deallocate(ptr); - } else { - free_large(ptr); - } -} - -void cpu_pages::free(void* ptr, size_t size) { - auto obj_cpu = object_cpu_id(ptr); - if (obj_cpu != cpu_id) { - return free_cross_cpu(obj_cpu, ptr); - } - if (size <= max_small_allocation) { - auto pool = &small_pools[small_pool::size_to_idx(size)]; - pool->deallocate(ptr); - } else { - free_large(ptr); - } -} - -cpu_pages::~cpu_pages() { - live_cpus[cpu_id].store(false, std::memory_order_relaxed); -} - -bool cpu_pages::initialize() { - if (nr_pages) { - return false; - } - cpu_id = cpu_id_gen.fetch_add(1, std::memory_order_relaxed); - assert(cpu_id < max_cpus); - all_cpus[cpu_id] = this; - auto base = mem_base() + (size_t(cpu_id) << cpu_id_shift); - auto size = 32 << 20; // Small size for bootstrap - auto r = ::mmap(base, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, - -1, 0); - if (r == MAP_FAILED) { - abort(); - } - ::madvise(base, size, MADV_HUGEPAGE); - pages = reinterpret_cast(base); - memory = base; - nr_pages = size / page_size; - // we reserve the end page so we don't have to special case - // the last span. - auto reserved = align_up(sizeof(page) * (nr_pages + 1), page_size) / page_size; - for (pageidx i = 0; i < reserved; ++i) { - pages[i].free = false; - } - pages[nr_pages].free = false; - free_span_no_merge(reserved, nr_pages - reserved); - live_cpus[cpu_id].store(true, std::memory_order_relaxed); - return true; -} - -mmap_area -allocate_anonymous_memory(optional where, size_t how_much) { - return mmap_anonymous(where.value_or(nullptr), - how_much, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | (where ? MAP_FIXED : 0)); -} - -mmap_area -allocate_hugetlbfs_memory(file_desc& fd, optional where, size_t how_much) { - auto pos = fd.size(); - fd.truncate(pos + how_much); - auto ret = fd.map( - how_much, - PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_POPULATE | (where ? MAP_FIXED : 0), - pos, - where.value_or(nullptr)); - return ret; -} - -void cpu_pages::replace_memory_backing(allocate_system_memory_fn alloc_sys_mem) { - // We would like to use ::mremap() to atomically replace the old anonymous - // memory with hugetlbfs backed memory, but mremap() does not support hugetlbfs - // (for no reason at all). So we must copy the anonymous memory to some other - // place, map hugetlbfs in place, and copy it back, without modifying it during - // the operation. - auto bytes = nr_pages * page_size; - auto old_mem = mem(); - auto relocated_old_mem = mmap_anonymous(nullptr, bytes, PROT_READ|PROT_WRITE, MAP_PRIVATE); - std::memcpy(relocated_old_mem.get(), old_mem, bytes); - alloc_sys_mem({old_mem}, bytes).release(); - std::memcpy(old_mem, relocated_old_mem.get(), bytes); -} - -void cpu_pages::init_virt_to_phys_map() { - auto nr_entries = nr_pages / (huge_page_size / page_size); - virt_to_phys_map.resize(nr_entries); - auto fd = file_desc::open("/proc/self/pagemap", O_RDONLY | O_CLOEXEC); - for (size_t i = 0; i != nr_entries; ++i) { - uint64_t entry = 0; - auto phys = std::numeric_limits::max(); - auto pfn = reinterpret_cast(mem() + i * huge_page_size) / page_size; - fd.pread(&entry, 8, pfn * 8); - assert(entry & 0x8000'0000'0000'0000); - phys = (entry & 0x007f'ffff'ffff'ffff) << page_bits; - virt_to_phys_map[i] = phys; - } -} - -translation -cpu_pages::translate(const void* addr, size_t size) { - auto a = reinterpret_cast(addr) - reinterpret_cast(mem()); - auto pfn = a / huge_page_size; - if (pfn >= virt_to_phys_map.size()) { - return {}; - } - auto phys = virt_to_phys_map[pfn]; - if (phys == std::numeric_limits::max()) { - return {}; - } - auto translation_size = align_up(a + 1, huge_page_size) - a; - size = std::min(size, translation_size); - phys += a & (huge_page_size - 1); - return translation{phys, size}; -} - -void cpu_pages::do_resize(size_t new_size, allocate_system_memory_fn alloc_sys_mem) { - auto new_pages = new_size / page_size; - if (new_pages <= nr_pages) { - return; - } - auto old_size = nr_pages * page_size; - auto mmap_start = memory + old_size; - auto mmap_size = new_size - old_size; - auto mem = alloc_sys_mem({mmap_start}, mmap_size); - mem.release(); - ::madvise(mmap_start, mmap_size, MADV_HUGEPAGE); - // one past last page structure is a sentinel - auto new_page_array_pages = align_up(sizeof(page[new_pages + 1]), page_size) / page_size; - auto new_page_array - = reinterpret_cast(allocate_large(new_page_array_pages)); - std::copy(pages, pages + nr_pages, new_page_array); - // mark new one-past-last page as taken to avoid boundary conditions - new_page_array[new_pages].free = false; - auto old_pages = reinterpret_cast(pages); - auto old_nr_pages = nr_pages; - auto old_pages_size = align_up(sizeof(page[nr_pages + 1]), page_size); - pages = new_page_array; - nr_pages = new_pages; - auto old_pages_start = (old_pages - memory) / page_size; - if (old_pages_start == 0) { - // keep page 0 allocated - old_pages_start = 1; - old_pages_size -= page_size; - } - free_span(old_pages_start, old_pages_size / page_size); - free_span(old_nr_pages, new_pages - old_nr_pages); -} - -void cpu_pages::resize(size_t new_size, allocate_system_memory_fn alloc_memory) { - new_size = align_down(new_size, huge_page_size); - while (nr_pages * page_size < new_size) { - // don't reallocate all at once, since there might not - // be enough free memory available to relocate the pages array - auto tmp_size = std::min(new_size, 4 * nr_pages * page_size); - do_resize(tmp_size, alloc_memory); - } -} - -void cpu_pages::reclaim() { - current_min_free_pages = 0; - reclaim_hook([this] { - ++g_reclaims; - for (auto&& r : reclaimers) { - r->do_reclaim(); - } - current_min_free_pages = min_free_pages; - }); -} - -void cpu_pages::set_reclaim_hook(std::function)> hook) { - reclaim_hook = hook; - current_min_free_pages = min_free_pages; -} - -small_pool::small_pool(unsigned object_size) noexcept - : _object_size(object_size), _span_size(1) { - while (_object_size > span_bytes() - || (_span_size < 32 && waste() > 0.05) - || (span_bytes() / object_size < 32)) { - _span_size *= 2; - } - _max_free = std::max(100, span_bytes() * 2 / _object_size); - _min_free = _max_free / 2; -} - -small_pool::~small_pool() { - _min_free = _max_free = 0; - trim_free_list(); -} - -void* -small_pool::allocate() { - if (!_free) { - add_more_objects(); - } - auto* obj = _free; - _free = _free->next; - --_free_count; - return obj; -} - -void -small_pool::deallocate(void* object) { - auto o = reinterpret_cast(object); - o->next = _free; - _free = o; - ++_free_count; - if (_free_count >= _max_free) { - trim_free_list(); - } -} - -void -small_pool::add_more_objects() { - auto goal = (_min_free + _max_free) / 2; - while (!_span_list.empty() && _free_count < goal) { - page& span = _span_list.front(cpu_mem.pages); - _span_list.pop_front(cpu_mem.pages); - while (span.freelist) { - auto obj = span.freelist; - span.freelist = span.freelist->next; - obj->next = _free; - _free = obj; - ++_free_count; - ++span.nr_small_alloc; - } - } - while (_free_count < goal) { - auto data = reinterpret_cast(cpu_mem.allocate_large(_span_size)); - auto span = cpu_mem.to_page(data); - for (unsigned i = 0; i < _span_size; ++i) { - span[i].offset_in_span = i; - span[i].pool = this; - } - span->nr_small_alloc = 0; - span->freelist = nullptr; - for (unsigned offset = 0; offset <= span_bytes() - _object_size; offset += _object_size) { - auto h = reinterpret_cast(data + offset); - h->next = _free; - _free = h; - ++_free_count; - ++span->nr_small_alloc; - } - } -} - -void -small_pool::trim_free_list() { - auto goal = (_min_free + _max_free) / 2; - while (_free && _free_count > goal) { - auto obj = _free; - _free = _free->next; - --_free_count; - page* span = cpu_mem.to_page(obj); - span -= span->offset_in_span; - if (!span->freelist) { - new (&span->link) page_list_link(); - _span_list.push_front(cpu_mem.pages, *span); - } - obj->next = span->freelist; - span->freelist = obj; - if (--span->nr_small_alloc == 0) { - _span_list.erase(cpu_mem.pages, *span); - cpu_mem.free_span(span - cpu_mem.pages, span->span_size); - } - } -} - -float small_pool::waste() { - return (span_bytes() % _object_size) / (1.0 * span_bytes()); -} - -void* allocate_large(size_t size) { - unsigned size_in_pages = (size + page_size - 1) >> page_bits; - assert((size_t(size_in_pages) << page_bits) >= size); - return cpu_mem.allocate_large(size_in_pages); - -} - -void* allocate_large_aligned(size_t align, size_t size) { - unsigned size_in_pages = (size + page_size - 1) >> page_bits; - unsigned align_in_pages = std::max(align, page_size) >> page_bits; - return cpu_mem.allocate_large_aligned(align_in_pages, size_in_pages); -} - -void free_large(void* ptr) { - return cpu_mem.free_large(ptr); -} - -size_t object_size(void* ptr) { - return cpu_pages::all_cpus[object_cpu_id(ptr)]->object_size(ptr); -} - -void* allocate(size_t size) { - ++g_allocs; - if (size <= sizeof(free_object)) { - size = sizeof(free_object); - } - if (size <= max_small_allocation) { - return cpu_mem.allocate_small(size); - } else { - return allocate_large(size); - } -} - -void* allocate_aligned(size_t align, size_t size) { - ++g_allocs; - size = std::max(size, align); - if (size <= sizeof(free_object)) { - size = sizeof(free_object); - } - if (size <= max_small_allocation && align <= page_size) { - // Our small allocator only guarantees alignment for power-of-two - // allocations which are not larger than a page. - size = 1 << log2(size); - return cpu_mem.allocate_small(size); - } else { - return allocate_large_aligned(align, size); - } -} - -void free(void* obj) { - ++g_frees; - cpu_mem.free(obj); -} - -void free(void* obj, size_t size) { - ++g_frees; - cpu_mem.free(obj, size); -} - -void set_reclaim_hook(std::function)> hook) { - cpu_mem.set_reclaim_hook(hook); -} - -reclaimer::reclaimer(std::function reclaim) - : _reclaim(std::move(reclaim)) { - cpu_mem.reclaimers.push_back(this); -} - -reclaimer::~reclaimer() { - auto& r = cpu_mem.reclaimers; - r.erase(std::find(r.begin(), r.end(), this)); -} - -void configure(std::vector m, - optional hugetlbfs_path) { - size_t total = 0; - for (auto&& x : m) { - total += x.bytes; - } - allocate_system_memory_fn sys_alloc = allocate_anonymous_memory; - if (hugetlbfs_path) { - // std::function is copyable, but file_desc is not, so we must use - // a shared_ptr to allow sys_alloc to be copied around - auto fdp = make_lw_shared(file_desc::temporary(*hugetlbfs_path)); - sys_alloc = [fdp] (optional where, size_t how_much) { - return allocate_hugetlbfs_memory(*fdp, where, how_much); - }; - cpu_mem.replace_memory_backing(sys_alloc); - } - cpu_mem.resize(total, sys_alloc); - size_t pos = 0; - for (auto&& x : m) { -#ifdef HAVE_NUMA - unsigned long nodemask = 1UL << x.nodeid; - auto r = ::mbind(cpu_mem.mem() + pos, x.bytes, - MPOL_BIND, - &nodemask, std::numeric_limits::digits, - MPOL_MF_MOVE); - assert(r == 0); -#endif - pos += x.bytes; - } - if (hugetlbfs_path) { - cpu_mem.init_virt_to_phys_map(); - } -} - -statistics stats() { - return statistics{g_allocs, g_frees, g_cross_cpu_frees, - cpu_mem.nr_free_pages * page_size, g_reclaims}; -} - -bool drain_cross_cpu_freelist() { - return cpu_mem.drain_cross_cpu_freelist(); -} - -translation -translate(const void* addr, size_t size) { - auto cpu_id = object_cpu_id(addr); - if (cpu_id >= max_cpus) { - return {}; - } - auto cp = cpu_pages::all_cpus[cpu_id]; - if (!cp) { - return {}; - } - return cp->translate(addr, size); -} - -} - -using namespace memory; - -extern "C" -[[gnu::visibility("default")]] -[[gnu::externally_visible]] -void* malloc(size_t n) throw () { - if (n == 0) { - return nullptr; - } - try { - return allocate(n); - } catch (std::bad_alloc& ba) { - return nullptr; - } -} - -extern "C" -[[gnu::alias("malloc")]] -[[gnu::visibility("default")]] -void* __libc_malloc(size_t n) throw (); - -extern "C" -[[gnu::visibility("default")]] -[[gnu::externally_visible]] -void free(void* ptr) { - if (ptr) { - memory::free(ptr); - } -} - -extern "C" -[[gnu::alias("free")]] -[[gnu::visibility("default")]] -void* __libc_free(void* obj) throw (); - -extern "C" -[[gnu::visibility("default")]] -void* calloc(size_t nmemb, size_t size) { - auto s1 = __int128(nmemb) * __int128(size); - assert(s1 == size_t(s1)); - size_t s = s1; - auto p = malloc(s); - if (p) { - std::memset(p, 0, s); - } - return p; -} - -extern "C" -[[gnu::alias("calloc")]] -[[gnu::visibility("default")]] -void* __libc_calloc(size_t n, size_t m) throw (); - -extern "C" -[[gnu::visibility("default")]] -void* realloc(void* ptr, size_t size) { - auto old_size = ptr ? object_size(ptr) : 0; - auto nptr = malloc(size); - if (!nptr) { - return nptr; - } - if (ptr) { - std::memcpy(nptr, ptr, std::min(size, old_size)); - ::free(ptr); - } - return nptr; -} - -extern "C" -[[gnu::alias("realloc")]] -[[gnu::visibility("default")]] -void* __libc_realloc(void* obj, size_t size) throw (); - -extern "C" -[[gnu::visibility("default")]] -[[gnu::externally_visible]] -int posix_memalign(void** ptr, size_t align, size_t size) { - try { - *ptr = allocate_aligned(align, size); - return 0; - } catch (std::bad_alloc&) { - return ENOMEM; - } -} - -extern "C" -[[gnu::alias("posix_memalign")]] -[[gnu::visibility("default")]] -int __libc_posix_memalign(void** ptr, size_t align, size_t size) throw (); - -extern "C" -[[gnu::visibility("default")]] -void* memalign(size_t align, size_t size) { - try { - return allocate_aligned(align, size); - } catch (std::bad_alloc&) { - return NULL; - } -} - -extern "C" -[[gnu::visibility("default")]] -void *aligned_alloc(size_t align, size_t size) { - try { - return allocate_aligned(align, size); - } catch (std::bad_alloc&) { - return NULL; - } -} - -extern "C" -[[gnu::alias("memalign")]] -[[gnu::visibility("default")]] -void* __libc_memalign(size_t align, size_t size); - -extern "C" -[[gnu::visibility("default")]] -void cfree(void* obj) { - return ::free(obj); -} - -extern "C" -[[gnu::alias("cfree")]] -[[gnu::visibility("default")]] -void __libc_cfree(void* obj); - -extern "C" -[[gnu::visibility("default")]] -size_t malloc_usable_size(void* obj) { - return object_size(obj); -} - -extern "C" -[[gnu::visibility("default")]] -int malloc_trim(size_t pad) { - return 0; -} - -[[gnu::visibility("default")]] -void* operator new(size_t size) { - if (size == 0) { - size = 1; - } - return allocate(size); -} - -[[gnu::visibility("default")]] -void* operator new[](size_t size) { - if (size == 0) { - size = 1; - } - return allocate(size); -} - -[[gnu::visibility("default")]] -void operator delete(void* ptr) throw () { - if (ptr) { - memory::free(ptr); - } -} - -[[gnu::visibility("default")]] -void operator delete[](void* ptr) throw () { - if (ptr) { - memory::free(ptr); - } -} - -[[gnu::visibility("default")]] -void operator delete(void* ptr, size_t size) throw () { - if (ptr) { - memory::free(ptr, size); - } -} - -[[gnu::visibility("default")]] -void operator delete[](void* ptr, size_t size) throw () { - if (ptr) { - memory::free(ptr, size); - } -} - -[[gnu::visibility("default")]] -void* operator new(size_t size, std::nothrow_t) throw () { - if (size == 0) { - size = 1; - } - try { - return allocate(size); - } catch (...) { - return nullptr; - } -} - -[[gnu::visibility("default")]] -void* operator new[](size_t size, std::nothrow_t) throw () { - if (size == 0) { - size = 1; - } - try { - return allocate(size); - } catch (...) { - return nullptr; - } -} - -[[gnu::visibility("default")]] -void operator delete(void* ptr, std::nothrow_t) throw () { - if (ptr) { - memory::free(ptr); - } -} - -[[gnu::visibility("default")]] -void operator delete[](void* ptr, std::nothrow_t) throw () { - if (ptr) { - memory::free(ptr); - } -} - -[[gnu::visibility("default")]] -void operator delete(void* ptr, size_t size, std::nothrow_t) throw () { - if (ptr) { - memory::free(ptr, size); - } -} - -[[gnu::visibility("default")]] -void operator delete[](void* ptr, size_t size, std::nothrow_t) throw () { - if (ptr) { - memory::free(ptr, size); - } -} - -void* operator new(size_t size, with_alignment wa) { - return allocate_aligned(wa.alignment(), size); -} - -void* operator new[](size_t size, with_alignment wa) { - return allocate_aligned(wa.alignment(), size); -} - -void operator delete(void* ptr, with_alignment wa) { - // only called for matching operator new, so we know ptr != nullptr - return memory::free(ptr); -} - -void operator delete[](void* ptr, with_alignment wa) { - // only called for matching operator new, so we know ptr != nullptr - return memory::free(ptr); -} - -#else - -namespace memory { - -reclaimer::reclaimer(std::function reclaim) { -} - -reclaimer::~reclaimer() { -} - -void set_reclaim_hook(std::function)> hook) { -} - -void configure(std::vector m, std::experimental::optional hugepages_path) { -} - -statistics stats() { - return statistics{0, 0, 0, 0, 0}; -} - -bool drain_cross_cpu_freelist() { - return false; -} - -translation -translate(const void* addr, size_t size) { - return {}; -} - -} - -void* operator new(size_t size, with_alignment wa) { - void* ret; - if (posix_memalign(&ret, wa.alignment(), size) != 0) { - throw std::bad_alloc(); - } - return ret; -} - -void* operator new[](size_t size, with_alignment wa) { - void* ret; - if (posix_memalign(&ret, wa.alignment(), size) != 0) { - throw std::bad_alloc(); - } - return ret; -} - -void operator delete(void* ptr, with_alignment wa) { - return ::free(ptr); -} - -void operator delete[](void* ptr, with_alignment wa) { - return ::free(ptr); -} - -#endif - -/// \endcond diff --git a/core/memory.hh b/core/memory.hh deleted file mode 100644 index 7e92acfef5..0000000000 --- a/core/memory.hh +++ /dev/null @@ -1,175 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef MEMORY_HH_ -#define MEMORY_HH_ - -#include "resource.hh" -#include -#include -#include - - -/// \defgroup memory-module Memory management -/// -/// Functions and classes for managing memory. -/// -/// Memory management in seastar consists of the following: -/// -/// - Low-level memory management in the \ref memory namespace. -/// - Various smart pointers: \ref shared_ptr, \ref lw_shared_ptr, -/// and \ref foreign_ptr. -/// - zero-copy support: \ref temporary_buffer and \ref deleter. - -/// Low-level memory management support -/// -/// The \c memory namespace provides functions and classes for interfacing -/// with the seastar memory allocator. -/// -/// The seastar memory allocator splits system memory into a pool per -/// logical core (lcore). Memory allocated one an lcore should be freed -/// on the same lcore; failing to do so carries a severe performance -/// penalty. It is possible to share memory with another core, but this -/// should be limited to avoid cache coherency traffic. -namespace memory { - -/// \cond internal -// TODO: Use getpagesize() in order to learn a size of a system PAGE. -static constexpr size_t page_bits = 12; -static constexpr size_t page_size = 1 << page_bits; // 4K -static constexpr size_t huge_page_size = 512 * page_size; // 2M - -void configure(std::vector m, - std::experimental::optional hugetlbfs_path = {}); - -void* allocate_reclaimable(size_t size); - -class reclaimer { - std::function _reclaim; -public: - reclaimer(std::function reclaim); - ~reclaimer(); - void do_reclaim() { _reclaim(); } -}; - -// Call periodically to recycle objects that were freed -// on cpu other than the one they were allocated on. -// -// Returns @true if any work was actually performed. -bool drain_cross_cpu_freelist(); - - -// We don't want the memory code calling back into the rest of -// the system, so allow the rest of the system to tell the memory -// code how to initiate reclaim. -// -// When memory is low, calling \c hook(fn) will result in fn being called -// in a safe place wrt. allocations. -void set_reclaim_hook( - std::function)> hook); - -using physical_address = uint64_t; - -struct translation { - translation() = default; - translation(physical_address a, size_t s) : addr(a), size(s) {} - physical_address addr = 0; - size_t size = 0; -}; - -// Translate a virtual address range to a physical range. -// -// Can return a smaller range (in which case the reminder needs -// to be translated again), or a zero sized range in case the -// translation is not known. -translation translate(const void* addr, size_t size); - -/// \endcond - -class statistics; - -/// Capture a snapshot of memory allocation statistics for this lcore. -statistics stats(); - -/// Memory allocation statistics. -class statistics { - uint64_t _mallocs; - uint64_t _frees; - uint64_t _cross_cpu_frees; - size_t _free_memory; - uint64_t _reclaims; -private: - statistics(uint64_t mallocs, uint64_t frees, uint64_t cross_cpu_frees, - uint64_t free_memory, uint64_t reclaims) - : _mallocs(mallocs), _frees(frees), _cross_cpu_frees(cross_cpu_frees) - , _free_memory(free_memory), _reclaims(reclaims) {} -public: - /// Total number of memory allocations calls since the system was started. - uint64_t mallocs() const { return _mallocs; } - /// Total number of memory deallocations calls since the system was started. - uint64_t frees() const { return _frees; } - /// Total number of memory deallocations that occured on a different lcore - /// than the one on which they were allocated. - uint64_t cross_cpu_frees() const { return _cross_cpu_frees; } - /// Total number of objects which were allocated but not freed. - size_t live_objects() const { return mallocs() - frees(); } - /// Total free memory (in bytes) - size_t free_memory() const { return _free_memory; } - /// Number of reclaims performed due to low memory - uint64_t reclaims() const { return _reclaims; } - friend statistics stats(); -}; - - -} - -class with_alignment { - size_t _align; -public: - with_alignment(size_t align) : _align(align) {} - size_t alignment() const { return _align; } -}; - -void* operator new(size_t size, with_alignment wa); -void* operator new[](size_t size, with_alignment wa); -void operator delete(void* ptr, with_alignment wa); -void operator delete[](void* ptr, with_alignment wa); - -template -class aligned_allocator { -public: - using value_type = T; - T* allocate(size_t n) const { - // FIXME: multiply can overflow? - return reinterpret_cast(::operator new(n * sizeof(T), with_alignment(Align))); - } - void deallocate(T* ptr) const { - return ::operator delete(ptr, with_alignment(Align)); - } - bool operator==(const aligned_allocator& x) const { - return true; - } - bool operator!=(const aligned_allocator& x) const { - return false; - } -}; - -#endif /* MEMORY_HH_ */ diff --git a/core/pipe.hh b/core/pipe.hh deleted file mode 100644 index 1c1c76c8a9..0000000000 --- a/core/pipe.hh +++ /dev/null @@ -1,239 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include "future.hh" -#include "queue.hh" - -#include - -/// \defgroup fiber-module Fibers -/// -/// \brief Fibers of execution -/// -/// Seastar continuations are normally short, but often chained to one -/// another, so that one continuation does a bit of work and then schedules -/// another continuation for later. Such chains can be long, and often even -/// involve loopings - see for example \ref repeat. We call such chains -/// "fibers" of execution. -/// -/// These fibers are not threads - each is just a string of continuations - -/// but they share some common requirements with traditional threads. -/// For example, we want to avoid one fiber getting starved while a second -/// fiber continuously runs its continuations one after another. -/// As another example, fibers may want to communicate - e.g., one fiber -/// produces data that a second fiber consumes, and we wish to ensure that -/// both fibers get a chance to run, and that if one stops prematurely, -/// the other doesn't hang forever. -/// -/// Consult the following table to see which APIs are useful for fiber tasks: -/// -/// Task | APIs -/// -----------------------------------------------|------------------- -/// Repeat a blocking task indefinitely | \ref keep_doing() -/// Repeat a blocking task, then exit | \ref repeat(), \ref do_until() -/// Provide mutual exclusion between two tasks | \ref semaphore, \ref shared_mutex -/// Pass a stream of data between two fibers | \ref seastar::pipe -/// Safely shut down a resource | \ref seastar::gate, \ref seastar::with_gate() -/// Hold on to an object while a fiber is running | \ref do_with() -/// - -/// Seastar API namespace -namespace seastar { - -/// \addtogroup fiber-module -/// @{ - -class broken_pipe_exception : public std::exception { -public: - virtual const char* what() const noexcept { - return "Broken pipe"; - } -}; - -/// \cond internal -namespace internal { -template -class pipe_buffer { -private: - queue> _buf; - bool _read_open = true; - bool _write_open = true; -public: - pipe_buffer(size_t size) : _buf(size) {} - future> read() { - return _buf.pop_eventually(); - } - future<> write(T&& data) { - return _buf.push_eventually(std::move(data)); - } - bool readable() const { - return _write_open || !_buf.empty(); - } - bool writeable() const { - return _read_open; - } - bool close_read() { - // If a writer blocking (on a full queue), need to stop it. - if (_buf.full()) { - _buf.abort(std::make_exception_ptr(broken_pipe_exception())); - } - _read_open = false; - return !_write_open; - } - bool close_write() { - // If the queue is empty, write the EOF (disengaged optional) to the - // queue to wake a blocked reader. If the queue is not empty, there is - // no need to write the EOF to the queue - the reader will return an - // EOF when it sees that _write_open == false. - if (_buf.empty()) { - _buf.push({}); - } - _write_open = false; - return !_read_open; - } -}; -} // namespace internal -/// \endcond - -template -class pipe; - -/// \brief Read side of a \ref seastar::pipe -/// -/// The read side of a pipe, which allows only reading from the pipe. -/// A pipe_reader object cannot be created separately, but only as part of a -/// reader/writer pair through \ref seastar::pipe. -template -class pipe_reader { -private: - internal::pipe_buffer *_bufp; - pipe_reader(internal::pipe_buffer *bufp) : _bufp(bufp) { } - friend class pipe; -public: - /// \brief Read next item from the pipe - /// - /// Returns a future value, which is fulfilled when the pipe's buffer - /// becomes non-empty, or the write side is closed. The value returned - /// is an optional, which is disengaged to mark and end of file - /// (i.e., the write side was closed, and we've read everything it sent). - future> read() { - if (_bufp->readable()) { - return _bufp->read(); - } else { - return make_ready_future>(); - } - } - ~pipe_reader() { - if (_bufp && _bufp->close_read()) { - delete _bufp; - } - } - // Allow move, but not copy, of pipe_reader - pipe_reader(pipe_reader&& other) : _bufp(other._bufp) { - other._bufp = nullptr; - } - pipe_reader& operator=(pipe_reader&& other) { - std::swap(_bufp, other._bufp); - } -}; - -/// \brief Write side of a \ref seastar::pipe -/// -/// The write side of a pipe, which allows only writing to the pipe. -/// A pipe_writer object cannot be created separately, but only as part of a -/// reader/writer pair through \ref seastar::pipe. -template -class pipe_writer { -private: - internal::pipe_buffer *_bufp; - pipe_writer(internal::pipe_buffer *bufp) : _bufp(bufp) { } - friend class pipe; -public: - /// \brief Write an item to the pipe - /// - /// Returns a future value, which is fulfilled when the data was written - /// to the buffer (when it become non-full). If the data could not be - /// written because the read side was closed, an exception - /// \ref broken_pipe_exception is returned in the future. - future<> write(T&& data) { - if (_bufp->writeable()) { - return _bufp->write(std::move(data)); - } else { - return make_exception_future<>(broken_pipe_exception()); - } - } - ~pipe_writer() { - if (_bufp && _bufp->close_write()) { - delete _bufp; - } - } - // Allow move, but not copy, of pipe_writer - pipe_writer(pipe_writer&& other) : _bufp(other._bufp) { - other._bufp = nullptr; - } - pipe_writer& operator=(pipe_writer&& other) { - std::swap(_bufp, other._bufp); - } -}; - -/// \brief A fixed-size pipe for communicating between two fibers. -/// -/// A pipe is a mechanism to transfer data between two fibers, one -/// producing data, and the other consuming it. The fixed-size buffer also -/// ensures a balanced execution of the two fibers, because the producer -/// fiber blocks when it writes to a full pipe, until the consumer fiber gets -/// to run and read from the pipe. -/// -/// A pipe resembles a Unix pipe, in that it has a read side, a write side, -/// and a fixed-sized buffer between them, and supports either end to be closed -/// independently (and EOF or broken pipe when using the other side). -/// A pipe object holds the reader and write sides of the pipe as two -/// separate objects. These objects can be moved into two different fibers. -/// Importantly, if one of the pipe ends is destroyed (i.e., the continuations -/// capturing it end), the other end of the pipe will stop blocking, so the -/// other fiber will not hang. -/// -/// The pipe's read and write interfaces are future-based blocking. I.e., the -/// write() and read() methods return a future which is fulfilled when the -/// operation is complete. The pipe is single-reader single-writer, meaning -/// that until the future returned by read() is fulfilled, read() must not be -/// called again (and same for write). -/// -/// Note: The pipe reader and writer are movable, but *not* copyable. It is -/// often convenient to wrap each end in a shared pointer, so it can be -/// copied (e.g., used in an std::function which needs to be copyable) or -/// easily captured into multiple continuations. -template -class pipe { -public: - pipe_reader reader; - pipe_writer writer; - explicit pipe(size_t size) : pipe(new internal::pipe_buffer(size)) { } -private: - pipe(internal::pipe_buffer *bufp) : reader(bufp), writer(bufp) { } -}; - - -/// @} - -} // namespace seastar diff --git a/core/posix.cc b/core/posix.cc deleted file mode 100644 index 536a8b7b68..0000000000 --- a/core/posix.cc +++ /dev/null @@ -1,116 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "posix.hh" -#include "align.hh" -#include - -file_desc -file_desc::temporary(sstring directory) { - // FIXME: add O_TMPFILE support one day - directory += "/XXXXXX"; - std::vector templat(directory.c_str(), directory.c_str() + directory.size() + 1); - int fd = ::mkstemp(templat.data()); - throw_system_error_on(fd == -1); - int r = ::unlink(templat.data()); - throw_system_error_on(r == -1); // leaks created file, but what can we do? - return file_desc(fd); -} - -void mmap_deleter::operator()(void* ptr) const { - ::munmap(ptr, _size); -} - -mmap_area mmap_anonymous(void* addr, size_t length, int prot, int flags) { - auto ret = ::mmap(addr, length, prot, flags | MAP_ANONYMOUS, -1, 0); - throw_system_error_on(ret == MAP_FAILED); - return mmap_area(reinterpret_cast(ret), mmap_deleter{length}); -} - -void* posix_thread::start_routine(void* arg) { - auto pfunc = reinterpret_cast*>(arg); - try { - (*pfunc)(); - } catch (...) { - // We're in POSIX thread callback, so all exceptions need to be handled. - std::terminate(); - } - return nullptr; -} - -posix_thread::posix_thread(std::function func) - : posix_thread(attr{}, std::move(func)) { -} - -posix_thread::posix_thread(attr a, std::function func) - : _func(std::make_unique>(std::move(func))) { - pthread_attr_t pa; - auto r = pthread_attr_init(&pa); - if (r) { - throw std::system_error(r, std::system_category()); - } - auto stack_size = a._stack_size.size; - if (!stack_size) { - stack_size = 2 << 20; - } - // allocate guard area as well - _stack = mmap_anonymous(nullptr, stack_size + (4 << 20), - PROT_NONE, MAP_PRIVATE | MAP_NORESERVE); - auto stack_start = align_up(_stack.get() + 1, 2 << 20); - mmap_area real_stack = mmap_anonymous(stack_start, stack_size, - PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_STACK); - real_stack.release(); // protected by @_stack - ::madvise(stack_start, stack_size, MADV_HUGEPAGE); - r = pthread_attr_setstack(&pa, stack_start, stack_size); - if (r) { - throw std::system_error(r, std::system_category()); - } - r = pthread_create(&_pthread, &pa, - &posix_thread::start_routine, _func.get()); - if (r) { - throw std::system_error(r, std::system_category()); - } -} - -posix_thread::posix_thread(posix_thread&& x) - : _func(std::move(x._func)), _pthread(x._pthread), _valid(x._valid) - , _stack(std::move(x._stack)) { - x._valid = false; -} - -posix_thread::~posix_thread() { - assert(!_valid); -} - -void posix_thread::join() { - assert(_valid); - pthread_join(_pthread, NULL); - _valid = false; -} - -void pin_this_thread(unsigned cpu_id) { - cpu_set_t cs; - CPU_ZERO(&cs); - CPU_SET(cpu_id, &cs); - auto r = pthread_setaffinity_np(pthread_self(), sizeof(cs), &cs); - assert(r == 0); -} - diff --git a/core/posix.hh b/core/posix.hh deleted file mode 100644 index d226592673..0000000000 --- a/core/posix.hh +++ /dev/null @@ -1,385 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef FILE_DESC_HH_ -#define FILE_DESC_HH_ - -#include "sstring.hh" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "net/api.hh" - -inline void throw_system_error_on(bool condition); - -template -inline void throw_kernel_error(T r); - -struct mmap_deleter { - size_t _size; - void operator()(void* ptr) const; -}; - -using mmap_area = std::unique_ptr; - -mmap_area mmap_anonymous(void* addr, size_t length, int prot, int flags); - -class file_desc { - int _fd; -public: - file_desc() = delete; - file_desc(const file_desc&) = delete; - file_desc(file_desc&& x) : _fd(x._fd) { x._fd = -1; } - ~file_desc() { if (_fd != -1) { ::close(_fd); } } - void operator=(const file_desc&) = delete; - file_desc& operator=(file_desc&& x) { - if (this != &x) { - std::swap(_fd, x._fd); - if (x._fd != -1) { - x.close(); - } - } - return *this; - } - void close() { - assert(_fd != -1); - auto r = ::close(_fd); - throw_system_error_on(r == -1); - _fd = -1; - } - int get() const { return _fd; } - static file_desc open(sstring name, int flags, mode_t mode = 0) { - int fd = ::open(name.c_str(), flags, mode); - throw_system_error_on(fd == -1); - return file_desc(fd); - } - static file_desc socket(int family, int type, int protocol = 0) { - int fd = ::socket(family, type, protocol); - throw_system_error_on(fd == -1); - return file_desc(fd); - } - static file_desc eventfd(unsigned initval, int flags) { - int fd = ::eventfd(initval, flags); - throw_system_error_on(fd == -1); - return file_desc(fd); - } - static file_desc epoll_create(int flags = 0) { - int fd = ::epoll_create1(flags); - throw_system_error_on(fd == -1); - return file_desc(fd); - } - static file_desc timerfd_create(int clockid, int flags) { - int fd = ::timerfd_create(clockid, flags); - throw_system_error_on(fd == -1); - return file_desc(fd); - } - static file_desc temporary(sstring directory); - file_desc dup() const { - int fd = ::dup(get()); - throw_system_error_on(fd == -1); - return file_desc(fd); - } - file_desc accept(sockaddr& sa, socklen_t& sl, int flags = 0) { - auto ret = ::accept4(_fd, &sa, &sl, flags); - throw_system_error_on(ret == -1); - return file_desc(ret); - } - void shutdown(int how) { - auto ret = ::shutdown(_fd, how); - throw_system_error_on(ret == -1); - } - void truncate(size_t size) { - auto ret = ::ftruncate(_fd, size); - throw_system_error_on(ret); - } - int ioctl(int request) { - return ioctl(request, 0); - } - int ioctl(int request, int value) { - int r = ::ioctl(_fd, request, value); - throw_system_error_on(r == -1); - return r; - } - int ioctl(int request, unsigned int value) { - int r = ::ioctl(_fd, request, value); - throw_system_error_on(r == -1); - return r; - } - template - int ioctl(int request, X& data) { - int r = ::ioctl(_fd, request, &data); - throw_system_error_on(r == -1); - return r; - } - template - int ioctl(int request, X&& data) { - int r = ::ioctl(_fd, request, &data); - throw_system_error_on(r == -1); - return r; - } - template - int setsockopt(int level, int optname, X&& data) { - int r = ::setsockopt(_fd, level, optname, &data, sizeof(data)); - throw_system_error_on(r == -1); - return r; - } - int setsockopt(int level, int optname, const char* data) { - int r = ::setsockopt(_fd, level, optname, data, strlen(data) + 1); - throw_system_error_on(r == -1); - return r; - } - template - int getsockopt(int level, int optname, X&& data) { - socklen_t len = sizeof(data); - int r = ::getsockopt(_fd, level, optname, &data, &len); - throw_system_error_on(r == -1); - return r; - } - int getsockopt(int level, int optname, char* data, socklen_t len) { - int r = ::getsockopt(_fd, level, optname, data, &len); - throw_system_error_on(r == -1); - return r; - } - size_t size() { - struct stat buf; - auto r = ::fstat(_fd, &buf); - throw_system_error_on(r == -1); - return buf.st_size; - } - boost::optional read(void* buffer, size_t len) { - auto r = ::read(_fd, buffer, len); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { size_t(r) }; - } - boost::optional recv(void* buffer, size_t len, int flags) { - auto r = ::recv(_fd, buffer, len, flags); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { ssize_t(r) }; - } - boost::optional recvmsg(msghdr* mh, int flags) { - auto r = ::recvmsg(_fd, mh, flags); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { size_t(r) }; - } - boost::optional send(const void* buffer, size_t len, int flags) { - auto r = ::send(_fd, buffer, len, flags); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { size_t(r) }; - } - boost::optional sendto(socket_address& addr, const void* buf, size_t len, int flags) { - auto r = ::sendto(_fd, buf, len, flags, &addr.u.sa, sizeof(addr.u.sas)); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { size_t(r) }; - } - boost::optional sendmsg(const msghdr* msg, int flags) { - auto r = ::sendmsg(_fd, msg, flags); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { size_t(r) }; - } - void bind(sockaddr& sa, socklen_t sl) { - auto r = ::bind(_fd, &sa, sl); - throw_system_error_on(r == -1); - } - void connect(sockaddr& sa, socklen_t sl) { - auto r = ::connect(_fd, &sa, sl); - if (r == -1 && errno == EINPROGRESS) { - return; - } - throw_system_error_on(r == -1); - } - socket_address get_address() { - socket_address addr; - auto len = (socklen_t) sizeof(addr.u.sas); - auto r = ::getsockname(_fd, &addr.u.sa, &len); - throw_system_error_on(r == -1); - return addr; - } - void listen(int backlog) { - auto fd = ::listen(_fd, backlog); - throw_system_error_on(fd == -1); - } - boost::optional write(const void* buf, size_t len) { - auto r = ::write(_fd, buf, len); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { size_t(r) }; - } - boost::optional writev(const iovec *iov, int iovcnt) { - auto r = ::writev(_fd, iov, iovcnt); - if (r == -1 && errno == EAGAIN) { - return {}; - } - throw_system_error_on(r == -1); - return { size_t(r) }; - } - size_t pread(void* buf, size_t len, off_t off) { - auto r = ::pread(_fd, buf, len, off); - throw_system_error_on(r == -1); - return size_t(r); - } - void timerfd_settime(int flags, const itimerspec& its) { - auto r = ::timerfd_settime(_fd, flags, &its, NULL); - throw_system_error_on(r == -1); - } - - mmap_area map(size_t size, unsigned prot, unsigned flags, size_t offset, - void* addr = nullptr) { - void *x = mmap(addr, size, prot, flags, _fd, offset); - throw_system_error_on(x == MAP_FAILED); - return mmap_area(static_cast(x), mmap_deleter{size}); - } - - mmap_area map_shared_rw(size_t size, size_t offset) { - return map(size, PROT_READ | PROT_WRITE, MAP_SHARED, offset); - } - - mmap_area map_shared_ro(size_t size, size_t offset) { - return map(size, PROT_READ, MAP_SHARED, offset); - } - - mmap_area map_private_rw(size_t size, size_t offset) { - return map(size, PROT_READ | PROT_WRITE, MAP_PRIVATE, offset); - } - - mmap_area map_private_ro(size_t size, size_t offset) { - return map(size, PROT_READ, MAP_PRIVATE, offset); - } - -private: - file_desc(int fd) : _fd(fd) {} - }; - -class posix_thread { -public: - class attr; -private: - // must allocate, since this class is moveable - std::unique_ptr> _func; - pthread_t _pthread; - bool _valid = true; - mmap_area _stack; -private: - static void* start_routine(void* arg); -public: - posix_thread(std::function func); - posix_thread(attr a, std::function func); - posix_thread(posix_thread&& x); - ~posix_thread(); - void join(); -public: - class attr { - public: - struct stack_size { size_t size = 0; }; - attr() = default; - template - attr(A... a) { - set(std::forward(a)...); - } - void set() {} - template - void set(A a, Rest... rest) { - set(std::forward(a)); - set(std::forward(rest)...); - } - void set(stack_size ss) { _stack_size = ss; } - private: - stack_size _stack_size; - friend class posix_thread; - }; -}; - - -inline -void throw_system_error_on(bool condition) { - if (condition) { - throw std::system_error(errno, std::system_category()); - } -} - -template -inline -void throw_kernel_error(T r) { - static_assert(std::is_signed::value, "kernel error variables must be signed"); - if (r < 0) { - throw std::system_error(-r, std::system_category()); - } -} - -inline -sigset_t make_sigset_mask(int signo) { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, signo); - return set; -} - -inline -sigset_t make_full_sigset_mask() { - sigset_t set; - sigfillset(&set); - return set; -} - -inline -sigset_t make_empty_sigset_mask() { - sigset_t set; - sigemptyset(&set); - return set; -} - -void pin_this_thread(unsigned cpu_id); - -#endif /* FILE_DESC_HH_ */ diff --git a/core/prefetch.hh b/core/prefetch.hh deleted file mode 100644 index 296f22f664..0000000000 --- a/core/prefetch.hh +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef PREFETCH_HH_ -#define PREFETCH_HH_ - -#include -#include -#include -#include "align.hh" - -static constexpr size_t cacheline_size = 64; -template -struct prefetcher; - -template -struct prefetcher<0, RW, LOC> { - prefetcher(uintptr_t ptr) {} -}; - -template -struct prefetcher { - prefetcher(uintptr_t ptr) { - __builtin_prefetch(reinterpret_cast(ptr), RW, LOC); - std::atomic_signal_fence(std::memory_order_seq_cst); - prefetcher(ptr + 64); - } -}; - -// LOC is a locality from __buitin_prefetch() gcc documentation: -// "The value locality must be a compile-time constant integer between zero and three. A value of -// zero means that the data has no temporal locality, so it need not be left in the cache after -// the access. A value of three means that the data has a high degree of temporal locality and -// should be left in all levels of cache possible. Values of one and two mean, respectively, a -// low or moderate degree of temporal locality. The default is three." -template -void prefetch(T* ptr) { - prefetcher(reinterpret_cast(ptr)); -} - -template -void prefetch(Iterator begin, Iterator end) { - std::for_each(begin, end, [] (auto v) { prefetch(v); }); -} - -template -void prefetch_n(T** pptr) { - boost::mpl::for_each< boost::mpl::range_c >( [pptr] (size_t x) { prefetch(*(pptr + x)); } ); -} - -template -void prefetch(void* ptr) { - prefetcher(reinterpret_cast(ptr)); -} - -template -void prefetch_n(Iterator begin, Iterator end) { - std::for_each(begin, end, [] (auto v) { prefetch(v); }); -} - -template -void prefetch_n(T** pptr) { - boost::mpl::for_each< boost::mpl::range_c >( [pptr] (size_t x) { prefetch(*(pptr + x)); } ); -} - -template -void prefetchw(T* ptr) { - prefetcher(reinterpret_cast(ptr)); -} - -template -void prefetchw_n(Iterator begin, Iterator end) { - std::for_each(begin, end, [] (auto v) { prefetchw(v); }); -} - -template -void prefetchw_n(T** pptr) { - boost::mpl::for_each< boost::mpl::range_c >( [pptr] (size_t x) { prefetchw(*(pptr + x)); } ); -} - -template -void prefetchw(void* ptr) { - prefetcher(reinterpret_cast(ptr)); -} - -template -void prefetchw_n(Iterator begin, Iterator end) { - std::for_each(begin, end, [] (auto v) { prefetchw(v); }); -} - -template -void prefetchw_n(T** pptr) { - boost::mpl::for_each< boost::mpl::range_c >( [pptr] (size_t x) { prefetchw(*(pptr + x)); } ); -} - -#endif diff --git a/core/print.hh b/core/print.hh deleted file mode 100644 index 4ece04d39c..0000000000 --- a/core/print.hh +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef PRINT_HH_ -#define PRINT_HH_ - -#include -#include -#include -#include -#include "core/sstring.hh" - -inline void -apply_format(boost::format& fmt) { -} - -template -inline void -apply_format(boost::format& fmt, A0&& a0, Arest&&... arest) { - apply_format(fmt % std::forward(a0), std::forward(arest)...); -} - -template -std::ostream& -fprint(std::ostream& os, boost::format& fmt, A&&... a) { - apply_format(fmt, std::forward(a)...); - return os << fmt; -} - -template -void -print(boost::format& fmt, A&&... a) { - fprint(std::cout, fmt, std::forward(a)...); -} - -template -std::ostream& -fprint(std::ostream& os, const char* fmt, A&&... a) { - boost::format bfmt(fmt); - return fprint(os, bfmt, std::forward(a)...); -} - -template -void -print(const char* fmt, A&&... a) { - boost::format bfmt(fmt); - return print(bfmt, std::forward(a)...); -} - -template -std::string -sprint(const char* fmt, A&&... a) { - boost::format bfmt(fmt); - apply_format(bfmt, std::forward(a)...); - return bfmt.str(); -} - -template -std::string -sprint(const sstring& fmt, A&&... a) { - return sprint(fmt.c_str(), std::forward(a)...); -} - -template -std::string -format_separated(Iterator b, Iterator e, const char* sep = ", ") { - std::string ret; - if (b == e) { - return ret; - } - ret += *b++; - while (b != e) { - ret += sep; - ret += *b++; - } - return ret; -} - -template -struct usecfmt_wrapper { - TimePoint val; -}; - -template -inline -usecfmt_wrapper -usecfmt(TimePoint tp) { - return { tp }; -}; - -template -std::ostream& -operator<<(std::ostream& os, usecfmt_wrapper>> tp) { - auto usec = std::chrono::duration_cast(tp.val.time_since_epoch()).count(); - std::ostream tmp(os.rdbuf()); - tmp << std::setw(12) << (usec / 1000000) << "." << std::setw(6) << std::setfill('0') << (usec % 1000000); - return os; -} - -template -void -log(A&&... a) { - std::cout << usecfmt(std::chrono::high_resolution_clock::now()) << " "; - print(std::forward(a)...); -} - -#endif /* PRINT_HH_ */ diff --git a/core/queue.hh b/core/queue.hh deleted file mode 100644 index c2c3739201..0000000000 --- a/core/queue.hh +++ /dev/null @@ -1,220 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef QUEUE_HH_ -#define QUEUE_HH_ - -#include "circular_buffer.hh" -#include "future.hh" -#include -#include - -template -class queue { - std::queue> _q; - size_t _max; - std::experimental::optional> _not_empty; - std::experimental::optional> _not_full; -private: - void notify_not_empty(); - void notify_not_full(); -public: - explicit queue(size_t size); - - // Push an item. - // - // Returns false if the queue was full and the item was not pushed. - bool push(T&& a); - - // pops an item. - T pop(); - - // Consumes items from the queue, passing them to @func, until @func - // returns false or the queue it empty - // - // Returns false if func returned false. - template - bool consume(Func&& func); - - // Returns true when the queue is empty. - bool empty() const; - - // Returns true when the queue is full. - bool full() const; - - // Returns a future<> that becomes available when pop() or consume() - // can be called. - future<> not_empty(); - - // Returns a future<> that becomes available when push() can be called. - future<> not_full(); - - // Pops element now or when ther is some. Returns a future that becomes - // available when some element is available. - future pop_eventually(); - - // Pushes the element now or when there is room. Returns a future<> which - // resolves when data was pushed. - future<> push_eventually(T&& data); - - size_t size() const { return _q.size(); } - - // Destroy any items in the queue, and pass the provided exception to any - // waiting readers or writers. - void abort(std::exception_ptr ex) { - while (!_q.empty()) { - _q.pop(); - } - if (_not_full) { - _not_full->set_exception(ex); - _not_full= std::experimental::nullopt; - } - if (_not_empty) { - _not_empty->set_exception(std::move(ex)); - _not_empty = std::experimental::nullopt; - } - } -}; - -template -inline -queue::queue(size_t size) - : _max(size) { -} - -template -inline -void queue::notify_not_empty() { - if (_not_empty) { - _not_empty->set_value(); - _not_empty = std::experimental::optional>(); - } -} - -template -inline -void queue::notify_not_full() { - if (_not_full) { - _not_full->set_value(); - _not_full = std::experimental::optional>(); - } -} - -template -inline -bool queue::push(T&& data) { - if (_q.size() < _max) { - _q.push(std::move(data)); - notify_not_empty(); - return true; - } else { - return false; - } -} - -template -inline -T queue::pop() { - if (_q.size() == _max) { - notify_not_full(); - } - T data = std::move(_q.front()); - _q.pop(); - return data; -} - -template -inline -future queue::pop_eventually() { - if (empty()) { - return not_empty().then([this] { - return make_ready_future(pop()); - }); - } else { - return make_ready_future(pop()); - } -} - -template -inline -future<> queue::push_eventually(T&& data) { - if (full()) { - return not_full().then([this, data = std::move(data)] () mutable { - _q.push(std::move(data)); - notify_not_empty(); - }); - } else { - _q.push(std::move(data)); - notify_not_empty(); - return make_ready_future<>(); - } -} - -template -template -inline -bool queue::consume(Func&& func) { - if (_q.size() == _max) { - notify_not_full(); - } - bool running = true; - while (!_q.empty() && running) { - running = func(std::move(_q.front())); - _q.pop(); - } - return running; -} - -template -inline -bool queue::empty() const { - return _q.empty(); -} - -template -inline -bool queue::full() const { - return _q.size() == _max; -} - -template -inline -future<> queue::not_empty() { - if (!empty()) { - return make_ready_future<>(); - } else { - _not_empty = promise<>(); - return _not_empty->get_future(); - } -} - -template -inline -future<> queue::not_full() { - if (!full()) { - return make_ready_future<>(); - } else { - _not_full = promise<>(); - return _not_full->get_future(); - } -} - -#endif /* QUEUE_HH_ */ diff --git a/core/ragel.hh b/core/ragel.hh deleted file mode 100644 index daefca6cf8..0000000000 --- a/core/ragel.hh +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef RAGEL_HH_ -#define RAGEL_HH_ - -#include "sstring.hh" -#include "temporary_buffer.hh" -#include "util/eclipse.hh" -#include -#include -#include -#include -#include "future.hh" - -// Support classes for Ragel parsers - -// Builds an sstring that can be scattered across multiple packets. -// -// Use a sstring_build::guard variable to designate each scattered -// char array, and call mark_start() and mark_end() at the start -// and end points, respectively. sstring_builder will collect data -// from intervening segments, if needed. -// -// After mark_end() has been called, use the get() method to obtain -// the built string. -// -// FIXME: switch to string_view. -// -class sstring_builder { - sstring _value; - const char* _start = nullptr; -public: - class guard; -public: - sstring get() && { - return std::move(_value); - } - void reset() { - _value.reset(); - _start = nullptr; - } - friend class guard; -}; - -class sstring_builder::guard { - sstring_builder& _builder; - const char* _block_end; -public: - guard(sstring_builder& builder, const char* block_start, const char* block_end) - : _builder(builder), _block_end(block_end) { - if (!_builder._value.empty()) { - mark_start(block_start); - } - } - ~guard() { - if (_builder._start) { - mark_end(_block_end); - } - } - void mark_start(const char* p) { - _builder._start = p; - } - void mark_end(const char* p) { - if (_builder._value.empty()) { - // avoid an allocation in the common case - _builder._value = sstring(_builder._start, p); - } else { - _builder._value += sstring(_builder._start, p); - } - _builder._start = nullptr; - } -}; - - -// CRTP -template -class ragel_parser_base { -protected: - int _fsm_cs; - std::unique_ptr _fsm_stack = nullptr; - int _fsm_stack_size = 0; - int _fsm_top; - int _fsm_act; - char* _fsm_ts; - char* _fsm_te; - sstring_builder _builder; -protected: - void init_base() { - _builder.reset(); - } - void prepush() { - if (_fsm_top == _fsm_stack_size) { - auto old = _fsm_stack_size; - _fsm_stack_size = std::max(_fsm_stack_size * 2, 16); - assert(_fsm_stack_size > old); - std::unique_ptr new_stack{new int[_fsm_stack_size]}; - std::copy(_fsm_stack.get(), _fsm_stack.get() + _fsm_top, new_stack.get()); - std::swap(_fsm_stack, new_stack); - } - } - void postpop() {} - sstring get_str() { - auto s = std::move(_builder).get(); - return std::move(s); - } -public: - using unconsumed_remainder = std::experimental::optional>; - future operator()(temporary_buffer buf) { - char* p = buf.get_write(); - char* pe = p + buf.size(); - char* eof = buf.empty() ? pe : nullptr; - char* parsed = static_cast(this)->parse(p, pe, eof); - if (parsed) { - buf.trim_front(parsed - p); - return make_ready_future(std::move(buf)); - } - return make_ready_future(); - } -}; - -#endif /* RAGEL_HH_ */ diff --git a/core/reactor.cc b/core/reactor.cc deleted file mode 100644 index 9c25a6e6c0..0000000000 --- a/core/reactor.cc +++ /dev/null @@ -1,2075 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#include -#include "task.hh" -#include "reactor.hh" -#include "memory.hh" -#include "core/posix.hh" -#include "net/packet.hh" -#include "resource.hh" -#include "print.hh" -#include "scollectd.hh" -#include "util/conversions.hh" -#include "core/future-util.hh" -#include "thread.hh" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_DPDK -#include -#include -#include -#endif -#include "prefetch.hh" -#include -#include -#ifdef __GNUC__ -#include -#include -#include -#endif - -#ifdef HAVE_OSV -#include -#endif - -using namespace net; - -std::atomic lowres_clock::_now; -constexpr std::chrono::milliseconds lowres_clock::_granularity; - -timespec to_timespec(clock_type::time_point t) { - using ns = std::chrono::nanoseconds; - auto n = std::chrono::duration_cast(t.time_since_epoch()).count(); - return { n / 1'000'000'000, n % 1'000'000'000 }; -} - -lowres_clock::lowres_clock() { - _timer.set_callback([this] { update(); }); - _timer.arm_periodic(_granularity); -} - -void lowres_clock::update() { - auto ticks = _granularity.count(); - _now.fetch_add(ticks, std::memory_order_relaxed); -} - -template -struct syscall_result { - T result; - int error; - void throw_if_error() { - if (long(result) == -1) { - throw std::system_error(error, std::system_category()); - } - } -}; - -// Wrapper for a system call result containing the return value, -// an output parameter that was returned from the syscall, and errno. -template -struct syscall_result_extra { - int result; - Extra extra; - int error; - void throw_if_error() { - if (result == -1) { - throw std::system_error(error, std::system_category()); - } - } -}; - -template -syscall_result -wrap_syscall(T result) { - syscall_result sr; - sr.result = result; - sr.error = errno; - return sr; -} - -template -syscall_result_extra -wrap_syscall(int result, const Extra& extra) { - return {result, extra, errno}; -} - -reactor_backend_epoll::reactor_backend_epoll() - : _epollfd(file_desc::epoll_create(EPOLL_CLOEXEC)) { -} - -reactor::signals::signals() : _pending_signals(0) { -} - -reactor::signals::~signals() { - sigset_t mask; - sigfillset(&mask); - ::sigprocmask(SIG_BLOCK, &mask, NULL); -} - -reactor::signals::signal_handler::signal_handler(int signo, std::function&& handler) - : _handler(std::move(handler)) { - auto mask = make_sigset_mask(signo); - auto r = ::sigprocmask(SIG_UNBLOCK, &mask, NULL); - throw_system_error_on(r == -1); - struct sigaction sa; - sa.sa_sigaction = action; - sa.sa_mask = make_empty_sigset_mask(); - sa.sa_flags = SA_SIGINFO | SA_RESTART; - r = ::sigaction(signo, &sa, nullptr); - throw_system_error_on(r == -1); -} - -void -reactor::signals::handle_signal(int signo, std::function&& handler) { - _signal_handlers.emplace(std::piecewise_construct, - std::make_tuple(signo), std::make_tuple(signo, std::move(handler))); -} - -void -reactor::signals::handle_signal_once(int signo, std::function&& handler) { - return handle_signal(signo, [fired = false, handler = std::move(handler)] () mutable { - if (!fired) { - fired = true; - handler(); - } - }); -} - -bool reactor::signals::poll_signal() { - auto signals = _pending_signals.load(std::memory_order_relaxed); - if (signals) { - _pending_signals.fetch_and(~signals, std::memory_order_relaxed); - for (size_t i = 0; i < sizeof(signals)*8; i++) { - if (signals & (1ull << i)) { - _signal_handlers.at(i)._handler(); - } - } - } - return signals; -} - -void reactor::signals::action(int signo, siginfo_t* siginfo, void* ignore) { - engine()._signals._pending_signals.fetch_or(1ull << signo, std::memory_order_relaxed); -} - -inline int alarm_signal() { - // We don't want to use SIGALRM, because the boost unit test library - // also plays with it. - return SIGRTMIN; -} - -reactor::reactor() - : _backend() -#ifdef HAVE_OSV - , _timer_thread( - [&] { timer_thread_func(); }, sched::thread::attr().stack(4096).name("timer_thread").pin(sched::cpu::current())) - , _engine_thread(sched::thread::current()) -#endif - , _exit_future(_exit_promise.get_future()) - , _cpu_started(0) - , _io_context(0) - , _io_context_available(max_aio) - , _reuseport(posix_reuseport_detect()) { - - seastar::thread_impl::init(); - auto r = ::io_setup(max_aio, &_io_context); - assert(r >= 0); -#ifdef HAVE_OSV - _timer_thread.start(); -#else - struct sigevent sev; - sev.sigev_notify = SIGEV_THREAD_ID; - sev._sigev_un._tid = syscall(SYS_gettid); - sev.sigev_signo = alarm_signal(); - r = timer_create(CLOCK_REALTIME, &sev, &_timer); - assert(r >= 0); - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, alarm_signal()); - r = ::sigprocmask(SIG_BLOCK, &mask, NULL); - assert(r == 0); -#endif - memory::set_reclaim_hook([this] (std::function reclaim_fn) { - // push it in the front of the queue so we reclaim memory quickly - _pending_tasks.push_front(make_task([fn = std::move(reclaim_fn)] { - fn(); - })); - }); -} - -reactor::~reactor() { - timer_delete(_timer); - auto eraser = [](auto& list) { - while (!list.empty()) { - auto& timer = *list.begin(); - timer.cancel(); - } - }; - eraser(_expired_timers); - eraser(_expired_lowres_timers); -} - -#ifdef HAVE_OSV -void reactor::timer_thread_func() { - sched::timer tmr(*sched::thread::current()); - WITH_LOCK(_timer_mutex) { - while (!_stopped) { - if (_timer_due != 0) { - set_timer(tmr, _timer_due); - _timer_cond.wait(_timer_mutex, &tmr); - if (tmr.expired()) { - _timer_due = 0; - _engine_thread->unsafe_stop(); - _pending_tasks.push_front(make_task([this] { - complete_timers(_timers, _expired_timers, [this] { - if (!_timers.empty()) { - enable_timer(_timers.get_next_timeout()); - } - }); - })); - _engine_thread->wake(); - } else { - tmr.cancel(); - } - } else { - _timer_cond.wait(_timer_mutex); - } - } - } -} - -void reactor::set_timer(sched::timer &tmr, s64 t) { - using namespace osv::clock; - tmr.set(wall::time_point(std::chrono::nanoseconds(t))); -} -#endif - -void reactor::configure(boost::program_options::variables_map vm) { - auto network_stack_ready = vm.count("network-stack") - ? network_stack_registry::create(sstring(vm["network-stack"].as()), vm) - : network_stack_registry::create(vm); - network_stack_ready.then([this] (std::unique_ptr stack) { - _network_stack_ready_promise.set_value(std::move(stack)); - }); - - _handle_sigint = !vm.count("no-handle-interrupt"); - _task_quota = vm["task-quota"].as(); -} - -future<> reactor_backend_epoll::get_epoll_future(pollable_fd_state& pfd, - promise<> pollable_fd_state::*pr, int event) { - if (pfd.events_known & event) { - pfd.events_known &= ~event; - return make_ready_future(); - } - pfd.events_requested |= event; - if (!(pfd.events_epoll & event)) { - auto ctl = pfd.events_epoll ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; - pfd.events_epoll |= event; - ::epoll_event eevt; - eevt.events = pfd.events_epoll; - eevt.data.ptr = &pfd; - int r = ::epoll_ctl(_epollfd.get(), ctl, pfd.fd.get(), &eevt); - assert(r == 0); - engine().start_epoll(); - } - pfd.*pr = promise<>(); - return (pfd.*pr).get_future(); -} - -void reactor_backend_epoll::abort_fd(pollable_fd_state& pfd, std::exception_ptr ex, - promise<> pollable_fd_state::* pr, int event) { - if (pfd.events_epoll & event) { - pfd.events_epoll &= ~event; - auto ctl = pfd.events_epoll ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; - ::epoll_event eevt; - eevt.events = pfd.events_epoll; - eevt.data.ptr = &pfd; - int r = ::epoll_ctl(_epollfd.get(), ctl, pfd.fd.get(), &eevt); - assert(r == 0); - } - if (pfd.events_requested & event) { - pfd.events_requested &= ~event; - (pfd.*pr).set_exception(std::move(ex)); - } - pfd.events_known &= ~event; -} - -future<> reactor_backend_epoll::readable(pollable_fd_state& fd) { - return get_epoll_future(fd, &pollable_fd_state::pollin, EPOLLIN); -} - -future<> reactor_backend_epoll::writeable(pollable_fd_state& fd) { - return get_epoll_future(fd, &pollable_fd_state::pollout, EPOLLOUT); -} - -void reactor_backend_epoll::abort_reader(pollable_fd_state& fd, std::exception_ptr ex) { - abort_fd(fd, std::move(ex), &pollable_fd_state::pollin, EPOLLIN); -} - -void reactor_backend_epoll::abort_writer(pollable_fd_state& fd, std::exception_ptr ex) { - abort_fd(fd, std::move(ex), &pollable_fd_state::pollout, EPOLLOUT); -} - -void reactor_backend_epoll::forget(pollable_fd_state& fd) { - if (fd.events_epoll) { - ::epoll_ctl(_epollfd.get(), EPOLL_CTL_DEL, fd.fd.get(), nullptr); - } -} - -future<> reactor_backend_epoll::notified(reactor_notifier *n) { - // Currently reactor_backend_epoll doesn't need to support notifiers, - // because we add to it file descriptors instead. But this can be fixed - // later. - std::cout << "reactor_backend_epoll does not yet support notifiers!\n"; - abort(); -} - - -pollable_fd -reactor::posix_listen(socket_address sa, listen_options opts) { - file_desc fd = file_desc::socket(sa.u.sa.sa_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); - if (opts.reuse_address) { - fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1); - } - if (_reuseport) - fd.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1); - - fd.bind(sa.u.sa, sizeof(sa.u.sas)); - fd.listen(100); - return pollable_fd(std::move(fd)); -} - -bool -reactor::posix_reuseport_detect() { - try { - file_desc fd = file_desc::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); - fd.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1); - return true; - } catch(std::system_error& e) { - return false; - } -} - -future -reactor::posix_connect(socket_address sa, socket_address local) { - file_desc fd = file_desc::socket(sa.u.sa.sa_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); - fd.bind(local.u.sa, sizeof(sa.u.sas)); - fd.connect(sa.u.sa, sizeof(sa.u.sas)); - auto pfd = pollable_fd(std::move(fd)); - auto f = pfd.writeable(); - return f.then([pfd = std::move(pfd)] () mutable { - int err; - pfd.get_file_desc().getsockopt(SOL_SOCKET, SO_ERROR, err); - throw_system_error_on(err != 0); - return make_ready_future(std::move(pfd)); - }); -} - -server_socket -reactor::listen(socket_address sa, listen_options opt) { - return _network_stack->listen(sa, opt); -} - -future -reactor::connect(socket_address sa) { - return _network_stack->connect(sa); -} - -void reactor_backend_epoll::complete_epoll_event(pollable_fd_state& pfd, promise<> pollable_fd_state::*pr, - int events, int event) { - if (pfd.events_requested & events & event) { - pfd.events_requested &= ~event; - pfd.events_known &= ~event; - (pfd.*pr).set_value(); - pfd.*pr = promise<>(); - } -} - -template -future -reactor::submit_io(Func prepare_io) { - return _io_context_available.wait(1).then([this, prepare_io = std::move(prepare_io)] () mutable { - auto pr = std::make_unique>(); - iocb io; - prepare_io(io); - io.data = pr.get(); - _pending_aio.push_back(io); - if (_pending_aio.size() >= max_aio / 4) { - flush_pending_aio(); - } - return pr.release()->get_future(); - }); -} - -bool -reactor::flush_pending_aio() { - while (!_pending_aio.empty()) { - auto nr = _pending_aio.size(); - struct iocb* iocbs[max_aio]; - for (size_t i = 0; i < nr; ++i) { - iocbs[i] = &_pending_aio[i]; - } - auto r = ::io_submit(_io_context, nr, iocbs); - throw_kernel_error(r); - if (size_t(r) == nr) { - _pending_aio.clear(); - } else { - _pending_aio.erase(_pending_aio.begin(), _pending_aio.begin() + r); - } - } - return false; // We always submit all pending aios -} - -template -future -reactor::submit_io_read(Func prepare_io) { - ++_aio_reads; - return submit_io(std::move(prepare_io)); -} - -template -future -reactor::submit_io_write(Func prepare_io) { - ++_aio_writes; - return submit_io(std::move(prepare_io)); -} - -bool reactor::process_io() -{ - io_event ev[max_aio]; - struct timespec timeout = {0, 0}; - auto n = ::io_getevents(_io_context, 1, max_aio, ev, &timeout); - assert(n >= 0); - for (size_t i = 0; i < size_t(n); ++i) { - auto pr = reinterpret_cast*>(ev[i].data); - pr->set_value(ev[i]); - delete pr; - } - _io_context_available.signal(n); - return n; -} - -posix_file_impl::~posix_file_impl() { - if (_fd != -1) { - if (std::uncaught_exception()) { - std::cerr << "WARNING: closing file in reactor thread during exception recovery\n"; - } else { - std::cerr << "WARNING: closing file in reactor thread\n"; - } - ::close(_fd); - } -} - -future -posix_file_impl::write_dma(uint64_t pos, const void* buffer, size_t len) { - return engine().submit_io_write([this, pos, buffer, len] (iocb& io) { - io_prep_pwrite(&io, _fd, const_cast(buffer), len, pos); - }).then([] (io_event ev) { - throw_kernel_error(long(ev.res)); - return make_ready_future(size_t(ev.res)); - }); -} - -future -posix_file_impl::write_dma(uint64_t pos, std::vector iov) { - return engine().submit_io_write([this, pos, iov = std::move(iov)] (iocb& io) { - io_prep_pwritev(&io, _fd, iov.data(), iov.size(), pos); - }).then([] (io_event ev) { - throw_kernel_error(long(ev.res)); - return make_ready_future(size_t(ev.res)); - }); -} - -future -posix_file_impl::read_dma(uint64_t pos, void* buffer, size_t len) { - return engine().submit_io_read([this, pos, buffer, len] (iocb& io) { - io_prep_pread(&io, _fd, buffer, len, pos); - }).then([] (io_event ev) { - throw_kernel_error(long(ev.res)); - return make_ready_future(size_t(ev.res)); - }); -} - -future -posix_file_impl::read_dma(uint64_t pos, std::vector iov) { - return engine().submit_io_read([this, pos, iov = std::move(iov)] (iocb& io) { - io_prep_preadv(&io, _fd, iov.data(), iov.size(), pos); - }).then([] (io_event ev) { - throw_kernel_error(long(ev.res)); - return make_ready_future(size_t(ev.res)); - }); -} - -future -reactor::open_file_dma(sstring name, open_flags flags) { - return _thread_pool.submit>([name, flags] { - return wrap_syscall(::open(name.c_str(), O_DIRECT | O_CLOEXEC | static_cast(flags), S_IRWXU)); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future(file(sr.result)); - }); -} - -future<> -reactor::remove_file(sstring pathname) { - return engine()._thread_pool.submit>([this, pathname] { - return wrap_syscall(::remove(pathname.c_str())); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future<>(); - }); -} - -future<> -reactor::rename_file(sstring old_pathname, sstring new_pathname) { - return engine()._thread_pool.submit>([this, old_pathname, new_pathname] { - return wrap_syscall(::rename(old_pathname.c_str(), new_pathname.c_str())); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future<>(); - }); -} - -directory_entry_type stat_to_entry_type(__mode_t type) { - if (S_ISDIR(type)) { - return directory_entry_type::directory; - } - if (S_ISBLK(type)) { - return directory_entry_type::block_device; - } - if (S_ISCHR(type)) { - return directory_entry_type::char_device; - } - if (S_ISFIFO(type)) { - return directory_entry_type::fifo; - } - if (S_ISLNK(type)) { - return directory_entry_type::link; - } - return directory_entry_type::regular; - -} - -future> -reactor::file_type(sstring name) { - return _thread_pool.submit>([name] { - struct stat st; - auto ret = stat(name.c_str(), &st); - return wrap_syscall(ret, st); - }).then([] (syscall_result_extra sr) { - if (long(sr.result) == -1) { - if (sr.error != ENOENT && sr.error != ENOTDIR) { - sr.throw_if_error(); - } - return make_ready_future > - (std::experimental::optional() ); - } - return make_ready_future > - (std::experimental::optional(stat_to_entry_type(sr.extra.st_mode)) ); - }); -} - -future -reactor::open_directory(sstring name) { - return _thread_pool.submit>([name] { - return wrap_syscall(::open(name.c_str(), O_DIRECTORY | O_CLOEXEC | O_RDONLY)); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future(file(sr.result)); - }); -} - -future<> -reactor::make_directory(sstring name) { - return _thread_pool.submit>([name = std::move(name)] { - return wrap_syscall(::mkdir(name.c_str(), S_IRWXU)); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - }); -} - -future<> -posix_file_impl::flush(void) { - ++engine()._fsyncs; - return engine()._thread_pool.submit>([this] { - return wrap_syscall(::fsync(_fd)); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future<>(); - }); -} - -future -posix_file_impl::stat(void) { - return engine()._thread_pool.submit>([this] { - struct stat st; - auto ret = ::fstat(_fd, &st); - return wrap_syscall(ret, st); - }).then([] (syscall_result_extra ret) { - ret.throw_if_error(); - return make_ready_future(ret.extra); - }); -} - -future<> -posix_file_impl::truncate(uint64_t length) { - return engine()._thread_pool.submit>([this, length] { - return wrap_syscall(::ftruncate(_fd, length)); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future<>(); - }); -} - -future<> -blockdev_file_impl::truncate(uint64_t length) { - return make_ready_future<>(); -} - -future<> -posix_file_impl::discard(uint64_t offset, uint64_t length) { - return engine()._thread_pool.submit>([this, offset, length] () mutable { - return wrap_syscall(::fallocate(_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, - offset, length)); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future<>(); - }); -} - -future<> -posix_file_impl::allocate(uint64_t position, uint64_t length) { -#ifdef FALLOC_FL_ZERO_RANGE - // FALLOC_FL_ZERO_RANGE is fairly new, so don't fail if it's not supported. - static bool supported = true; - if (!supported) { - return make_ready_future<>(); - } - return engine()._thread_pool.submit>([this, position, length] () mutable { - auto ret = ::fallocate(_fd, FALLOC_FL_ZERO_RANGE|FALLOC_FL_KEEP_SIZE, position, length); - if (ret == -1 && errno == EOPNOTSUPP) { - ret = 0; - supported = false; // Racy, but harmless. At most we issue an extra call or two. - } - return wrap_syscall(ret); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future<>(); - }); -#else - return make_ready_future<>(); -#endif -} - -future<> -blockdev_file_impl::discard(uint64_t offset, uint64_t length) { - return engine()._thread_pool.submit>([this, offset, length] () mutable { - uint64_t range[2] { offset, length }; - return wrap_syscall(::ioctl(_fd, BLKDISCARD, &range)); - }).then([] (syscall_result sr) { - sr.throw_if_error(); - return make_ready_future<>(); - }); -} - -future<> -blockdev_file_impl::allocate(uint64_t position, uint64_t length) { - // nothing to do for block device - return make_ready_future<>(); -} - -future -posix_file_impl::size(void) { - auto r = ::lseek(_fd, 0, SEEK_END); - if (r == -1) { - return make_exception_future(std::system_error(errno, std::system_category())); - } - return make_ready_future(r); -} - -future<> -posix_file_impl::close() { - return engine()._thread_pool.submit>([fd = _fd] { - return wrap_syscall(::close(fd)); - }).then([this] (syscall_result sr) { - _fd = -1; - sr.throw_if_error(); - }); -} - -future -blockdev_file_impl::size(void) { - return engine()._thread_pool.submit>([this] { - uint64_t size; - int ret = ::ioctl(_fd, BLKGETSIZE64, &size); - return wrap_syscall(ret, size); - }).then([] (syscall_result_extra ret) { - ret.throw_if_error(); - return make_ready_future(ret.extra); - }); -} - -subscription -posix_file_impl::list_directory(std::function (directory_entry de)> next) { - struct work { - stream s; - unsigned current = 0; - unsigned total = 0; - bool eof = false; - int error = 0; - char buffer[8192]; - }; - - // While it would be natural to use fdopendir()/readdir(), - // our syscall thread pool doesn't support malloc(), which is - // required for this to work. So resort to using getdents() - // instead. - - // From getdents(2): - struct linux_dirent { - unsigned long d_ino; /* Inode number */ - unsigned long d_off; /* Offset to next linux_dirent */ - unsigned short d_reclen; /* Length of this linux_dirent */ - char d_name[]; /* Filename (null-terminated) */ - /* length is actually (d_reclen - 2 - - offsetof(struct linux_dirent, d_name)) */ - /* - char pad; // Zero padding byte - char d_type; // File type (only since Linux - // 2.6.4); offset is (d_reclen - 1) - */ - }; - - auto w = make_lw_shared(); - auto ret = w->s.listen(std::move(next)); - w->s.started().then([w, this] { - auto eofcond = [w] { return w->eof; }; - return do_until(eofcond, [w, this] { - if (w->current == w->total) { - return engine()._thread_pool.submit>([w , this] () { - auto ret = ::syscall(__NR_getdents, _fd, reinterpret_cast(w->buffer), sizeof(w->buffer)); - return wrap_syscall(ret); - }).then([w] (syscall_result ret) { - ret.throw_if_error(); - if (ret.result == 0) { - w->eof = true; - } else { - w->current = 0; - w->total = ret.result; - } - }); - } - auto start = w->buffer + w->current; - auto de = reinterpret_cast(start); - std::experimental::optional type; - switch (start[de->d_reclen - 1]) { - case DT_BLK: - type = directory_entry_type::block_device; - break; - case DT_CHR: - type = directory_entry_type::char_device; - break; - case DT_DIR: - type = directory_entry_type::directory; - break; - case DT_FIFO: - type = directory_entry_type::fifo; - break; - case DT_REG: - type = directory_entry_type::regular; - break; - case DT_SOCK: - type = directory_entry_type::socket; - break; - default: - // unknown, ignore - ; - } - w->current += de->d_reclen; - sstring name = de->d_name; - if (name == "." || name == "..") { - return make_ready_future<>(); - } - return w->s.produce({std::move(name), type}); - }); - }).then([w] { - w->s.close(); - }); - return ret; -} - -void reactor::enable_timer(clock_type::time_point when) -{ -#ifndef HAVE_OSV - itimerspec its; - its.it_interval = {}; - its.it_value = to_timespec(when); - auto ret = timer_settime(_timer, TIMER_ABSTIME, &its, NULL); - throw_system_error_on(ret == -1); -#else - using ns = std::chrono::nanoseconds; - WITH_LOCK(_timer_mutex) { - _timer_due = std::chrono::duration_cast(when.time_since_epoch()).count(); - _timer_cond.wake_one(); - } -#endif -} - -void reactor::add_timer(timer<>* tmr) { - if (queue_timer(tmr)) { - enable_timer(_timers.get_next_timeout()); - } -} - -bool reactor::queue_timer(timer<>* tmr) { - return _timers.insert(*tmr); -} - -void reactor::del_timer(timer<>* tmr) { - if (tmr->_expired) { - _expired_timers.erase(_expired_timers.iterator_to(*tmr)); - tmr->_expired = false; - } else { - _timers.remove(*tmr); - } -} - -void reactor::add_timer(timer* tmr) { - if (queue_timer(tmr)) { - _lowres_next_timeout = _lowres_timers.get_next_timeout(); - } -} - -bool reactor::queue_timer(timer* tmr) { - return _lowres_timers.insert(*tmr); -} - -void reactor::del_timer(timer* tmr) { - if (tmr->_expired) { - _expired_lowres_timers.erase(_expired_lowres_timers.iterator_to(*tmr)); - tmr->_expired = false; - } else { - _lowres_timers.remove(*tmr); - } -} - -future<> reactor::run_exit_tasks() { - _exit_promise.set_value(); - return std::move(_exit_future); -} - -void reactor::stop() { - assert(engine()._id == 0); - run_exit_tasks().then([this] { - auto sem = new semaphore(0); - for (unsigned i = 1; i < smp::count; i++) { - smp::submit_to<>(i, []() { - return engine().run_exit_tasks().then([] { - engine()._stopped = true; - }); - }).then([sem, i]() { - sem->signal(); - }); - } - sem->wait(smp::count - 1).then([sem, this](){ - _stopped = true; - delete sem; - }); - }); -} - -void reactor::exit(int ret) { - smp::submit_to(0, [this, ret] { _return = ret; stop(); }); -} - - -struct reactor::collectd_registrations { - scollectd::registrations regs; -}; - -reactor::collectd_registrations -reactor::register_collectd_metrics() { - return collectd_registrations{ { - // queue_length value:GAUGE:0:U - // Absolute value of num tasks in queue. - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "queue_length", "tasks-pending") - , scollectd::make_typed(scollectd::data_type::GAUGE - , std::bind(&decltype(_pending_tasks)::size, &_pending_tasks)) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "total_operations", "tasks-processed") - , scollectd::make_typed(scollectd::data_type::DERIVE, _tasks_processed) - ), - // queue_length value:GAUGE:0:U - // Absolute value of num timers in queue. - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "queue_length", "timers-pending") - , scollectd::make_typed(scollectd::data_type::GAUGE - , std::bind(&decltype(_timers)::size, &_timers)) - ), - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "queue_length", "idle") - , scollectd::make_typed(scollectd::data_type::GAUGE, - [this] () -> uint32_t { return _load * 100; }) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "total_operations", "aio-reads") - , scollectd::make_typed(scollectd::data_type::DERIVE, _aio_reads) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "total_operations", "aio-writes") - , scollectd::make_typed(scollectd::data_type::DERIVE, _aio_writes) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "total_operations", "fsyncs") - , scollectd::make_typed(scollectd::data_type::DERIVE, _fsyncs) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("reactor" - , scollectd::per_cpu_plugin_instance - , "total_operations", "io-threaded-fallbacks") - , scollectd::make_typed(scollectd::data_type::DERIVE, - std::bind(&thread_pool::operation_count, &_thread_pool)) - ), - scollectd::add_polled_metric( - scollectd::type_instance_id("memory", - scollectd::per_cpu_plugin_instance, - "total_operations", "malloc"), - scollectd::make_typed(scollectd::data_type::DERIVE, - [] { return memory::stats().mallocs(); }) - ), - scollectd::add_polled_metric( - scollectd::type_instance_id("memory", - scollectd::per_cpu_plugin_instance, - "total_operations", "free"), - scollectd::make_typed(scollectd::data_type::DERIVE, - [] { return memory::stats().frees(); }) - ), - scollectd::add_polled_metric( - scollectd::type_instance_id("memory", - scollectd::per_cpu_plugin_instance, - "total_operations", "cross_cpu_free"), - scollectd::make_typed(scollectd::data_type::DERIVE, - [] { return memory::stats().cross_cpu_frees(); }) - ), - scollectd::add_polled_metric( - scollectd::type_instance_id("memory", - scollectd::per_cpu_plugin_instance, - "objects", "malloc"), - scollectd::make_typed(scollectd::data_type::GAUGE, - [] { return memory::stats().live_objects(); }) - ), - scollectd::add_polled_metric( - scollectd::type_instance_id("memory", - scollectd::per_cpu_plugin_instance, - "memory", "free_memory"), - scollectd::make_typed(scollectd::data_type::GAUGE, - [] { return memory::stats().free_memory(); }) - ), - scollectd::add_polled_metric( - scollectd::type_instance_id("memory", - scollectd::per_cpu_plugin_instance, - "total_operations", "reclaims"), - scollectd::make_typed(scollectd::data_type::DERIVE, - [] { return memory::stats().reclaims(); }) - ), - } }; -} - -void reactor::run_tasks(circular_buffer>& tasks, size_t quota) { - task_quota = quota; - while (!tasks.empty() && task_quota) { - --task_quota; - auto tsk = std::move(tasks.front()); - tasks.pop_front(); - tsk->run(); - tsk.reset(); - ++_tasks_processed; - } -} - -int reactor::run() { - auto collectd_metrics = register_collectd_metrics(); - -#ifndef HAVE_OSV - poller io_poller([&] { return process_io(); }); -#endif - - poller sig_poller([&] { return _signals.poll_signal(); } ); - poller aio_poller(std::bind(&reactor::flush_pending_aio, this)); - - if (_id == 0) { - if (_handle_sigint) { - _signals.handle_signal_once(SIGINT, [this] { stop(); }); - } - _signals.handle_signal_once(SIGTERM, [this] { stop(); }); - } - - _cpu_started.wait(smp::count).then([this] { - _network_stack->initialize().then([this] { - _start_promise.set_value(); - }); - }); - _network_stack_ready_promise.get_future().then([this] (std::unique_ptr stack) { - _network_stack = std::move(stack); - for (unsigned c = 0; c < smp::count; c++) { - smp::submit_to(c, [] { - engine()._cpu_started.signal(); - }); - } - }); - - // Register smp queues poller - std::experimental::optional smp_poller; - if (smp::count > 1) { - smp_poller = poller(smp::poll_queues); - } - -#ifndef HAVE_OSV - _signals.handle_signal(alarm_signal(), [this] { - complete_timers(_timers, _expired_timers, [this] { - if (!_timers.empty()) { - enable_timer(_timers.get_next_timeout()); - } - }); - }); -#endif - - poller drain_cross_cpu_freelist([] { - return memory::drain_cross_cpu_freelist(); - }); - - poller expire_lowres_timers([this] { - if (_lowres_next_timeout == lowres_clock::time_point()) { - return false; - } - auto now = lowres_clock::now(); - if (now > _lowres_next_timeout) { - complete_timers(_lowres_timers, _expired_lowres_timers, [this] { - if (!_lowres_timers.empty()) { - _lowres_next_timeout = _lowres_timers.get_next_timeout(); - } else { - _lowres_next_timeout = lowres_clock::time_point(); - } - }); - return true; - } - return false; - }); - - using namespace std::chrono_literals; - timer load_timer; - std::chrono::high_resolution_clock::rep idle_count = 0; - auto idle_start = std::chrono::high_resolution_clock::now(), idle_end = idle_start; - load_timer.set_callback([this, &idle_count, &idle_start, &idle_end] () mutable { - auto load = double(idle_count + (idle_end - idle_start).count()) / double(std::chrono::duration_cast(1s).count()); - load = std::min(load, 1.0); - idle_count = 0; - idle_start = idle_end; - _loads.push_front(load); - if (_loads.size() > 5) { - auto drop = _loads.back(); - _loads.pop_back(); - _load -= (drop/5); - } - _load += (load/5); - }); - load_timer.arm_periodic(1s); - - bool idle = false; - - while (true) { - run_tasks(_pending_tasks, _task_quota); - if (_stopped) { - load_timer.cancel(); - run_tasks(_at_destroy_tasks, _at_destroy_tasks.size()); - if (_id == 0) { - smp::join_all(); - } - break; - } - - if (!poll_once() && _pending_tasks.empty()) { - idle_end = std::chrono::high_resolution_clock::now(); - if (!idle) { - idle_start = idle_end; - idle = true; - } - _mm_pause(); - } else { - if (idle) { - idle_count += (idle_end - idle_start).count(); - idle = false; - } - } - } - return _return; -} - -bool -reactor::poll_once() { - bool work = false; - for (auto c : _pollers) { - work |= c->poll_and_check_more_work(); - } - - return work; -} - -class reactor::poller::registration_task : public task { -private: - poller* _p; -public: - explicit registration_task(poller* p) : _p(p) {} - virtual void run() noexcept override { - if (_p) { - engine().register_poller(_p->_pollfn.get()); - _p->_registration_task = nullptr; - } - } - void cancel() { - _p = nullptr; - } - void moved(poller* p) { - _p = p; - } -}; - -class reactor::poller::deregistration_task : public task { -private: - std::unique_ptr _p; -public: - explicit deregistration_task(std::unique_ptr&& p) : _p(std::move(p)) {} - virtual void run() noexcept override { - engine().unregister_poller(_p.get()); - } -}; - -void reactor::register_poller(pollfn* p) { - _pollers.push_back(p); -} - -void reactor::unregister_poller(pollfn* p) { - _pollers.erase(std::find(_pollers.begin(), _pollers.end(), p)); -} - -void reactor::replace_poller(pollfn* old, pollfn* neww) { - std::replace(_pollers.begin(), _pollers.end(), old, neww); -} - -reactor::poller::poller(poller&& x) - : _pollfn(std::move(x._pollfn)), _registration_task(x._registration_task) { - if (_pollfn && _registration_task) { - _registration_task->moved(this); - } -} - -reactor::poller& -reactor::poller::operator=(poller&& x) { - if (this != &x) { - this->~poller(); - new (this) poller(std::move(x)); - } - return *this; -} - -void -reactor::poller::do_register() { - // We can't just insert a poller into reactor::_pollers, because we - // may be running inside a poller ourselves, and so in the middle of - // iterating reactor::_pollers itself. So we schedule a task to add - // the poller instead. - auto task = std::make_unique(this); - auto tmp = task.get(); - engine().add_task(std::move(task)); - _registration_task = tmp; -} - -reactor::poller::~poller() { - // We can't just remove the poller from reactor::_pollers, because we - // may be running inside a poller ourselves, and so in the middle of - // iterating reactor::_pollers itself. So we schedule a task to remove - // the poller instead. - // - // Since we don't want to call the poller after we exit the destructor, - // we replace it atomically with another one, and schedule a task to - // delete the replacement. - if (_pollfn) { - if (_registration_task) { - // not added yet, so don't do it at all. - _registration_task->cancel(); - } else { - auto dummy = make_pollfn([] { return false; }); - auto dummy_p = dummy.get(); - auto task = std::make_unique(std::move(dummy)); - engine().add_task(std::move(task)); - engine().replace_poller(_pollfn.get(), dummy_p); - } - } -} - -bool -reactor_backend_epoll::wait_and_process() { - std::array eevt; - int nr = ::epoll_wait(_epollfd.get(), eevt.data(), eevt.size(), 0); - if (nr == -1 && errno == EINTR) { - return false; // gdb can cause this - } - assert(nr != -1); - for (int i = 0; i < nr; ++i) { - auto& evt = eevt[i]; - auto pfd = reinterpret_cast(evt.data.ptr); - auto events = evt.events & (EPOLLIN | EPOLLOUT); - auto events_to_remove = events & ~pfd->events_requested; - complete_epoll_event(*pfd, &pollable_fd_state::pollin, events, EPOLLIN); - complete_epoll_event(*pfd, &pollable_fd_state::pollout, events, EPOLLOUT); - if (events_to_remove) { - pfd->events_epoll &= ~events_to_remove; - evt.events = pfd->events_epoll; - auto op = evt.events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; - ::epoll_ctl(_epollfd.get(), op, pfd->fd.get(), &evt); - } - } - return nr; -} - -syscall_work_queue::syscall_work_queue() - : _pending() - , _completed() - , _start_eventfd(0) { -} - -void syscall_work_queue::submit_item(syscall_work_queue::work_item* item) { - _queue_has_room.wait().then([this, item] { - _pending.push(item); - _start_eventfd.signal(1); - }); -} - -void syscall_work_queue::complete() { - auto nr = _completed.consume_all([this] (work_item* wi) { - wi->complete(); - delete wi; - }); - _queue_has_room.signal(nr); -} - -smp_message_queue::smp_message_queue() - : _pending() - , _completed() -{ -} - -void smp_message_queue::move_pending() { - auto queue_room = queue_length - _current_queue_length; - auto nr = std::min(queue_room, _tx.a.pending_fifo.size()); - if (!nr) { - return; - } - auto begin = _tx.a.pending_fifo.begin(); - auto end = begin + nr; - _pending.push(begin, end); - _tx.a.pending_fifo.erase(begin, end); - _current_queue_length += nr; - _last_snt_batch = nr; - _sent += nr; -} - -void smp_message_queue::submit_item(smp_message_queue::work_item* item) { - _tx.a.pending_fifo.push_back(item); - if (_tx.a.pending_fifo.size() >= batch_size) { - move_pending(); - } -} - -void smp_message_queue::respond(work_item* item) { - _completed_fifo.push_back(item); - if (_completed_fifo.size() >= batch_size || engine()._stopped) { - flush_response_batch(); - } -} - -void smp_message_queue::flush_response_batch() { - if (!_completed_fifo.empty()) { - _completed.push(_completed_fifo.begin(), _completed_fifo.end()); - _completed_fifo.clear(); - } -} - -template -size_t smp_message_queue::process_queue(lf_queue& q, Func process) { - // copy batch to local memory in order to minimize - // time in which cross-cpu data is accessed - work_item* items[queue_length + PrefetchCnt]; - work_item* wi; - if (!q.pop(wi)) - return 0; - // start prefecthing first item before popping the rest to overlap memory - // access with potential cache miss the second pop may cause - prefetch<2>(wi); - auto nr = q.pop(items); - std::fill(std::begin(items) + nr, std::begin(items) + nr + PrefetchCnt, nr ? items[nr - 1] : wi); - unsigned i = 0; - do { - prefetch_n<2>(std::begin(items) + i, std::begin(items) + i + PrefetchCnt); - process(wi); - wi = items[i++]; - } while(i <= nr); - - return nr + 1; -} - -size_t smp_message_queue::process_completions() { - auto nr = process_queue(_completed, [] (work_item* wi) { - wi->complete(); - delete wi; - }); - _current_queue_length -= nr; - _compl += nr; - _last_cmpl_batch = nr; - - return nr; -} - -void smp_message_queue::flush_request_batch() { - move_pending(); -} - -size_t smp_message_queue::process_incoming() { - auto nr = process_queue(_pending, [this] (work_item* wi) { - wi->process().then([this, wi] { - respond(wi); - }); - }); - _received += nr; - _last_rcv_batch = nr; - return nr; -} - -void smp_message_queue::start(unsigned cpuid) { - _tx.init(); - char instance[10]; - std::snprintf(instance, sizeof(instance), "%u-%u", engine().cpu_id(), cpuid); - _collectd_regs = scollectd::registrations({ - // queue_length value:GAUGE:0:U - // Absolute value of num packets in last tx batch. - scollectd::add_polled_metric(scollectd::type_instance_id("smp" - , instance - , "queue_length", "send-batch") - , scollectd::make_typed(scollectd::data_type::GAUGE, _last_snt_batch) - ), - scollectd::add_polled_metric(scollectd::type_instance_id("smp" - , instance - , "queue_length", "receive-batch") - , scollectd::make_typed(scollectd::data_type::GAUGE, _last_rcv_batch) - ), - scollectd::add_polled_metric(scollectd::type_instance_id("smp" - , instance - , "queue_length", "complete-batch") - , scollectd::make_typed(scollectd::data_type::GAUGE, _last_cmpl_batch) - ), - scollectd::add_polled_metric(scollectd::type_instance_id("smp" - , instance - , "queue_length", "send-queue-length") - , scollectd::make_typed(scollectd::data_type::GAUGE, _current_queue_length) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("smp" - , instance - , "total_operations", "received-messages") - , scollectd::make_typed(scollectd::data_type::DERIVE, _received) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("smp" - , instance - , "total_operations", "sent-messages") - , scollectd::make_typed(scollectd::data_type::DERIVE, _sent) - ), - // total_operations value:DERIVE:0:U - scollectd::add_polled_metric(scollectd::type_instance_id("smp" - , instance - , "total_operations", "completed-messages") - , scollectd::make_typed(scollectd::data_type::DERIVE, _compl) - ), - }); -} - -/* not yet implemented for OSv. TODO: do the notification like we do class smp. */ -#ifndef HAVE_OSV -thread_pool::thread_pool() : _worker_thread([this] { work(); }), _notify(pthread_self()) { - engine()._signals.handle_signal(SIGUSR1, [this] { inter_thread_wq.complete(); }); -} - -void thread_pool::work() { - sigset_t mask; - sigfillset(&mask); - auto r = ::sigprocmask(SIG_BLOCK, &mask, NULL); - throw_system_error_on(r == -1); - while (true) { - uint64_t count; - auto r = ::read(inter_thread_wq._start_eventfd.get_read_fd(), &count, sizeof(count)); - assert(r == sizeof(count)); - if (_stopped.load(std::memory_order_relaxed)) { - break; - } - inter_thread_wq._pending.consume_all([this] (syscall_work_queue::work_item* wi) { - wi->process(); - inter_thread_wq._completed.push(wi); - }); - pthread_kill(_notify, SIGUSR1); - } -} - -thread_pool::~thread_pool() { - _stopped.store(true, std::memory_order_relaxed); - inter_thread_wq._start_eventfd.signal(1); - _worker_thread.join(); -} -#endif - -readable_eventfd writeable_eventfd::read_side() { - return readable_eventfd(_fd.dup()); -} - -file_desc writeable_eventfd::try_create_eventfd(size_t initial) { - assert(size_t(int(initial)) == initial); - return file_desc::eventfd(initial, EFD_CLOEXEC); -} - -void writeable_eventfd::signal(size_t count) { - uint64_t c = count; - auto r = _fd.write(&c, sizeof(c)); - assert(r == sizeof(c)); -} - -writeable_eventfd readable_eventfd::write_side() { - return writeable_eventfd(_fd.get_file_desc().dup()); -} - -file_desc readable_eventfd::try_create_eventfd(size_t initial) { - assert(size_t(int(initial)) == initial); - return file_desc::eventfd(initial, EFD_CLOEXEC | EFD_NONBLOCK); -} - -future readable_eventfd::wait() { - return engine().readable(*_fd._s).then([this] { - uint64_t count; - int r = ::read(_fd.get_fd(), &count, sizeof(count)); - assert(r == sizeof(count)); - return make_ready_future(count); - }); -} - -void schedule(std::unique_ptr t) { - engine().add_task(std::move(t)); -} - -bool operator==(const ::sockaddr_in a, const ::sockaddr_in b) { - return (a.sin_addr.s_addr == b.sin_addr.s_addr) && (a.sin_port == b.sin_port); -} - -void network_stack_registry::register_stack(sstring name, - boost::program_options::options_description opts, - std::function> (options opts)> create, bool make_default) { - _map()[name] = std::move(create); - options_description().add(opts); - if (make_default) { - _default() = name; - } -} - -sstring network_stack_registry::default_stack() { - return _default(); -} - -std::vector network_stack_registry::list() { - std::vector ret; - for (auto&& ns : _map()) { - ret.push_back(ns.first); - } - return ret; -} - -future> -network_stack_registry::create(options opts) { - return create(_default(), opts); -} - -future> -network_stack_registry::create(sstring name, options opts) { - return _map()[name](opts); -} - -boost::program_options::options_description -reactor::get_options_description() { - namespace bpo = boost::program_options; - bpo::options_description opts("Core options"); - auto net_stack_names = network_stack_registry::list(); - opts.add_options() - ("network-stack", bpo::value(), - sprint("select network stack (valid values: %s)", - format_separated(net_stack_names.begin(), net_stack_names.end(), ", ")).c_str()) - ("no-handle-interrupt", "ignore SIGINT (for gdb)") - ("task-quota", bpo::value()->default_value(200), "Max number of tasks executed between polls and in loops") - ; - opts.add(network_stack_registry::options_description()); - return opts; -} - -// We need a wrapper class, because boost::program_options wants validate() -// (below) to be in the same namespace as the type it is validating. -struct cpuset_wrapper { - resource::cpuset value; -}; - -// Overload for boost program options parsing/validation -void validate(boost::any& v, - const std::vector& values, - cpuset_wrapper* target_type, int) { - using namespace boost::program_options; - static std::regex r("(\\d+-)?(\\d+)(,(\\d+-)?(\\d+))*"); - validators::check_first_occurrence(v); - // Extract the first string from 'values'. If there is more than - // one string, it's an error, and exception will be thrown. - auto&& s = validators::get_single_string(values); - std::smatch match; - if (std::regex_match(s, match, r)) { - std::vector ranges; - boost::split(ranges, s, boost::is_any_of(",")); - cpuset_wrapper ret; - for (auto&& range: ranges) { - std::string beg = range; - std::string end = range; - auto dash = range.find('-'); - if (dash != range.npos) { - beg = range.substr(0, dash); - end = range.substr(dash + 1); - } - auto b = boost::lexical_cast(beg); - auto e = boost::lexical_cast(end); - if (b > e) { - throw validation_error(validation_error::invalid_option_value); - } - for (auto i = b; i <= e; ++i) { - std::cout << "adding " << i << "\n"; - ret.value.insert(i); - } - } - v = std::move(ret); - } else { - throw validation_error(validation_error::invalid_option_value); - } -} - -boost::program_options::options_description -smp::get_options_description() -{ - namespace bpo = boost::program_options; - bpo::options_description opts("SMP options"); - opts.add_options() - ("smp,c", bpo::value(), "number of threads (default: one per CPU)") - ("cpuset", bpo::value(), "CPUs to use (in cpuset(7) format; default: all))") - ("memory,m", bpo::value(), "memory to use, in bytes (ex: 4G) (default: all)") - ("reserve-memory", bpo::value()->default_value("512M"), "memory reserved to OS") - ("hugepages", bpo::value(), "path to accessible hugetlbfs mount (typically /dev/hugepages/something)") - ; - return opts; -} - -std::vector smp::_threads; -smp_message_queue** smp::_qs; -std::thread::id smp::_tmain; -unsigned smp::count = 1; - -void smp::start_all_queues() -{ - for (unsigned c = 0; c < count; c++) { - if (c != engine().cpu_id()) { - _qs[c][engine().cpu_id()].start(c); - } - } -} - -#ifdef HAVE_DPDK - -int dpdk_thread_adaptor(void* f) -{ - (*static_cast*>(f))(); - return 0; -} - -void smp::join_all() -{ - rte_eal_mp_wait_lcore(); -} - -void smp::pin(unsigned cpu_id) { -} -#else -void smp::join_all() -{ - for (auto&& t: smp::_threads) { - t.join(); - } -} - -void smp::pin(unsigned cpu_id) { - pin_this_thread(cpu_id); -} -#endif - -void smp::allocate_reactor() { - struct reactor_deleter { - void operator()(reactor* p) { - p->~reactor(); - free(p); - } - }; - static thread_local std::unique_ptr - reactor_holder; - - assert(!reactor_holder); - - // we cannot just write "local_engin = new reactor" since reactor's constructor - // uses local_engine - void *buf; - int r = posix_memalign(&buf, 64, sizeof(reactor)); - assert(r == 0); - local_engine = reinterpret_cast(buf); - new (buf) reactor; - reactor_holder.reset(local_engine); -} - -void smp::cleanup() { - smp::_threads = std::vector(); -} - -void smp::configure(boost::program_options::variables_map configuration) -{ - smp::count = 1; - smp::_tmain = std::this_thread::get_id(); - auto nr_cpus = resource::nr_processing_units(); - resource::cpuset cpu_set; - std::copy(boost::counting_iterator(0), boost::counting_iterator(nr_cpus), - std::inserter(cpu_set, cpu_set.end())); - if (configuration.count("cpuset")) { - cpu_set = configuration["cpuset"].as().value; - } - if (configuration.count("smp")) { - nr_cpus = configuration["smp"].as(); - } else { - nr_cpus = cpu_set.size(); - } - smp::count = nr_cpus; - resource::configuration rc; - if (configuration.count("memory")) { - rc.total_memory = parse_memory_size(configuration["memory"].as()); -#ifdef HAVE_DPDK - if (configuration.count("hugepages") && - !configuration["network-stack"].as().compare("native") && - configuration.count("dpdk-pmd")) { - size_t dpdk_memory = dpdk::eal::mem_size(smp::count); - - if (dpdk_memory >= rc.total_memory) { - std::cerr<<"Can't run with the given amount of memory: "; - std::cerr<(); - std::cerr<<". Consider giving more."<()); - } - std::experimental::optional hugepages_path; - if (configuration.count("hugepages")) { - hugepages_path = configuration["hugepages"].as(); - } - rc.cpus = smp::count; - rc.cpu_set = std::move(cpu_set); - std::vector allocations = resource::allocate(rc); - smp::pin(allocations[0].cpu_id); - memory::configure(allocations[0].mem, hugepages_path); - smp::_qs = new smp_message_queue* [smp::count]; - for(unsigned i = 0; i < smp::count; i++) { - smp::_qs[i] = new smp_message_queue[smp::count]; - } - -#ifdef HAVE_DPDK - dpdk::eal::cpuset cpus; - for (auto&& a : allocations) { - cpus[a.cpu_id] = true; - } - dpdk::eal::init(cpus, configuration); -#endif - - // Better to put it into the smp class, but at smp construction time - // correct smp::count is not known. - static boost::barrier inited(smp::count); - - unsigned i; - for (i = 1; i < smp::count; i++) { - auto allocation = allocations[i]; - _threads.emplace_back([configuration, hugepages_path, i, allocation] { - smp::pin(allocation.cpu_id); - memory::configure(allocation.mem, hugepages_path); - sigset_t mask; - sigfillset(&mask); - auto r = ::sigprocmask(SIG_BLOCK, &mask, NULL); - throw_system_error_on(r == -1); - allocate_reactor(); - engine()._id = i; - start_all_queues(); - inited.wait(); - engine().configure(configuration); - engine().run(); - }); - } - - allocate_reactor(); - -#ifdef HAVE_DPDK - auto it = _threads.begin(); - RTE_LCORE_FOREACH_SLAVE(i) { - rte_eal_remote_launch(dpdk_thread_adaptor, static_cast(&*(it++)), i); - } -#endif - - start_all_queues(); - inited.wait(); - engine().configure(configuration); - engine()._lowres_clock = std::make_unique(); -} - -__thread size_t future_avail_count = 0; -__thread size_t task_quota = 0; - -__thread reactor* local_engine; - -class reactor_notifier_epoll : public reactor_notifier { - writeable_eventfd _write; - readable_eventfd _read; -public: - reactor_notifier_epoll() - : _write() - , _read(_write.read_side()) { - } - virtual future<> wait() override { - // convert _read.wait(), a future, to a future<>: - return _read.wait().then([this] (size_t ignore) { - return make_ready_future<>(); - }); - } - virtual void signal() override { - _write.signal(1); - } -}; - -std::unique_ptr -reactor_backend_epoll::make_reactor_notifier() { - return std::make_unique(); -} - -#ifdef HAVE_OSV -class reactor_notifier_osv : - public reactor_notifier, private osv::newpoll::pollable { - promise<> _pr; - // TODO: pollable should probably remember its poller, so we shouldn't - // need to keep another copy of this pointer - osv::newpoll::poller *_poller = nullptr; - bool _needed = false; -public: - virtual future<> wait() override { - return engine().notified(this); - } - virtual void signal() override { - wake(); - } - virtual void on_wake() override { - _pr.set_value(); - _pr = promise<>(); - // We try to avoid del()/add() ping-pongs: After an one occurance of - // the event, we don't del() but rather set needed=false. We guess - // the future's continuation (scheduler by _pr.set_value() above) - // will make the pollable needed again. Only if we reach this callback - // a second time, and needed is still false, do we finally del(). - if (!_needed) { - _poller->del(this); - _poller = nullptr; - - } - _needed = false; - } - - void enable(osv::newpoll::poller &poller) { - _needed = true; - if (_poller == &poller) { - return; - } - assert(!_poller); // don't put same pollable on multiple pollers! - _poller = &poller; - _poller->add(this); - } - - virtual ~reactor_notifier_osv() { - if (_poller) { - _poller->del(this); - } - } - - friend class reactor_backend_osv; -}; - -std::unique_ptr -reactor_backend_osv::make_reactor_notifier() { - return std::make_unique(); -} -#endif - - -#ifdef HAVE_OSV -reactor_backend_osv::reactor_backend_osv() { -} - -bool -reactor_backend_osv::wait_and_process() { - _poller.process(); - // osv::poller::process runs pollable's callbacks, but does not currently - // have a timer expiration callback - instead if gives us an expired() - // function we need to check: - if (_poller.expired()) { - _timer_promise.set_value(); - _timer_promise = promise<>(); - } - return true; -} - -future<> -reactor_backend_osv::notified(reactor_notifier *notifier) { - // reactor_backend_osv::make_reactor_notifier() generates a - // reactor_notifier_osv, so we only can work on such notifiers. - reactor_notifier_osv *n = dynamic_cast(notifier); - if (n->read()) { - return make_ready_future<>(); - } - n->enable(_poller); - return n->_pr.get_future(); -} - - -future<> -reactor_backend_osv::readable(pollable_fd_state& fd) { - std::cout << "reactor_backend_osv does not support file descriptors - readable() shouldn't have been called!\n"; - abort(); -} - -future<> -reactor_backend_osv::writeable(pollable_fd_state& fd) { - std::cout << "reactor_backend_osv does not support file descriptors - writeable() shouldn't have been called!\n"; - abort(); -} - -void -reactor_backend_osv::forget(pollable_fd_state& fd) { - std::cout << "reactor_backend_osv does not support file descriptors - forget() shouldn't have been called!\n"; - abort(); -} - -void -reactor_backend_osv::enable_timer(clock_type::time_point when) { - _poller.set_timer(when); -} - -#endif - -void report_exception(sstring message, std::exception_ptr eptr) { -#ifndef __GNUC__ - std::cerr << message << ".\n"; -#else - try { - std::rethrow_exception(eptr); - } catch(...) { - auto tp = abi::__cxa_current_exception_type(); - std::cerr << message; - if (tp) { - int status; - char *demangled = abi::__cxa_demangle(tp->name(), 0, 0, &status); - std::cerr << " of type '"; - if (status == 0) { - std::cerr << demangled; - free(demangled); - } else { - std::cerr << tp->name(); - } - std::cerr << "'"; - } else { - std::cerr << " of unknown type"; - } - // Print more information on some known exception types - try { - throw; - } catch(const std::system_error &e) { - std::cerr << ": Error " << e.code() << " (" << e.code().message() << ")\n"; - } catch(const std::exception& e) { - std::cerr << ": " << e.what() << "\n"; - } catch(...) { - std::cerr << ".\n"; - } - } -#endif -} - -/** - * engine_exit() exits the reactor. It should be given a pointer to the - * exception which prompted this exit - or a null pointer if the exit - * request was not caused by any exception. - */ -void engine_exit(std::exception_ptr eptr) { - if (!eptr) { - engine().exit(0); - return; - } - report_exception("Exiting on unhandled exception", eptr); - engine().exit(1); -} - -void report_failed_future(std::exception_ptr eptr) { - report_exception("WARNING: exceptional future ignored", eptr); -} - - -future open_file_dma(sstring name, open_flags flags) { - return engine().open_file_dma(std::move(name), flags); -} - -future open_directory(sstring name) { - return engine().open_directory(std::move(name)); -} - -future<> make_directory(sstring name) { - return engine().make_directory(std::move(name)); -} - -future<> touch_directory(sstring name) { - return make_directory(name).then_wrapped([] (future<> f) { - try { - f.get(); - } catch (std::system_error& e) { - if (e.code() != std::error_code(EEXIST, std::system_category())) { - throw; - } - } - }); -} - -/// \cond internal -future<> do_flush_directory(sstring name) { - if (name.empty()) { - return make_ready_future<>(); - } - - return open_directory(name).then([] (file f) { - return f.flush().then([f] () mutable { - return f.close(); - }); - }); -} - -future<> do_recursive_touch_directory(sstring base, sstring name) { - static const sstring::value_type separator = '/'; - - if (name.empty()) { - return make_ready_future<>(); - } - - size_t pos = std::min(name.find(separator), name.size() - 1); - base += name.substr(0 , pos + 1); - name = name.substr(pos + 1); - return touch_directory(base).then([base, name] { - return do_recursive_touch_directory(base, name); - }).then([base] { - // We will now flush the directory that holds the entry we potentially - // created. Technically speaking, we only need to touch when we did - // create. But flushing the unchanged ones should be cheap enough - and - // it simplifies the code considerably. - return do_flush_directory(base); - }); -} -/// \endcond - -future<> recursive_touch_directory(sstring name) { - // If the name is empty, it will be of the type a/b/c, which should be interpreted as - // a relative path. This means we have to flush our current directory - sstring base = ""; - if (name[0] == '/' || name[0] == '.') { - base = "./"; - } - return do_recursive_touch_directory(base, name); -} - -future<> remove_file(sstring pathname) { - return engine().remove_file(std::move(pathname)); -} - -future<> rename_file(sstring old_pathname, sstring new_pathname) { - return engine().rename_file(std::move(old_pathname), std::move(new_pathname)); -} - -server_socket listen(socket_address sa) { - return engine().listen(sa); -} - -server_socket listen(socket_address sa, listen_options opts) { - return engine().listen(sa, opts); -} - -future connect(socket_address sa) { - return engine().connect(sa); -} diff --git a/core/reactor.hh b/core/reactor.hh deleted file mode 100644 index a061890d1c..0000000000 --- a/core/reactor.hh +++ /dev/null @@ -1,1369 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#ifndef REACTOR_HH_ -#define REACTOR_HH_ - -#include "seastar.hh" -#include "iostream.hh" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "util/eclipse.hh" -#include "future.hh" -#include "posix.hh" -#include "apply.hh" -#include "sstring.hh" -#include "deleter.hh" -#include "net/api.hh" -#include "temporary_buffer.hh" -#include "circular_buffer.hh" -#include "file.hh" -#include "semaphore.hh" -#include "core/scattered_message.hh" -#include "core/enum.hh" -#include -#include "timer.hh" - -#ifdef HAVE_OSV -#include -#include -#include -#include -#endif - -namespace scollectd { class registration; } - -class reactor; -class pollable_fd; -class pollable_fd_state; - -struct free_deleter { - void operator()(void* p) { ::free(p); } -}; - -template -inline -std::unique_ptr allocate_aligned_buffer(size_t size, size_t align) { - static_assert(sizeof(CharType) == 1, "must allocate byte type"); - void* ret; - auto r = posix_memalign(&ret, align, size); - assert(r == 0); - return std::unique_ptr(reinterpret_cast(ret)); -} - -class lowres_clock { -public: - typedef int64_t rep; - // The lowres_clock's resolution is 10ms. However, to make it is easier to - // do calcuations with std::chrono::milliseconds, we make the clock's - // period to 1ms instead of 10ms. - typedef std::ratio<1, 1000> period; - typedef std::chrono::duration duration; - typedef std::chrono::time_point time_point; - lowres_clock(); - static time_point now() { - auto nr = _now.load(std::memory_order_relaxed); - return time_point(duration(nr)); - } -private: - static void update(); - // _now is updated by cpu0 and read by other cpus. Make _now on its own - // cache line to avoid false sharing. - static std::atomic _now [[gnu::aligned(64)]]; - // High resolution timer to drive this low resolution clock - timer<> _timer [[gnu::aligned(64)]]; - // High resolution timer expires every 10 milliseconds - static constexpr std::chrono::milliseconds _granularity{10}; -}; - -class pollable_fd_state { -public: - struct speculation { - int events = 0; - explicit speculation(int epoll_events_guessed = 0) : events(epoll_events_guessed) {} - }; - ~pollable_fd_state(); - explicit pollable_fd_state(file_desc fd, speculation speculate = speculation()) - : fd(std::move(fd)), events_known(speculate.events) {} - pollable_fd_state(const pollable_fd_state&) = delete; - void operator=(const pollable_fd_state&) = delete; - void speculate_epoll(int events) { events_known |= events; } - file_desc fd; - int events_requested = 0; // wanted by pollin/pollout promises - int events_epoll = 0; // installed in epoll - int events_known = 0; // returned from epoll - promise<> pollin; - promise<> pollout; - friend class reactor; - friend class pollable_fd; -}; - -inline -size_t iovec_len(const std::vector& iov) -{ - size_t ret = 0; - for (auto&& e : iov) { - ret += e.iov_len; - } - return ret; -} - -class pollable_fd { -public: - using speculation = pollable_fd_state::speculation; - pollable_fd(file_desc fd, speculation speculate = speculation()) - : _s(std::make_unique(std::move(fd), speculate)) {} -public: - pollable_fd(pollable_fd&&) = default; - pollable_fd& operator=(pollable_fd&&) = default; - future read_some(char* buffer, size_t size); - future read_some(uint8_t* buffer, size_t size); - future read_some(const std::vector& iov); - future<> write_all(const char* buffer, size_t size); - future<> write_all(const uint8_t* buffer, size_t size); - future write_some(net::packet& p); - future<> write_all(net::packet& p); - future<> readable(); - future<> writeable(); - void abort_reader(std::exception_ptr ex); - void abort_writer(std::exception_ptr ex); - future accept(); - future sendmsg(struct msghdr *msg); - future recvmsg(struct msghdr *msg); - future sendto(socket_address addr, const void* buf, size_t len); - file_desc& get_file_desc() const { return _s->fd; } - void shutdown(int how) { _s->fd.shutdown(how); } - void close() { _s.reset(); } -protected: - int get_fd() const { return _s->fd.get(); } - friend class reactor; - friend class readable_eventfd; - friend class writeable_eventfd; -private: - std::unique_ptr _s; -}; - -class connected_socket_impl { -public: - virtual ~connected_socket_impl() {} - virtual input_stream input() = 0; - virtual output_stream output() = 0; - virtual void shutdown_input() = 0; - virtual void shutdown_output() = 0; -}; - -/// \addtogroup networking-module -/// @{ - -/// A TCP (or other stream-based protocol) connection. -/// -/// A \c connected_socket represents a full-duplex stream between -/// two endpoints, a local endpoint and a remote endpoint. -class connected_socket { - std::unique_ptr _csi; -public: - /// Constructs a \c connected_socket not corresponding to a connection - connected_socket() {}; - /// \cond internal - explicit connected_socket(std::unique_ptr csi) - : _csi(std::move(csi)) {} - /// \endcond - /// Moves a \c connected_socket object. - connected_socket(connected_socket&& cs) = default; - /// Move-assigns a \c connected_socket object. - connected_socket& operator=(connected_socket&& cs) = default; - /// Gets the input stream. - /// - /// Gets an object returning data sent from the remote endpoint. - input_stream input(); - /// Gets the output stream. - /// - /// Gets an object that sends data to the remote endpoint. - output_stream output(); - /// Disables output to the socket. - /// - /// Current or future writes that have not been successfully flushed - /// will immediately fail with an error. This is useful to abort - /// operations on a socket that is not making progress due to a - /// peer failure. - void shutdown_output(); - /// Disables input from the socket. - /// - /// Current or future reads will immediately fail with an error. - /// This is useful to abort operations on a socket that is not making - /// progress due to a peer failure. - void shutdown_input(); - /// Disables socket input and output. - /// - /// Equivalent to \ref shutdown_input() and \ref shutdown_output(). -}; -/// @} - -/// \cond internal -class server_socket_impl { -public: - virtual ~server_socket_impl() {} - virtual future accept() = 0; - virtual void abort_accept() = 0; -}; -/// \endcond - -namespace std { - -template <> -struct hash<::sockaddr_in> { - size_t operator()(::sockaddr_in a) const { - return a.sin_port ^ a.sin_addr.s_addr; - } -}; - -} - -bool operator==(const ::sockaddr_in a, const ::sockaddr_in b); - -/// \addtogroup networking-module -/// @{ - -/// A listening socket, waiting to accept incoming network connections. -class server_socket { - std::unique_ptr _ssi; -public: - /// Constructs a \c server_socket not corresponding to a connection - server_socket() {} - /// \cond internal - explicit server_socket(std::unique_ptr ssi) - : _ssi(std::move(ssi)) {} - /// \endcond - /// Moves a \c server_socket object. - server_socket(server_socket&& ss) = default; - /// Move-assigns a \c server_socket object. - server_socket& operator=(server_socket&& cs) = default; - - /// Accepts the next connection to successfully connect to this socket. - /// - /// \return a \ref connected_socket representing the connection, and - /// a \ref socket_address describing the remote endpoint. - /// - /// \see listen(socket_address sa) - /// \see listen(socket_address sa, listen_options opts) - future accept() { - return _ssi->accept(); - } - - /// Stops any \ref accept() in progress. - /// - /// Current and future \ref accept() calls will terminate immediately - /// with an error. - void abort_accept() { - return _ssi->abort_accept(); - } -}; -/// @} - -class network_stack { -public: - virtual ~network_stack() {} - virtual server_socket listen(socket_address sa, listen_options opts) = 0; - // FIXME: local parameter assumes ipv4 for now, fix when adding other AF - virtual future connect(socket_address sa, socket_address local = socket_address(::sockaddr_in{AF_INET, INADDR_ANY, 0})) = 0; - virtual net::udp_channel make_udp_channel(ipv4_addr addr = {}) = 0; - virtual future<> initialize() { - return make_ready_future(); - } - virtual bool has_per_core_namespace() = 0; -}; - -class network_stack_registry { -public: - using options = boost::program_options::variables_map; -private: - static std::unordered_map> (options opts)>>& _map() { - static std::unordered_map> (options opts)>> map; - return map; - } - static sstring& _default() { - static sstring def; - return def; - } -public: - static boost::program_options::options_description& options_description() { - static boost::program_options::options_description opts; - return opts; - } - static void register_stack(sstring name, - boost::program_options::options_description opts, - std::function> (options opts)> create, - bool make_default = false); - static sstring default_stack(); - static std::vector list(); - static future> create(options opts); - static future> create(sstring name, options opts); -}; - -class network_stack_registrator { -public: - using options = boost::program_options::variables_map; - explicit network_stack_registrator(sstring name, - boost::program_options::options_description opts, - std::function> (options opts)> factory, - bool make_default = false) { - network_stack_registry::register_stack(name, opts, factory, make_default); - } -}; - -class writeable_eventfd; - -class readable_eventfd { - pollable_fd _fd; -public: - explicit readable_eventfd(size_t initial = 0) : _fd(try_create_eventfd(initial)) {} - readable_eventfd(readable_eventfd&&) = default; - writeable_eventfd write_side(); - future wait(); - int get_write_fd() { return _fd.get_fd(); } -private: - explicit readable_eventfd(file_desc&& fd) : _fd(std::move(fd)) {} - static file_desc try_create_eventfd(size_t initial); - - friend class writeable_eventfd; -}; - -class writeable_eventfd { - file_desc _fd; -public: - explicit writeable_eventfd(size_t initial = 0) : _fd(try_create_eventfd(initial)) {} - writeable_eventfd(writeable_eventfd&&) = default; - readable_eventfd read_side(); - void signal(size_t nr); - int get_read_fd() { return _fd.get(); } -private: - explicit writeable_eventfd(file_desc&& fd) : _fd(std::move(fd)) {} - static file_desc try_create_eventfd(size_t initial); - - friend class readable_eventfd; -}; - -// The reactor_notifier interface is a simplified version of Linux's eventfd -// interface (with semaphore behavior off, and signal() always signaling 1). -// -// A call to signal() causes an ongoing wait() to invoke its continuation. -// If no wait() is ongoing, the next wait() will continue immediately. -class reactor_notifier { -public: - virtual future<> wait() = 0; - virtual void signal() = 0; - virtual ~reactor_notifier() {} -}; - -class thread_pool; -class smp; - -class syscall_work_queue { - static constexpr size_t queue_length = 128; - struct work_item; - using lf_queue = boost::lockfree::spsc_queue>; - lf_queue _pending; - lf_queue _completed; - writeable_eventfd _start_eventfd; - semaphore _queue_has_room = { queue_length }; - struct work_item { - virtual ~work_item() {} - virtual void process() = 0; - virtual void complete() = 0; - }; - template - struct work_item_returning : work_item { - Func _func; - promise _promise; - boost::optional _result; - work_item_returning(Func&& func) : _func(std::move(func)) {} - virtual void process() override { _result = this->_func(); } - virtual void complete() override { _promise.set_value(std::move(*_result)); } - future get_future() { return _promise.get_future(); } - }; -public: - syscall_work_queue(); - template - future submit(Func func) { - auto wi = new work_item_returning(std::move(func)); - auto fut = wi->get_future(); - submit_item(wi); - return fut; - } -private: - void work(); - void complete(); - void submit_item(work_item* wi); - - friend class thread_pool; -}; - -class smp_message_queue { - static constexpr size_t queue_length = 128; - static constexpr size_t batch_size = 16; - static constexpr size_t prefetch_cnt = 2; - struct work_item; - using lf_queue = boost::lockfree::spsc_queue>; - lf_queue _pending; - lf_queue _completed; - struct alignas(64) { - size_t _sent = 0; - size_t _compl = 0; - size_t _last_snt_batch = 0; - size_t _last_cmpl_batch = 0; - size_t _current_queue_length = 0; - }; - // keep this between two structures with statistics - // this makes sure that they have at least one cache line - // between them, so hw prefecther will not accidentally prefetch - // cache line used by aother cpu. - std::vector _collectd_regs; - struct alignas(64) { - size_t _received = 0; - size_t _last_rcv_batch = 0; - }; - struct work_item { - virtual ~work_item() {} - virtual future<> process() = 0; - virtual void complete() = 0; - }; - template - struct async_work_item : work_item { - Func _func; - using futurator = futurize>; - using future_type = typename futurator::type; - using value_type = typename future_type::value_type; - std::experimental::optional _result; - std::exception_ptr _ex; // if !_result - typename futurator::promise_type _promise; // used on local side - async_work_item(Func&& func) : _func(std::move(func)) {} - virtual future<> process() override { - try { - return futurator::apply(this->_func).then_wrapped([this] (auto&& f) { - try { - _result = f.get(); - } catch (...) { - _ex = std::current_exception(); - } - }); - } catch (...) { - _ex = std::current_exception(); - return make_ready_future(); - } - } - virtual void complete() override { - if (_result) { - _promise.set_value(std::move(*_result)); - } else { - // FIXME: _ex was allocated on another cpu - _promise.set_exception(std::move(_ex)); - } - } - future_type get_future() { return _promise.get_future(); } - }; - union tx_side { - tx_side() {} - ~tx_side() {} - void init() { new (&a) aa; } - struct aa { - std::deque pending_fifo; - } a; - } _tx; - std::vector _completed_fifo; -public: - smp_message_queue(); - template - futurize_t> submit(Func&& func) { - auto wi = new async_work_item(std::forward(func)); - auto fut = wi->get_future(); - submit_item(wi); - return fut; - } - void start(unsigned cpuid); - template - size_t process_queue(lf_queue& q, Func process); - size_t process_incoming(); - size_t process_completions(); -private: - void work(); - void submit_item(work_item* wi); - void respond(work_item* wi); - void move_pending(); - void flush_request_batch(); - void flush_response_batch(); - - friend class smp; -}; - -class thread_pool { - uint64_t _aio_threaded_fallbacks = 0; -#ifndef HAVE_OSV - // FIXME: implement using reactor_notifier abstraction we used for SMP - syscall_work_queue inter_thread_wq; - posix_thread _worker_thread; - std::atomic _stopped = { false }; - pthread_t _notify; -public: - thread_pool(); - ~thread_pool(); - template - future submit(Func func) {return inter_thread_wq.submit(std::move(func));} - uint64_t operation_count() const { return _aio_threaded_fallbacks; } -#else -public: - template - future submit(Func func) { std::cout << "thread_pool not yet implemented on osv\n"; abort(); } -#endif -private: - void work(); -}; - -// The "reactor_backend" interface provides a method of waiting for various -// basic events on one thread. We have one implementation based on epoll and -// file-descriptors (reactor_backend_epoll) and one implementation based on -// OSv-specific file-descriptor-less mechanisms (reactor_backend_osv). -class reactor_backend { -public: - virtual ~reactor_backend() {}; - // wait_and_process() waits for some events to become available, and - // processes one or more of them. If block==false, it doesn't wait, - // and just processes events that have already happened, if any. - // After the optional wait, just before processing the events, the - // pre_process() function is called. - virtual bool wait_and_process() = 0; - // Methods that allow polling on file descriptors. This will only work on - // reactor_backend_epoll. Other reactor_backend will probably abort if - // they are called (which is fine if no file descriptors are waited on): - virtual future<> readable(pollable_fd_state& fd) = 0; - virtual future<> writeable(pollable_fd_state& fd) = 0; - virtual void forget(pollable_fd_state& fd) = 0; - // Methods that allow polling on a reactor_notifier. This is currently - // used only for reactor_backend_osv, but in the future it should really - // replace the above functions. - virtual future<> notified(reactor_notifier *n) = 0; - // Methods for allowing sending notifications events between threads. - virtual std::unique_ptr make_reactor_notifier() = 0; -}; - -// reactor backend using file-descriptor & epoll, suitable for running on -// Linux. Can wait on multiple file descriptors, and converts other events -// (such as timers, signals, inter-thread notifications) into file descriptors -// using mechanisms like timerfd, signalfd and eventfd respectively. -class reactor_backend_epoll : public reactor_backend { -private: - file_desc _epollfd; - future<> get_epoll_future(pollable_fd_state& fd, - promise<> pollable_fd_state::* pr, int event); - void complete_epoll_event(pollable_fd_state& fd, - promise<> pollable_fd_state::* pr, int events, int event); - void abort_fd(pollable_fd_state& fd, std::exception_ptr ex, - promise<> pollable_fd_state::* pr, int event); -public: - reactor_backend_epoll(); - virtual ~reactor_backend_epoll() override { } - virtual bool wait_and_process() override; - virtual future<> readable(pollable_fd_state& fd) override; - virtual future<> writeable(pollable_fd_state& fd) override; - virtual void forget(pollable_fd_state& fd) override; - virtual future<> notified(reactor_notifier *n) override; - virtual std::unique_ptr make_reactor_notifier() override; - void abort_reader(pollable_fd_state& fd, std::exception_ptr ex); - void abort_writer(pollable_fd_state& fd, std::exception_ptr ex); -}; - -#ifdef HAVE_OSV -// reactor_backend using OSv-specific features, without any file descriptors. -// This implementation cannot currently wait on file descriptors, but unlike -// reactor_backend_epoll it doesn't need file descriptors for waiting on a -// timer, for example, so file descriptors are not necessary. -class reactor_notifier_osv; -class reactor_backend_osv : public reactor_backend { -private: - osv::newpoll::poller _poller; - future<> get_poller_future(reactor_notifier_osv *n); - promise<> _timer_promise; -public: - reactor_backend_osv(); - virtual ~reactor_backend_osv() override { } - virtual bool wait_and_process() override; - virtual future<> readable(pollable_fd_state& fd) override; - virtual future<> writeable(pollable_fd_state& fd) override; - virtual void forget(pollable_fd_state& fd) override; - virtual future<> notified(reactor_notifier *n) override; - virtual std::unique_ptr make_reactor_notifier() override; - void enable_timer(clock_type::time_point when); - friend class reactor_notifier_osv; -}; -#endif /* HAVE_OSV */ - -enum class open_flags { - rw = O_RDWR, - ro = O_RDONLY, - wo = O_WRONLY, - create = O_CREAT, - truncate = O_TRUNC, - exclusive = O_EXCL, -}; - -inline open_flags operator|(open_flags a, open_flags b) { - return open_flags(static_cast(a) | static_cast(b)); -} - -class reactor { -private: - struct pollfn { - virtual ~pollfn() {} - virtual bool poll_and_check_more_work() = 0; - }; - -public: - class poller { - std::unique_ptr _pollfn; - class registration_task; - class deregistration_task; - registration_task* _registration_task; - public: - template // signature: bool () - explicit poller(Func&& poll_and_check_more_work) - : _pollfn(make_pollfn(std::forward(poll_and_check_more_work))) { - do_register(); - } - ~poller(); - poller(poller&& x); - poller& operator=(poller&& x); - void do_register(); - friend class reactor; - }; - -private: - // FIXME: make _backend a unique_ptr, not a compile-time #ifdef. -#ifdef HAVE_OSV - reactor_backend_osv _backend; - sched::thread _timer_thread; - sched::thread *_engine_thread; - mutable mutex _timer_mutex; - condvar _timer_cond; - s64 _timer_due = 0; -#else - reactor_backend_epoll _backend; -#endif - std::vector _pollers; - static constexpr size_t max_aio = 128; - promise<> _exit_promise; - future<> _exit_future; - unsigned _id = 0; - bool _stopped = false; - bool _handle_sigint = true; - promise> _network_stack_ready_promise; - int _return = 0; - timer_t _timer = {}; - promise<> _start_promise; - semaphore _cpu_started; - uint64_t _tasks_processed = 0; - seastar::timer_set, &timer<>::_link> _timers; - seastar::timer_set, &timer<>::_link>::timer_list_t _expired_timers; - seastar::timer_set, &timer::_link> _lowres_timers; - seastar::timer_set, &timer::_link>::timer_list_t _expired_lowres_timers; - io_context_t _io_context; - std::vector _pending_aio; - semaphore _io_context_available; - uint64_t _aio_reads = 0; - uint64_t _aio_writes = 0; - uint64_t _fsyncs = 0; - circular_buffer> _pending_tasks; - circular_buffer> _at_destroy_tasks; - size_t _task_quota; - std::unique_ptr _network_stack; - // _lowres_clock will only be created on cpu 0 - std::unique_ptr _lowres_clock; - lowres_clock::time_point _lowres_next_timeout; - std::experimental::optional _epoll_poller; - const bool _reuseport; - circular_buffer _loads; - double _load = 0; -private: - bool flush_pending_aio(); - void abort_on_error(int ret); - template - void complete_timers(T&, E&, EnableFunc&& enable_fn); - - /** - * Returns TRUE if all pollers allow blocking. - * - * @return FALSE if at least one of the blockers requires a non-blocking - * execution. - */ - bool poll_once(); - template // signature: bool () - static std::unique_ptr make_pollfn(Func&& func); - - class signals { - public: - signals(); - ~signals(); - - bool poll_signal(); - void handle_signal(int signo, std::function&& handler); - void handle_signal_once(int signo, std::function&& handler); - static void action(int signo, siginfo_t* siginfo, void* ignore); - - private: - struct signal_handler { - signal_handler(int signo, std::function&& handler); - std::function _handler; - }; - std::atomic _pending_signals; - std::unordered_map _signal_handlers; - }; - - signals _signals; - thread_pool _thread_pool; - friend thread_pool; - - void run_tasks(circular_buffer>& tasks, size_t task_quota); - bool posix_reuseport_detect(); -public: - static boost::program_options::options_description get_options_description(); - reactor(); - reactor(const reactor&) = delete; - ~reactor(); - void operator=(const reactor&) = delete; - - void configure(boost::program_options::variables_map config); - - server_socket listen(socket_address sa, listen_options opts = {}); - - future connect(socket_address sa); - - pollable_fd posix_listen(socket_address sa, listen_options opts = {}); - - bool posix_reuseport_available() const { return _reuseport; } - - future posix_connect(socket_address sa, socket_address local); - - future accept(pollable_fd_state& listen_fd); - - future read_some(pollable_fd_state& fd, void* buffer, size_t size); - future read_some(pollable_fd_state& fd, const std::vector& iov); - - future write_some(pollable_fd_state& fd, const void* buffer, size_t size); - - future<> write_all(pollable_fd_state& fd, const void* buffer, size_t size); - - future open_file_dma(sstring name, open_flags flags); - future open_directory(sstring name); - future<> make_directory(sstring name); - future> file_type(sstring name); - future<> remove_file(sstring pathname); - future<> rename_file(sstring old_pathname, sstring new_pathname); - - template - future submit_io(Func prepare_io); - template - future submit_io_read(Func prepare_io); - template - future submit_io_write(Func prepare_io); - - int run(); - void exit(int ret); - future<> when_started() { return _start_promise.get_future(); } - - template - void at_exit(Func&& func) { - _exit_future = _exit_future.then(std::forward(func)); - } - - template - void at_destroy(Func&& func) { - _at_destroy_tasks.push_back(make_task(std::forward(func))); - } - - void add_task(std::unique_ptr&& t) { _pending_tasks.push_back(std::move(t)); } - - network_stack& net() { return *_network_stack; } - unsigned cpu_id() const { return _id; } - - void start_epoll() { - if (!_epoll_poller) { - _epoll_poller = poller([this] { - return wait_and_process(); - }); - } - } - -#ifdef HAVE_OSV - void timer_thread_func(); - void set_timer(sched::timer &tmr, s64 t); -#endif -private: - /** - * Add a new "poller" - a non-blocking function returning a boolean, that - * will be called every iteration of a main loop. - * If it returns FALSE then reactor's main loop is forbidden to block in the - * current iteration. - * - * @param fn a new "poller" function to register - */ - void register_poller(pollfn* p); - void unregister_poller(pollfn* p); - void replace_poller(pollfn* old, pollfn* neww); - struct collectd_registrations; - collectd_registrations register_collectd_metrics(); - future<> write_all_part(pollable_fd_state& fd, const void* buffer, size_t size, size_t completed); - - bool process_io(); - - void add_timer(timer<>*); - bool queue_timer(timer<>*); - void del_timer(timer<>*); - void add_timer(timer*); - bool queue_timer(timer*); - void del_timer(timer*); - - future<> run_exit_tasks(); - void stop(); - friend class pollable_fd; - friend class pollable_fd_state; - friend class posix_file_impl; - friend class blockdev_file_impl; - friend class readable_eventfd; - friend class timer<>; - friend class timer; - friend class smp; - friend class smp_message_queue; - friend class poller; -public: - bool wait_and_process() { - return _backend.wait_and_process(); - } - - future<> readable(pollable_fd_state& fd) { - return _backend.readable(fd); - } - future<> writeable(pollable_fd_state& fd) { - return _backend.writeable(fd); - } - void forget(pollable_fd_state& fd) { - _backend.forget(fd); - } - future<> notified(reactor_notifier *n) { - return _backend.notified(n); - } - void abort_reader(pollable_fd_state& fd, std::exception_ptr ex) { - return _backend.abort_reader(fd, std::move(ex)); - } - void abort_writer(pollable_fd_state& fd, std::exception_ptr ex) { - return _backend.abort_writer(fd, std::move(ex)); - } - void enable_timer(clock_type::time_point when); - std::unique_ptr make_reactor_notifier() { - return _backend.make_reactor_notifier(); - } -}; - -template // signature: bool () -inline -std::unique_ptr -reactor::make_pollfn(Func&& func) { - struct the_pollfn : pollfn { - the_pollfn(Func&& func) : func(std::forward(func)) {} - Func func; - virtual bool poll_and_check_more_work() override { - return func(); - } - }; - return std::make_unique(std::forward(func)); -} - -extern __thread reactor* local_engine; -extern __thread size_t task_quota; - -inline reactor& engine() { - return *local_engine; -} - -class smp { -#if HAVE_DPDK - using thread_adaptor = std::function; -#else - using thread_adaptor = posix_thread; -#endif - static std::vector _threads; - static smp_message_queue** _qs; - static std::thread::id _tmain; - - template - using returns_future = is_future>; - template - using returns_void = std::is_same, void>; -public: - static boost::program_options::options_description get_options_description(); - static void configure(boost::program_options::variables_map vm); - static void cleanup(); - static void join_all(); - static bool main_thread() { return std::this_thread::get_id() == _tmain; } - - /// Runs a function on a remote core. - /// - /// \param t designates the core to run the function on (may be a remote - /// core or the local core). - /// \param func a callable to run on core \c t. If \c func is a temporary object, - /// its lifetime will be extended by moving it. If @func is a reference, - /// the caller must guarantee that it will survive the call. - /// \return whatever \c func returns, as a future<> (if \c func does not return a future, - /// submit_to() will wrap it in a future<>). - template - static futurize_t> submit_to(unsigned t, Func&& func) { - using ret_type = std::result_of_t; - if (t == engine().cpu_id()) { - if (!is_future::value) { - // Non-deferring function, so don't worry about func lifetime - return futurize::apply(std::forward(func)); - } else if (std::is_lvalue_reference::value) { - // func is an lvalue, so caller worries about its lifetime - return futurize::apply(func); - } else { - // Deferring call on rvalue function, make sure to preserve it across call - auto w = std::make_unique(std::move(func)); - auto ret = futurize::apply(*w); - return ret.finally([w = std::move(w)] {}); - } - } else { - return _qs[t][engine().cpu_id()].submit(std::forward(func)); - } - } - static bool poll_queues() { - size_t got = 0; - for (unsigned i = 0; i < count; i++) { - if (engine().cpu_id() != i) { - auto& rxq = _qs[engine().cpu_id()][i]; - rxq.flush_response_batch(); - got += rxq.process_incoming(); - auto& txq = _qs[i][engine()._id]; - txq.flush_request_batch(); - got += txq.process_completions(); - } - } - return got != 0; - } - static boost::integer_range all_cpus() { - return boost::irange(0u, count); - } -private: - static void start_all_queues(); - static void pin(unsigned cpu_id); - static void allocate_reactor(); -public: - static unsigned count; -}; - -inline -pollable_fd_state::~pollable_fd_state() { - engine().forget(*this); -} - -inline -size_t iovec_len(const iovec* begin, size_t len) -{ - size_t ret = 0; - auto end = begin + len; - while (begin != end) { - ret += begin++->iov_len; - } - return ret; -} - -inline -future -reactor::accept(pollable_fd_state& listenfd) { - return readable(listenfd).then([this, &listenfd] () mutable { - socket_address sa; - socklen_t sl = sizeof(&sa.u.sas); - file_desc fd = listenfd.fd.accept(sa.u.sa, sl, SOCK_NONBLOCK | SOCK_CLOEXEC); - pollable_fd pfd(std::move(fd), pollable_fd::speculation(EPOLLOUT)); - return make_ready_future(std::move(pfd), std::move(sa)); - }); -} - -inline -future -reactor::read_some(pollable_fd_state& fd, void* buffer, size_t len) { - return readable(fd).then([this, &fd, buffer, len] () mutable { - auto r = fd.fd.read(buffer, len); - if (!r) { - return read_some(fd, buffer, len); - } - if (size_t(*r) == len) { - fd.speculate_epoll(EPOLLIN); - } - return make_ready_future(*r); - }); -} - -inline -future -reactor::read_some(pollable_fd_state& fd, const std::vector& iov) { - return readable(fd).then([this, &fd, iov = iov] () mutable { - ::msghdr mh = {}; - mh.msg_iov = &iov[0]; - mh.msg_iovlen = iov.size(); - auto r = fd.fd.recvmsg(&mh, 0); - if (!r) { - return read_some(fd, iov); - } - if (size_t(*r) == iovec_len(iov)) { - fd.speculate_epoll(EPOLLIN); - } - return make_ready_future(*r); - }); -} - -inline -future -reactor::write_some(pollable_fd_state& fd, const void* buffer, size_t len) { - return writeable(fd).then([this, &fd, buffer, len] () mutable { - auto r = fd.fd.send(buffer, len, MSG_NOSIGNAL); - if (!r) { - return write_some(fd, buffer, len); - } - if (size_t(*r) == len) { - fd.speculate_epoll(EPOLLOUT); - } - return make_ready_future(*r); - }); -} - -inline -future<> -reactor::write_all_part(pollable_fd_state& fd, const void* buffer, size_t len, size_t completed) { - if (completed == len) { - return make_ready_future<>(); - } else { - return write_some(fd, static_cast(buffer) + completed, len - completed).then( - [&fd, buffer, len, completed, this] (size_t part) mutable { - return write_all_part(fd, buffer, len, completed + part); - }); - } -} - -inline -future<> -reactor::write_all(pollable_fd_state& fd, const void* buffer, size_t len) { - assert(len); - return write_all_part(fd, buffer, len, 0); -} - -template -void reactor::complete_timers(T& timers, E& expired_timers, EnableFunc&& enable_fn) { - expired_timers = timers.expire(timers.now()); - for (auto& t : expired_timers) { - t._expired = true; - } - while (!expired_timers.empty()) { - auto t = &*expired_timers.begin(); - expired_timers.pop_front(); - t->_queued = false; - if (t->_armed) { - t->_armed = false; - if (t->_period) { - t->readd_periodic(); - } - t->_callback(); - } - } - enable_fn(); -} - -inline -future pollable_fd::read_some(char* buffer, size_t size) { - return engine().read_some(*_s, buffer, size); -} - -inline -future pollable_fd::read_some(uint8_t* buffer, size_t size) { - return engine().read_some(*_s, buffer, size); -} - -inline -future pollable_fd::read_some(const std::vector& iov) { - return engine().read_some(*_s, iov); -} - -inline -future<> pollable_fd::write_all(const char* buffer, size_t size) { - return engine().write_all(*_s, buffer, size); -} - -inline -future<> pollable_fd::write_all(const uint8_t* buffer, size_t size) { - return engine().write_all(*_s, buffer, size); -} - -inline -future pollable_fd::write_some(net::packet& p) { - return engine().writeable(*_s).then([this, &p] () mutable { - static_assert(offsetof(iovec, iov_base) == offsetof(net::fragment, base) && - sizeof(iovec::iov_base) == sizeof(net::fragment::base) && - offsetof(iovec, iov_len) == offsetof(net::fragment, size) && - sizeof(iovec::iov_len) == sizeof(net::fragment::size) && - alignof(iovec) == alignof(net::fragment) && - sizeof(iovec) == sizeof(net::fragment) - , "net::fragment and iovec should be equivalent"); - - iovec* iov = reinterpret_cast(p.fragment_array()); - auto r = get_file_desc().writev(iov, p.nr_frags()); - if (!r) { - return write_some(p); - } - if (size_t(*r) == p.len()) { - _s->speculate_epoll(EPOLLOUT); - } - return make_ready_future(*r); - }); -} - -inline -future<> pollable_fd::write_all(net::packet& p) { - return write_some(p).then([this, &p] (size_t size) { - if (p.len() == size) { - return make_ready_future<>(); - } - p.trim_front(size); - return write_all(p); - }); -} - -inline -future<> pollable_fd::readable() { - return engine().readable(*_s); -} - -inline -future<> pollable_fd::writeable() { - return engine().writeable(*_s); -} - -inline -void -pollable_fd::abort_reader(std::exception_ptr ex) { - engine().abort_reader(*_s, std::move(ex)); -} - -inline -void -pollable_fd::abort_writer(std::exception_ptr ex) { - engine().abort_writer(*_s, std::move(ex)); -} - -inline -future pollable_fd::accept() { - return engine().accept(*_s); -} - -inline -future pollable_fd::recvmsg(struct msghdr *msg) { - return engine().readable(*_s).then([this, msg] { - auto r = get_file_desc().recvmsg(msg, 0); - if (!r) { - return recvmsg(msg); - } - // We always speculate here to optimize for throughput in a workload - // with multiple outstanding requests. This way the caller can consume - // all messages without resorting to epoll. However this adds extra - // recvmsg() call when we hit the empty queue condition, so it may - // hurt request-response workload in which the queue is empty when we - // initially enter recvmsg(). If that turns out to be a problem, we can - // improve speculation by using recvmmsg(). - _s->speculate_epoll(EPOLLIN); - return make_ready_future(*r); - }); -}; - -inline -future pollable_fd::sendmsg(struct msghdr* msg) { - return engine().writeable(*_s).then([this, msg] () mutable { - auto r = get_file_desc().sendmsg(msg, 0); - if (!r) { - return sendmsg(msg); - } - // For UDP this will always speculate. We can't know if there's room - // or not, but most of the time there should be so the cost of mis- - // speculation is amortized. - if (size_t(*r) == iovec_len(msg->msg_iov, msg->msg_iovlen)) { - _s->speculate_epoll(EPOLLOUT); - } - return make_ready_future(*r); - }); -} - -inline -future pollable_fd::sendto(socket_address addr, const void* buf, size_t len) { - return engine().writeable(*_s).then([this, buf, len, addr] () mutable { - auto r = get_file_desc().sendto(addr, buf, len, 0); - if (!r) { - return sendto(std::move(addr), buf, len); - } - // See the comment about speculation in sendmsg(). - if (size_t(*r) == len) { - _s->speculate_epoll(EPOLLOUT); - } - return make_ready_future(*r); - }); -} - -template -inline -timer::timer(callback_t&& callback) : _callback(std::move(callback)) { -} - -template -inline -timer::~timer() { - if (_queued) { - engine().del_timer(this); - } -} - -template -inline -void timer::set_callback(callback_t&& callback) { - _callback = std::move(callback); -} - -template -inline -void timer::arm_state(time_point until, std::experimental::optional period) { - assert(!_armed); - _period = period; - _armed = true; - _expired = false; - _expiry = until; - _queued = true; -} - -template -inline -void timer::arm(time_point until, std::experimental::optional period) { - arm_state(until, period); - engine().add_timer(this); -} - -template -inline -void timer::rearm(time_point until, std::experimental::optional period) { - if (_armed) { - cancel(); - } - arm(until, period); -} - -template -inline -void timer::arm(duration delta) { - return arm(Clock::now() + delta); -} - -template -inline -void timer::arm_periodic(duration delta) { - arm(Clock::now() + delta, {delta}); -} - -template -inline -void timer::readd_periodic() { - arm_state(Clock::now() + _period.value(), {_period.value()}); - engine().queue_timer(this); -} - -template -inline -bool timer::cancel() { - if (!_armed) { - return false; - } - _armed = false; - if (_queued) { - engine().del_timer(this); - _queued = false; - } - return true; -} - -template -inline -typename timer::time_point timer::get_timeout() { - return _expiry; -} - -inline -input_stream -connected_socket::input() { - return _csi->input(); -} - -inline -output_stream -connected_socket::output() { - return _csi->output(); -} - -inline -void -connected_socket::shutdown_input() { - return _csi->shutdown_input(); -} - -inline -void -connected_socket::shutdown_output() { - return _csi->shutdown_output(); -} - -#endif /* REACTOR_HH_ */ diff --git a/core/resource.cc b/core/resource.cc deleted file mode 100644 index f7ce6cda32..0000000000 --- a/core/resource.cc +++ /dev/null @@ -1,191 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "resource.hh" -#include "core/align.hh" - -#ifdef HAVE_HWLOC - -#include "util/defer.hh" -#include -#include - -cpu_set_t cpuid_to_cpuset(unsigned cpuid) { - cpu_set_t cs; - CPU_ZERO(&cs); - CPU_SET(cpuid, &cs); - return cs; -} - -namespace resource { - -size_t div_roundup(size_t num, size_t denom) { - return (num + denom - 1) / denom; -} - -static unsigned find_memory_depth(hwloc_topology_t& topology) { - auto depth = hwloc_get_type_depth(topology, HWLOC_OBJ_PU); - auto obj = hwloc_get_next_obj_by_depth(topology, depth, nullptr); - - while (!obj->memory.local_memory && obj) { - obj = hwloc_get_ancestor_obj_by_depth(topology, --depth, obj); - } - assert(obj); - return depth; -} - -static size_t alloc_from_node(cpu& this_cpu, hwloc_obj_t node, std::unordered_map& used_mem, size_t alloc) { - auto taken = std::min(node->memory.local_memory - used_mem[node], alloc); - if (taken) { - used_mem[node] += taken; - auto node_id = hwloc_bitmap_first(node->nodeset); - assert(node_id != -1); - this_cpu.mem.push_back({taken, unsigned(node_id)}); - } - return taken; -} - -std::vector allocate(configuration c) { - hwloc_topology_t topology; - hwloc_topology_init(&topology); - auto free_hwloc = defer([&] { hwloc_topology_destroy(topology); }); - hwloc_topology_load(topology); - if (c.cpu_set) { - auto bm = hwloc_bitmap_alloc(); - auto free_bm = defer([&] { hwloc_bitmap_free(bm); }); - for (auto idx : *c.cpu_set) { - hwloc_bitmap_set(bm, idx); - } - auto r = hwloc_topology_restrict(topology, bm, - HWLOC_RESTRICT_FLAG_ADAPT_DISTANCES - | HWLOC_RESTRICT_FLAG_ADAPT_MISC - | HWLOC_RESTRICT_FLAG_ADAPT_IO); - if (r == -1) { - if (errno == ENOMEM) { - throw std::bad_alloc(); - } - if (errno == EINVAL) { - throw std::runtime_error("bad cpuset"); - } - abort(); - } - } - auto machine_depth = hwloc_get_type_depth(topology, HWLOC_OBJ_MACHINE); - assert(hwloc_get_nbobjs_by_depth(topology, machine_depth) == 1); - auto machine = hwloc_get_obj_by_depth(topology, machine_depth, 0); - auto available_memory = machine->memory.total_memory; - available_memory -= c.reserve_memory.value_or(256 << 20); - size_t mem = c.total_memory.value_or(available_memory); - if (mem > available_memory) { - throw std::runtime_error("insufficient physical memory"); - } - unsigned available_procs = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_PU); - unsigned procs = c.cpus.value_or(available_procs); - if (procs > available_procs) { - throw std::runtime_error("insufficient processing units"); - } - auto mem_per_proc = align_down(mem / procs, 2 << 20); - std::vector cpu_sets{procs}; - auto root = hwloc_get_root_obj(topology); - hwloc_distribute(topology, root, cpu_sets.data(), cpu_sets.size(), INT_MAX); - std::vector ret; - std::unordered_map topo_used_mem; - std::vector> remains; - size_t remain; - unsigned depth = find_memory_depth(topology); - - // Divide local memory to cpus - for (auto&& cs : cpu_sets) { - auto cpu_id = hwloc_bitmap_first(cs); - assert(cpu_id != -1); - auto pu = hwloc_get_pu_obj_by_os_index(topology, cpu_id); - auto node = hwloc_get_ancestor_obj_by_depth(topology, depth, pu); - cpu this_cpu; - this_cpu.cpu_id = cpu_id; - remain = mem_per_proc - alloc_from_node(this_cpu, node, topo_used_mem, mem_per_proc); - - remains.emplace_back(std::move(this_cpu), remain); - } - - // Divide the rest of the memory - for (auto&& r : remains) { - cpu this_cpu; - size_t remain; - std::tie(this_cpu, remain) = r; - auto pu = hwloc_get_pu_obj_by_os_index(topology, this_cpu.cpu_id); - auto node = hwloc_get_ancestor_obj_by_depth(topology, depth, pu); - auto obj = node; - - while (remain) { - remain -= alloc_from_node(this_cpu, obj, topo_used_mem, remain); - do { - obj = hwloc_get_next_obj_by_depth(topology, depth, obj); - } while (!obj); - if (obj == node) - break; - } - assert(!remain); - ret.push_back(std::move(this_cpu)); - } - return ret; -} - -unsigned nr_processing_units() { - hwloc_topology_t topology; - hwloc_topology_init(&topology); - auto free_hwloc = defer([&] { hwloc_topology_destroy(topology); }); - hwloc_topology_load(topology); - return hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_PU); -} - -} - -#else - -#include "resource.hh" -#include - -namespace resource { - -std::vector allocate(configuration c) { - auto available_memory = ::sysconf(_SC_PAGESIZE) * size_t(::sysconf(_SC_PHYS_PAGES)); - available_memory -= c.reserve_memory.value_or(256 << 20); - size_t mem = c.total_memory.value_or(available_memory); - if (mem > available_memory) { - throw std::runtime_error("insufficient physical memory"); - } - auto cpuset_procs = c.cpu_set ? c.cpu_set->size() : nr_processing_units(); - auto procs = c.cpus.value_or(cpuset_procs); - std::vector ret; - ret.reserve(procs); - for (unsigned i = 0; i < procs; ++i) { - ret.push_back(cpu{i, {{mem / procs, 0}}}); - } - return ret; -} - -unsigned nr_processing_units() { - return ::sysconf(_SC_NPROCESSORS_ONLN); -} - -} - -#endif diff --git a/core/resource.hh b/core/resource.hh deleted file mode 100644 index d8f4ebc04a..0000000000 --- a/core/resource.hh +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef RESOURCE_HH_ -#define RESOURCE_HH_ - -#include -#include -#include -#include - -cpu_set_t cpuid_to_cpuset(unsigned cpuid); - -namespace resource { - -using std::experimental::optional; - -using cpuset = std::set; - -struct configuration { - optional total_memory; - optional reserve_memory; // if total_memory not specified - optional cpus; - optional cpu_set; -}; - -struct memory { - size_t bytes; - unsigned nodeid; - -}; - -struct cpu { - unsigned cpu_id; - std::vector mem; -}; - -std::vector allocate(configuration c); -unsigned nr_processing_units(); - -} - -#endif /* RESOURCE_HH_ */ diff --git a/core/rwlock.hh b/core/rwlock.hh deleted file mode 100644 index 32b9e5560b..0000000000 --- a/core/rwlock.hh +++ /dev/null @@ -1,56 +0,0 @@ -/* -* This file is open source software, licensed to you under the terms -* of the Apache License, Version 2.0 (the "License"). See the NOTICE file -* distributed with this work for additional information regarding copyright -* ownership. You may not use this file except in compliance with the License. -* -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#ifndef CORE_RWLOCK_HH_ -#define CORE_RWLOCK_HH_ - -#include "semaphore.hh" - -class rwlock { - static const size_t max_ops = std::numeric_limits::max(); - - semaphore _sem; -public: - rwlock() - : _sem(max_ops) { - } - - future<> read_lock() { - return _sem.wait(); - } - void read_unlock() { - _sem.signal(); - } - future<> write_lock() { - return _sem.wait(max_ops); - } - void write_unlock() { - _sem.signal(max_ops); - } - bool try_read_lock() { - return _sem.try_wait(); - } - bool try_write_lock() { - return _sem.try_wait(max_ops); - } -}; - -#endif /* CORE_RWLOCK_HH_ */ diff --git a/core/scattered_message.hh b/core/scattered_message.hh deleted file mode 100644 index d7529b372a..0000000000 --- a/core/scattered_message.hh +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef SCATTERED_MESSAGE_HH -#define SCATTERED_MESSAGE_HH - -#include "core/deleter.hh" -#include "core/temporary_buffer.hh" -#include "net/packet.hh" -#include "sstring.hh" -#include -#include -#include - -template -class scattered_message { -private: - using fragment = net::fragment; - using packet = net::packet; - using char_type = CharType; - packet _p; -public: - scattered_message() {} - scattered_message(scattered_message&&) = default; - scattered_message(const scattered_message&) = delete; - - void append_static(const char_type* buf, size_t size) { - if (size) { - _p = packet(std::move(_p), fragment{(char_type*)buf, size}, deleter()); - } - } - - template - void append_static(const char_type(&s)[N]) { - append_static(s, N - 1); - } - - void append_static(const char_type* s) { - append_static(s, strlen(s)); - } - - template - void append_static(const basic_sstring& s) { - append_static(s.begin(), s.size()); - } - - void append_static(const std::experimental::string_view& s) { - append_static(s.data(), s.size()); - } - - template - void append(basic_sstring s) { - if (s.size()) { - _p = packet(std::move(_p), std::move(s).release()); - } - } - - template - void append(const basic_sstring& s, Callback callback) { - if (s.size()) { - _p = packet(std::move(_p), fragment{s.begin(), s.size()}, make_deleter(std::move(callback))); - } - } - - void reserve(int n_frags) { - _p.reserve(n_frags); - } - - packet release() && { - return std::move(_p); - } - - template - void on_delete(Callback callback) { - _p = packet(std::move(_p), std::move(callback)); - } - - operator bool() const { - return _p.len(); - } - - size_t size() { - return _p.len(); - } -}; - -#endif diff --git a/core/scollectd.cc b/core/scollectd.cc deleted file mode 100644 index 37d8e2daaf..0000000000 --- a/core/scollectd.cc +++ /dev/null @@ -1,524 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - * - * This work is open source software, licensed under the terms of the - * BSD license as described in the LICENSE file in the top-level directory. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scollectd.hh" -#include "core/future-util.hh" -#include "core/reactor.hh" -#include "net/api.hh" -#include "scollectd_api.hh" - -bool scollectd::type_instance_id::operator<( - const scollectd::type_instance_id& id2) const { - auto& id1 = *this; - return std::tie(id1.plugin(), id1.plugin_instance(), id1.type(), - id1.type_instance()) - < std::tie(id2.plugin(), id2.plugin_instance(), id2.type(), - id2.type_instance()); -} -bool scollectd::type_instance_id::operator==( - const scollectd::type_instance_id & id2) const { - auto& id1 = *this; - return std::tie(id1.plugin(), id1.plugin_instance(), id1.type(), - id1.type_instance()) - == std::tie(id2.plugin(), id2.plugin_instance(), id2.type(), - id2.type_instance()); -} - -namespace scollectd { - -using namespace std::chrono_literals; -using clock_type = std::chrono::high_resolution_clock; - - -static const ipv4_addr default_addr("239.192.74.66:25826"); -static const clock_type::duration default_period(1s); -const plugin_instance_id per_cpu_plugin_instance("#cpu"); - -future<> send_metric(const type_instance_id &, const value_list &); - -class impl { - net::udp_channel _chan; - timer<> _timer; - - sstring _host = "localhost"; - ipv4_addr _addr = default_addr; - clock_type::duration _period = default_period; - uint64_t _num_packets = 0; - uint64_t _millis = 0; - uint64_t _bytes = 0; - double _avg = 0; - -public: - typedef std::map > value_list_map; - typedef value_list_map::value_type value_list_pair; - - void add_polled(const type_instance_id & id, - const shared_ptr & values) { - _values[id] = values; - } - void remove_polled(const type_instance_id & id) { - auto i = _values.find(id); - if (i != _values.end()) { - i->second = nullptr; - } - } - // explicitly send a type_instance value list (outside polling) - future<> send_metric(const type_instance_id & id, - const value_list & values) { - if (values.empty()) { - return make_ready_future(); - } - cpwriter out; - out.put(_host, clock_type::duration(), id, values); - return _chan.send(_addr, net::packet(out.data(), out.size())); - } - future<> send_notification(const type_instance_id & id, - const sstring & msg) { - cpwriter out; - out.put(_host, id); - out.put(part_type::Message, msg); - return _chan.send(_addr, net::packet(out.data(), out.size())); - } - // initiates actual value polling -> send to target "loop" - void start(const sstring & host, const ipv4_addr & addr, - const clock_type::duration period) { - _period = period; - _addr = addr; - _host = host; - _chan = engine().net().make_udp_channel(); - _timer.set_callback(std::bind(&impl::run, this)); - - // dogfood ourselves - _regs = { - // total_bytes value:DERIVE:0:U - add_polled_metric( - type_instance_id("scollectd", per_cpu_plugin_instance, - "total_bytes", "sent"), - make_typed(data_type::DERIVE, _bytes)), - // total_requests value:DERIVE:0:U - add_polled_metric( - type_instance_id("scollectd", per_cpu_plugin_instance, - "total_requests"), - make_typed(data_type::DERIVE, _num_packets) - ), - // latency value:GAUGE:0:U - add_polled_metric( - type_instance_id("scollectd", per_cpu_plugin_instance, - "latency"), _avg), - // total_time_in_ms value:DERIVE:0:U - add_polled_metric( - type_instance_id("scollectd", per_cpu_plugin_instance, - "total_time_in_ms"), - make_typed(data_type::DERIVE, _millis) - ), - // total_values value:DERIVE:0:U - add_polled_metric( - type_instance_id("scollectd", per_cpu_plugin_instance, - "total_values"), - make_typed(data_type::DERIVE, std::bind(&value_list_map::size, &_values)) - ), - // records value:GAUGE:0:U - add_polled_metric( - type_instance_id("scollectd", per_cpu_plugin_instance, - "records"), - make_typed(data_type::GAUGE, std::bind(&value_list_map::size, &_values)) - ), - }; - - send_notification( - type_instance_id("scollectd", per_cpu_plugin_instance, - "network"), "daemon started"); - arm(); - } - void stop() { - _timer.cancel(); - _regs.clear(); - } - -private: - static const size_t payload_size = 1024; - - void arm() { - if (_period != clock_type::duration()) { - _timer.arm(_period); - } - } - - enum class part_type - : uint16_t { - Host = 0x0000, // The name of the host to associate with subsequent data values - Time = 0x0001, // Time Numeric The timestamp to associate with subsequent data values, unix time format (seconds since epoch) - TimeHr = 0x0008, // Time (high resolution) Numeric The timestamp to associate with subsequent data values. Time is defined in 2–30 seconds since epoch. New in Version 5.0. - Plugin = 0x0002, // Plugin String The plugin name to associate with subsequent data values, e.g. "cpu" - PluginInst = 0x0003, // Plugin instance String The plugin instance name to associate with subsequent data values, e.g. "1" - Type = 0x0004, // Type String The type name to associate with subsequent data values, e.g. "cpu" - TypeInst = 0x0005, // Type instance String The type instance name to associate with subsequent data values, e.g. "idle" - Values = 0x0006, // Values other Data values, see above - Interval = 0x0007, // Interval Numeric Interval used to set the "step" when creating new RRDs unless rrdtool plugin forces StepSize. Also used to detect values that have timed out. - IntervalHr = 0x0009, // Interval (high resolution) Numeric The interval in which subsequent data values are collected. The interval is given in 2–30 seconds. New in Version 5.0. - Message = 0x0100, // Message (notifications) String - Severity = 0x0101, // Severity Numeric - Signature = 0x0200, // Signature (HMAC-SHA-256) other (todo) - Encryption = 0x0210, // Encryption (AES-256/OFB - }; - - // "Time is defined in 2^–30 seconds since epoch. New in Version 5.0." - typedef std::chrono::duration> collectd_hres_duration; - - // yet another writer type, this one to construct collectd network - // protocol data. - struct cpwriter { - typedef std::array buffer_type; - typedef buffer_type::iterator mark_type; - typedef buffer_type::const_iterator const_mark_type; - - buffer_type _buf; - mark_type _pos; - bool _overflow = false; - - std::unordered_map _cache; - - cpwriter() - : _pos(_buf.begin()) { - } - mark_type mark() const { - return _pos; - } - bool overflow() const { - return _overflow; - } - void reset(mark_type m) { - _pos = m; - _overflow = false; - } - size_t size() const { - return std::distance(_buf.begin(), const_mark_type(_pos)); - } - bool empty() const { - return _pos == _buf.begin(); - } - void clear() { - reset(_buf.begin()); - _cache.clear(); - _overflow = false; - } - const char * data() const { - return &_buf.at(0); - } - char * data() { - return &_buf.at(0); - } - cpwriter& check(size_t sz) { - size_t av = std::distance(_pos, _buf.end()); - _overflow |= av < sz; - return *this; - } - explicit operator bool() const { - return !_overflow; - } - bool operator!() const { - return !operator bool(); - } - template - cpwriter & write(_Iter s, _Iter e) { - if (check(std::distance(s, e))) { - _pos = std::copy(s, e, _pos); - } - return *this; - } - template - typename std::enable_if::value, cpwriter &>::type write( - const T & t) { - T tmp = net::hton(t); - auto * p = reinterpret_cast(&tmp); - auto * e = p + sizeof(T); - write(p, e); - return *this; - } - cpwriter & write(const sstring & s) { - write(s.begin(), s.end() + 1); // include \0 - return *this; - } - cpwriter & put(part_type type, const sstring & s) { - write(uint16_t(type)); - write(uint16_t(4 + s.size() + 1)); // include \0 - write(s); // include \0 - return *this; - } - cpwriter & put_cached(part_type type, const sstring & s) { - auto & cached = _cache[uint16_t(type)]; - if (cached != s) { - put(type, s); - cached = s; - } - return *this; - } - template - typename std::enable_if::value, cpwriter &>::type put( - part_type type, T t) { - write(uint16_t(type)); - write(uint16_t(4 + sizeof(t))); - write(t); - return *this; - } - cpwriter & put(part_type type, const value_list & v) { - auto s = v.size(); - auto sz = 6 + s + s * sizeof(uint64_t); - if (check(sz)) { - write(uint16_t(type)); - write(uint16_t(sz)); - write(uint16_t(s)); - v.types(reinterpret_cast(&(*_pos))); - _pos += s; - v.values(reinterpret_cast *>(&(*_pos))); - _pos += s * sizeof(uint64_t); - } - return *this; - } - cpwriter & put(const sstring & host, const type_instance_id & id) { - const auto ts = std::chrono::system_clock::now().time_since_epoch(); - const auto lrts = - std::chrono::duration_cast(ts).count(); - - put_cached(part_type::Host, host); - put(part_type::Time, uint64_t(lrts)); - // Seems hi-res timestamp does not work very well with - // at the very least my default collectd in fedora (or I did it wrong?) - // Use lo-res ts for now, it is probably quite sufficient. - put_cached(part_type::Plugin, id.plugin()); - // Optional - put_cached(part_type::PluginInst, - id.plugin_instance() == per_cpu_plugin_instance ? - to_sstring(engine().cpu_id()) : id.plugin_instance()); - put_cached(part_type::Type, id.type()); - // Optional - put_cached(part_type::TypeInst, id.type_instance()); - return *this; - } - cpwriter & put(const sstring & host, - const clock_type::duration & period, - const type_instance_id & id, const value_list & v) { - const auto ps = std::chrono::duration_cast( - period).count(); - put(host, id); - put(part_type::Values, v); - if (ps != 0) { - put(part_type::IntervalHr, ps); - } - return *this; - } - }; - - void run() { - typedef value_list_map::iterator iterator; - typedef std::tuple context; - - auto ctxt = make_lw_shared(); - - // note we're doing this unsynced since we assume - // all registrations to this instance will be done on the - // same cpu, and without interuptions (no wait-states) - std::get(*ctxt) = _values.begin(); - - auto stop_when = [this, ctxt]() { - auto done = std::get(*ctxt) == _values.end(); - return done; - }; - // append as many values as we can fit into a packet (1024 bytes) - auto send_packet = [this, ctxt]() { - auto start = std::chrono::high_resolution_clock::now(); - auto & i = std::get(*ctxt); - auto & out = std::get(*ctxt); - - out.clear(); - - while (i != _values.end()) { - // nullptr value list means removed value. so remove. - if (!i->second) { - i = _values.erase(i); - continue; - } - auto m = out.mark(); - out.put(_host, _period, i->first, *i->second); - if (!out) { - out.reset(m); - break; - } - ++i; - } - if (out.empty()) { - return make_ready_future(); - } - return _chan.send(_addr, net::packet(out.data(), out.size())).then([start, ctxt, this]() { - auto & out = std::get(*ctxt); - auto now = std::chrono::high_resolution_clock::now(); - // dogfood stats - ++_num_packets; - _millis += std::chrono::duration_cast(now - start).count(); - _bytes += out.size(); - _avg = double(_millis) / _num_packets; - }).then_wrapped([] (auto&& f) { - try { - f.get(); - } catch (std::exception & ex) { - std::cout << "send failed: " << ex.what() << std::endl; - } catch (...) { - std::cout << "send failed: - unknown exception" << std::endl; - } - }); - }; - do_until(stop_when, send_packet).then([this]() { - arm(); - }); - } -public: - shared_ptr get_values(const type_instance_id & id) { - return _values[id]; - } - - std::vector get_instance_ids() { - std::vector res; - for (auto i: _values) { - // Need to check for empty value_list, since unreg is two-stage. - // Not an issue for most uses, but unit testing etc that would like - // fully deterministic operation here would like us to only return - // actually active ids - if (i.second) { - res.emplace_back(i.first); - } - } - return res; - } - -private: - value_list_map _values; - registrations _regs; -}; - -impl & get_impl() { - static thread_local impl per_cpu_instance; - return per_cpu_instance; -} - -void add_polled(const type_instance_id & id, - const shared_ptr & values) { - get_impl().add_polled(id, values); -} - -void remove_polled_metric(const type_instance_id & id) { - get_impl().remove_polled(id); -} - -future<> send_notification(const type_instance_id & id, - const sstring & msg) { - return get_impl().send_notification(id, msg); -} - -future<> send_metric(const type_instance_id & id, - const value_list & values) { - return get_impl().send_metric(id, values); -} - -void configure(const boost::program_options::variables_map & opts) { - bool enable = opts["collectd"].as(); - if (!enable) { - return; - } - auto addr = ipv4_addr(opts["collectd-address"].as()); - auto period = std::chrono::duration_cast( - std::chrono::milliseconds( - opts["collectd-poll-period"].as())); - - auto host = opts["collectd-hostname"].as(); - - // Now create send loops on each cpu - for (unsigned c = 0; c < smp::count; c++) { - smp::submit_to(c, [=] () { - get_impl().start(host, addr, period); - }); - } -} - -boost::program_options::options_description get_options_description() { - namespace bpo = boost::program_options; - bpo::options_description opts("COLLECTD options"); - char hostname[PATH_MAX]; - gethostname(hostname, sizeof(hostname)); - hostname[PATH_MAX-1] = '\0'; - opts.add_options()("collectd", bpo::value()->default_value(true), - "enable collectd daemon")("collectd-address", - bpo::value()->default_value("239.192.74.66:25826"), - "address to send/broadcast metrics to")("collectd-poll-period", - bpo::value()->default_value(1000), - "poll period - frequency of sending counter metrics (default: 1000ms, 0 disables)")( - "collectd-hostname", - bpo::value()->default_value(hostname), - "collectd host name"); - return opts; -} - -std::vector get_collectd_value( - const scollectd::type_instance_id& id) { - std::vector res_values; - auto raw_types = get_impl().get_values(id); - if (raw_types == nullptr) { - return res_values; - } - std::vector types(raw_types->size()); - raw_types->types(&(*types.begin())); - std::vector> value(raw_types->size()); - raw_types->values(&(*value.begin())); - - auto i = value.begin(); - for (auto t : types) { - collectd_value c(t, be64toh(*i)); - res_values.push_back(c); - } - return res_values; -} - -std::vector get_collectd_types( - const scollectd::type_instance_id& id) { - auto res = get_impl().get_values(id); - if (res == nullptr) { - return std::vector(); - } - std::vector vals(res->size()); - res->types(&(*vals.begin())); - return vals; -} - -std::vector get_collectd_ids() { - return get_impl().get_instance_ids(); -} -} diff --git a/core/scollectd.hh b/core/scollectd.hh deleted file mode 100644 index 89609083d0..0000000000 --- a/core/scollectd.hh +++ /dev/null @@ -1,471 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - * - * This work is open source software, licensed under the terms of the - * BSD license as described in the LICENSE file in the top-level directory. - */ - -#ifndef SCOLLECTD_HH_ -#define SCOLLECTD_HH_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "future.hh" -#include "net/byteorder.hh" -#include "core/shared_ptr.hh" -#include "core/sstring.hh" - -/** - * Implementation of rudimentary collectd data gathering. - * - * Usage is hopefully straight forward. Though, feel free to read - * https://collectd.org/wiki/index.php/Naming_schema - * for an explanation on the naming model. - * - * Typically, you'll add values something like: - * - * scollectd::type_instance_id typ("", "", "", ""); - * scollectd::add_polled_metric(typ, [ | scollectd::make_typed(, ) [, ...]); - * - * Where - * would be the overall 'module', e.g. "cpu" - * -> optional distinguisher between plugin instances. For cpu, the built-in - * scollectd::per_cpu_plugin_instance constant is a good choice, i.e. 0->N cpu. - * If there are no instances (e.g. only one), empty constant is appropriate (none) - * is the 'type' of metric collected, for ex. "usage" (cpu/0/usage) - * is a distinguisher for metric parts of the type, e.g. "idle", "user", "kernel" - * -> cpu/0/usage/idle | cpu/0/usage/user | cpu/0/usage/kernel - * - * Each type instance can bind an arbitrary number of values, ech representing some aspect in turn of the instance. - * The structure and interpretation is up to the producer/consumer - * - * There is a single "scollectd" instance per cpu, and values should be bound locally - * to this cpu. Polling is done at a frequency set in the seastar config (def once per s), - * and all registered values will be sent via UDP packages to the destination host(s) - * - * Note that the tuple { plugin, plugin_instance, type, type_instance } is considered a - * unique ID for a value registration, so using the same tuple twice will remove the previously - * registered values. - * - * Values can be unregistered at any time, though they must be so on the same thread/cpu - * as they we're registered. The "registration" achor type provides RAII style value unregistration - * semantics. - * - */ - -namespace scollectd { - -// The value binding data types -enum class data_type : uint8_t { - COUNTER, // unsigned int 64 - GAUGE, // double - DERIVE, // signed int 64 - ABSOLUTE, // unsigned int 64 -}; - -// don't use directly. use make_typed. -template -struct typed { - typed(data_type t, T && v) - : type(t), value(std::forward(v)) { - } - data_type type; - T value; -}; - -template -static inline typed make_typed(data_type type, T&& t) { - return typed(type, std::forward(t)); -} - -typedef sstring plugin_id; -typedef sstring plugin_instance_id; -typedef sstring type_id; -typedef sstring type_instance; - -class type_instance_id { -public: - type_instance_id() = default; - type_instance_id(const plugin_id & p, const plugin_instance_id & pi, - const type_id & t, const scollectd::type_instance & ti = std::string()) - : _plugin(p), _plugin_instance(pi), _type(t), _type_instance(ti) { - } - type_instance_id(type_instance_id &&) = default; - type_instance_id(const type_instance_id &) = default; - - type_instance_id & operator=(type_instance_id &&) = default; - type_instance_id & operator=(const type_instance_id &) = default; - - const plugin_id & plugin() const { - return _plugin; - } - const plugin_instance_id & plugin_instance() const { - return _plugin_instance; - } - const type_id & type() const { - return _type; - } - const scollectd::type_instance & type_instance() const { - return _type_instance; - } - bool operator<(const type_instance_id&) const; - bool operator==(const type_instance_id&) const; -private: - plugin_id _plugin; - plugin_instance_id _plugin_instance; - type_id _type; - scollectd::type_instance _type_instance; -}; - -extern const plugin_instance_id per_cpu_plugin_instance; - -void configure(const boost::program_options::variables_map&); -boost::program_options::options_description get_options_description(); -void remove_polled_metric(const type_instance_id &); - -/** - * Anchor for polled registration. - * Iff the registered type is in some way none-persistent, - * use this as receiver of the reg and ensure it dies before the - * added value(s). - * - * Use: - * uint64_t v = 0; - * registration r = add_polled_metric(v); - * ++r; - * - */ -struct registration { - registration() = default; - registration(const type_instance_id& id) - : _id(id) { - } - registration(type_instance_id&& id) - : _id(std::move(id)) { - } - registration(const registration&) = delete; - registration(registration&&) = default; - ~registration() { - unregister(); - } - registration & operator=(const registration&) = delete; - registration & operator=(registration&&) = default; - - void unregister() { - remove_polled_metric(_id); - _id = type_instance_id(); - } -private: - type_instance_id _id; -}; - -/** - * Helper type to make generating vectors of registration objects - * easier, since it constructs from an initializer list of - * type_instance_id:s, avoiding early conversion to registration objs, - * which in case of init lists, are copy semantics, not move... - */ -class registrations - : public std::vector -{ -public: - typedef std::vector vector_type; - - registrations() - {} - registrations(vector_type&& v) : vector_type(std::move(v)) - {} - registrations(const std::initializer_list& l) - : vector_type(l.begin(),l.end()) - {} - registrations& operator=(vector_type&& v) { - vector_type::operator=(std::move(v)); - return *this; - } - registrations& operator=(const std::initializer_list& l) { - return registrations::operator=(registrations(l)); - } -}; - -// lots of template junk to build typed value list tuples -// for registered values. -template -struct data_type_for; - -template -struct is_callable; - -template -struct is_callable::type>::value, -void>::type> : public std::true_type { -}; - -template -struct is_callable::value, void>::type> : public std::false_type { -}; - -template -struct data_type_for::value && std::is_unsigned::value, -void>::type> : public std::integral_constant { -}; -template -struct data_type_for::value && std::is_signed::value, void>::type> : public std::integral_constant< -data_type, data_type::DERIVE> { -}; -template -struct data_type_for::value, void>::type> : public std::integral_constant< -data_type, data_type::GAUGE> { -}; -template -struct data_type_for::value, void>::type> : public data_type_for< -typename std::result_of::type> { -}; -template -struct data_type_for> : public data_type_for { -}; - -template -class value { -public: - template - struct wrap { - wrap(const W & v) - : _v(v) { - } - const W & operator()() const { - return _v; - } - const W & _v; - }; - - typedef typename std::remove_reference::type value_type; - typedef typename std::conditional< - is_callable::type>::value, - value_type, wrap >::type stored_type; - - value(const value_type & t) - : value(data_type_for::value, t) { - } - value(data_type type, const value_type & t) - : _type(type), _t(t) { - } - uint64_t operator()() const { - auto v = _t(); - if (_type == data_type::GAUGE) { - return convert(double(v)); - } else { - uint64_t u = v; - return convert(u); - } - } - operator uint64_t() const { - return (*this)(); - } - operator data_type() const { - return _type; - } - data_type type() const { - return _type; - } -private: - // not super quick value -> protocol endian 64-bit values. - template - void bpack(_Iter s, _Iter e, uint64_t v) const { - while (s != e) { - *s++ = (v & 0xff); - v >>= 8; - } - } - template - typename std::enable_if::value, uint64_t>::type convert( - V v) const { - uint64_t i = v; - // network byte order - return ntohq(i); - } - template - typename std::enable_if::value, uint64_t>::type convert( - V t) const { - union { - uint64_t i; - double v; - } v; - union { - uint64_t i; - uint8_t b[8]; - } u; - v.v = t; - // intel byte order. could also obviously be faster. - // could be ignored if we just assume we're le (for now), - // but this is ok me thinks. - bpack(std::begin(u.b), std::end(u.b), v.i); - return u.i; - } - ; - - const data_type _type; - const stored_type _t; -}; - -template -class value> : public value { -public: - value(const typed & args) -: value(args.type, args.value) { - } -}; - -class value_list { -public: - virtual ~value_list() {} - virtual size_t size() const = 0; - - virtual void types(data_type *) const = 0; - virtual void values(net::packed *) const = 0; - - bool empty() const { - return size() == 0; - } -}; - -template -class values_impl: public value_list { -public: - static const size_t num_values = sizeof...(Args); - - values_impl(Args&& ...args) - : _values(std::forward(args)...) - {} - - values_impl(values_impl&& a) = default; - values_impl(const values_impl& a) = default; - - size_t size() const override { - return num_values; - } - void types(data_type * p) const override { - unpack(_values, [p](Args... args) { - std::initializer_list tmp = { args... }; - std::copy(tmp.begin(), tmp.end(), p); - }); - } - void values(net::packed * p) const override { - unpack(_values, [p](Args... args) { - std::initializer_list tmp = { args... }; - std::copy(tmp.begin(), tmp.end(), p); - }); - } -private: - template - void unpack(const std::tuple& t, _Op&& op) const { - do_unpack(t, std::index_sequence_for {}, std::forward<_Op>(op)); - } - - template - void do_unpack(const std::tuple& t, const std::index_sequence &, _Op&& op) const { - op(std::get(t)...); - } - - std::tuple < Args... > _values; -}; - -void add_polled(const type_instance_id &, const shared_ptr &); - -typedef std::function notify_function; -template -static auto make_type_instance(_Args && ... args) -> values_impl < decltype(value<_Args>(std::forward<_Args>(args)))... > -{ - return values_impl(std::forward<_Args>(args)))... > - (value<_Args>(std::forward<_Args>(args))...); -} -template -static type_instance_id add_polled_metric(const plugin_id & plugin, - const plugin_instance_id & plugin_instance, const type_id & type, - const scollectd::type_instance & type_instance, _Args&& ... args) { - return add_polled_metric( - type_instance_id(plugin, plugin_instance, type, type_instance), - std::forward<_Args>(args)...); -} -template -static future<> send_explicit_metric(const plugin_id & plugin, - const plugin_instance_id & plugin_instance, const type_id & type, - const scollectd::type_instance & type_instance, _Args&& ... args) { - return send_explicit_metric( - type_instance_id(plugin, plugin_instance, type, type_instance), - std::forward<_Args>(args)...); -} -template -static notify_function create_explicit_metric(const plugin_id & plugin, - const plugin_instance_id & plugin_instance, const type_id & type, - const scollectd::type_instance & type_instance, _Args&& ... args) { - return create_explicit_metric( - type_instance_id(plugin, plugin_instance, type, type_instance), - std::forward<_Args>(args)...); -} -template -static type_instance_id add_polled_metric(const type_instance_id & id, - _Args&& ... args) { - typedef decltype(make_type_instance(std::forward<_Args>(args)...)) impl_type; - add_polled(id, - ::make_shared( - make_type_instance(std::forward<_Args>(args)...))); - return id; -} -// "Explicit" metric sends. Sends a single value list as a message. -// Obviously not super efficient either. But maybe someone needs it sometime. -template -static future<> send_explicit_metric(const type_instance_id & id, - _Args&& ... args) { - return send_metric(id, make_type_instance(std::forward<_Args>(args)...)); -} -template -static notify_function create_explicit_metric(const type_instance_id & id, - _Args&& ... args) { - auto list = make_type_instance(std::forward<_Args>(args)...); - return [id, list=std::move(list)]() { - send_metric(id, list); - }; -} - -// Send a message packet (string) -future<> send_notification(const type_instance_id & id, const sstring & msg); -}; - -#endif /* SCOLLECTD_HH_ */ diff --git a/core/scollectd_api.hh b/core/scollectd_api.hh deleted file mode 100644 index 2a46ad5e50..0000000000 --- a/core/scollectd_api.hh +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef CORE_SCOLLECTD_API_HH_ -#define CORE_SCOLLECTD_API_HH_ - -#include "core/scollectd.hh" - -namespace scollectd { - -struct collectd_value { - union { - double _d; - uint64_t _ui; - int64_t _i; - } u; - scollectd::data_type _type; - collectd_value() - : _type(data_type::GAUGE) { - } - collectd_value(data_type t, uint64_t i) - : _type(t) { - u._ui = i; - } - - collectd_value& operator=(const collectd_value& c) = default; - - collectd_value& operator+=(const collectd_value& c) { - *this = *this + c; - return *this; - } - - collectd_value operator+(const collectd_value& c) { - collectd_value res(*this); - switch (_type) { - case data_type::GAUGE: - res.u._d += c.u._d; - break; - case data_type::DERIVE: - res.u._i += c.u._i; - break; - default: - res.u._ui += c.u._ui; - break; - } - return res; - } -}; - -std::vector get_collectd_value( - const scollectd::type_instance_id& id); - -std::vector get_collectd_ids(); - -} - -#endif /* CORE_SCOLLECTD_API_HH_ */ diff --git a/core/seastar.hh b/core/seastar.hh deleted file mode 100644 index b7345273a3..0000000000 --- a/core/seastar.hh +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -/// \mainpage -/// -/// Seastar is a high performance C++ application framework for high -/// concurrency server applications. -/// -/// Please see: -/// - \ref future-module Documentation on futures and promises, which are -/// the seastar building blocks. -/// - \ref future-util Utililty functions for working with futures -/// - \ref memory-module Memory management -/// - \ref networking-module TCP/IP networking -/// - \ref fileio-module File Input/Output -/// - \ref smp-module Multicore support -/// - \ref fiber-module Utilities for managing loosely coupled chains of -/// continuations, also known as fibers -/// - \ref thread-module Support for traditional threaded execution - -#include "sstring.hh" -#include "future.hh" - -// iostream.hh -template class input_stream; -template class output_stream; - -// reactor.hh -class server_socket; -class connected_socket; -class socket_address; -class listen_options; - -// file.hh -class file; -enum class open_flags; - -// Networking API - -/// \defgroup networking-module Networking -/// -/// Seastar provides a simple networking API, backed by two -/// TCP/IP stacks: the POSIX stack, utilizing the kernel's -/// BSD socket APIs, and the native stack, implement fully -/// within seastar and able to drive network cards directly. -/// The native stack supports zero-copy on both transmit -/// and receive, and is implemented using seastar's high -/// performance, lockless sharded design. The network stack -/// can be selected with the \c \--network-stack command-line -/// parameter. - -/// \addtogroup networking-module -/// @{ - -/// Listen for connections on a given port -/// -/// Starts listening on a given address for incoming connections. -/// -/// \param sa socket address to listen on -/// -/// \return \ref server_socket object ready to accept connections. -/// -/// \see listen(socket_address sa, listen_options opts) -server_socket listen(socket_address sa); - -/// Listen for connections on a given port -/// -/// Starts listening on a given address for incoming connections. -/// -/// \param sa socket address to listen on -/// \param opts options controlling the listen operation -/// -/// \return \ref server_socket object ready to accept connections. -/// -/// \see listen(socket_address sa) -server_socket listen(socket_address sa, listen_options opts); - -/// Establishes a connection to a given address -/// -/// Attempts to connect to the given address. -/// -/// \param sa socket address to connect to -/// -/// \return a \ref connected_socket object, or an exception -future connect(socket_address sa); - -/// @} - -/// \defgroup fileio-module File Input/Output -/// -/// Seastar provides a file API to deal with persistent storage. -/// Unlike most file APIs, seastar offers unbuffered file I/O -/// (similar to, and based on, \c O_DIRECT). Unbuffered I/O means -/// that the application is required to do its own caching, but -/// delivers better performance if this caching is done correctly. -/// -/// For random I/O or sequential unbuffered I/O, the \ref file -/// class provides a set of methods for reading, writing, discarding, -/// or otherwise manipulating a file. For buffered sequential I/O, -/// see \ref make_file_input_stream() and \ref make_file_output_stream(). - -/// \addtogroup fileio-module -/// @{ - -/// Opens or creates a file. The "dma" in the name refers to the fact -/// that data transfers are unbuffered and uncached. -/// -/// \param name the name of the file to open or create -/// \param flags various flags controlling the open process -/// \return a \ref file object, as a future -/// -/// \note -/// The file name is not guaranteed to be stable on disk, unless the -/// containing directory is sync'ed. -/// -/// \relates file -future open_file_dma(sstring name, open_flags flags); - -/// Opens a directory. -/// -/// \param name name of the directory to open -/// -/// \return a \ref file object representing a directory. The only -/// legal operations are \ref file::list_directory(), -/// \ref file::fsync(), and \ref file::close(). -/// -/// \relates file -future open_directory(sstring name); - -/// Creates a new directory. -/// -/// \param name name of the directory to create -/// -/// \note -/// The directory is not guaranteed to be stable on disk, unless the -/// containing directory is sync'ed. -future<> make_directory(sstring name); - -/// Ensures a directory exists -/// -/// Checks whether a directory exists, and if not, creates it. Only -/// the last component of the directory name is created. -/// -/// \param name name of the directory to potentially create -/// -/// \note -/// The directory is not guaranteed to be stable on disk, unless the -/// containing directory is sync'ed. -future<> touch_directory(sstring name); - -/// Recursively ensures a directory exists -/// -/// Checks whether each component of a directory exists, and if not, creates it. -/// -/// \param name name of the directory to potentially create -/// \param separator character used as directory separator -/// -/// \note -/// This function fsyncs each component created, and is therefore guaranteed to be stable on disk. -future<> recursive_touch_directory(sstring name); - -/// Removes (unlinks) a file. -/// -/// \param name name of the file to remove -/// -/// \note -/// The removal is not guaranteed to be stable on disk, unless the -/// containing directory is sync'ed. -future<> remove_file(sstring name); - -/// Renames (moves) a file. -/// -/// \param old_name existing file name -/// \param new_name new file name -/// -/// \note -/// The rename is not guaranteed to be stable on disk, unless the -/// both containing directories are sync'ed. -future<> rename_file(sstring old_name, sstring new_name); - -/// @} diff --git a/core/semaphore.hh b/core/semaphore.hh deleted file mode 100644 index d9aaf3ee60..0000000000 --- a/core/semaphore.hh +++ /dev/null @@ -1,217 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef CORE_SEMAPHORE_HH_ -#define CORE_SEMAPHORE_HH_ - -#include "future.hh" -#include -#include -#include -#include "timer.hh" - -/// \addtogroup fiber-module -/// @{ - -/// Exception thrown when a semaphore is broken by -/// \ref semaphore::broken(). -class broken_semaphore : public std::exception { -public: - /// Reports the exception reason. - virtual const char* what() const noexcept { - return "Semaphore broken"; - } -}; - -/// Exception thrown when a semaphore wait operation -/// times out. -/// -/// \see semaphore::wait(typename timer<>::duration timeout, size_t nr) -class semaphore_timed_out : public std::exception { -public: - /// Reports the exception reason. - virtual const char* what() const noexcept { - return "Semaphore timedout"; - } -}; - -/// \brief Counted resource guard. -/// -/// This is a standard computer science semaphore, adapted -/// for futures. You can deposit units into a counter, -/// or take them away. Taking units from the counter may wait -/// if not enough units are available. -/// -/// To support exceptional conditions, a \ref broken() method -/// is provided, which causes all current waiters to stop waiting, -/// with an exceptional future returned. This allows causing all -/// fibers that are blocked on a semaphore to continue. This is -/// similar to POSIX's `pthread_cancel()`, with \ref wait() acting -/// as a cancellation point. -class semaphore { -private: - size_t _count; - struct entry { - promise<> pr; - size_t nr; - timer<> tr; - entry(promise<>&& pr_, size_t nr_) : pr(std::move(pr_)), nr(nr_) {} - }; - std::list _wait_list; -public: - /// Constructs a semaphore object with a specific number of units - /// in its internal counter. The default is 1, suitable for use as - /// an unlocked mutex. - /// - /// \param count number of initial units present in the counter (default 1). - semaphore(size_t count = 1) : _count(count) {} - /// Waits until at least a specific number of units are available in the - /// counter, and reduces the counter by that amount of units. - /// - /// \note Waits are serviced in FIFO order, though if several are awakened - /// at once, they may be reordered by the scheduler. - /// - /// \param nr Amount of units to wait for (default 1). - /// \return a future that becomes ready when sufficient units are availble - /// to satisfy the request. If the semaphore was \ref broken(), may - /// contain an exception. - future<> wait(size_t nr = 1) { - if (_count >= nr && _wait_list.empty()) { - _count -= nr; - return make_ready_future<>(); - } - promise<> pr; - auto fut = pr.get_future(); - _wait_list.push_back(entry(std::move(pr), nr)); - return fut; - } - /// Waits until at least a specific number of units are available in the - /// counter, and reduces the counter by that amount of units. If the request - /// cannot be satisfied in time, the request is aborted. - /// - /// \note Waits are serviced in FIFO order, though if several are awakened - /// at once, they may be reordered by the scheduler. - /// - /// \param timeout how long to wait. - /// \param nr Amount of units to wait for (default 1). - /// \return a future that becomes ready when sufficient units are availble - /// to satisfy the request. On timeout, the future contains a - /// \ref semaphore_timed_out exception. If the semaphore was - /// \ref broken(), may contain an exception. - future<> wait(typename timer<>::duration timeout, size_t nr = 1) { - auto fut = wait(nr); - if (!fut.available()) { - auto& e = _wait_list.back(); - e.tr.set_callback([&e, this] { - e.pr.set_exception(semaphore_timed_out()); - e.nr = 0; - signal(0); - }); - e.tr.arm(timeout); - } - return std::move(fut); - } - /// Deposits a specified number of units into the counter. - /// - /// The counter is incremented by the specified number of units. - /// If the new counter value is sufficient to satisfy the request - /// of one or more waiters, their futures (in FIFO order) become - /// ready, and the value of the counter is reduced according to - /// the amount requested. - /// - /// \param nr Number of units to deposit (default 1). - void signal(size_t nr = 1) { - _count += nr; - while (!_wait_list.empty() && _wait_list.front().nr <= _count) { - auto& x = _wait_list.front(); - if (x.nr) { - _count -= x.nr; - x.pr.set_value(); - x.tr.cancel(); - } - _wait_list.pop_front(); - } - } - /// Attempts to reduce the counter value by a specified number of units. - /// - /// If sufficient units are available in the counter, and if no - /// other fiber is waiting, then the counter is reduced. Otherwise, - /// nothing happens. This is useful for "opportunistic" waits where - /// useful work can happen if the counter happens to be ready, but - /// when it is not worthwhile to wait. - /// - /// \param nr number of units to reduce the counter by (default 1). - /// \return `true` if the counter had sufficient units, and was decremented. - bool try_wait(size_t nr = 1) { - if (_count >= nr && _wait_list.empty()) { - _count -= nr; - return true; - } else { - return false; - } - } - /// Returns the number of units available in the counter. - /// - /// Does not take into account any waiters. - size_t current() const { return _count; } - - /// Signal to waiters that an error occurred. \ref wait() will see - /// an exceptional future<> containing a \ref broken_semaphore exception. - /// The future is made available immediately. - /// - /// This may only be used once per semaphore; after using it the - /// semaphore is in an indeterminate state and should not be waited on. - void broken() { broken(std::make_exception_ptr(broken_semaphore())); } - - /// Signal to waiters that an error occurred. \ref wait() will see - /// an exceptional future<> containing the provided exception parameter. - /// The future is made available immediately. - /// - /// This may only be used once per semaphore; after using it the - /// semaphore is in an indeterminate state and should not be waited on. - template - void broken(const Exception& ex) { - broken(std::make_exception_ptr(ex)); - } - - /// Signal to waiters that an error occurred. \ref wait() will see - /// an exceptional future<> containing the provided exception parameter. - /// The future is made available immediately. - /// - /// This may only be used once per semaphore; after using it the - /// semaphore is in an indeterminate state and should not be waited on. - void broken(std::exception_ptr ex); -}; - -inline -void -semaphore::broken(std::exception_ptr xp) { - while (!_wait_list.empty()) { - auto& x = _wait_list.front(); - x.pr.set_exception(xp); - x.tr.cancel(); - _wait_list.pop_front(); - } -} - -/// @} - -#endif /* CORE_SEMAPHORE_HH_ */ diff --git a/core/sharded.hh b/core/sharded.hh deleted file mode 100644 index d30bea043b..0000000000 --- a/core/sharded.hh +++ /dev/null @@ -1,423 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include "reactor.hh" -#include "future-util.hh" -#include "util/is_smart_ptr.hh" - -namespace seastar { - -/// \defgroup smp-module Multicore -/// -/// \brief Support for exploiting multiple cores on a server. -/// -/// Seastar supports multicore servers by using \i sharding. Each logical -/// core (lcore) runs a separate event loop, with its own memory allocator, -/// TCP/IP stack, and other services. Shards communicate by explicit message -/// passing, rather than using locks and condition variables as with traditional -/// threaded programming. - -/// \addtogroup smp-module -/// @{ - -/// Template helper to distribute a service across all logical cores. -/// -/// The \c sharded template manages a sharded service, by creating -/// a copy of the service on each logical core, providing mechanisms to communicate -/// with each shard's copy, and a way to stop the service. -/// -/// \tparam Service a class to be instantiated on each core. Should expose -/// a \c stop() method that returns a \c future<>, to be called when -/// the service is stopped. -template -class sharded { - std::vector _instances; -public: - /// Constructs an empty \c sharded object. No instances of the service are - /// created. - sharded() {} - sharded(const sharded& other) = delete; - /// Moves a \c sharded object. - sharded(sharded&& other) = default; - sharded& operator=(const sharded& other) = delete; - /// Moves a \c sharded object. - sharded& operator=(sharded& other) = default; - /// Destroyes a \c sharded object. Must not be in a started state. - ~sharded(); - - /// Starts \c Service by constructing an instance on every logical core - /// with a copy of \c args passed to the constructor. - /// - /// \param args Arguments to be forwarded to \c Service constructor - /// \return a \ref future<> that becomes ready when all instances have been - /// constructed. - template - future<> start(Args&&... args); - - /// Starts \c Service by constructing an instance on a single logical core - /// with a copy of \c args passed to the constructor. - /// - /// \param args Arguments to be forwarded to \c Service constructor - /// \return a \ref future<> that becomes ready when the instance has been - /// constructed. - template - future<> start_single(Args&&... args); - - /// Stops all started instances and destroys them. - /// - /// For every started instance, its \c stop() method is called, and then - /// it is destroyed. - future<> stop(); - - // Invoke a method on all instances of @Service. - // The return value becomes ready when all instances have processed - // the message. - template - future<> invoke_on_all(future<> (Service::*func)(Args...), Args... args); - - /// Invoke a method on all \c Service instances in parallel. - /// - /// \param func member function to be called. Must return \c void or - /// \c future<>. - /// \param args arguments to be passed to \c func. - /// \return future that becomes ready when the method has been invoked - /// on all instances. - template - future<> invoke_on_all(void (Service::*func)(Args...), Args... args); - - /// Invoke a callable on all instances of \c Service. - /// - /// \param func a callable with the signature `void (Service&)` - /// or `future<> (Service&)`, to be called on each core - /// with the local instance as an argument. - /// \return a `future<>` that becomes ready when all cores have - /// processed the message. - template - future<> invoke_on_all(Func&& func); - - /// Invoke a method on all instances of `Service` and reduce the results using - /// `Reducer`. - /// - /// \see map_reduce(Iterator begin, Iterator end, Mapper&& mapper, Reducer&& r) - template - inline - auto - map_reduce(Reducer&& r, Ret (Service::*func)(FuncArgs...), Args&&... args) - -> typename reducer_traits::future_type - { - unsigned c = 0; - return ::map_reduce(_instances.begin(), _instances.end(), - [&c, func, args = std::make_tuple(std::forward(args)...)] (Service* inst) mutable { - return smp::submit_to(c++, [inst, func, args] () mutable { - return apply([inst, func] (Args&&... args) mutable { - return (inst->*func)(std::forward(args)...); - }, std::move(args)); - }); - }, std::forward(r)); - } - - /// Invoke a callable on all instances of `Service` and reduce the results using - /// `Reducer`. - /// - /// \see map_reduce(Iterator begin, Iterator end, Mapper&& mapper, Reducer&& r) - template - inline - auto map_reduce(Reducer&& r, Func&& func) -> typename reducer_traits::future_type - { - unsigned c = 0; - return ::map_reduce(_instances.begin(), _instances.end(), - [&c, &func] (Service* inst) mutable { - return smp::submit_to(c++, [inst, func] () mutable { - return func(*inst); - }); - }, std::forward(r)); - } - - /// Applies a map function to all shards, then reduces the output by calling a reducer function. - /// - /// \param map callable with the signature `Value (Service&)` or - /// `future (Service&)` (for some `Value` type). - /// used as the second input to \c reduce - /// \param initial initial value used as the first input to \c reduce. - /// \param reduce binary function used to left-fold the return values of \c map - /// into \c initial . - /// - /// Each \c map invocation runs on the shard associated with the service. - /// - /// \tparam Mapper unary function taking `Service&` and producing some result. - /// \tparam Initial any value type - /// \tparam Reduce a binary function taking two Initial values and returning an Initial - /// \return Result of applying `map` to each instance in parallel, reduced by calling - /// `reduce()` on each adjacent pair of results. - template - inline - future - map_reduce0(Mapper map, Initial initial, Reduce reduce) { - auto wrapped_map = [this, map] (unsigned c) { - return smp::submit_to(c, [map, inst = _instances[c]] { - return map(*inst); - }); - }; - return ::map_reduce(smp::all_cpus().begin(), smp::all_cpus().end(), - std::move(wrapped_map), - std::move(initial), - std::move(reduce)); - } - - /// Invoke a method on a specific instance of `Service`. - /// - /// \param id shard id to call - /// \param func a method of `Service` - /// \param args arguments to be passed to `func` - /// \return result of calling `func(args)` on the designated instance - template > - FutureRet - invoke_on(unsigned id, Ret (Service::*func)(FuncArgs...), Args&&... args) { - using futurator = futurize; - auto inst = _instances[id]; - return smp::submit_to(id, [func, args = std::make_tuple(inst, std::forward(args)...)] () mutable { - return futurator::apply(std::mem_fn(func), std::move(args)); - }); - } - - /// Invoke a callable on a specific instance of `Service`. - /// - /// \param id shard id to call - /// \param func a callable with signature `Value (Service&)` or - /// `future (Service&)` (for some `Value` type) - /// \return result of calling `func(instance)` on the designated instance - template >> - Ret - invoke_on(unsigned id, Func&& func) { - auto inst = _instances[id]; - return smp::submit_to(id, [inst, func = std::forward(func)] () mutable { - return func(*inst); - }); - } - - /// Gets a reference to the local instance. - Service& local(); - - /// Checks whether the local instance has been initialized. - bool local_is_initialized(); -}; - -template -sharded::~sharded() { - assert(_instances.empty()); -} - -template -template -future<> -sharded::start(Args&&... args) { - _instances.resize(smp::count); - unsigned c = 0; - return parallel_for_each(_instances.begin(), _instances.end(), - [this, &c, args = std::make_tuple(std::forward(args)...)] (Service*& inst) mutable { - return smp::submit_to(c++, [&inst, args] () mutable { - inst = apply([] (Args&&... args) { - return new Service(std::forward(args)...); - }, std::move(args)); - }); - }); -} - -template -template -future<> -sharded::start_single(Args&&... args) { - assert(_instances.empty()); - _instances.resize(1); - return smp::submit_to(0, [this, args = std::make_tuple(std::forward(args)...)] () mutable { - _instances[0] = apply([] (Args&&... args) { - return new Service(std::forward(args)...); - }, std::move(args)); - }); -} - -template -future<> -sharded::stop() { - unsigned c = 0; - return parallel_for_each(_instances.begin(), _instances.end(), [&c] (Service*& inst) mutable { - return smp::submit_to(c++, [inst] () mutable { - return inst->stop().then([inst] () mutable { - delete inst; - }); - }); - }).then([this] { - _instances.clear(); - _instances = std::vector(); - }); -} - -template -template -inline -future<> -sharded::invoke_on_all(future<> (Service::*func)(Args...), Args... args) { - unsigned c = 0; - return parallel_for_each(_instances.begin(), _instances.end(), [&c, func, args...] (Service* inst) { - return smp::submit_to(c++, [inst, func, args...] { - return (inst->*func)(args...); - }); - }); -} - -template -template -inline -future<> -sharded::invoke_on_all(void (Service::*func)(Args...), Args... args) { - unsigned c = 0; - return parallel_for_each(_instances.begin(), _instances.end(), [&c, func, args...] (Service* inst) { - return smp::submit_to(c++, [inst, func, args...] { - (inst->*func)(args...); - }); - }); -} - -template -template -inline -future<> -sharded::invoke_on_all(Func&& func) { - static_assert(std::is_same>, future<>>::value, - "invoke_on_all()'s func must return void or future<>"); - unsigned c = 0; - return parallel_for_each(_instances.begin(), _instances.end(), [&c, &func] (Service* inst) { - return smp::submit_to(c++, [inst, func] { - return func(*inst); - }); - }); -} - -template -Service& sharded::local() { - assert(local_is_initialized()); - return *_instances[engine().cpu_id()]; -} - -template -inline bool sharded::local_is_initialized() { - return _instances.size() > engine().cpu_id() && - _instances[engine().cpu_id()]; -} - -} - -/// Smart pointer wrapper which makes it safe to move across CPUs. -/// -/// \c foreign_ptr<> is a smart pointer wrapper which, unlike -/// \ref shared_ptr and \ref lw_shared_ptr, is safe to move to a -/// different core. -/// -/// As seastar avoids locking, any but the most trivial objects must -/// be destroyed on the same core they were created on, so that, -/// for example, their destructors can unlink references to the -/// object from various containers. In addition, for performance -/// reasons, the shared pointer types do not use atomic operations -/// to manage their reference counts. As a result they cannot be -/// used on multiple cores in parallel. -/// -/// \c foreign_ptr<> provides a solution to that problem. -/// \c foreign_ptr<> wraps any pointer type -- raw pointer, -/// \ref shared_ptr<>, or similar, and remembers on what core this -/// happened. When the \c foreign_ptr<> object is destroyed, it -/// sends a message to the original core so that the wrapped object -/// can be safely destroyed. -/// -/// \c foreign_ptr<> is a move-only object; it cannot be copied. -/// -template -class foreign_ptr { -private: - PtrType _value; - unsigned _cpu; -private: - bool on_origin() { - return engine().cpu_id() == _cpu; - } -public: - using element_type = typename std::pointer_traits::element_type; - - /// Constructs a null \c foreign_ptr<>. - foreign_ptr() - : _value(PtrType()) - , _cpu(engine().cpu_id()) { - } - /// Constructs a null \c foreign_ptr<>. - foreign_ptr(std::nullptr_t) : foreign_ptr() {} - /// Wraps a pointer object and remembers the current core. - foreign_ptr(PtrType value) - : _value(std::move(value)) - , _cpu(engine().cpu_id()) { - } - // The type is intentionally non-copyable because copies - // are expensive because each copy requires across-CPU call. - foreign_ptr(const foreign_ptr&) = delete; - /// Moves a \c foreign_ptr<> to another object. - foreign_ptr(foreign_ptr&& other) = default; - /// Destroys the wrapped object on its original cpu. - ~foreign_ptr() { - if (_value && !on_origin()) { - smp::submit_to(_cpu, [v = std::move(_value)] () mutable { - auto local(std::move(v)); - }); - } - } - /// release the wrapped object on a local cpu. If executed on cpu - /// other than the one object was created on object will be copied - /// to local memory. - element_type make_local_and_release() { - if (on_origin()) { - return std::move(*_value); - } else { - // copied to caller's cpu here - return *_value; - } - } - /// Accesses the wrapped object. - element_type& operator*() const { return *_value; } - /// Accesses the wrapped object. - element_type* operator->() const { return &*_value; } - /// Checks whether the wrapped pointer is non-null. - operator bool() const { return static_cast(_value); } - /// Move-assigns a \c foreign_ptr<>. - foreign_ptr& operator=(foreign_ptr&& other) = default; -}; - -/// Wraps a raw or smart pointer object in a \ref foreign_ptr<>. -/// -/// \relates foreign_ptr -template -foreign_ptr make_foreign(T ptr) { - return foreign_ptr(std::move(ptr)); -} - -template -struct is_smart_ptr<::foreign_ptr> : std::true_type {}; - -/// @} - diff --git a/core/shared_mutex.hh b/core/shared_mutex.hh deleted file mode 100644 index 5b57e4157d..0000000000 --- a/core/shared_mutex.hh +++ /dev/null @@ -1,163 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include "future.hh" -#include "circular_buffer.hh" - -namespace seastar { - -/// \addtogroup fiber-module -/// @{ - -/// \brief Shared/exclusive mutual exclusion. -/// -/// Similar to \c std::shared_mutex, this class provides protection -/// for a shared resource, with two levels of access protection: shared -/// and exclusive. Shared access allows multiple tasks to access the -/// shared resource concurrently, while exclusive access allows just -/// one task to access the resource at a time. -/// -/// Note that many seastar tasks do not require protection at all, -/// since the seastar scheduler is not preemptive; however tasks that do -/// (by waiting on a future) may require explicit locking. -/// -/// The \ref with_shared(shared_mutex&, Func&&) and -/// \ref with_lock(shared_mutex&, Func&&) provide exception-safe -/// wrappers for use with \c shared_mutex. -/// -/// \see semaphore simpler mutual exclusion -class shared_mutex { - unsigned _readers = 0; - bool _writer = false; - struct waiter { - waiter(promise<>&& pr, bool for_write) : pr(std::move(pr)), for_write(for_write) {} - promise<> pr; - bool for_write; - }; - circular_buffer _waiters; -public: - shared_mutex() = default; - shared_mutex(shared_mutex&&) = default; - shared_mutex& operator=(shared_mutex&&) = default; - shared_mutex(const shared_mutex&) = delete; - void operator=(const shared_mutex&) = delete; - /// Lock the \c shared_mutex for shared access - /// - /// \return a future that becomes ready when no exclusive access - /// is granted to anyone. - future<> lock_shared() { - if (!_writer && _waiters.empty()) { - ++_readers; - return make_ready_future<>(); - } - _waiters.emplace_back(promise<>(), false); - return _waiters.back().pr.get_future(); - } - /// Unlocks a \c shared_mutex after a previous call to \ref lock_shared(). - void unlock_shared() { - --_readers; - wake(); - } - /// Lock the \c shared_mutex for exclusive access - /// - /// \return a future that becomes ready when no access, shared or exclusive - /// is granted to anyone. - future<> lock() { - if (!_readers && !_writer) { - _writer = true; - return make_ready_future<>(); - } - _waiters.emplace_back(promise<>(), true); - return _waiters.back().pr.get_future(); - } - /// Unlocks a \c shared_mutex after a previous call to \ref lock(). - void unlock() { - _writer = false; - wake(); - } -private: - void wake() { - while (!_waiters.empty()) { - auto& w = _waiters.front(); - // note: _writer == false in wake() - if (w.for_write) { - if (!_readers) { - _writer = true; - w.pr.set_value(); - _waiters.pop_front(); - } - break; - } else { // for read - ++_readers; - w.pr.set_value(); - _waiters.pop_front(); - } - } - } -}; - -/// Executes a function while holding shared access to a resource. -/// -/// Executes a function while holding shared access to a resource. When -/// the function returns, the mutex is automatically unlocked. -/// -/// \param sm a \ref shared_mutex guarding access to the shared resource -/// \param func callable object to invoke while the mutex is held for shared access -/// \return whatever \c func returns, as a future -/// -/// \relates shared_mutex -template -inline -futurize_t> -with_shared(shared_mutex& sm, Func&& func) { - return sm.lock_shared().then([func = std::forward(func)] () mutable { - return func(); - }).finally([&sm] { - sm.unlock_shared(); - }); -} - -/// Executes a function while holding exclusive access to a resource. -/// -/// Executes a function while holding exclusive access to a resource. When -/// the function returns, the mutex is automatically unlocked. -/// -/// \param sm a \ref shared_mutex guarding access to the shared resource -/// \param func callable object to invoke while the mutex is held for shared access -/// \return whatever \c func returns, as a future -/// -/// \relates shared_mutex -template -inline -futurize_t> -with_lock(shared_mutex& sm, Func&& func) { - return sm.lock().then([func = std::forward(func)] () mutable { - return func(); - }).finally([&sm] { - sm.unlock(); - }); -} - -/// @} - -} diff --git a/core/shared_ptr.hh b/core/shared_ptr.hh deleted file mode 100644 index 4684f573c5..0000000000 --- a/core/shared_ptr.hh +++ /dev/null @@ -1,729 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef SHARED_PTR_HH_ -#define SHARED_PTR_HH_ - -#include "shared_ptr_debug_helper.hh" -#include -#include -#include -#include -#include "util/is_smart_ptr.hh" - -// This header defines two shared pointer facilities, lw_shared_ptr<> and -// shared_ptr<>, both modeled after std::shared_ptr<>. -// -// Unlike std::shared_ptr<>, neither of these implementations are thread -// safe, and two pointers sharing the same object must not be used in -// different threads. -// -// lw_shared_ptr<> is the more lightweight variant, with a lw_shared_ptr<> -// occupying just one machine word, and adding just one word to the shared -// object. However, it does not support polymorphism. -// -// shared_ptr<> is more expensive, with a pointer occupying two machine -// words, and with two words of overhead in the shared object. In return, -// it does support polymorphism. -// -// Both variants support shared_from_this() via enable_shared_from_this<> -// and lw_enable_shared_from_this<>(). -// - -#ifndef DEBUG_SHARED_PTR -using shared_ptr_counter_type = long; -#else -using shared_ptr_counter_type = debug_shared_ptr_counter_type; -#endif - -template -class lw_shared_ptr; - -template -class shared_ptr; - -template -class enable_lw_shared_from_this; - -template -class enable_shared_from_this; - -template -lw_shared_ptr make_lw_shared(A&&... a); - -template -lw_shared_ptr make_lw_shared(T&& a); - -template -lw_shared_ptr make_lw_shared(T& a); - -template -shared_ptr make_shared(A&&... a); - -template -shared_ptr make_shared(T&& a); - -template -shared_ptr static_pointer_cast(const shared_ptr& p); - -template -shared_ptr dynamic_pointer_cast(const shared_ptr& p); - -template -shared_ptr const_pointer_cast(const shared_ptr& p); - -// We want to support two use cases for shared_ptr: -// -// 1. T is any type (primitive or class type) -// -// 2. T is a class type that inherits from enable_shared_from_this. -// -// In the first case, we must wrap T in an object containing the counter, -// since T may be a primitive type and cannot be a base class. -// -// In the second case, we want T to reach the counter through its -// enable_shared_from_this<> base class, so that we can implement -// shared_from_this(). -// -// To implement those two conflicting requirements (T alongside its counter; -// T inherits from an object containing the counter) we use std::conditional<> -// and some accessor functions to select between two implementations. - - -// CRTP from this to enable shared_from_this: -template -class enable_lw_shared_from_this { - shared_ptr_counter_type _count = 0; - using ctor = T; - T* to_value() { return static_cast(this); } - T* to_internal_object() { return static_cast(this); } -protected: - enable_lw_shared_from_this() noexcept {} - enable_lw_shared_from_this(enable_lw_shared_from_this&&) noexcept {} - enable_lw_shared_from_this(const enable_lw_shared_from_this&) noexcept {} - enable_lw_shared_from_this& operator=(const enable_lw_shared_from_this&) noexcept { return *this; } - enable_lw_shared_from_this& operator=(enable_lw_shared_from_this&&) noexcept { return *this; } -public: - lw_shared_ptr shared_from_this(); - lw_shared_ptr shared_from_this() const; - template - friend class lw_shared_ptr; -}; - -template -struct shared_ptr_no_esft { - shared_ptr_counter_type _count = 0; - T _value; - using ctor = shared_ptr_no_esft; - - T* to_value() { return &_value; } - shared_ptr_no_esft* to_internal_object() { return this; } - shared_ptr_no_esft() = default; - shared_ptr_no_esft(const T& x) : _value(x) {} - shared_ptr_no_esft(T&& x) : _value(std::move(x)) {} - template - shared_ptr_no_esft(A&&... a) : _value(std::forward(a)...) {} - template - friend class lw_shared_ptr; -}; - -template -using shared_ptr_impl - = std::conditional_t< - std::is_base_of>, T>::value, - enable_lw_shared_from_this>, - shared_ptr_no_esft> - >; - -template -class lw_shared_ptr { - mutable shared_ptr_impl* _p = nullptr; -private: - lw_shared_ptr(shared_ptr_impl* p) noexcept : _p(p) { - if (_p) { - ++_p->_count; - } - } - template - static lw_shared_ptr make(A&&... a) { - return lw_shared_ptr(new typename shared_ptr_impl::ctor(std::forward(a)...)); - } -public: - using element_type = T; - - lw_shared_ptr() noexcept = default; - lw_shared_ptr(std::nullptr_t) noexcept : lw_shared_ptr() {} - lw_shared_ptr(const lw_shared_ptr& x) noexcept : _p(x._p) { - if (_p) { - ++_p->_count; - } - } - lw_shared_ptr(lw_shared_ptr&& x) noexcept : _p(x._p) { - x._p = nullptr; - } - ~lw_shared_ptr() { - if (_p && !--_p->_count) { - delete _p->to_internal_object(); - } - } - lw_shared_ptr& operator=(const lw_shared_ptr& x) noexcept { - if (_p != x._p) { - this->~lw_shared_ptr(); - new (this) lw_shared_ptr(x); - } - return *this; - } - lw_shared_ptr& operator=(lw_shared_ptr&& x) noexcept { - if (_p != x._p) { - this->~lw_shared_ptr(); - new (this) lw_shared_ptr(std::move(x)); - } - return *this; - } - lw_shared_ptr& operator=(std::nullptr_t) noexcept { - return *this = lw_shared_ptr(); - } - lw_shared_ptr& operator=(T&& x) noexcept { - this->~lw_shared_ptr(); - new (this) lw_shared_ptr(make_lw_shared(std::move(x))); - return *this; - } - - T& operator*() const noexcept { return *_p->to_value(); } - T* operator->() const noexcept { return _p->to_value(); } - T* get() const noexcept { return _p->to_value(); } - - long int use_count() const noexcept { - if (_p) { - return _p->_count; - } else { - return 0; - } - } - - operator lw_shared_ptr() const noexcept { - return lw_shared_ptr(_p); - } - - explicit operator bool() const noexcept { - return _p; - } - - bool owned() const noexcept { - return _p->_count == 1; - } - - bool operator==(const lw_shared_ptr& x) const { - return _p == x._p; - } - - bool operator!=(const lw_shared_ptr& x) const { - return !operator==(x); - } - - bool operator==(const lw_shared_ptr>& x) const { - return _p == x._p; - } - - bool operator!=(const lw_shared_ptr>& x) const { - return !operator==(x); - } - - template - friend class lw_shared_ptr; - - template - friend lw_shared_ptr make_lw_shared(A&&...); - - template - friend lw_shared_ptr make_lw_shared(U&&); - - template - friend lw_shared_ptr make_lw_shared(U&); - - template - friend class enable_lw_shared_from_this; -}; - -template -inline -lw_shared_ptr make_lw_shared(A&&... a) { - return lw_shared_ptr::make(std::forward(a)...); -} - -template -inline -lw_shared_ptr make_lw_shared(T&& a) { - return lw_shared_ptr::make(std::move(a)); -} - -template -inline -lw_shared_ptr make_lw_shared(T& a) { - return lw_shared_ptr::make(a); -} - -template -inline -lw_shared_ptr -enable_lw_shared_from_this::shared_from_this() { - return lw_shared_ptr(this); -} - -template -inline -lw_shared_ptr -enable_lw_shared_from_this::shared_from_this() const { - return lw_shared_ptr(const_cast(this)); -} - -template -static inline -std::ostream& operator<<(std::ostream& out, const lw_shared_ptr& p) { - if (!p) { - return out << "null"; - } - return out << *p; -} - -// Polymorphic shared pointer class - -struct shared_ptr_count_base { - // destructor is responsible for fully-typed deletion - virtual ~shared_ptr_count_base() {} - shared_ptr_counter_type count = 0; -}; - -template -struct shared_ptr_count_for : shared_ptr_count_base { - T data; - template - shared_ptr_count_for(A&&... a) : data(std::forward(a)...) {} -}; - -template -class enable_shared_from_this : private shared_ptr_count_base { -public: - shared_ptr shared_from_this(); - shared_ptr shared_from_this() const; - - template - friend class shared_ptr; - - template - friend struct shared_ptr_make_helper; -}; - -template -class shared_ptr { - mutable shared_ptr_count_base* _b = nullptr; - mutable T* _p = nullptr; -private: - explicit shared_ptr(shared_ptr_count_for* b) noexcept : _b(b), _p(&b->data) { - ++_b->count; - } - shared_ptr(shared_ptr_count_base* b, T* p) noexcept : _b(b), _p(p) { - if (_b) { - ++_b->count; - } - } - explicit shared_ptr(enable_shared_from_this>* p) noexcept : _b(p), _p(static_cast(p)) { - if (_b) { - ++_b->count; - } - } -public: - using element_type = T; - - shared_ptr() noexcept = default; - shared_ptr(std::nullptr_t) noexcept : shared_ptr() {} - shared_ptr(const shared_ptr& x) noexcept - : _b(x._b) - , _p(x._p) { - if (_b) { - ++_b->count; - } - } - shared_ptr(shared_ptr&& x) noexcept - : _b(x._b) - , _p(x._p) { - x._b = nullptr; - x._p = nullptr; - } - template ::value>> - shared_ptr(const shared_ptr& x) noexcept - : _b(x._b) - , _p(x._p) { - if (_b) { - ++_b->count; - } - } - template ::value>> - shared_ptr(shared_ptr&& x) noexcept - : _b(x._b) - , _p(x._p) { - x._b = nullptr; - x._p = nullptr; - } - ~shared_ptr() { - if (_b && !--_b->count) { - delete _b; - } - } - shared_ptr& operator=(const shared_ptr& x) noexcept { - if (this != &x) { - this->~shared_ptr(); - new (this) shared_ptr(x); - } - return *this; - } - shared_ptr& operator=(shared_ptr&& x) noexcept { - if (this != &x) { - this->~shared_ptr(); - new (this) shared_ptr(std::move(x)); - } - return *this; - } - shared_ptr& operator=(std::nullptr_t) noexcept { - return *this = shared_ptr(); - } - template ::value>> - shared_ptr& operator=(const shared_ptr& x) noexcept { - if (*this != x) { - this->~shared_ptr(); - new (this) shared_ptr(x); - } - return *this; - } - template ::value>> - shared_ptr& operator=(shared_ptr&& x) noexcept { - if (*this != x) { - this->~shared_ptr(); - new (this) shared_ptr(std::move(x)); - } - return *this; - } - explicit operator bool() const noexcept { - return _p; - } - T& operator*() const noexcept { - return *_p; - } - T* operator->() const noexcept { - return _p; - } - T* get() const noexcept { - return _p; - } - long use_count() const noexcept { - if (_b) { - return _b->count; - } else { - return 0; - } - } - - template - struct make_helper; - - template - friend shared_ptr make_shared(A&&... a); - - template - friend shared_ptr make_shared(U&& a); - - template - friend shared_ptr static_pointer_cast(const shared_ptr& p); - - template - friend shared_ptr dynamic_pointer_cast(const shared_ptr& p); - - template - friend shared_ptr const_pointer_cast(const shared_ptr& p); - - template - static shared_ptr make(A&&... a); - - template - friend class enable_shared_from_this; - - template - friend struct shared_ptr_make_helper; - - template - friend class shared_ptr; -}; - -template -struct shared_ptr_make_helper; - -template -struct shared_ptr_make_helper { - template - static shared_ptr make(A&&... a) { - return shared_ptr(new shared_ptr_count_for(std::forward(a)...)); - } -}; - -template -struct shared_ptr_make_helper { - template - static shared_ptr make(A&&... a) { - auto p = new T(std::forward(a)...); - return shared_ptr(p, p); - } -}; - -template -inline -shared_ptr -make_shared(A&&... a) { - using helper = shared_ptr_make_helper::value>; - return helper::make(std::forward(a)...); -} - -template -inline -shared_ptr -make_shared(T&& a) { - using helper = shared_ptr_make_helper::value>; - return helper::make(std::forward(a)); -} - -template -inline -shared_ptr -static_pointer_cast(const shared_ptr& p) { - return shared_ptr(p._b, static_cast(p._p)); -} - -template -inline -shared_ptr -dynamic_pointer_cast(const shared_ptr& p) { - auto q = dynamic_cast(p._p); - return shared_ptr(q ? p._b : nullptr, q); -} - -template -inline -shared_ptr -const_pointer_cast(const shared_ptr& p) { - return shared_ptr(p._b, const_cast(p._p)); -} - -template -inline -shared_ptr -enable_shared_from_this::shared_from_this() { - auto unconst = reinterpret_cast>*>(this); - return shared_ptr(unconst); -} - -template -inline -shared_ptr -enable_shared_from_this::shared_from_this() const { - auto esft = const_cast(this); - auto unconst = reinterpret_cast>*>(esft); - return shared_ptr(unconst); -} - -template -inline -bool -operator==(const shared_ptr& x, const shared_ptr& y) { - return x.get() == y.get(); -} - -template -inline -bool -operator==(const shared_ptr& x, std::nullptr_t) { - return x.get() == nullptr; -} - -template -inline -bool -operator==(std::nullptr_t, const shared_ptr& y) { - return nullptr == y.get(); -} - -template -inline -bool -operator!=(const shared_ptr& x, const shared_ptr& y) { - return x.get() != y.get(); -} - -template -inline -bool -operator!=(const shared_ptr& x, std::nullptr_t) { - return x.get() != nullptr; -} - -template -inline -bool -operator!=(std::nullptr_t, const shared_ptr& y) { - return nullptr != y.get(); -} - -template -inline -bool -operator<(const shared_ptr& x, const shared_ptr& y) { - return x.get() < y.get(); -} - -template -inline -bool -operator<(const shared_ptr& x, std::nullptr_t) { - return x.get() < nullptr; -} - -template -inline -bool -operator<(std::nullptr_t, const shared_ptr& y) { - return nullptr < y.get(); -} - -template -inline -bool -operator<=(const shared_ptr& x, const shared_ptr& y) { - return x.get() <= y.get(); -} - -template -inline -bool -operator<=(const shared_ptr& x, std::nullptr_t) { - return x.get() <= nullptr; -} - -template -inline -bool -operator<=(std::nullptr_t, const shared_ptr& y) { - return nullptr <= y.get(); -} - -template -inline -bool -operator>(const shared_ptr& x, const shared_ptr& y) { - return x.get() > y.get(); -} - -template -inline -bool -operator>(const shared_ptr& x, std::nullptr_t) { - return x.get() > nullptr; -} - -template -inline -bool -operator>(std::nullptr_t, const shared_ptr& y) { - return nullptr > y.get(); -} - -template -inline -bool -operator>=(const shared_ptr& x, const shared_ptr& y) { - return x.get() >= y.get(); -} - -template -inline -bool -operator>=(const shared_ptr& x, std::nullptr_t) { - return x.get() >= nullptr; -} - -template -inline -bool -operator>=(std::nullptr_t, const shared_ptr& y) { - return nullptr >= y.get(); -} - -template -static inline -std::ostream& operator<<(std::ostream& out, const shared_ptr& p) { - if (!p) { - return out << "null"; - } - return out << *p; -} - -template -struct shared_ptr_equal_by_value { - bool operator()(const shared_ptr& i1, const shared_ptr& i2) const { - if (bool(i1) ^ bool(i2)) { - return false; - } - return !i1 || *i1 == *i2; - } -}; - -template -struct shared_ptr_value_hash { - size_t operator()(const shared_ptr& p) const { - if (p) { - return std::hash()(*p); - } - return 0; - } -}; - -namespace std { - -template -struct hash> : private hash { - size_t operator()(const lw_shared_ptr& p) const { - return hash::operator()(p.get()); - } -}; - -template -struct hash<::shared_ptr> : private hash { - size_t operator()(const ::shared_ptr& p) const { - return hash::operator()(p.get()); - } -}; - -} - -template -struct is_smart_ptr<::shared_ptr> : std::true_type {}; - -template -struct is_smart_ptr<::lw_shared_ptr> : std::true_type {}; - -#endif /* SHARED_PTR_HH_ */ diff --git a/core/shared_ptr_debug_helper.hh b/core/shared_ptr_debug_helper.hh deleted file mode 100644 index 869fb96348..0000000000 --- a/core/shared_ptr_debug_helper.hh +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#ifdef DEBUG_SHARED_PTR - -#include -#include - -// A counter that is only comfortable being incremented on the cpu -// it was created on. Useful for verifying that a shared_ptr -// or lw_shared_ptr isn't misued across cores. -class debug_shared_ptr_counter_type { - long _counter = 0; - std::thread::id _cpu = std::this_thread::get_id(); -public: - debug_shared_ptr_counter_type(long x) : _counter(x) {} - operator long() const { - check(); - return _counter; - } - debug_shared_ptr_counter_type& operator++() { - check(); - ++_counter; - return *this; - } - long operator++(int) { - check(); - return _counter++; - } - debug_shared_ptr_counter_type& operator--() { - check(); - --_counter; - return *this; - } - long operator--(int) { - check(); - return _counter--; - } -private: - void check() const { - assert(_cpu == std::this_thread::get_id()); - } -}; - -#endif - diff --git a/core/slab.hh b/core/slab.hh deleted file mode 100644 index c3e9db21bc..0000000000 --- a/core/slab.hh +++ /dev/null @@ -1,572 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ -#ifndef __SLAB_ALLOCATOR__ -#define __SLAB_ALLOCATOR__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "core/scollectd.hh" -#include "core/align.hh" -#include "core/memory.hh" - -static constexpr uint16_t SLAB_MAGIC_NUMBER = 0x51AB; // meant to be 'SLAB' :-) -typedef uint64_t uintptr_t; - -namespace bi = boost::intrusive; - -/* - * Item requirements - * - Extend it to slab_item_base. - * - First parameter of constructor must be uint32_t _slab_page_index. - * - Implement get_slab_page_index() to return _slab_page_index. - * - Implement is_unlocked() to check if Item can be evicted. - */ - -/* - * slab_page_desc is 1:1 mapped to slab page. - * footprint: 80b for each slab page. - */ -struct slab_page_desc { -private: - bi::list_member_hook<> _lru_link; - bi::list_member_hook<> _free_pages_link; - void *_slab_page; - std::vector _free_objects; - uint32_t _refcnt; - uint32_t _index; // index into slab page vector - uint16_t _magic; - uint8_t _slab_class_id; -public: - slab_page_desc(void *slab_page, size_t objects, size_t object_size, uint8_t slab_class_id, uint32_t index) - : _slab_page(slab_page) - , _refcnt(0U) - , _index(index) - , _magic(SLAB_MAGIC_NUMBER) - , _slab_class_id(slab_class_id) - { - auto object = reinterpret_cast(slab_page); - _free_objects.reserve(objects - 1); - for (auto i = 1u; i < objects; i++) { - object += object_size; - _free_objects.push_back(object); - } - } - - bool empty() const { - return _free_objects.empty(); - } - - size_t size() const { - return _free_objects.size(); - } - - uint32_t& refcnt() { - return _refcnt; - } - - uint32_t index() const { - return _index; - } - - uint16_t magic() const { - return _magic; - } - - uint8_t slab_class_id() const { - return _slab_class_id; - } - - void* slab_page() const { - return _slab_page; - } - - std::vector& free_objects() { - return _free_objects; - } - - void* allocate_object() { - assert(!_free_objects.empty()); - auto object = reinterpret_cast(_free_objects.back()); - _free_objects.pop_back(); - return object; - } - - void free_object(void *object) { - _free_objects.push_back(reinterpret_cast(object)); - } - - template - friend class slab_class; - template - friend class slab_allocator; -}; - -class slab_item_base { - bi::list_member_hook<> _lru_link; - - template - friend class slab_class; -}; - -template -class slab_class { -private: - bi::list, - &slab_page_desc::_free_pages_link>> _free_slab_pages; - bi::list, - &slab_item_base::_lru_link>> _lru; - size_t _size; // size of objects - uint8_t _slab_class_id; -private: - template - inline - Item* create_item(void *object, uint32_t slab_page_index, Args&&... args) { - Item *new_item = new(object) Item(slab_page_index, std::forward(args)...); - _lru.push_front(reinterpret_cast(*new_item)); - return new_item; - } - - inline - std::pair evict_lru_item(std::function& erase_func) { - if (_lru.empty()) { - return { nullptr, 0U }; - } - - Item& victim = reinterpret_cast(_lru.back()); - uint32_t index = victim.get_slab_page_index(); - assert(victim.is_unlocked()); - _lru.erase(_lru.iterator_to(reinterpret_cast(victim))); - // WARNING: You need to make sure that erase_func will not release victim back to slab. - erase_func(victim); - - return { reinterpret_cast(&victim), index }; - } -public: - slab_class(size_t size, uint8_t slab_class_id) - : _size(size) - , _slab_class_id(slab_class_id) - { - } - slab_class(slab_class&&) = default; - ~slab_class() { - _free_slab_pages.clear(); - _lru.clear(); - } - - size_t size() const { - return _size; - } - - bool empty() const { - return _free_slab_pages.empty(); - } - - bool has_no_slab_pages() const { - return _lru.empty(); - } - - template - Item *create(Args&&... args) { - assert(!_free_slab_pages.empty()); - auto& desc = _free_slab_pages.back(); - auto object = desc.allocate_object(); - if (desc.empty()) { - // if empty, remove desc from the list of slab pages with free objects. - _free_slab_pages.erase(_free_slab_pages.iterator_to(desc)); - } - - return create_item(object, desc.index(), std::forward(args)...); - } - - template - Item *create_from_new_page(uint64_t max_object_size, uint32_t slab_page_index, - std::function insert_slab_page_desc, - Args&&... args) { - // allocate slab page. - constexpr size_t alignment = std::alignment_of::value; - void *slab_page = aligned_alloc(alignment, max_object_size); - if (!slab_page) { - throw std::bad_alloc{}; - } - // allocate descriptor to slab page. - slab_page_desc *desc = nullptr; - assert(_size % alignment == 0); - try { - auto objects = max_object_size / _size; - desc = new slab_page_desc(slab_page, objects, _size, _slab_class_id, slab_page_index); - } catch (const std::bad_alloc& e) { - ::free(slab_page); - throw std::bad_alloc{}; - } - - _free_slab_pages.push_front(*desc); - insert_slab_page_desc(*desc); - - // first object from the allocated slab page is returned. - return create_item(slab_page, slab_page_index, std::forward(args)...); - } - - template - Item *create_from_lru(std::function& erase_func, Args&&... args) { - auto ret = evict_lru_item(erase_func); - if (!ret.first) { - throw std::bad_alloc{}; - } - return create_item(ret.first, ret.second, std::forward(args)...); - } - - void free_item(Item *item, slab_page_desc& desc) { - void *object = item; - _lru.erase(_lru.iterator_to(reinterpret_cast(*item))); - desc.free_object(object); - if (desc.size() == 1) { - // push back desc into the list of slab pages with free objects. - _free_slab_pages.push_back(desc); - } - } - - void touch_item(Item *item) { - auto& item_ref = reinterpret_cast(*item); - _lru.erase(_lru.iterator_to(item_ref)); - _lru.push_front(item_ref); - } - - void remove_item_from_lru(Item *item) { - auto& item_ref = reinterpret_cast(*item); - _lru.erase(_lru.iterator_to(item_ref)); - } - - void insert_item_into_lru(Item *item) { - auto& item_ref = reinterpret_cast(*item); - _lru.push_front(item_ref); - } - - void remove_desc_from_free_list(slab_page_desc& desc) { - assert(desc.slab_class_id() == _slab_class_id); - _free_slab_pages.erase(_free_slab_pages.iterator_to(desc)); - } -}; - -template -class slab_allocator { -private: - std::vector _slab_class_sizes; - std::vector> _slab_classes; - std::vector _registrations; - // erase_func() is used to remove the item from the cache using slab. - std::function _erase_func; - std::vector _slab_pages_vector; - bi::list, - &slab_page_desc::_lru_link>> _slab_page_desc_lru; - uint64_t _max_object_size; - uint64_t _available_slab_pages; - struct collectd_stats { - uint64_t allocs; - uint64_t frees; - } _stats; - memory::reclaimer *_reclaimer = nullptr; - bool _reclaimed = false; -private: - void evict_lru_slab_page() { - if (_slab_page_desc_lru.empty()) { - // NOTE: Nothing to evict. If this happens, it implies that all - // slab pages in the slab are being used at the same time. - // That being said, this event is very unlikely to happen. - return; - } - // get descriptor of the least-recently-used slab page and related info. - auto& desc = _slab_page_desc_lru.back(); - assert(desc.refcnt() == 0); - uint8_t slab_class_id = desc.slab_class_id(); - auto slab_class = get_slab_class(slab_class_id); - void *slab_page = desc.slab_page(); - - auto& free_objects = desc.free_objects(); - if (!desc.empty()) { - // if not empty, remove desc from the list of slab pages with free objects. - slab_class->remove_desc_from_free_list(desc); - // and sort the array of free objects for binary search later on. - std::sort(free_objects.begin(), free_objects.end()); - } - // remove desc from the list of slab page descriptors. - _slab_page_desc_lru.erase(_slab_page_desc_lru.iterator_to(desc)); - // remove desc from the slab page vector. - _slab_pages_vector[desc.index()] = nullptr; - - // Iterate through objects in the slab page and if the object is an allocated - // item, the item should be removed from LRU and then erased. - uintptr_t object = reinterpret_cast(slab_page); - auto object_size = slab_class->size(); - auto objects = _max_object_size / object_size; - for (auto i = 0u; i < objects; i++, object += object_size) { - if (!desc.empty()) { - // if binary_search returns true, it means that object at the current - // offset isn't an item. - if (std::binary_search(free_objects.begin(), free_objects.end(), object)) { - continue; - } - } - Item* item = reinterpret_cast(object); - assert(item->is_unlocked()); - slab_class->remove_item_from_lru(item); - _erase_func(*item); - _stats.frees++; - } -#ifdef DEBUG - printf("lru slab page eviction succeeded! desc_empty?=%d\n", desc.empty()); -#endif - ::free(slab_page); // free slab page object - delete &desc; // free its descriptor - } - - /* - * Reclaim the least recently used slab page that is unused. - */ - void reclaim() { - // once reclaimer was called, slab pages should no longer be allocated, as the - // memory used by slab is supposed to be calibrated. - _reclaimed = true; - // FIXME: Should reclaim() only evict a single slab page at a time? - evict_lru_slab_page(); - } - - void initialize_slab_allocator(double growth_factor, uint64_t limit) { - constexpr size_t alignment = std::alignment_of::value; - constexpr size_t initial_size = 96; - size_t size = initial_size; // initial object size - uint8_t slab_class_id = 0U; - - while (_max_object_size / size > 1) { - size = align_up(size, alignment); - _slab_class_sizes.push_back(size); - _slab_classes.emplace_back(size, slab_class_id); - size *= growth_factor; - assert(slab_class_id < std::numeric_limits::max()); - slab_class_id++; - } - _slab_class_sizes.push_back(_max_object_size); - _slab_classes.emplace_back(_max_object_size, slab_class_id); - - // If slab limit is zero, enable reclaimer. - if (!limit) { - _reclaimer = new memory::reclaimer([this] { reclaim(); }); - } else { - _slab_pages_vector.reserve(_available_slab_pages); - } - } - - slab_class* get_slab_class(const size_t size) { - // given a size, find slab class with binary search. - auto i = std::lower_bound(_slab_class_sizes.begin(), _slab_class_sizes.end(), size); - if (i == _slab_class_sizes.end()) { - return nullptr; - } - auto dist = std::distance(_slab_class_sizes.begin(), i); - return &_slab_classes[dist]; - } - - slab_class* get_slab_class(const uint8_t slab_class_id) { - assert(slab_class_id >= 0 && slab_class_id < _slab_classes.size()); - return &_slab_classes[slab_class_id]; - } - - void register_collectd_metrics() { - auto add = [this] (auto type_name, auto name, auto data_type, auto func) { - _registrations.push_back( - scollectd::add_polled_metric(scollectd::type_instance_id("slab", - scollectd::per_cpu_plugin_instance, - type_name, name), - scollectd::make_typed(data_type, func))); - }; - - add("total_operations", "malloc", scollectd::data_type::DERIVE, [&] { return _stats.allocs; }); - add("total_operations", "free", scollectd::data_type::DERIVE, [&] { return _stats.frees; }); - add("objects", "malloc", scollectd::data_type::GAUGE, [&] { return _stats.allocs - _stats.frees; }); - } - - inline slab_page_desc& get_slab_page_desc(Item *item) - { - auto desc = _slab_pages_vector[item->get_slab_page_index()]; - assert(desc != nullptr); - assert(desc->magic() == SLAB_MAGIC_NUMBER); - return *desc; - } - - inline bool can_allocate_page(slab_class& sc) { - return (_reclaimer && !_reclaimed) || - (_available_slab_pages > 0 || sc.has_no_slab_pages()); - } -public: - slab_allocator(double growth_factor, uint64_t limit, uint64_t max_object_size) - : _max_object_size(max_object_size) - , _available_slab_pages(limit / max_object_size) - { - initialize_slab_allocator(growth_factor, limit); - register_collectd_metrics(); - } - - slab_allocator(double growth_factor, uint64_t limit, uint64_t max_object_size, - std::function erase_func) - : _erase_func(std::move(erase_func)) - , _max_object_size(max_object_size) - , _available_slab_pages(limit / max_object_size) - { - initialize_slab_allocator(growth_factor, limit); - register_collectd_metrics(); - } - - ~slab_allocator() - { - _slab_page_desc_lru.clear(); - for (auto desc : _slab_pages_vector) { - if (!desc) { - continue; - } - ::free(desc->slab_page()); - delete desc; - } - _registrations.clear(); - delete _reclaimer; - } - - /** - * Create an item from a given slab class based on requested size. - */ - template - Item* create(const size_t size, Args&&... args) { - auto slab_class = get_slab_class(size); - if (!slab_class) { - throw std::bad_alloc{}; - } - - Item *item = nullptr; - if (!slab_class->empty()) { - item = slab_class->create(std::forward(args)...); - _stats.allocs++; - } else { - if (can_allocate_page(*slab_class)) { - auto index_to_insert = _slab_pages_vector.size(); - item = slab_class->create_from_new_page(_max_object_size, index_to_insert, - [this](slab_page_desc& desc) { - if (_reclaimer) { - // insert desc into the LRU list of slab page descriptors. - _slab_page_desc_lru.push_front(desc); - } - // insert desc into the slab page vector. - _slab_pages_vector.push_back(&desc); - }, - std::forward(args)...); - if (_available_slab_pages > 0) { - _available_slab_pages--; - } - _stats.allocs++; - } else if (_erase_func) { - item = slab_class->create_from_lru(_erase_func, std::forward(args)...); - } - } - return item; - } - - void lock_item(Item *item) { - auto& desc = get_slab_page_desc(item); - if (_reclaimer) { - auto& refcnt = desc.refcnt(); - - if (++refcnt == 1) { - // remove slab page descriptor from list of slab page descriptors. - _slab_page_desc_lru.erase(_slab_page_desc_lru.iterator_to(desc)); - } - } - // remove item from the lru of its slab class. - auto slab_class = get_slab_class(desc.slab_class_id()); - slab_class->remove_item_from_lru(item); - } - - void unlock_item(Item *item) { - auto& desc = get_slab_page_desc(item); - if (_reclaimer) { - auto& refcnt = desc.refcnt(); - - if (--refcnt == 0) { - // insert slab page descriptor back into list of slab page descriptors. - _slab_page_desc_lru.push_front(desc); - } - } - // insert item into the lru of its slab class. - auto slab_class = get_slab_class(desc.slab_class_id()); - slab_class->insert_item_into_lru(item); - } - - /** - * Free an item back to its original slab class. - */ - void free(Item *item) { - if (item) { - auto& desc = get_slab_page_desc(item); - auto slab_class = get_slab_class(desc.slab_class_id()); - slab_class->free_item(item, desc); - _stats.frees++; - } - } - - /** - * Update item position in the LRU of its slab class. - */ - void touch(Item *item) { - if (item) { - auto& desc = get_slab_page_desc(item); - auto slab_class = get_slab_class(desc.slab_class_id()); - slab_class->touch_item(item); - } - } - - /** - * Helper function: Print all available slab classes and their respective properties. - */ - void print_slab_classes() { - auto class_id = 0; - for (auto& slab_class : _slab_classes) { - size_t size = slab_class.size(); - printf("slab[%3d]\tsize: %10lu\tper-slab-page: %5lu\n", class_id, size, _max_object_size / size); - class_id++; - } - } - - /** - * Helper function: Useful for getting a slab class' chunk size from a size parameter. - */ - size_t class_size(const size_t size) { - auto slab_class = get_slab_class(size); - return (slab_class) ? slab_class->size() : 0; - } -}; - -#endif /* __SLAB_ALLOCATOR__ */ diff --git a/core/sleep.hh b/core/sleep.hh deleted file mode 100644 index f25c6d40c0..0000000000 --- a/core/sleep.hh +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include -#include - -#include "core/shared_ptr.hh" -#include "core/reactor.hh" -#include "core/future.hh" - -template -future<> sleep(std::chrono::duration dur) { - struct sleeper { - promise<> done; - timer tmr; - sleeper(std::chrono::duration dur) - : tmr([this] { done.set_value(); }) - { - tmr.arm(dur); - } - }; - sleeper *s = new sleeper(dur); - future<> fut = s->done.get_future(); - return fut.then([s] { delete s; }); -} diff --git a/core/sstring.hh b/core/sstring.hh deleted file mode 100644 index 1a2dc21cae..0000000000 --- a/core/sstring.hh +++ /dev/null @@ -1,688 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#ifndef SSTRING_HH_ -#define SSTRING_HH_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "core/temporary_buffer.hh" - -template -class basic_sstring { - static_assert( - (std::is_same::value - || std::is_same::value - || std::is_same::value), - "basic_sstring only supports single byte char types"); - union contents { - struct external_type { - char_type* str; - Size size; - int8_t pad; - } external; - struct internal_type { - char_type str[max_size]; - int8_t size; - } internal; - static_assert(sizeof(external_type) <= sizeof(internal_type), "max_size too small"); - static_assert(max_size <= 127, "max_size too large"); - } u; - bool is_internal() const noexcept { - return u.internal.size >= 0; - } - bool is_external() const noexcept { - return !is_internal(); - } - const char_type* str() const { - return is_internal() ? u.internal.str : u.external.str; - } - char_type* str() { - return is_internal() ? u.internal.str : u.external.str; - } -public: - using value_type = char_type; - using traits_type = std::char_traits; - using allocator_type = std::allocator; - using reference = char_type&; - using const_reference = const char_type&; - using pointer = char_type*; - using const_pointer = const char_type*; - using iterator = char_type*; - using const_iterator = const char_type*; - // FIXME: add reverse_iterator and friend - using difference_type = ssize_t; // std::make_signed_t can be too small - using size_type = Size; - static constexpr size_type npos = static_cast(-1); -public: - struct initialized_later {}; - - basic_sstring() noexcept { - u.internal.size = 0; - u.internal.str[0] = '\0'; - } - basic_sstring(const basic_sstring& x) { - if (x.is_internal()) { - u.internal = x.u.internal; - } else { - u.internal.size = -1; - u.external.str = reinterpret_cast(std::malloc(x.u.external.size + 1)); - if (!u.external.str) { - throw std::bad_alloc(); - } - std::copy(x.u.external.str, x.u.external.str + x.u.external.size + 1, u.external.str); - u.external.size = x.u.external.size; - } - } - basic_sstring(basic_sstring&& x) noexcept { - u = x.u; - x.u.internal.size = 0; - x.u.internal.str[0] = '\0'; - } - basic_sstring(initialized_later, size_t size) { - if (size_type(size) != size) { - throw std::overflow_error("sstring overflow"); - } - if (size + 1 <= sizeof(u.internal.str)) { - u.internal.str[size] = '\0'; - u.internal.size = size; - } else { - u.internal.size = -1; - u.external.str = reinterpret_cast(std::malloc(size + 1)); - if (!u.external.str) { - throw std::bad_alloc(); - } - u.external.size = size; - u.external.str[size] = '\0'; - } - } - basic_sstring(const char_type* x, size_t size) { - if (size_type(size) != size) { - throw std::overflow_error("sstring overflow"); - } - if (size + 1 <= sizeof(u.internal.str)) { - std::copy(x, x + size, u.internal.str); - u.internal.str[size] = '\0'; - u.internal.size = size; - } else { - u.internal.size = -1; - u.external.str = reinterpret_cast(std::malloc(size + 1)); - if (!u.external.str) { - throw std::bad_alloc(); - } - u.external.size = size; - std::copy(x, x + size, u.external.str); - u.external.str[size] = '\0'; - } - } - - basic_sstring(size_t size, char_type x) : basic_sstring(initialized_later(), size) { - memset(begin(), x, size); - } - - basic_sstring(const char* x) : basic_sstring(reinterpret_cast(x), std::strlen(x)) {} - basic_sstring(std::basic_string& x) : basic_sstring(x.c_str(), x.size()) {} - basic_sstring(std::initializer_list x) : basic_sstring(x.begin(), x.end() - x.begin()) {} - basic_sstring(const char_type* b, const char_type* e) : basic_sstring(b, e - b) {} - basic_sstring(const std::basic_string& s) - : basic_sstring(s.data(), s.size()) {} - template - basic_sstring(InputIterator first, InputIterator last) - : basic_sstring(initialized_later(), std::distance(first, last)) { - std::copy(first, last, begin()); - } - ~basic_sstring() noexcept { - if (is_external()) { - std::free(u.external.str); - } - } - basic_sstring& operator=(const basic_sstring& x) { - basic_sstring tmp(x); - swap(tmp); - return *this; - } - basic_sstring& operator=(basic_sstring&& x) noexcept { - if (this != &x) { - swap(x); - x.reset(); - } - return *this; - } - operator std::basic_string() const { - return { str(), size() }; - } - size_t size() const noexcept { - return is_internal() ? u.internal.size : u.external.size; - } - - size_t length() const noexcept { - return size(); - } - - size_t find(char_type t, size_t pos = 0) const noexcept { - const char_type* it = str() + pos; - const char_type* end = str() + size(); - while (it < end) { - if (*it == t) { - return it - str(); - } - it++; - } - return npos; - } - - size_t find(const basic_sstring& s, size_t pos = 0) const noexcept { - const char_type* it = str() + pos; - const char_type* end = str() + size(); - const char_type* c_str = s.str(); - const char_type* c_str_end = s.str() + s.size(); - - while (it < end) { - auto i = it; - auto j = c_str; - while ( i < end && j < c_str_end && *i == *j) { - i++; - j++; - } - if (j == c_str_end) { - return it - str(); - } - it++; - } - return npos; - } - - /** - * find_last_of find the last occurrence of c in the string. - * When pos is specified, the search only includes characters - * at or before position pos. - * - */ - size_t find_last_of (char_type c, size_t pos = npos) const noexcept { - const char_type* str_start = str(); - if (size()) { - if (pos >= size()) { - pos = size() - 1; - } - const char_type* p = str_start + pos + 1; - do { - p--; - if (*p == c) { - return (p - str_start); - } - } while (p != str_start); - } - return npos; - } - - /** - * Append a C substring. - * @param s The C string to append. - * @param n The number of characters to append. - * @return Reference to this string. - */ - basic_sstring& append (const char_type* s, size_t n) { - basic_sstring ret(initialized_later(), size() + n); - std::copy(begin(), end(), ret.begin()); - std::copy(s, s + n, ret.begin() + size()); - *this = std::move(ret); - return *this; - } - - /** - * Replace characters with a value of a C style substring. - * - */ - basic_sstring& replace(size_type pos, size_type n1, const char_type* s, - size_type n2) { - if (pos > size()) { - throw std::out_of_range("sstring::replace out of range"); - } - - if (n1 > size() - pos) { - n1 = size() - pos; - } - - if (n1 == n2) { - if (n2) { - std::copy(s, s + n2, begin() + pos); - } - return *this; - } - basic_sstring ret(initialized_later(), size() + n2 - n1); - char_type* p= ret.begin(); - std::copy(begin(), begin() + pos, p); - p += pos; - if (n2) { - std::copy(s, s + n2, p); - } - p += n2; - std::copy(begin() + pos + n1, end(), p); - *this = std::move(ret); - return *this; - } - - template - basic_sstring& replace (const_iterator i1, const_iterator i2, - InputIterator first, InputIterator last) { - if (i1 < begin() || i1 > end() || i2 < begin()) { - throw std::out_of_range("sstring::replace out of range"); - } - if (i2 > end()) { - i2 = end(); - } - - if (i2 - i1 == last - first) { - //in place replacement - std::copy(first, last, const_cast(i1)); - return *this; - } - basic_sstring ret(initialized_later(), size() + (last - first) - (i2 - i1)); - char_type* p = ret.begin(); - p = std::copy(cbegin(), i1, p); - p = std::copy(first, last, p); - std::copy(i2, cend(), p); - *this = std::move(ret); - return *this; - } - - iterator erase(iterator first, iterator last) { - size_t pos = first - begin(); - replace(pos, last - first, nullptr, 0); - return begin() + pos; - } - - /** - * Inserts additional characters into the string right before - * the character indicated by p. - */ - template - void insert(const_iterator p, InputIterator beg, InputIterator end) { - replace(p, p, beg, end); - } - - /** - * Returns a read/write reference to the data at the last - * element of the string. - * This function shall not be called on empty strings. - */ - reference - back() noexcept { - return operator[](size() - 1); - } - - /** - * Returns a read-only (constant) reference to the data at the last - * element of the string. - * This function shall not be called on empty strings. - */ - const_reference - back() const noexcept { - return operator[](size() - 1); - } - - basic_sstring substr(size_t from, size_t len = npos) const { - if (from > size()) { - throw std::out_of_range("sstring::substr out of range"); - } - if (len > size() - from) { - len = size() - from; - } - if (len == 0) { - return ""; - } - return { str() + from , len }; - } - - const char_type& at(size_t pos) const { - if (pos >= size()) { - throw std::out_of_range("sstring::at out of range"); - } - return *(str() + pos); - } - - char_type& at(size_t pos) { - if (pos >= size()) { - throw std::out_of_range("sstring::at out of range"); - } - return *(str() + pos); - } - - bool empty() const noexcept { - return u.internal.size == 0; - } - void reset() noexcept { - if (is_external()) { - std::free(u.external.str); - } - u.internal.size = 0; - u.internal.str[0] = '\0'; - } - temporary_buffer release() && { - if (is_external()) { - auto ptr = u.external.str; - auto size = u.external.size; - u.external.str = nullptr; - u.external.size = 0; - return temporary_buffer(ptr, size, make_free_deleter(ptr)); - } else { - auto buf = temporary_buffer(u.internal.size); - std::copy(u.internal.str, u.internal.str + u.internal.size, buf.get_write()); - u.internal.size = 0; - u.internal.str[0] = '\0'; - return buf; - } - } - int compare(const basic_sstring& x) const noexcept { - auto n = traits_type::compare(begin(), x.begin(), std::min(size(), x.size())); - if (n != 0) { - return n; - } - if (size() < x.size()) { - return -1; - } else if (size() > x.size()) { - return 1; - } else { - return 0; - } - } - - int compare(size_t pos, size_t sz, const basic_sstring& x) const { - if (pos > size()) { - throw std::out_of_range("pos larger than string size"); - } - - sz = std::min(size() - pos, sz); - auto n = traits_type::compare(begin() + pos, x.begin(), std::min(sz, x.size())); - if (n != 0) { - return n; - } - if (sz < x.size()) { - return -1; - } else if (sz > x.size()) { - return 1; - } else { - return 0; - } - } - - void swap(basic_sstring& x) noexcept { - contents tmp; - tmp = x.u; - x.u = u; - u = tmp; - } - const char_type* c_str() const { - return str(); - } - const char_type* begin() const { return str(); } - const char_type* end() const { return str() + size(); } - const char_type* cbegin() const { return str(); } - const char_type* cend() const { return str() + size(); } - char_type* begin() { return str(); } - char_type* end() { return str() + size(); } - bool operator==(const basic_sstring& x) const { - return size() == x.size() && std::equal(begin(), end(), x.begin()); - } - bool operator!=(const basic_sstring& x) const { - return !operator==(x); - } - bool operator<(const basic_sstring& x) const { - return compare(x) < 0; - } - basic_sstring operator+(const basic_sstring& x) const { - basic_sstring ret(initialized_later(), size() + x.size()); - std::copy(begin(), end(), ret.begin()); - std::copy(x.begin(), x.end(), ret.begin() + size()); - return ret; - } - basic_sstring& operator+=(const basic_sstring& x) { - return *this = *this + x; - } - char_type& operator[](size_type pos) { - return str()[pos]; - } - const char_type& operator[](size_type pos) const { - return str()[pos]; - } - - operator std::experimental::basic_string_view() const { - return std::experimental::basic_string_view(str(), size()); - } -}; -template -constexpr Size basic_sstring::npos; - -template -inline -basic_sstring -operator+(const char(&s)[N], const basic_sstring& t) { - using sstring = basic_sstring; - // don't copy the terminating NUL character - sstring ret(typename sstring::initialized_later(), N-1 + t.size()); - auto p = std::copy(std::begin(s), std::end(s)-1, ret.begin()); - std::copy(t.begin(), t.end(), p); - return ret; -} - -template -static inline -size_t str_len(const char(&s)[N]) { return N - 1; } - -template -static inline -const char* str_begin(const char(&s)[N]) { return s; } - -template -static inline -const char* str_end(const char(&s)[N]) { return str_begin(s) + str_len(s); } - -template -static inline -const char_type* str_begin(const basic_sstring& s) { return s.begin(); } - -template -static inline -const char_type* str_end(const basic_sstring& s) { return s.end(); } - -template -static inline -size_type str_len(const basic_sstring& s) { return s.size(); } - -template -static inline -const size_t str_len(const First& first, const Second& second, const Tail&... tail) { - return str_len(first) + str_len(second, tail...); -} - -template -inline -void swap(basic_sstring& x, - basic_sstring& y) noexcept -{ - return x.swap(y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, - const basic_sstring& s) { - return os.write(s.begin(), s.size()); -} - -template -inline -std::basic_istream& -operator>>(std::basic_istream& is, - basic_sstring& s) { - std::string tmp; - is >> tmp; - s = tmp; - return is; -} - -namespace std { - -template -struct hash> { - size_t operator()(const basic_sstring& s) const { - return std::hash>()(s); - } -}; - -} - -using sstring = basic_sstring; - -static inline -char* copy_str_to(char* dst) { - return dst; -} - -template -static inline -char* copy_str_to(char* dst, const Head& head, const Tail&... tail) { - return copy_str_to(std::copy(str_begin(head), str_end(head), dst), tail...); -} - -template -static String make_sstring(Args&&... args) -{ - String ret(sstring::initialized_later(), str_len(args...)); - copy_str_to(ret.begin(), args...); - return ret; -} - -template -inline -string_type to_sstring_sprintf(T value, const char* fmt) { - char tmp[sizeof(value) * 3 + 3]; - auto len = std::sprintf(tmp, fmt, value); - using char_type = typename string_type::value_type; - return string_type(reinterpret_cast(tmp), len); -} - -template -inline -sstring -to_sstring_sprintf(T value, const char* fmt) { - return to_sstring_sprintf(value, fmt); -} - -template -inline -string_type to_sstring(int value, void* = nullptr) { - return to_sstring_sprintf(value, "%d"); -} - -template -inline -string_type to_sstring(unsigned value, void* = nullptr) { - return to_sstring_sprintf(value, "%u"); -} - -template -inline -string_type to_sstring(long value, void* = nullptr) { - return to_sstring_sprintf(value, "%ld"); -} - -template -inline -string_type to_sstring(unsigned long value, void* = nullptr) { - return to_sstring_sprintf(value, "%lu"); -} - -template -inline -string_type to_sstring(long long value, void* = nullptr) { - return to_sstring_sprintf(value, "%lld"); -} - -template -inline -string_type to_sstring(unsigned long long value, void* = nullptr) { - return to_sstring_sprintf(value, "%llu"); -} - -template -inline -string_type to_sstring(float value, void* = nullptr) { - return to_sstring_sprintf(value, "%f"); -} - -template -inline -string_type to_sstring(double value, void* = nullptr) { - return to_sstring_sprintf(value, "%f"); -} - -template -inline -string_type to_sstring(long double value, void* = nullptr) { - return to_sstring_sprintf(value, "%Lf"); -} - -template -inline -string_type to_sstring(const char* value, void* = nullptr) { - return string_type(value); -} - -template -inline -string_type to_sstring(sstring value, void* = nullptr) { - return value; -} - -template -static string_type to_sstring(const temporary_buffer& buf) { - return string_type(buf.get(), buf.size()); -} - -template -inline -std::ostream& operator<<(std::ostream& os, const std::vector& v) { - bool first = true; - os << "{"; - for (auto&& elem : v) { - if (!first) { - os << ", "; - } else { - first = false; - } - os << elem; - } - os << "}"; - return os; -} - -#endif /* SSTRING_HH_ */ diff --git a/core/stream.hh b/core/stream.hh deleted file mode 100644 index 35be8eb163..0000000000 --- a/core/stream.hh +++ /dev/null @@ -1,226 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef STREAM_HH_ -#define STREAM_HH_ - -#include "future.hh" -#include -#include - -// A stream/subscription pair is similar to a promise/future pair, -// but apply to a sequence of values instead of a single value. -// -// A stream<> is the producer side. It may call produce() as long -// as the future<> returned from the previous invocation is ready. -// To signify no more data is available, call close(). -// -// A subscription<> is the consumer side. It is created by a call -// to stream::listen(). Calling subscription::start(), -// which registers the data processing callback, starts processing -// events. It may register for end-of-stream notifications by -// chaining the when_done() future, which also delivers error -// events (as exceptions). -// -// The consumer can pause generation of new data by returning -// a non-ready future; when the future becomes ready, the producer -// will resume processing. - -template -class stream; - -template -class subscription; - -template -class stream { - subscription* _sub = nullptr; - promise<> _done; - promise<> _ready; -public: - using next_fn = std::function (T...)>; - stream() = default; - stream(const stream&) = delete; - stream(stream&&) = delete; - ~stream(); - void operator=(const stream&) = delete; - void operator=(stream&&) = delete; - - // Returns a subscription that reads value from this - // stream. - subscription listen(); - - // Returns a subscription that reads value from this - // stream, and also sets up the listen function. - subscription listen(next_fn next); - - // Becomes ready when the listener is ready to accept - // values. Call only once, when beginning to produce - // values. - future<> started(); - - // Produce a value. Call only after started(), and after - // a previous produce() is ready. - future<> produce(T... data); - - // End the stream. Call only after started(), and after - // a previous produce() is ready. No functions may be called - // after this. - void close(); - - // Signal an error. Call only after started(), and after - // a previous produce() is ready. No functions may be called - // after this. - template - void set_exception(E ex); -private: - void pause(future<> can_continue); - void start(); - friend class subscription; -}; - -template -class subscription { -public: - using next_fn = typename stream::next_fn; -private: - stream* _stream; - next_fn _next; -private: - explicit subscription(stream* s); -public: - subscription(subscription&& x); - ~subscription(); - - /// \brief Start receiving events from the stream. - /// - /// \param next Callback to call for each event - void start(std::function (T...)> next); - - // Becomes ready when the stream is empty, or when an error - // happens (in that case, an exception is held). - future<> done(); - - friend class stream; -}; - - -template -inline -stream::~stream() { - if (_sub) { - _sub->_stream = nullptr; - } -} - -template -inline -subscription -stream::listen() { - return subscription(this); -} - -template -inline -subscription -stream::listen(next_fn next) { - auto sub = subscription(this); - sub.start(std::move(next)); - return sub; -} - -template -inline -future<> -stream::started() { - return _ready.get_future(); -} - -template -inline -future<> -stream::produce(T... data) { - try { - return _sub->_next(std::move(data)...); - } catch (...) { - _done.set_exception(std::current_exception()); - // FIXME: tell the producer to stop producing - abort(); - } -} - -template -inline -void -stream::close() { - _done.set_value(); -} - -template -template -inline -void -stream::set_exception(E ex) { - _sub->_done.set_exception(ex); -} - -template -inline -subscription::subscription(stream* s) - : _stream(s) { - assert(!_stream->_sub); - _stream->_sub = this; -} - -template -inline -void -subscription::start(std::function (T...)> next) { - _next = std::move(next); - _stream->_ready.set_value(); -} - -template -inline -subscription::~subscription() { - if (_stream) { - _stream->_sub = nullptr; - } -} - -template -inline -subscription::subscription(subscription&& x) - : _stream(x._stream), _next(std::move(x._next)) { - x._stream = nullptr; - if (_stream) { - _stream->_sub = this; - } -} - -template -inline -future<> -subscription::done() { - return _stream->_done.get_future(); -} - -#endif /* STREAM_HH_ */ diff --git a/core/task.hh b/core/task.hh deleted file mode 100644 index 80b0db506e..0000000000 --- a/core/task.hh +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include - -class task { -public: - virtual ~task() noexcept {} - virtual void run() noexcept = 0; -}; - -void schedule(std::unique_ptr t); - -template -class lambda_task final : public task { - Func _func; -public: - lambda_task(const Func& func) : _func(func) {} - lambda_task(Func&& func) : _func(std::move(func)) {} - virtual void run() noexcept override { _func(); } -}; - -template -inline -std::unique_ptr -make_task(Func&& func) { - return std::make_unique>(std::forward(func)); -} diff --git a/core/temporary_buffer.hh b/core/temporary_buffer.hh deleted file mode 100644 index 790883cf06..0000000000 --- a/core/temporary_buffer.hh +++ /dev/null @@ -1,201 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef TEMPORARY_BUFFER_HH_ -#define TEMPORARY_BUFFER_HH_ - -#include "deleter.hh" -#include "util/eclipse.hh" -#include - -/// \addtogroup memory-module -/// @{ - -/// Temporary, self-managed byte buffer. -/// -/// A \c temporary_buffer is similar to an \c std::string or a \c std::unique_ptr, -/// but provides more flexible memory management. A \c temporary_buffer can own the memory -/// it points to, or it can be shared with another \c temporary_buffer, or point at a substring -/// of a buffer. It uses a \ref deleter to manage its memory. -/// -/// A \c temporary_buffer should not be held indefinitely. It can be held while a request -/// is processed, or for a similar duration, but not longer, as it can tie up more memory -/// that its size indicates. -/// -/// A buffer can be shared: two \c temporary_buffer objects will point to the same data, -/// or a subset of it. See the \ref temporary_buffer::share() method. -/// -/// Unless you created a \c temporary_buffer yourself, do not modify its contents, as they -/// may be shared with another user that does not expect the data to change. -/// -/// Use cases for a \c temporary_buffer include: -/// - passing a substring of a tcp packet for the user to consume (zero-copy -/// tcp input) -/// - passing a refcounted blob held in memory to tcp, ensuring that when the TCP ACK -/// is received, the blob is released (by decrementing its reference count) (zero-copy -/// tcp output) -/// -/// \tparam CharType underlying character type (must be a variant of \c char). -template -class temporary_buffer { - static_assert(sizeof(CharType) == 1, "must buffer stream of bytes"); - CharType* _buffer; - size_t _size; - deleter _deleter; -public: - /// Creates a \c temporary_buffer of a specified size. The buffer is not shared - /// with anyone, and is not initialized. - /// - /// \param size buffer size, in bytes - explicit temporary_buffer(size_t size) - : _buffer(static_cast(malloc(size * sizeof(CharType)))), _size(size) - , _deleter(make_free_deleter(_buffer)) { - if (size && !_buffer) { - throw std::bad_alloc(); - } - } - //explicit temporary_buffer(CharType* borrow, size_t size) : _buffer(borrow), _size(size) {} - /// Creates an empty \c temporary_buffer that does not point at anything. - temporary_buffer() - : _buffer(nullptr) - , _size(0) {} - temporary_buffer(const temporary_buffer&) = delete; - /// Moves a \c temporary_buffer. - temporary_buffer(temporary_buffer&& x) : _buffer(x._buffer), _size(x._size), _deleter(std::move(x._deleter)) { - x._buffer = nullptr; - x._size = 0; - } - /// Creates a \c temporary_buffer with a specific deleter. - /// - /// \param buf beginning of the buffer held by this \c temporary_buffer - /// \param size size of the buffer - /// \param d deleter controlling destruction of the buffer. The deleter - /// will be destroyed when there are no longer any users for the buffer. - temporary_buffer(CharType* buf, size_t size, deleter d) - : _buffer(buf), _size(size), _deleter(std::move(d)) {} - void operator=(const temporary_buffer&) = delete; - /// Moves a \c temporary_buffer. - temporary_buffer& operator=(temporary_buffer&& x) { - if (this != &x) { - _buffer = x._buffer; - _size = x._size; - _deleter = std::move(x._deleter); - x._buffer = nullptr; - x._size = 0; - } - return *this; - } - /// Gets a pointer to the beginning of the buffer. - const CharType* get() const { return _buffer; } - /// Gets a writable pointer to the beginning of the buffer. Use only - /// when you are certain no user expects the buffer data not to change. - CharType* get_write() { return _buffer; } - /// Gets the buffer size. - size_t size() const { return _size; } - /// Gets a pointer to the beginning of the buffer. - const CharType* begin() { return _buffer; } - /// Gets a pointer to the end of the buffer. - const CharType* end() { return _buffer + _size; } - /// Returns the buffer, but with a reduced size. The original - /// buffer is consumed by this call and can no longer be used. - /// - /// \param size New size; must be smaller than current size. - /// \return the same buffer, with a prefix removed. - temporary_buffer prefix(size_t size) && { - auto ret = std::move(*this); - ret._size = size; - return ret; - } - /// Reads a character from a specific position in the buffer. - /// - /// \param pos position to read character from; must be less than size. - CharType operator[](size_t pos) const { - return _buffer[pos]; - } - /// Checks whether the buffer is empty. - bool empty() const { return !size(); } - /// Checks whether the buffer is not empty. - operator bool() { return size(); } - /// Create a new \c temporary_buffer object referring to the same - /// underlying data. The underlying \ref deleter will not be destroyed - /// until both the original and the clone have been destroyed. - /// - /// \return a clone of the buffer object. - temporary_buffer share() { - return temporary_buffer(_buffer, _size, _deleter.share()); - } - /// Create a new \c temporary_buffer object referring to a substring of the - /// same underlying data. The underlying \ref deleter will not be destroyed - /// until both the original and the clone have been destroyed. - /// - /// \param pos Position of the first character to share. - /// \param len Length of substring to share. - /// \return a clone of the buffer object, referring to a substring. - temporary_buffer share(size_t pos, size_t len) { - auto ret = share(); - ret._buffer += pos; - ret._size = len; - return ret; - } - /// Remove a prefix from the buffer. The underlying data - /// is not modified. - /// - /// \param pos Position of first character to retain. - void trim_front(size_t pos) { - _buffer += pos; - _size -= pos; - } - /// Remove a suffix from the buffer. The underlying data - /// is not modified. - /// - /// \param pos Position of first character to drop. - void trim(size_t pos) { - _size = pos; - } - /// Stops automatic memory management. When the \c temporary_buffer - /// object is destroyed, the underlying \ref deleter will not be called. - /// Instead, it is the caller's responsibility to destroy the deleter object - /// when the data is no longer needed. - /// - /// \return \ref deleter object managing the data's lifetime. - deleter release() { - return std::move(_deleter); - } - /// Creates a \c temporary_buffer object with a specified size, with - /// memory aligned to a specific boundary. - /// - /// \param alignment Required alignment; must be a power of two. - /// \param size Required size. - /// \return a new \c temporary_buffer object. - static temporary_buffer aligned(size_t alignment, size_t size) { - void *ptr = nullptr; - auto ret = ::posix_memalign(&ptr, alignment, size * sizeof(CharType)); - auto buf = static_cast(ptr); - if (ret) { - throw std::bad_alloc(); - } - return temporary_buffer(buf, size, make_free_deleter(buf)); - } -}; - -/// @} - -#endif /* TEMPORARY_BUFFER_HH_ */ diff --git a/core/thread.cc b/core/thread.cc deleted file mode 100644 index d6e2792204..0000000000 --- a/core/thread.cc +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include "thread.hh" -#include "posix.hh" -#include - -/// \cond internal - -namespace seastar { - -thread_local jmp_buf_link g_unthreaded_context; -thread_local jmp_buf_link* g_current_context; - -thread_context::thread_context(std::function func) - : _func(std::move(func)) { - setup(); -} - -void -thread_context::setup() { - // use setcontext() for the initial jump, as it allows us - // to set up a stack, but continue with longjmp() as it's - // much faster. - ucontext_t initial_context; - auto q = uint64_t(reinterpret_cast(this)); - auto main = reinterpret_cast(&thread_context::s_main); - auto r = getcontext(&initial_context); - throw_system_error_on(r == -1); - initial_context.uc_stack.ss_sp = _stack.get(); - initial_context.uc_stack.ss_size = _stack_size; - initial_context.uc_link = nullptr; - makecontext(&initial_context, main, 2, int(q), int(q >> 32)); - auto prev = g_current_context; - _context.link = prev; - _context.thread = this; - g_current_context = &_context; - if (setjmp(prev->jmpbuf) == 0) { - setcontext(&initial_context); - } -} - -void -thread_context::switch_in() { - auto prev = g_current_context; - g_current_context = &_context; - _context.link = prev; - if (setjmp(prev->jmpbuf) == 0) { - longjmp(_context.jmpbuf, 1); - } -} - -void -thread_context::switch_out() { - g_current_context = _context.link; - if (setjmp(_context.jmpbuf) == 0) { - longjmp(g_current_context->jmpbuf, 1); - } -} - -void -thread_context::s_main(unsigned int lo, unsigned int hi) { - uintptr_t q = lo | (uint64_t(hi) << 32); - reinterpret_cast(q)->main(); -} - -void -thread_context::main() { - try { - _func(); - _done.set_value(); - } catch (...) { - _done.set_exception(std::current_exception()); - } - g_current_context = _context.link; - longjmp(g_current_context->jmpbuf, 1); -} - -namespace thread_impl { - -thread_context* get() { - return g_current_context->thread; -} - -void switch_in(thread_context* to) { - to->switch_in(); -} - -void switch_out(thread_context* from) { - from->switch_out(); -} - -void init() { - g_unthreaded_context.link = nullptr; - g_unthreaded_context.thread = nullptr; - g_current_context = &g_unthreaded_context; -} - -} - - -} - -/// \endcond diff --git a/core/thread.hh b/core/thread.hh deleted file mode 100644 index 9afb000727..0000000000 --- a/core/thread.hh +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include "future.hh" -#include "do_with.hh" -#include "future-util.hh" -#include -#include -#include - -/// \defgroup thread-module Seastar threads -/// -/// Seastar threads provide an execution environment where blocking -/// is tolerated; you can issue I/O, and wait for it in the same function, -/// rather then establishing a callback to be called with \ref future<>::then(). -/// -/// Seastar threads are not the same as operating system threads: -/// - seastar threads are cooperative; they are never preempted except -/// at blocking points (see below) -/// - seastar threads always run on the same core they were launched on -/// -/// Like other seastar code, seastar threads may not issue blocking system calls. -/// -/// A seastar thread blocking point is any function that returns a \ref future<>. -/// you block by calling \ref future<>::get(); this waits for the future to become -/// available, and in the meanwhile, other seastar threads and seastar non-threaded -/// code may execute. -/// -/// Example: -/// \code -/// seastar::thread th([] { -/// sleep(5s).get(); // blocking point -/// }); -/// \endcode -/// -/// An easy way to launch a thread and carry out some computation, and return a -/// result from this execution is by using the \ref seastar::async() function. -/// The result is returned as a future, so that non-threaded code can wait for -/// the thread to terminate and yield a result. - -/// Seastar API namespace -namespace seastar { - -/// \addtogroup thread-module -/// @{ - -class thread; - -/// \cond internal -class thread_context; - -namespace thread_impl { - -thread_context* get(); -void switch_in(thread_context* to); -void switch_out(thread_context* from); -void init(); - -} - -struct jmp_buf_link { - jmp_buf jmpbuf; - jmp_buf_link* link; - thread_context* thread; -}; - -extern thread_local jmp_buf_link g_unthreaded_context; - -// Internal class holding thread state. We can't hold this in -// \c thread itself because \c thread is movable, and we want pointers -// to this state to be captured. -class thread_context { - static constexpr size_t _stack_size = 128*1024; - std::unique_ptr _stack{new char[_stack_size]}; - std::function _func; - jmp_buf_link _context; - promise<> _done; - bool _joined = false; -private: - static void s_main(unsigned int lo, unsigned int hi); - void setup(); - void main(); -public: - thread_context(std::function func); - void switch_in(); - void switch_out(); - friend class thread; - friend void thread_impl::switch_in(thread_context*); - friend void thread_impl::switch_out(thread_context*); -}; - -/// \endcond - - -/// \brief thread - stateful thread of execution -/// -/// Threads allow using seastar APIs in a blocking manner, -/// by calling future::get() on a non-ready future. When -/// this happens, the thread is put to sleep until the future -/// becomes ready. -class thread { - std::unique_ptr _context; - static thread_local thread* _current; -public: - /// \brief Constructs a \c thread object that does not represent a thread - /// of execution. - thread() = default; - /// \brief Constructs a \c thread object that represents a thread of execution - /// - /// \param func Callable object to execute in thread. The callable is - /// called immediately. - template - thread(Func func); - /// \brief Moves a thread object. - thread(thread&& x) noexcept = default; - /// \brief Move-assigns a thread object. - thread& operator=(thread&& x) noexcept = default; - /// \brief Destroys a \c thread object. - /// - /// The thread must not represent a running thread of execution (see join()). - ~thread() { assert(!_context || _context->_joined); } - /// \brief Waits for thread execution to terminate. - /// - /// Waits for thread execution to terminate, and marks the thread object as not - /// representing a running thread of execution. - future<> join(); -}; - -template -inline -thread::thread(Func func) - : _context(std::make_unique(func)) { -} - -inline -future<> -thread::join() { - _context->_joined = true; - return _context->_done.get_future(); -} - -/// Executes a callable in a seastar thread. -/// -/// Runs a block of code in a threaded context, -/// which allows it to block (using \ref future::get()). The -/// result of the callable is returned as a future. -/// -/// \param func a callable to be executed in a thread -/// \param args a parameter pack to be forwarded to \c func. -/// \return whatever \c func returns, as a future. -/// -/// Example: -/// \code -/// future compute_sum(int a, int b) { -/// return seastar::async([a, b] { -/// // some blocking code: -/// sleep(1s).get(); -/// return a + b; -/// }); -/// } -/// \endcode -template -inline -futurize_t(std::decay_t...)>> -async(Func&& func, Args&&... args) { - using return_type = std::result_of_t(std::decay_t...)>; - struct work { - Func func; - std::tuple args; - promise pr; - thread th; - }; - return do_with(work{std::forward(func), std::forward_as_tuple(std::forward(args)...)}, [] (work& w) { - auto ret = w.pr.get_future(); - w.th = thread([&w] { - futurize::apply(std::move(w.func), std::move(w.args)).forward_to(std::move(w.pr)); - }); - return w.th.join().then([ret = std::move(ret)] () mutable { - return std::move(ret); - }); - }); -} - -/// @} - -} diff --git a/core/timer-set.hh b/core/timer-set.hh deleted file mode 100644 index 7a29906bed..0000000000 --- a/core/timer-set.hh +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -/* - * Imported from OSv: - * - * Copyright (C) 2014 Cloudius Systems, Ltd. - * - * This work is open source software, licensed under the terms of the - * BSD license as described in the LICENSE file in the top-level directory. - */ - -#ifndef __TIMER_SET_HH -#define __TIMER_SET_HH - -#include -#include -#include -#include -#include -#include "bitset-iter.hh" - -namespace bi = boost::intrusive; - -namespace seastar { -/** - * A data structure designed for holding and expiring timers. It's - * optimized for timer non-delivery by deferring sorting cost until - * expiry time. The optimization is based on the observation that in - * many workloads timers are cancelled or rescheduled before they - * expire. That's especially the case for TCP timers. - * - * The template type "Timer" should have a method named - * get_timeout() which returns Timer::time_point which denotes - * timer's expiration. - */ -template Timer::*link> -class timer_set { -public: - using time_point = typename Timer::time_point; - using timer_list_t = bi::list, link>>; -private: - using duration = typename Timer::duration; - using timestamp_t = typename Timer::duration::rep; - - static constexpr timestamp_t max_timestamp = std::numeric_limits::max(); - static constexpr int timestamp_bits = std::numeric_limits::digits; - - // The last bucket is reserved for active timers with timeout <= _last. - static constexpr int n_buckets = timestamp_bits + 1; - - std::array _buckets; - timestamp_t _last; - timestamp_t _next; - - std::bitset _non_empty_buckets; -private: - static timestamp_t get_timestamp(time_point _time_point) - { - return _time_point.time_since_epoch().count(); - } - - static timestamp_t get_timestamp(Timer& timer) - { - return get_timestamp(timer.get_timeout()); - } - - int get_index(timestamp_t timestamp) const - { - if (timestamp <= _last) { - return n_buckets - 1; - } - - auto index = bitsets::count_leading_zeros(timestamp ^ _last); - assert(index < n_buckets - 1); - return index; - } - - int get_index(Timer& timer) const - { - return get_index(get_timestamp(timer)); - } - - int get_last_non_empty_bucket() const - { - return bitsets::get_last_set(_non_empty_buckets); - } -public: - timer_set() - : _last(0) - , _next(max_timestamp) - , _non_empty_buckets(0) - { - } - - ~timer_set() { - for (auto&& list : _buckets) { - while (!list.empty()) { - auto& timer = *list.begin(); - timer.cancel(); - } - } - } - - /** - * Adds timer to the active set. - * - * The value returned by timer.get_timeout() is used as timer's expiry. The result - * of timer.get_timeout() must not change while the timer is in the active set. - * - * Preconditions: - * - this timer must not be currently in the active set or in the expired set. - * - * Postconditions: - * - this timer will be added to the active set until it is expired - * by a call to expire() or removed by a call to remove(). - * - * Returns true if and only if this timer's timeout is less than get_next_timeout(). - * When this function returns true the caller should reschedule expire() to be - * called at timer.get_timeout() to ensure timers are expired in a timely manner. - */ - bool insert(Timer& timer) - { - auto timestamp = get_timestamp(timer); - auto index = get_index(timestamp); - - _buckets[index].push_back(timer); - _non_empty_buckets[index] = true; - - if (timestamp < _next) { - _next = timestamp; - return true; - } - return false; - } - - /** - * Removes timer from the active set. - * - * Preconditions: - * - timer must be currently in the active set. Note: it must not be in - * the expired set. - * - * Postconditions: - * - timer is no longer in the active set. - * - this object will no longer hold any references to this timer. - */ - void remove(Timer& timer) - { - auto index = get_index(timer); - auto& list = _buckets[index]; - list.erase(list.iterator_to(timer)); - if (list.empty()) { - _non_empty_buckets[index] = false; - } - } - - /** - * Expires active timers. - * - * The time points passed to this function must be monotonically increasing. - * Use get_next_timeout() to query for the next time point. - * - * Preconditions: - * - the time_point passed to this function must not be lesser than - * the previous one passed to this function. - * - * Postconditons: - * - all timers from the active set with Timer::get_timeout() <= now are moved - * to the expired set. - */ - timer_list_t expire(time_point now) - { - timer_list_t exp; - auto timestamp = get_timestamp(now); - - if (timestamp < _last) { - abort(); - } - - auto index = get_index(timestamp); - - for (int i : bitsets::for_each_set(_non_empty_buckets, index + 1)) { - exp.splice(exp.end(), _buckets[i]); - _non_empty_buckets[i] = false; - } - - _last = timestamp; - _next = max_timestamp; - - auto& list = _buckets[index]; - while (!list.empty()) { - auto& timer = *list.begin(); - list.pop_front(); - if (timer.get_timeout() <= now) { - exp.push_back(timer); - } else { - insert(timer); - } - } - - _non_empty_buckets[index] = !list.empty(); - - if (_next == max_timestamp && _non_empty_buckets.any()) { - for (auto& timer : _buckets[get_last_non_empty_bucket()]) { - _next = std::min(_next, get_timestamp(timer)); - } - } - return exp; - } - - /** - * Returns a time point at which expire() should be called - * in order to ensure timers are expired in a timely manner. - * - * Returned values are monotonically increasing. - */ - time_point get_next_timeout() const - { - return time_point(duration(std::max(_last, _next))); - } - - /** - * Clears both active and expired timer sets. - */ - void clear() - { - for (int i : bitsets::for_each_set(_non_empty_buckets)) { - _buckets[i].clear(); - } - } - - size_t size() const - { - size_t res = 0; - for (int i : bitsets::for_each_set(_non_empty_buckets)) { - res += _buckets[i].size(); - } - return res; - } - - /** - * Returns true if and only if there are no timers in the active set. - */ - bool empty() const - { - return _non_empty_buckets.none(); - } - - time_point now() { - return Timer::clock::now(); - } -}; -}; - -#endif diff --git a/core/timer.hh b/core/timer.hh deleted file mode 100644 index 318266e78f..0000000000 --- a/core/timer.hh +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#pragma once - -#include -#include -#include "future.hh" -#include "timer-set.hh" - -using clock_type = std::chrono::high_resolution_clock; - -template -class timer { -public: - typedef typename Clock::time_point time_point; - typedef typename Clock::duration duration; - typedef Clock clock; -private: - using callback_t = std::function; - boost::intrusive::list_member_hook<> _link; - callback_t _callback; - time_point _expiry; - std::experimental::optional _period; - bool _armed = false; - bool _queued = false; - bool _expired = false; - void readd_periodic(); - void arm_state(time_point until, std::experimental::optional period); -public: - timer() = default; - timer(timer&& t) noexcept : _callback(std::move(t._callback)), _expiry(std::move(t._expiry)), _period(std::move(t._period)), - _armed(t._armed), _queued(t._queued), _expired(t._expired) { - _link.swap_nodes(t._link); - t._queued = false; - t._armed = false; - } - explicit timer(callback_t&& callback); - ~timer(); - future<> expired(); - void set_callback(callback_t&& callback); - void arm(time_point until, std::experimental::optional period = {}); - void rearm(time_point until, std::experimental::optional period = {}); - void arm(duration delta); - void arm_periodic(duration delta); - bool armed() const { return _armed; } - bool cancel(); - time_point get_timeout(); - friend class reactor; - friend class seastar::timer_set; -}; - diff --git a/core/transfer.hh b/core/transfer.hh deleted file mode 100644 index 9263b9829c..0000000000 --- a/core/transfer.hh +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef TRANSFER_HH_ -#define TRANSFER_HH_ - -// Helper functions for copying or moving multiple objects in an exception -// safe manner, then destroying the sources. -// -// To transfer, call transfer_pass1(allocator, &from, &to) on all object pairs, -// (this copies the object from @from to @to). If no exceptions are encountered, -// call transfer_pass2(allocator, &from, &to). This destroys the object at the -// origin. If exceptions were encountered, simply destroy all copied objects. -// -// As an optimization, if the objects are moveable without throwing (noexcept) -// transfer_pass1() simply moves the objects and destroys the source, and -// transfer_pass2() does nothing. - -#include - -template -inline -void -transfer_pass1(Alloc& a, T* from, T* to, - typename std::enable_if::value>::type* = nullptr) { - a.construct(to, std::move(*from)); - a.destroy(from); -} - -template -inline -void -transfer_pass2(Alloc& a, T* from, T* to, - typename std::enable_if::value>::type* = nullptr) { -} - -template -inline -void -transfer_pass1(Alloc& a, T* from, T* to, - typename std::enable_if::value>::type* = nullptr) { - a.construct(to, *from); -} - -template -inline -void -transfer_pass2(Alloc& a, T* from, T* to, - typename std::enable_if::value>::type* = nullptr) { - a.destroy(from); -} - -#endif /* TRANSFER_HH_ */ diff --git a/core/unaligned.hh b/core/unaligned.hh deleted file mode 100644 index 7a21820299..0000000000 --- a/core/unaligned.hh +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -// The following unaligned_cast(p) is a portable replacement for -// reinterpret_cast(p) which should be used every time address p -// is not guaranteed to be properly aligned to alignof(T). -// -// On architectures like x86 and ARM, where unaligned access is allowed, -// unaligned_cast will behave the same as reinterpret_cast and will generate -// the same code. -// -// Certain architectures (e.g., MIPS) make it extremely slow or outright -// forbidden to use ordinary machine instructions on a primitive type at an -// unaligned addresses - e.g., access a uint32_t at an address which is not -// a multiple of 4. Gcc's "undefined behavior sanitizer" (enabled in our debug -// build) also catches such unaligned accesses and reports them as errors, -// even when running on x86. -// -// Therefore, reinterpret_cast on an address which is not guaranteed -// to be a multiple of 4 may generate extremely slow code or runtime errors, -// and must be avoided. The compiler needs to be told about the unaligned -// access, so it can generate reasonably-efficient code for the access -// (in MIPS, this means generating two instructions "lwl" and "lwr", instead -// of the one instruction "lw" which faults on unaligned/ access). The way to -// tell the compiler this is with __attribute__((packed)). This will also -// cause the sanitizer not to generate runtime alignment checks for this -// access. - -template -struct unaligned { - T raw; - unaligned() = default; - unaligned(T x) : raw(x) {} - unaligned& operator=(const T& x) { raw = x; return *this; } - operator T() const { return raw; } -} __attribute__((packed)); - -template -inline auto unaligned_cast(F* p) { - return reinterpret_cast>*>(p); -} - -template -inline auto unaligned_cast(const F* p) { - return reinterpret_cast>*>(p); -} diff --git a/core/units.hh b/core/units.hh deleted file mode 100644 index cca368b5bb..0000000000 --- a/core/units.hh +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef UNITS_HH_ -#define UNITS_HH_ - -static constexpr size_t KB = 1 << 10; -static constexpr size_t MB = 1 << 20; -static constexpr size_t GB = 1 << 30; - -#endif diff --git a/core/vector-data-sink.hh b/core/vector-data-sink.hh deleted file mode 100644 index c45b0f93fa..0000000000 --- a/core/vector-data-sink.hh +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#ifndef VECTOR_DATA_SINK_HH_ -#define VECTOR_DATA_SINK_HH_ - -#include "core/reactor.hh" - -class vector_data_sink final : public data_sink_impl { -public: - using vector_type = std::vector; -private: - vector_type& _v; -public: - vector_data_sink(vector_type& v) : _v(v) {} - - virtual future<> put(net::packet p) override { - _v.push_back(std::move(p)); - return make_ready_future<>(); - } - - virtual future<> close() override { - // TODO: close on local side - return make_ready_future<>(); - } -}; - -#endif diff --git a/core/vla.hh b/core/vla.hh deleted file mode 100644 index 9b79c9bd22..0000000000 --- a/core/vla.hh +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef VLA_HH_ -#define VLA_HH_ - -#include -#include -#include -#include - -// Some C APIs have a structure with a variable length array at the end. -// This is a helper function to help allocate it. -// -// for a structure -// -// struct xx { int a; float b[0]; }; -// -// use -// -// make_struct_with_vla(&xx::b, number_of_bs); -// -// to allocate it. -// -template -inline -std::unique_ptr -make_struct_with_vla(E S::*last, size_t nr) { - auto fake = reinterpret_cast(0); - size_t offset = reinterpret_cast(&(fake->*last)); - size_t element_size = sizeof((fake->*last)[0]); - assert(offset == sizeof(S)); - auto p = ::operator new(offset + element_size * nr); - return std::unique_ptr(new (p) S()); -} - - - -#endif /* VLA_HH_ */ diff --git a/core/xen/evtchn.cc b/core/xen/evtchn.cc deleted file mode 100644 index 4730870d3f..0000000000 --- a/core/xen/evtchn.cc +++ /dev/null @@ -1,219 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#include -#include -#include -#include // kernel interface -#include // userspace interface - -#include "core/reactor.hh" -#include "core/semaphore.hh" -#include "core/future-util.hh" -#include "evtchn.hh" -#include "osv_xen.hh" - -namespace xen { - -void evtchn::make_ready_port(int port) { - auto ports = _ports.equal_range(port); - for (auto i = ports.first; i != ports.second; ++i) { - i->second->_sem.signal(); - } -} - -void evtchn::port_moved(int prt, port* old, port* now) { - auto ports = _ports.equal_range(prt); - for (auto i = ports.first; i != ports.second; ++i) { - if (i->second == old) { - i->second = now; - } - } -} - -void evtchn::port_deleted(int prt, port* obj) { - auto ports = _ports.equal_range(prt); - for (auto i = ports.first; i != ports.second; ++i) { - if (i->second == obj) { - i = _ports.erase(i); - } - } -} - -port::port(int p) - : _port(p), _sem(0), _evtchn(evtchn::instance()) { - _evtchn->_ports.emplace(p, this); -} - -port::port(port&& other) - : _port(other._port), _sem(std::move(other._sem)), _evtchn(other._evtchn) { - if (_port != -1) { - _evtchn->port_moved(_port, &other, this); - } -} - -port::~port() { - // FIXME: unbind from Xen - if (_port != -1) { - _evtchn->port_deleted(_port, this); - } -} - -port& port::operator=(port&& other) { - if (this != &other) { - this->~port(); - new (this) port(std::move(other)); - } - return *this; -} - -future<> port::pending() { - return _sem.wait(); -} - -void port::notify() { - _evtchn->notify(_port); -} - -void port::umask() { - _evtchn->umask(&_port, 1); -} - -class userspace_evtchn: public evtchn { - pollable_fd _evtchn; - int ports[2]; -protected: - virtual void umask(int *port, unsigned count) override; -public: - userspace_evtchn(unsigned otherend); - virtual port bind() override; - virtual void notify(int port) override; -}; - -userspace_evtchn::userspace_evtchn(unsigned otherend) - : evtchn(otherend) - , _evtchn(pollable_fd(file_desc::open("/dev/xen/evtchn", O_RDWR | O_NONBLOCK))) -{ - keep_doing([this] { - return _evtchn.read_some(reinterpret_cast(&ports), sizeof(ports)).then([this] (size_t s) - { - auto count = s / sizeof(ports[0]); - for (unsigned i = 0; i < count; ++i) { - make_ready_port(ports[i]); - } - umask(ports, count); - return make_ready_future<>(); - }); - }); -} - -port userspace_evtchn::bind() -{ - struct ioctl_evtchn_bind_unbound_port bind = { _otherend }; - - auto ret = _evtchn.get_file_desc().ioctl(IOCTL_EVTCHN_BIND_UNBOUND_PORT, bind); - return port(ret); -} - -void userspace_evtchn::notify(int port) -{ - struct ioctl_evtchn_notify notify; - notify.port = port; - - _evtchn.get_file_desc().ioctl(IOCTL_EVTCHN_NOTIFY, notify); -} - -void userspace_evtchn::umask(int *port, unsigned count) -{ - _evtchn.get_file_desc().write(port, count * sizeof(port)); -} - -#ifdef HAVE_OSV -class kernel_evtchn: public evtchn { - // We need to convert extra-seastar events to intra-seastar events - // (in this case, the semaphore interface of evtchn). The interface for - // that currently is the reactor notifier. - std::unique_ptr _notified; - static void make_ready(void *arg); - void process_interrupts(int port); -public: - kernel_evtchn(unsigned otherend) - : evtchn(otherend) - , _notified(engine().make_reactor_notifier()) {} - virtual port bind() override; - virtual void notify(int port) override; -}; - -void kernel_evtchn::make_ready(void *arg) { - auto notifier = reinterpret_cast(arg); - notifier->signal(); -} - -port kernel_evtchn::bind() { - - unsigned int irq; - - int newp; - irq = bind_listening_port_to_irq(_otherend, &newp); - - port p(newp); - - auto notifier = reinterpret_cast(_notified.get()); - - intr_add_handler("", irq, NULL, make_ready, notifier, 0, 0); - unmask_evtchn(newp); - process_interrupts(newp); - return p; -} - -void kernel_evtchn::process_interrupts(int port) { - _notified->wait().then([this, port] () { - make_ready_port(port); - process_interrupts(port); - }); -} - -void kernel_evtchn::notify(int port) { - notify_remote_via_evtchn(port); -} -#endif - -evtchn *evtchn::_instance = nullptr; - -evtchn *evtchn::instance() -{ - if (!_instance) { - throw std::runtime_error("Acquiring evtchn instance without specifying otherend: invalid context"); - } - - return _instance; -} - -evtchn *evtchn::instance(bool userspace, unsigned otherend) -{ - if (!_instance) { -#ifdef HAVE_OSV - if (!userspace) { - _instance = new kernel_evtchn(otherend); - } else -#endif - _instance = new userspace_evtchn(otherend); - } - return _instance; -} - -} diff --git a/core/xen/evtchn.hh b/core/xen/evtchn.hh deleted file mode 100644 index ed8fc715cc..0000000000 --- a/core/xen/evtchn.hh +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#ifndef _XEN_EVTCHN_HH -#define _XEN_EVTCHN_HH - -#include "core/posix.hh" -#include "core/future.hh" - -namespace xen { - -class evtchn; - -class port { - int _port = -1; - semaphore _sem; - evtchn *_evtchn; -public: - explicit port(int p); - port() = default; - port(port&& other); - ~port(); - port& operator=(port&& other); - int number() const { return _port; } - future<> pending(); - void notify(); - void umask(); - - friend class evtchn; -}; - -class evtchn { - static evtchn *_instance; -protected: - unsigned _otherend; - void make_ready_port(int port); - void port_moved(int prt, port* old, port* now); - void port_deleted(int prt, port* old); - std::unordered_multimap _ports; - virtual void notify(int port) = 0; - virtual void umask(int *port, unsigned count) {}; - friend class port; -public: - static evtchn *instance(bool userspace, unsigned otherend); - static evtchn *instance(); - evtchn(unsigned otherend) : _otherend(otherend) {} - virtual port bind() = 0; - port bind(int p) { return port(p); }; -}; - -} - -#endif diff --git a/core/xen/gntalloc.cc b/core/xen/gntalloc.cc deleted file mode 100644 index d25112b02a..0000000000 --- a/core/xen/gntalloc.cc +++ /dev/null @@ -1,240 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#include -#include -#include -#include // Kernel interface -#include // Userspace interface -#include - -#include "osv_xen.hh" -#include "gntalloc.hh" - -namespace xen { - -gntref invalid_ref; - -// FIXME: Most of the destructors are yet to be coded -// - -class userspace_grant_head : public grant_head { - std::atomic _ref_head = { 0 }; - std::vector _refs; -public: - userspace_grant_head(std::vector v) : _refs(v) {} - virtual gntref new_ref() override; - virtual gntref new_ref(void *addr, size_t size) override; - virtual void free_ref(gntref& ref); -}; - -class userspace_gntalloc : public gntalloc { - file_desc _gntalloc; - mmap_area _gntmap; - struct ioctl_gntalloc_alloc_gref *get_gref(unsigned nr_ents); - -public: - explicit userspace_gntalloc(unsigned otherend); - ~userspace_gntalloc(); - virtual gntref alloc_ref() override; - virtual grant_head *alloc_ref(unsigned refs) override; -}; - -userspace_gntalloc::userspace_gntalloc(unsigned otherend) - : gntalloc(otherend) - , _gntalloc(file_desc::open("/dev/xen/gntalloc", O_RDWR)) { - -} - - -struct ioctl_gntalloc_alloc_gref* -userspace_gntalloc::get_gref(unsigned nr_ents) -{ - struct ioctl_gntalloc_alloc_gref *gref; - gref = static_cast(malloc(sizeof(*gref) + nr_ents * sizeof(gref->gref_ids[0]))); - gref->domid = _otherend; - gref->flags = GNTALLOC_FLAG_WRITABLE; - gref->count = nr_ents; - _gntalloc.ioctl(IOCTL_GNTALLOC_ALLOC_GREF, *gref); - - return gref; -} - -grant_head *userspace_gntalloc::alloc_ref(unsigned nr_ents) { - - auto gref = get_gref(nr_ents); - - _gntmap = _gntalloc.map_shared_rw(4096 * nr_ents, gref->index); - char *addr = _gntmap.get(); - - std::vector v; - for (unsigned i = 0; i < nr_ents; ++i) { - auto ref = gref->gref_ids[i]; - auto *page = addr + 4096 * i; - v.push_back({int(ref), page}); - } - - free(gref); - return new userspace_grant_head(v); -} - -gntref userspace_gntalloc::alloc_ref() { - - auto gref = get_gref(1); - - // FIXME: unmap? - void *addr = _gntalloc.map_shared_rw(4096, gref->index).release(); - auto p = gntref{int(gref->gref_ids[0]), addr}; - free(gref); - return p; -} - -gntref userspace_grant_head::new_ref() { - auto r = _refs.back(); - _refs.pop_back(); - return r; -} - -gntref userspace_grant_head::new_ref(void *addr, size_t size) { - auto ref = _refs.back(); - memcpy(ref.page, addr, size); - _refs.pop_back(); - return ref; -} - -void userspace_grant_head::free_ref(gntref& ref) { - _refs.push_back(ref); - ref = invalid_ref; -} - -#ifdef HAVE_OSV -class kernel_gntalloc; - -class kernel_grant_head : public grant_head { - uint32_t _head; -public: - kernel_grant_head(uint32_t head) : _head(head) {} - virtual gntref new_ref() override; - virtual gntref new_ref(void *addr, size_t size) override; - virtual void free_ref(gntref& ref); -}; - -class kernel_gntalloc : public gntalloc { - static constexpr int _tx_grants = 256; - static constexpr int _rx_grants = 256; - - uint32_t tx_head, rx_head; -protected: - void *new_frame(); -public: - kernel_gntalloc(unsigned otherend) : gntalloc(otherend) {} - - virtual gntref alloc_ref() override; - virtual grant_head *alloc_ref(unsigned refs) override; - friend class kernel_grant_head; -}; - -// FIXME: It is not actually that far-fetched to run seastar ontop of raw pv, -// and in that case we'll need to have an extra step here -inline uint64_t -virt_to_mfn(void *virt) { - return virt_to_phys(virt) >> 12; -} - -gntref kernel_grant_head::new_ref() { - auto gnt = dynamic_cast(gntalloc::instance()); - - auto ref = gnttab_claim_grant_reference(&_head); - auto page = gnt->new_frame(); - gnttab_grant_foreign_access_ref(ref, gnt->_otherend, virt_to_mfn(page), 0); - return gntref(ref, page); -} - -gntref kernel_grant_head::new_ref(void *addr, size_t size) { - - auto gnt = dynamic_cast(gntalloc::instance()); - - // FIXME: if we can guarantee that the packet allocation came from malloc, not - // mmap, we can grant it directly, without copying. We would also have to propagate - // the offset information, but that is easier - auto page = gnt->new_frame(); - memcpy(page, addr, size); - - auto ref = gnttab_claim_grant_reference(&_head); - gnttab_grant_foreign_access_ref(ref, gnt->_otherend, virt_to_mfn(page), 0); - return gntref(ref, page); -} - -void *kernel_gntalloc::new_frame() { - return aligned_alloc(4096, 4096); -} - -gntref kernel_gntalloc::alloc_ref() { - - void *page = new_frame(); - uint32_t ref; - - if (gnttab_grant_foreign_access(_otherend, virt_to_mfn(page), 0, &ref)) { - throw std::runtime_error("Failed to initialize allocate grant\n"); - } - - return gntref(int(ref), page); -} - -grant_head *kernel_gntalloc::alloc_ref(unsigned nr_ents) { - - std::vector v; - uint32_t head; - - if (gnttab_alloc_grant_references(nr_ents, &head)) { - throw std::runtime_error("Failed to initialize allocate grant\n"); - } - - return new kernel_grant_head(head); -} - -void kernel_grant_head::free_ref(gntref& ref) { - gnttab_end_foreign_access_ref(ref.xen_id); - gnttab_release_grant_reference(&_head, ref.xen_id); - free(ref.page); - ref = invalid_ref; -} -#endif - -gntalloc *gntalloc::_instance = nullptr; -gntalloc *gntalloc::instance(bool userspace, unsigned otherend) { - - if (!_instance) { -#ifdef HAVE_OSV - if (!userspace) { - _instance = new kernel_gntalloc(otherend); - } else -#endif - _instance = new userspace_gntalloc(otherend); - } - return _instance; -} - -gntalloc *gntalloc::instance() { - - if (!_instance) { - throw std::runtime_error("Acquiring grant instance without specifying otherend: invalid context"); - } - return _instance; -} - -} diff --git a/core/xen/gntalloc.hh b/core/xen/gntalloc.hh deleted file mode 100644 index 1062d7a0b3..0000000000 --- a/core/xen/gntalloc.hh +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#ifndef _XEN_GNTALLOC_HH -#define _XEN_GNTALLOC_HH - -#include "core/posix.hh" - -namespace xen { - -class gntref { -public: - int xen_id; - void* page; - bool operator==(const gntref &a) { return (xen_id == a.xen_id) && (page == a.page); } - gntref& operator=(const gntref &a) { xen_id = a.xen_id; page = a.page; return *this; } - gntref(int id, void *page) : xen_id(id), page(page) {} - gntref() : xen_id(-1), page(nullptr) {} - operator bool() const { return xen_id != -1 && page != nullptr; } -}; - -class gntalloc; - -class grant_head { -protected: - unsigned _id = 0; -public: - virtual gntref new_ref() = 0; - virtual gntref new_ref(void *addr, size_t size) = 0; - virtual void free_ref(gntref& ref) = 0; -}; - -class gntalloc { -protected: - static gntalloc *_instance; - unsigned _otherend; -public: - static gntalloc *instance(bool userspace, unsigned otherend); - static gntalloc *instance(); - gntalloc(unsigned otherend) : _otherend(otherend) {} - virtual gntref alloc_ref() = 0; - // The kernel interface can defer allocation, userspace allocation - // cannot. The boolean "alloc" tell us whether or not we should allocate - // now or try to defer. - virtual grant_head *alloc_ref(unsigned nr_ents) = 0; - friend class grant_head; -}; - -extern gntref invalid_ref; - -} - -#endif diff --git a/core/xen/osv_xen.hh b/core/xen/osv_xen.hh deleted file mode 100644 index 6f5402f4cd..0000000000 --- a/core/xen/osv_xen.hh +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#ifndef _XEN_HH -#define _XEN_HH -// Those should come directly from the OSv three. However, the xen exported -// functions are not currently living in osv/include, but spread around the -// BSD directory. We should move them there ASAP and then use them here. -extern int -bind_listening_port_to_irq(unsigned int remote_domain, int * port); -extern int -evtchn_from_irq(int irq); -extern int -notify_remote_via_evtchn(int port); -extern void -unmask_evtchn(int port); -extern "C" int -intr_add_handler(const char *name, int vector, void *filter, - void (*handler)(void *arg), void *arg, int flags, - void **cookiep); - -extern int -gnttab_alloc_grant_references(uint16_t count, uint32_t *head); -extern int -gnttab_claim_grant_reference(uint32_t *private_head); - -extern int -gnttab_grant_foreign_access(uint16_t domid, unsigned long frame, int readonly, uint32_t *result); -extern void gnttab_grant_foreign_access_ref(uint32_t ref, uint16_t domid, unsigned long frame, int readonly); -extern void -gnttab_release_grant_reference(uint32_t *private_head, unsigned ref); -extern int -gnttab_end_foreign_access_ref(unsigned ref); - -extern "C" uint64_t -virt_to_phys(void *virt); -#endif diff --git a/core/xen/xenstore.cc b/core/xen/xenstore.cc deleted file mode 100644 index 79196c19e2..0000000000 --- a/core/xen/xenstore.cc +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#include "core/reactor.hh" -#include "xenstore.hh" -#include -#include - -using xenstore_transaction = xenstore::xenstore_transaction; - -xenstore_transaction xenstore::_xs_null = xenstore::xenstore_transaction(); -xenstore *xenstore::_instance = nullptr; - -xenstore *xenstore::instance() { - - if (!_instance) { - _instance = new xenstore; - } - return _instance; -} - -xenstore::xenstore() - : _h(xs_daemon_open()) -{ - if (!_h) { - throw std::runtime_error("Failed to initialize xenstore"); - } -} - -xenstore::~xenstore() -{ - xs_close(_h); -} - -xs_transaction_t xenstore::start_transaction() -{ - auto t = xs_transaction_start(_h); - if (!t) { - throw std::runtime_error("Failed to initialize xenstore transaction"); - } - return t; -} - -void xenstore::end_transaction(xs_transaction_t t) -{ - xs_transaction_end(_h, t, false); -} - -void xenstore::write(std::string path, std::string value, xenstore_transaction &t) -{ - xs_write(_h, t.t(), path.c_str(), value.c_str(), value.size()); -} - -void xenstore::remove(std::string path, xenstore_transaction &t) -{ - xs_rm(_h, t.t(), path.c_str()); -} - -std::string xenstore::read(std::string path, xenstore_transaction &t) -{ - unsigned int len; - void *ret = xs_read(_h, t.t(), path.c_str(), &len); - std::string str(ret ? (const char *)ret : ""); - free(ret); - return str; -} - -std::list xenstore::ls(std::string path, xenstore_transaction &t) -{ - unsigned int num; - char **dir = xs_directory(_h, t.t(), path.c_str(), &num); - - std::list names; - for (unsigned int i = 0; i < num; ++i) { - names.push_back(dir[i]); - } - free(dir); - - return names; -} diff --git a/core/xen/xenstore.hh b/core/xen/xenstore.hh deleted file mode 100644 index a496a80c59..0000000000 --- a/core/xen/xenstore.hh +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef XENSTORE_HH_ -#define XENSTORE_HH_ - -#include - -extern "C" { - #include -} - -class xenstore { -public: - class xenstore_transaction { - private: - xenstore *_x; - protected: - xs_transaction_t _t = 0; - public: - explicit xenstore_transaction(xenstore *x) : _x(x) - , _t(x->start_transaction()) {} - explicit xenstore_transaction() {} - - ~xenstore_transaction() { if (_t) { _x->end_transaction(_t); } } - - xs_transaction_t t() { return _t; } - friend class xenstore; - }; - -protected: - xs_transaction_t start_transaction(); - void end_transaction(xs_transaction_t t); - - static xenstore_transaction _xs_null; // having it here simplify forward decls - -private: - static xenstore *_instance; - struct xs_handle *_h; - explicit xenstore(); - ~xenstore(); - -public: - static xenstore *instance(); - - virtual void write(std::string path, std::string value, xenstore_transaction &t = _xs_null); - virtual void remove(std::string path, xenstore_transaction &t = _xs_null); - - virtual std::string read(std::string path, xenstore_transaction &t = _xs_null); - - virtual std::list ls(std::string path, xenstore_transaction &t = _xs_null); - template - T read(std::string path, xenstore_transaction &t = _xs_null) { return boost::lexical_cast(read(path, t)); } - template - T read_or_default(std::string path, T deflt = T(), xenstore_transaction &t = _xs_null) { - auto val = read(path, t); - if (val.empty()) { - return deflt; - } else { - return boost::lexical_cast(val); - } - } - - template - void write(std::string path, T val, xenstore_transaction &t = _xs_null) { return write(path, std::to_string(val), t); } -}; - - -#endif /* XENSTORE_HH_ */ diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile deleted file mode 100644 index fc5f26981a..0000000000 --- a/docker/dev/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM fedora:21 - -RUN yum install -y gcc-c++ clang libasan libubsan hwloc hwloc-devel numactl-devel \ - python3 libaio-devel ninja-build boost-devel git ragel xen-devel \ - cryptopp-devel libpciaccess-devel libxml2-devel zlib-devel \ - jsoncpp-devel diff --git a/dpdk b/dpdk deleted file mode 160000 index 3b5e1551b3..0000000000 --- a/dpdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3b5e1551b3dbe0f6b6dc4cc5ce7f5fd951984945 diff --git a/http/api_docs.cc b/http/api_docs.cc deleted file mode 100644 index 473ec7fb15..0000000000 --- a/http/api_docs.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "api_docs.hh" -#include "handlers.hh" -#include "json/formatter.hh" -#include "transformers.hh" - -using namespace std; - -namespace httpd { - -const sstring api_registry_builder::DEFAULT_PATH = "/api-doc"; -const sstring api_registry_builder::DEFAULT_DIR = "."; - -} diff --git a/http/api_docs.hh b/http/api_docs.hh deleted file mode 100644 index facf059a0f..0000000000 --- a/http/api_docs.hh +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef API_DOCS_HH_ -#define API_DOCS_HH_ -#include "json/json_elements.hh" -#include "json/formatter.hh" -#include "routes.hh" -#include "transformers.hh" -#include - -namespace httpd { - -struct api_doc : public json::json_base { - json::json_element path; - json::json_element description; - - void register_params() { - add(&path, "path"); - add(&description, "description"); - - } - api_doc() { - register_params(); - } - api_doc(const api_doc & e) { - register_params(); - path = e.path; - description = e.description; - } - template - api_doc& operator=(const T& e) { - path = e.path; - description = e.description; - return *this; - } - api_doc& operator=(const api_doc& e) { - path = e.path; - description = e.description; - return *this; - } -}; - -struct api_docs : public json::json_base { - json::json_element apiVersion; - json::json_element swaggerVersion; - json::json_list apis; - - void register_params() { - add(&apiVersion, "apiVersion"); - add(&swaggerVersion, "swaggerVersion"); - add(&apis, "apis"); - - } - api_docs() { - apiVersion = "0.0.1"; - swaggerVersion = "1.2"; - register_params(); - } - api_docs(const api_docs & e) { - apiVersion = "0.0.1"; - swaggerVersion = "1.2"; - register_params(); - } - template - api_docs& operator=(const T& e) { - apis = e.apis; - return *this; - } - api_docs& operator=(const api_docs& e) { - apis = e.apis; - return *this; - } -}; - -class api_registry : public handler_base { - sstring _base_path; - sstring _file_directory; - api_docs _docs; - routes& _routes; - -public: - api_registry(routes& routes, const sstring& file_directory, - const sstring& base_path) - : _base_path(base_path), _file_directory(file_directory), _routes( - routes) { - _routes.put(GET, _base_path, this); - } - future> handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) override { - rep->_content = json::formatter::to_json(_docs); - rep->done("json"); - return make_ready_future>(std::move(rep)); - } - - void reg(const sstring& api, const sstring& description, - const sstring& alternative_path = "") { - api_doc doc; - doc.description = description; - doc.path = "/" + api; - _docs.apis.push(doc); - sstring path = - (alternative_path == "") ? - _file_directory + api + ".json" : alternative_path; - file_handler* index = new file_handler(path, - new content_replace("json")); - _routes.put(GET, _base_path + "/" + api, index); - } -}; - -class api_registry_builder { - sstring _file_directory; - sstring _base_path; - -public: - static const sstring DEFAULT_DIR; - static const sstring DEFAULT_PATH; - - api_registry_builder(const sstring& file_directory = DEFAULT_DIR, - const sstring& base_path = DEFAULT_PATH) - : _file_directory(file_directory), _base_path(base_path) { - } - - void set_api_doc(routes& r) { - new api_registry(r, _file_directory, _base_path); - } - - void register_function(routes& r, const sstring& api, - const sstring& description, const sstring& alternative_path = "") { - auto h = r.get_exact_match(GET, _base_path); - if (h) { - // if a handler is found, it was added there by the api_registry_builder - // with the set_api_doc method, so we know it's the type - static_cast(h)->reg(api, description, alternative_path); - }; - } -}; - -} - -#endif /* API_DOCS_HH_ */ diff --git a/http/common.cc b/http/common.cc deleted file mode 100644 index e6966680a4..0000000000 --- a/http/common.cc +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "common.hh" - -namespace httpd { - -operation_type str2type(const sstring& type) { - if (type == "DELETE") { - return DELETE; - } - if (type == "POST") { - return POST; - } - if (type == "PUT") { - return PUT; - } - return GET; -} - -} diff --git a/http/common.hh b/http/common.hh deleted file mode 100644 index 56f8326366..0000000000 --- a/http/common.hh +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef COMMON_HH_ -#define COMMON_HH_ - -#include -#include "core/sstring.hh" - -namespace httpd { - - -class parameters { - std::unordered_map params; -public: - const sstring& path(const sstring& key) const { - return params.at(key); - } - - sstring operator[](const sstring& key) const { - return params.at(key).substr(1); - } - - const sstring& at(const sstring& key) const { - return path(key); - } - - bool exists(const sstring& key) const { - return params.find(key) != params.end(); - } - - void set(const sstring& key, const sstring& value) { - params[key] = value; - } - - void clear() { - params.clear(); - } - -}; - -enum operation_type { - GET, POST, PUT, DELETE, NUM_OPERATION -}; - -/** - * Translate the string command to operation type - * @param type the string "GET" or "POST" - * @return the operation_type - */ -operation_type str2type(const sstring& type); - -} - -#endif /* COMMON_HH_ */ diff --git a/http/exception.hh b/http/exception.hh deleted file mode 100644 index e79d025bc1..0000000000 --- a/http/exception.hh +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef EXCEPTION_HH_ -#define EXCEPTION_HH_ - -#include "reply.hh" -#include "json/json_elements.hh" - -namespace httpd { - -/** - * The base_exception is a base for all http exception. - * It contains a message that will be return as the message content - * and a status that will be return as a status code. - */ -class base_exception : public std::exception { -public: - base_exception(const std::string& msg, reply::status_type status) - : _msg(msg), _status(status) { - } - - virtual const char* what() const throw () { - return _msg.c_str(); - } - - reply::status_type status() const { - return _status; - } - - virtual const std::string& str() const { - return _msg; - } -private: - std::string _msg; - reply::status_type _status; - -}; - -/** - * Throwing this exception will result in a redirect to the given url - */ -class redirect_exception : public base_exception { -public: - redirect_exception(const std::string& url) - : base_exception("", reply::status_type::moved_permanently), url( - url) { - } - std::string url; -}; - -/** - * Throwing this exception will result in a 404 not found result - */ -class not_found_exception : public base_exception { -public: - not_found_exception(const std::string& msg = "Not found") - : base_exception(msg, reply::status_type::not_found) { - } -}; - -/** - * Throwing this exception will result in a 400 bad request result - */ - -class bad_request_exception : public base_exception { -public: - bad_request_exception(const std::string& msg) - : base_exception(msg, reply::status_type::bad_request) { - } -}; - -class bad_param_exception : public bad_request_exception { -public: - bad_param_exception(const std::string& msg) - : bad_request_exception(msg) { - } -}; - -class missing_param_exception : public bad_request_exception { -public: - missing_param_exception(const std::string& param) - : bad_request_exception( - std::string("Missing mandatory parameter '") + param + "'") { - } -}; - -class server_error_exception : public base_exception { -public: - server_error_exception(const std::string& msg) - : base_exception(msg, reply::status_type::internal_server_error) { - } -}; - -class json_exception : public json::json_base { -public: - json::json_element _msg; - json::json_element _code; - void register_params() { - add(&_msg, "message"); - add(&_code, "code"); - } - - json_exception(const base_exception & e) { - set(e.str(), e.status()); - } - - json_exception(const std::exception& e) { - set(e.what(), reply::status_type::internal_server_error); - } -private: - void set(const std::string& msg, reply::status_type code) { - register_params(); - _msg = msg; - _code = (int) code; - } -}; - -} - -#endif /* EXCEPTION_HH_ */ diff --git a/http/file_handler.cc b/http/file_handler.cc deleted file mode 100644 index 78aa3fe4dc..0000000000 --- a/http/file_handler.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "file_handler.hh" -#include -#include -#include "core/reactor.hh" -#include "core/fstream.hh" -#include "core/shared_ptr.hh" -#include "core/app-template.hh" -#include "exception.hh" - -namespace httpd { - -directory_handler::directory_handler(const sstring& doc_root, - file_transformer* transformer) - : file_interaction_handler(transformer), doc_root(doc_root) { -} - -future> directory_handler::handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) { - sstring full_path = doc_root + req->param["path"]; - auto h = this; - return engine().file_type(full_path).then( - [h, full_path, req = std::move(req), rep = std::move(rep)](auto val) mutable { - if (val) { - if (val.value() == directory_entry_type::directory) { - if (h->redirect_if_needed(*req.get(), *rep.get())) { - return make_ready_future>(std::move(rep)); - } - full_path += "/index.html"; - } - return h->read(full_path, std::move(req), std::move(rep)); - } - rep->set_status(reply::status_type::not_found).done(); - return make_ready_future>(std::move(rep)); - - }); -} - -file_interaction_handler::~file_interaction_handler() { - delete transformer; -} - -sstring file_interaction_handler::get_extension(const sstring& file) { - size_t last_slash_pos = file.find_last_of('/'); - size_t last_dot_pos = file.find_last_of('.'); - sstring extension; - if (last_dot_pos != sstring::npos && last_dot_pos > last_slash_pos) { - extension = file.substr(last_dot_pos + 1); - } - return extension; -} - -struct reader { - reader(file f, std::unique_ptr rep) - : is( - make_file_input_stream(make_lw_shared(std::move(f)), - 0, 4096)), _rep(std::move(rep)) { - } - input_stream is; - std::unique_ptr _rep; - - // for input_stream::consume(): - using unconsumed_remainder = std::experimental::optional>; - future operator()(temporary_buffer data) { - if (data.empty()) { - _rep->done(); - return make_ready_future(std::move(data)); - } else { - _rep->_content.append(data.get(), data.size()); - return make_ready_future(); - } - } -}; - -future> file_interaction_handler::read( - const sstring& file_name, std::unique_ptr req, - std::unique_ptr rep) { - sstring extension = get_extension(file_name); - rep->set_content_type(extension); - return engine().open_file_dma(file_name, open_flags::ro).then( - [rep = std::move(rep), extension, this, req = std::move(req)](file f) mutable { - std::shared_ptr r = std::make_shared(std::move(f), std::move(rep)); - - return r->is.consume(*r).then([r, extension, this, req = std::move(req)]() { - if (transformer != nullptr) { - transformer->transform(r->_rep->_content, *req, extension); - } - r->_rep->done(); - return make_ready_future>(std::move(r->_rep)); - }); - }); -} - -bool file_interaction_handler::redirect_if_needed(const request& req, - reply& rep) const { - if (req._url.length() == 0 || req._url.back() != '/') { - rep.set_status(reply::status_type::moved_permanently); - rep._headers["Location"] = req.get_url() + "/"; - rep.done(); - return true; - } - return false; -} - -future> file_handler::handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) { - if (force_path && redirect_if_needed(*req.get(), *rep.get())) { - return make_ready_future>(std::move(rep)); - } - return read(file, std::move(req), std::move(rep)); -} - -} diff --git a/http/file_handler.hh b/http/file_handler.hh deleted file mode 100644 index 2c2bb66c93..0000000000 --- a/http/file_handler.hh +++ /dev/null @@ -1,161 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef HTTP_FILE_HANDLER_HH_ -#define HTTP_FILE_HANDLER_HH_ - -#include "handlers.hh" - -namespace httpd { -/** - * This is a base class for file transformer. - * - * File transformer adds the ability to modify a file content before returning - * the results. - * - * The transformer decides according to the file extension if transforming is - * needed. - */ -class file_transformer { -public: - /** - * Any file transformer should implement this method. - * @param content the content to transform - * @param req the request - * @param extension the file extension originating the content - */ - virtual void transform(sstring& content, const request& req, - const sstring& extension) = 0; - - virtual ~file_transformer() = default; -}; - -/** - * A base class for handlers that interact with files. - * directory and file handlers both share some common logic - * with regards to file handling. - * they both needs to read a file from the disk, optionally transform it, - * and return the result or page not found on error - */ -class file_interaction_handler : public handler_base { -public: - file_interaction_handler(file_transformer* p = nullptr) - : transformer(p) { - - } - - ~file_interaction_handler(); - - /** - * Allows setting a transformer to be used with the files returned. - * @param t the file transformer to use - * @return this - */ - file_interaction_handler* set_transformer(file_transformer* t) { - transformer = t; - return this; - } - - /** - * if the url ends without a slash redirect - * @param req the request - * @param rep the reply - * @return true on redirect - */ - bool redirect_if_needed(const request& req, reply& rep) const; - - /** - * A helper method that returns the file extension. - * @param file the file to check - * @return the file extension - */ - static sstring get_extension(const sstring& file); - -protected: - - /** - * read a file from the disk and return it in the replay. - * @param file the full path to a file on the disk - * @param req the reuest - * @param rep the reply - */ - future > read(const sstring& file, - std::unique_ptr req, std::unique_ptr rep); - file_transformer* transformer; -}; - -/** - * The directory handler get a disk path in the - * constructor. - * and expect a path parameter in the handle method. - * it would concatenate the two and return the file - * e.g. if the path is /usr/mgmt/public in the path - * parameter is index.html - * handle will return the content of /usr/mgmt/public/index.html - */ -class directory_handler : public file_interaction_handler { -public: - - /** - * The directory handler map a base path and a path parameter to a file - * @param doc_root the root directory to search the file from. - * For example if the root is '/usr/mgmt/public' and the path parameter - * will be '/css/style.css' the file wil be /usr/mgmt/public/css/style.css' - */ - explicit directory_handler(const sstring& doc_root, - file_transformer* transformer = nullptr); - - future> handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) override; - -private: - sstring doc_root; -}; - -/** - * The file handler get a path to a file on the disk - * in the constructor. - * it will always return the content of the file. - */ -class file_handler : public file_interaction_handler { -public: - - /** - * The file handler map a file to a url - * @param file the full path to the file on the disk - */ - explicit file_handler(const sstring& file, file_transformer* transformer = - nullptr, bool force_path = true) - : file_interaction_handler(transformer), file(file), force_path( - force_path) { - } - - future> handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) override; - -private: - sstring file; - bool force_path; -}; - -} - -#endif /* HTTP_FILE_HANDLER_HH_ */ diff --git a/http/function_handlers.hh b/http/function_handlers.hh deleted file mode 100644 index b9f9627186..0000000000 --- a/http/function_handlers.hh +++ /dev/null @@ -1,116 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#pragma once - -#include "handlers.hh" -#include -#include "json/json_elements.hh" - -namespace httpd { - -/** - * A request function is a lambda expression that gets only the request - * as its parameter - */ -typedef std::function request_function; - -/** - * A handle function is a lambda expression that gets request and reply - */ -typedef std::function handle_function; - -/** - * A json request function is a lambda expression that gets only the request - * as its parameter and return a json response. - * Using the json response is done implicitly. - */ -typedef std::function json_request_function; - -/** - * A future_json_function is a function that returns a future json reponse. - * Similiar to the json_request_function, using the json reponse is done - * implicitly. - */ -typedef std::function< - future(std::unique_ptr req)> future_json_function; -/** - * The function handler get a lambda expression in the constructor. - * it will call that expression to get the result - * This is suited for very simple handlers - * - */ -class function_handler : public handler_base { -public: - - function_handler(const handle_function & f_handle, const sstring& type) - : _f_handle( - [f_handle](std::unique_ptr req, std::unique_ptr rep) { - rep->_content += f_handle(*req.get(), *rep.get()); - return make_ready_future>(std::move(rep)); - }), _type(type) { - } - - function_handler(const request_function & _handle, const sstring& type) - : _f_handle( - [_handle](std::unique_ptr req, std::unique_ptr rep) { - rep->_content += _handle(*req.get()); - return make_ready_future>(std::move(rep)); - }), _type(type) { - } - - function_handler(const json_request_function& _handle) - : _f_handle( - [_handle](std::unique_ptr req, std::unique_ptr rep) { - json::json_return_type res = _handle(*req.get()); - rep->_content += res._res; - return make_ready_future>(std::move(rep)); - }), _type("json") { - } - - function_handler(const future_json_function& _handle) - : _f_handle( - [_handle](std::unique_ptr req, std::unique_ptr rep) { - return _handle(std::move(req)).then([rep = std::move(rep)](json::json_return_type res) mutable { - rep->_content += res._res; - return make_ready_future>(std::move(rep)); - } - ); - }), _type("json") { - } - - future> handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) override { - return _f_handle(std::move(req), std::move(rep)).then( - [this](std::unique_ptr rep) { - rep->done(_type); - return make_ready_future>(std::move(rep)); - }); - } - -protected: - std::function< - future>(std::unique_ptr req, - std::unique_ptr rep)> _f_handle; - sstring _type; -}; - -} diff --git a/http/handlers.hh b/http/handlers.hh deleted file mode 100644 index 9a9888af61..0000000000 --- a/http/handlers.hh +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef HANDLERS_HH_ -#define HANDLERS_HH_ - -#include "request.hh" -#include "common.hh" -#include "reply.hh" -#include "core/future-util.hh" - -#include - -namespace httpd { - -typedef const httpd::request& const_req; - -/** - * handlers holds the logic for serving an incoming request. - * All handlers inherit from the base httpserver_handler and - * implement the handle method. - * - */ -class handler_base { -public: - /** - * All handlers should implement this method. - * It fill the reply according to the request. - * @param path the url path used in this call - * @param params optional parameter object - * @param req the original request - * @param rep the reply - */ - virtual future > handle(const sstring& path, - std::unique_ptr req, std::unique_ptr rep) = 0; - - virtual ~handler_base() = default; - - /** - * Add a mandatory parameter - * @param param a parameter name - * @return a reference to the handler - */ - handler_base& mandatory(const sstring& param) { - _mandatory_param.push_back(param); - return *this; - } - - std::vector _mandatory_param; - -}; - -} - -#endif /* HANDLERS_HH_ */ diff --git a/http/httpd.cc b/http/httpd.cc deleted file mode 100644 index 5d99ace34b..0000000000 --- a/http/httpd.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "core/reactor.hh" -#include "core/sstring.hh" -#include "core/app-template.hh" -#include "core/circular_buffer.hh" -#include "core/distributed.hh" -#include "core/queue.hh" -#include "core/future-util.hh" -#include "core/scollectd.hh" -#include -#include -#include -#include -#include -#include -#include -#include -#include "httpd.hh" - -using namespace std::chrono_literals; - -namespace httpd { -http_stats::http_stats(http_server& server) - : _regs{ - scollectd::add_polled_metric( - scollectd::type_instance_id("httpd", scollectd::per_cpu_plugin_instance, - "connections", "http-connections"), - scollectd::make_typed(scollectd::data_type::DERIVE, - [&server] { return server.total_connections(); })), - scollectd::add_polled_metric( - scollectd::type_instance_id("httpd", scollectd::per_cpu_plugin_instance, - "current_connections", "current"), - scollectd::make_typed(scollectd::data_type::GAUGE, - [&server] { return server.current_connections(); })), - scollectd::add_polled_metric( - scollectd::type_instance_id("httpd", scollectd::per_cpu_plugin_instance, - "http_requests", "served"), - scollectd::make_typed(scollectd::data_type::DERIVE, - [&server] { return server.requests_served(); })), - } { -} -} diff --git a/http/httpd.hh b/http/httpd.hh deleted file mode 100644 index ca666f1f9e..0000000000 --- a/http/httpd.hh +++ /dev/null @@ -1,431 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef APPS_HTTPD_HTTPD_HH_ -#define APPS_HTTPD_HTTPD_HH_ - -#include "http/request_parser.hh" -#include "http/request.hh" -#include "core/reactor.hh" -#include "core/sstring.hh" -#include -#include "core/app-template.hh" -#include "core/circular_buffer.hh" -#include "core/distributed.hh" -#include "core/queue.hh" -#include "core/future-util.hh" -#include "core/scollectd.hh" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "reply.hh" -#include "http/routes.hh" - -namespace httpd { - -class http_server; -class http_stats; - -using namespace std::chrono_literals; - -class http_stats { - scollectd::registrations _regs; -public: - http_stats(http_server& server); -}; - -class http_server { - std::vector _listeners; - http_stats _stats { *this }; - uint64_t _total_connections = 0; - uint64_t _current_connections = 0; - uint64_t _requests_served = 0; - uint64_t _connections_being_accepted = 0; - sstring _date = http_date(); - timer<> _date_format_timer { [this] {_date = http_date();} }; - bool _stopping = false; - promise<> _all_connections_stopped; - future<> _stopped = _all_connections_stopped.get_future(); -private: - void maybe_idle() { - if (_stopping && !_connections_being_accepted && !_current_connections) { - _all_connections_stopped.set_value(); - } - } -public: - routes _routes; - - http_server() { - _date_format_timer.arm_periodic(1s); - } - future<> listen(ipv4_addr addr) { - listen_options lo; - lo.reuse_address = true; - _listeners.push_back(engine().listen(make_ipv4_address(addr), lo)); - _stopped = when_all(std::move(_stopped), do_accepts(_listeners.size() - 1)).discard_result(); - return make_ready_future<>(); - } - future<> stop() { - _stopping = true; - for (auto&& l : _listeners) { - l.abort_accept(); - } - for (auto&& c : _connections) { - c.shutdown(); - } - return std::move(_stopped); - } - future<> do_accepts(int which) { - ++_connections_being_accepted; - return _listeners[which].accept().then_wrapped( - [this, which] (future f_cs_sa) mutable { - --_connections_being_accepted; - if (_stopping) { - maybe_idle(); - return; - } - auto cs_sa = f_cs_sa.get(); - auto conn = new connection(*this, std::get<0>(std::move(cs_sa)), std::get<1>(std::move(cs_sa))); - conn->process().then_wrapped([this, conn] (auto&& f) { - delete conn; - try { - f.get(); - } catch (std::exception& ex) { - std::cerr << "request error " << ex.what() << std::endl; - } - }); - do_accepts(which); - }).then_wrapped([] (auto f) { - try { - f.get(); - } catch (std::exception& ex) { - std::cerr << "accept failed: " << ex.what() << std::endl; - } - }); - } - class connection : public boost::intrusive::list_base_hook<> { - http_server& _server; - connected_socket _fd; - input_stream _read_buf; - output_stream _write_buf; - static constexpr size_t limit = 4096; - using tmp_buf = temporary_buffer; - http_request_parser _parser; - std::unique_ptr _req; - std::unique_ptr _resp; - // null element marks eof - queue> _replies { 10 };bool _done = false; - public: - connection(http_server& server, connected_socket&& fd, - socket_address addr) - : _server(server), _fd(std::move(fd)), _read_buf(_fd.input()), _write_buf( - _fd.output()) { - ++_server._total_connections; - ++_server._current_connections; - _server._connections.push_back(*this); - } - ~connection() { - --_server._current_connections; - _server._connections.erase(_server._connections.iterator_to(*this)); - _server.maybe_idle(); - } - future<> process() { - // Launch read and write "threads" simultaneously: - return when_all(read(), respond()).then( - [] (std::tuple, future<>> joined) { - // FIXME: notify any exceptions in joined? - return make_ready_future<>(); - }); - } - void shutdown() { - _fd.shutdown_input(); - _fd.shutdown_output(); - } - future<> read() { - return do_until([this] {return _done;}, [this] { - return read_one(); - }).then_wrapped([this] (future<> f) { - // swallow error - // FIXME: count it? - return _replies.push_eventually( {}); - }); - } - future<> read_one() { - _parser.init(); - return _read_buf.consume(_parser).then([this] () mutable { - if (_parser.eof()) { - _done = true; - return make_ready_future<>(); - } - ++_server._requests_served; - std::unique_ptr req = _parser.get_parsed_request(); - - return _replies.not_full().then([req = std::move(req), this] () mutable { - return generate_reply(std::move(req)); - }).then([this](bool done) { - _done = done; - }); - }); - } - future<> respond() { - return _replies.pop_eventually().then( - [this] (std::unique_ptr resp) { - if (!resp) { - // eof - return make_ready_future<>(); - } - _resp = std::move(resp); - return start_response().then([this] { - return respond(); - }); - }); - } - future<> start_response() { - _resp->_headers["Server"] = "Seastar httpd"; - _resp->_headers["Date"] = _server._date; - _resp->_headers["Content-Length"] = to_sstring( - _resp->_content.size()); - return _write_buf.write(_resp->_response_line.begin(), - _resp->_response_line.size()).then([this] { - return write_reply_headers(_resp->_headers.begin()); - }).then([this] { - return _write_buf.write("\r\n", 2); - }).then([this] { - return write_body(); - }).then([this] { - return _write_buf.flush(); - }).then([this] { - _resp.reset(); - }); - } - future<> write_reply_headers( - std::unordered_map::iterator hi) { - if (hi == _resp->_headers.end()) { - return make_ready_future<>(); - } - return _write_buf.write(hi->first.begin(), hi->first.size()).then( - [this] { - return _write_buf.write(": ", 2); - }).then([hi, this] { - return _write_buf.write(hi->second.begin(), hi->second.size()); - }).then([this] { - return _write_buf.write("\r\n", 2); - }).then([hi, this] () mutable { - return write_reply_headers(++hi); - }); - } - - static short hex_to_byte(char c) { - if (c >='a' && c <= 'z') { - return c - 'a' + 10; - } else if (c >='A' && c <= 'Z') { - return c - 'A' + 10; - } - return c - '0'; - } - - /** - * Convert a hex encoded 2 bytes substring to char - */ - static char hexstr_to_char(const std::experimental::string_view& in, size_t from) { - - return static_cast(hex_to_byte(in[from]) * 16 + hex_to_byte(in[from + 1])); - } - - /** - * URL_decode a substring and place it in the given out sstring - */ - static bool url_decode(const std::experimental::string_view& in, sstring& out) { - size_t pos = 0; - char buff[in.length()]; - for (size_t i = 0; i < in.length(); ++i) { - if (in[i] == '%') { - if (i + 3 <= in.size()) { - buff[pos++] = hexstr_to_char(in, i + 1); - i += 2; - } else { - return false; - } - } else if (in[i] == '+') { - buff[pos++] = ' '; - } else { - buff[pos++] = in[i]; - } - } - out = sstring(buff, pos); - return true; - } - - /** - * Add a single query parameter to the parameter list - */ - static void add_param(request& req, const std::experimental::string_view& param) { - size_t split = param.find('='); - - if (split >= param.length() - 1) { - sstring key; - if (url_decode(param.substr(0,split) , key)) { - req.query_parameters[key] = ""; - } - } else { - sstring key; - sstring value; - if (url_decode(param.substr(0,split), key) - && url_decode(param.substr(split + 1), value)) { - req.query_parameters[key] = value; - } - } - - } - - /** - * Set the query parameters in the request objects. - * query param appear after the question mark and are separated - * by the ampersand sign - */ - static sstring set_query_param(request& req) { - size_t pos = req._url.find('?'); - if (pos == sstring::npos) { - return req._url; - } - size_t curr = pos + 1; - size_t end_param; - std::experimental::string_view url = req._url; - while ((end_param = req._url.find('&', curr)) != sstring::npos) { - add_param(req, url.substr(curr, end_param - curr) ); - curr = end_param + 1; - } - add_param(req, url.substr(curr)); - return req._url.substr(0, pos); - } - - future generate_reply(std::unique_ptr req) { - auto resp = std::make_unique(); - bool conn_keep_alive = false; - bool conn_close = false; - auto it = req->_headers.find("Connection"); - if (it != req->_headers.end()) { - if (it->second == "Keep-Alive") { - conn_keep_alive = true; - } else if (it->second == "Close") { - conn_close = true; - } - } - bool should_close; - // TODO: Handle HTTP/2.0 when it releases - resp->set_version(req->_version); - - if (req->_version == "1.0") { - if (conn_keep_alive) { - resp->_headers["Connection"] = "Keep-Alive"; - } - should_close = !conn_keep_alive; - } else if (req->_version == "1.1") { - should_close = conn_close; - } else { - // HTTP/0.9 goes here - should_close = true; - } - sstring url = set_query_param(*req.get()); - return _server._routes.handle(url, std::move(req), std::move(resp)). - // Caller guarantees enough room - then([this, should_close](std::unique_ptr rep) { - this->_replies.push(std::move(rep)); - return make_ready_future(should_close); - }); - } - future<> write_body() { - return _write_buf.write(_resp->_content.begin(), - _resp->_content.size()); - } - }; - uint64_t total_connections() const { - return _total_connections; - } - uint64_t current_connections() const { - return _current_connections; - } - uint64_t requests_served() const { - return _requests_served; - } - static sstring http_date() { - auto t = ::time(nullptr); - struct tm tm; - gmtime_r(&t, &tm); - char tmp[100]; - strftime(tmp, sizeof(tmp), "%d %b %Y %H:%M:%S GMT", &tm); - return tmp; - } -private: - boost::intrusive::list _connections; -}; - -/* - * A helper class to start, set and listen an http server - * typical use would be: - * - * auto server = new http_server_control(); - * server->start().then([server] { - * server->set_routes(set_routes); - * }).then([server, port] { - * server->listen(port); - * }).then([port] { - * std::cout << "Seastar HTTP server listening on port " << port << " ...\n"; - * }); - */ -class http_server_control { - distributed* _server_dist; -public: - http_server_control() : _server_dist(new distributed) { - } - - future<> start() { - return _server_dist->start(); - } - - future<> stop() { - return _server_dist->stop(); - } - - future<> set_routes(std::function fun) { - return _server_dist->invoke_on_all([fun](http_server& server) { - fun(server._routes); - }); - } - - future<> listen(uint16_t port) { - return _server_dist->invoke_on_all(&http_server::listen, ipv4_addr {port}); - } - - distributed& server() { - return *_server_dist; - } -}; - -} - -#endif /* APPS_HTTPD_HTTPD_HH_ */ diff --git a/http/json_path.cc b/http/json_path.cc deleted file mode 100644 index 695dd859b5..0000000000 --- a/http/json_path.cc +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "json_path.hh" - -namespace httpd { - -using namespace std; - -void path_description::set(routes& _routes, handler_base* handler) const { - for (auto& i : mandatory_queryparams) { - handler->mandatory(i); - } - - if (params.size() == 0) - _routes.put(operations.method, path, handler); - else { - match_rule* rule = new match_rule(handler); - rule->add_str(path); - for (auto i = params.begin(); i != params.end(); ++i) { - rule->add_param(std::get<0>(*i), std::get<1>(*i)); - } - _routes.add(rule, operations.method); - } -} - -void path_description::set(routes& _routes, - const json_request_function& f) const { - set(_routes, new function_handler(f)); -} - -void path_description::set(routes& _routes, const future_json_function& f) const { - set(_routes, new function_handler(f)); -} -path_description::path_description(const sstring& path, operation_type method, - const sstring& nickname, - const std::vector>& path_parameters, - const std::vector& mandatory_params) - : path(path), operations(method, nickname) { - - for (auto man : mandatory_params) { - pushmandatory_param(man); - } - for (auto param : path_parameters) { - params.push_back(param); - } - -} - -} diff --git a/http/json_path.hh b/http/json_path.hh deleted file mode 100644 index 8f234ba006..0000000000 --- a/http/json_path.hh +++ /dev/null @@ -1,137 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef JSON_PATH_HH_ -#define JSON_PATH_HH_ - -#include -#include -#include -#include "common.hh" -#include "core/sstring.hh" -#include "routes.hh" -#include "function_handlers.hh" - -namespace httpd { - -/** - * A json_operation contain a method and a nickname. - * operation are associated to a path, that can - * have multiple methods - */ -struct json_operation { - /** - * default constructor - */ - json_operation() - : method(GET) { - } - - /** - * Construct with assignment - * @param method the http method type - * @param nickname the http nickname - */ - json_operation(operation_type method, const sstring& nickname) - : method(method), nickname(nickname) { - } - - operation_type method; - sstring nickname; - -}; - -/** - * path description holds the path in the system. - * It maps a nickname to an operation, which allows - * defining the operation (path and method) by its - * nickname. - * - * the description are taken from the json swagger - * definition file, during auto code generation in the - * compilation. - */ -struct path_description { - /** - * default empty constructor - */ - path_description() = default; - - /** - * constructor for path with parameters - * The constructor is used by - * @param path the url path - * @param method the http method - * @param nickname the nickname - */ - path_description(const sstring& path, operation_type method, - const sstring& nickname, - const std::vector>& path_parameters, - const std::vector& mandatory_params); - - /** - * Add a parameter to the path definition - * for example, if the url should match /file/{path} - * The constructor would be followed by a call to - * pushparam("path") - * - * @param param the name of the parameters, this name will - * be used by the handler to identify the parameters. - * A name can appear at most once in a description - * @param all_path when set to true the parameter will assume to match - * until the end of the url. - * This is useful for situation like file path with - * a rule like /file/{path} and a url /file/etc/hosts. - * path should be equal to /ets/hosts and not only /etc - * @return the current path description - */ - path_description* pushparam(const sstring& param, - bool all_path = false) { - params.push_back( { param, all_path }); - return this; - } - - /** - * adds a mandatory query parameter to the path - * this parameter will be check before calling a handler - * @param param the parameter to head - * @return a pointer to the current path description - */ - path_description* pushmandatory_param(const sstring& param) { - mandatory_queryparams.push_back(param); - return this; - } - - std::vector> params; - sstring path; - json_operation operations; - - std::vector mandatory_queryparams; - - void set(routes& _routes, handler_base* handler) const; - - void set(routes& _routes, const json_request_function& f) const; - - void set(routes& _routes, const future_json_function& f) const; -}; - -} -#endif /* JSON_PATH_HH_ */ diff --git a/http/matcher.cc b/http/matcher.cc deleted file mode 100644 index 8e766c6d65..0000000000 --- a/http/matcher.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "matcher.hh" - -#include - -namespace httpd { - -using namespace std; - -/** - * Search for the end of the url parameter. - * @param url the url to search - * @param ind the position in the url - * @param entire_path when set to true, take all the reminaing url - * when set to false, search for the next slash - * @return the position in the url of the end of the parameter - */ -static size_t find_end_param(const sstring& url, size_t ind, bool entire_path) { - size_t pos = (entire_path) ? url.length() : url.find('/', ind + 1); - if (pos == sstring::npos) { - return url.length(); - } - return pos; -} - -size_t param_matcher::match(const sstring& url, size_t ind, parameters& param) { - size_t last = find_end_param(url, ind, _entire_path); - if (last == ind) { - /* - * empty parameter allows only for the case of entire_path - */ - if (_entire_path) { - param.set(_name, ""); - return ind; - } - return sstring::npos; - } - param.set(_name, url.substr(ind, last - ind)); - return last; -} - -size_t str_matcher::match(const sstring& url, size_t ind, parameters& param) { - if (url.length() >= _len + ind && (url.find(_cmp, ind) == ind) - && (url.length() == _len + ind || url.at(_len + ind) == '/')) { - return _len + ind; - } - return sstring::npos; -} - -} diff --git a/http/matcher.hh b/http/matcher.hh deleted file mode 100644 index 10d781b928..0000000000 --- a/http/matcher.hh +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef MATCHER_HH_ -#define MATCHER_HH_ - -#include "common.hh" - -#include "core/sstring.hh" - -namespace httpd { - -/** - * a base class for the url matching. - * Each implementation check if the given url matches a criteria - */ -class matcher { -public: - - virtual ~matcher() = default; - - /** - * check if the given url matches the rule - * @param url the url to check - * @param ind the position to start from - * @param fill the parameters hash - * @return the end of of the matched part, or sstring::npos if not matched - */ - virtual size_t match(const sstring& url, size_t ind, parameters& param) = 0; -}; - -/** - * Check if the url match a parameter and fill the parameters object - * - * Note that a non empty url will always return true with the parameters - * object filled - * - * Assume that the rule is /file/{path}/ and the param_matcher identify - * the /{path} - * - * For all non empty values, match will return true. - * If the entire url is /file/etc/hosts, and the part that is passed to - * param_matcher is /etc/hosts, if entire_path is true, the match will be - * '/etc/hosts' If entire_path is false, the match will be '/etc' - */ -class param_matcher : public matcher { -public: - /** - * Constructor - * @param name the name of the parameter, will be used as the key - * in the parameters object - * @param entire_path when set to true, the matched parameters will - * include all the remaining url until the end of it. - * when set to false the match will terminate at the next slash - */ - explicit param_matcher(const sstring& name, bool entire_path = false) - : _name(name), _entire_path(entire_path) { - } - - virtual size_t match(const sstring& url, size_t ind, parameters& param) - override; -private: - sstring _name; - bool _entire_path; -}; - -/** - * Check if the url match a predefine string. - * - * When parsing a match rule such as '/file/{path}' the str_match would parse - * the '/file' part - */ -class str_matcher : public matcher { -public: - /** - * Constructor - * @param cmp the string to match - */ - explicit str_matcher(const sstring& cmp) - : _cmp(cmp), _len(cmp.size()) { - } - - virtual size_t match(const sstring& url, size_t ind, parameters& param) - override; -private: - sstring _cmp; - unsigned _len; -}; - -} - -#endif /* MATCHER_HH_ */ diff --git a/http/matchrules.hh b/http/matchrules.hh deleted file mode 100644 index f2e575ef5c..0000000000 --- a/http/matchrules.hh +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef MATCH_RULES_HH_ -#define MATCH_RULES_HH_ - -#include "handlers.hh" -#include "matcher.hh" -#include "common.hh" - -#include "core/sstring.hh" -#include - -namespace httpd { - -/** - * match_rule check if a url matches criteria, that can contains - * parameters. - * the routes object would call the get method with a url and if - * it matches, the method will return a handler - * during the matching process, the method fill the parameters object. - */ -class match_rule { -public: - /** - * The destructor deletes matchers. - */ - ~match_rule() { - for (auto m : _match_list) { - delete m; - } - delete _handler; - } - - /** - * Constructor with a handler - * @param handler the handler to return when this match rule is met - */ - explicit match_rule(handler_base* handler) - : _handler(handler) { - } - - /** - * Check if url match the rule and return a handler if it does - * @param url a url to compare against the rule - * @param params the parameters object, matches parameters will fill - * the object during the matching process - * @return a handler if there is a full match or nullptr if not - */ - handler_base* get(const sstring& url, parameters& params) { - size_t ind = 0; - for (unsigned int i = 0; i < _match_list.size(); i++) { - ind = _match_list.at(i)->match(url, ind, params); - if (ind == sstring::npos) { - return nullptr; - } - } - return (ind + 1 >= url.length()) ? _handler : nullptr; - } - - /** - * Add a matcher to the rule - * @param match the matcher to add - * @return this - */ - match_rule& add_matcher(matcher* match) { - _match_list.push_back(match); - return *this; - } - - /** - * Add a static url matcher - * @param str the string to search for - * @return this - */ - match_rule& add_str(const sstring& str) { - add_matcher(new str_matcher(str)); - return *this; - } - - /** - * add a parameter matcher to the rule - * @param str the parameter name - * @param fullpath when set to true, parameter will included all the - * remaining url until its end - * @return this - */ - match_rule& add_param(const sstring& str, bool fullpath = false) { - add_matcher(new param_matcher(str, fullpath)); - return *this; - } - -private: - std::vector _match_list; - handler_base* _handler; -}; - -} - -#endif /* MATCH_RULES_HH_ */ diff --git a/http/mime_types.cc b/http/mime_types.cc deleted file mode 100644 index d57ced6ed0..0000000000 --- a/http/mime_types.cc +++ /dev/null @@ -1,44 +0,0 @@ -// -// mime_types.cpp -// ~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include "mime_types.hh" - -namespace httpd { -namespace mime_types { - -struct mapping { - const char* extension; - const char* mime_type; -} mappings[] = { - { "gif", "image/gif" }, - { "htm", "text/html" }, - { "css", "text/css" }, - { "js", "text/javascript" }, - { "html", "text/html" }, - { "jpg", "image/jpeg" }, - { "png", "image/png" }, - { "txt", "text/plain" }, - { "ico", "image/x-icon" }, - { "bin", "application/octet-stream" }, -}; - -const char* extension_to_type(const sstring& extension) -{ - for (mapping m : mappings) { - if (extension == m.extension) { - return m.mime_type; - } - } - return "text/plain"; -} - -} // namespace mime_types - -} // httpd diff --git a/http/mime_types.hh b/http/mime_types.hh deleted file mode 100644 index 7241887de6..0000000000 --- a/http/mime_types.hh +++ /dev/null @@ -1,32 +0,0 @@ -// -// mime_types.hpp -// ~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef HTTP_MIME_TYPES_HH -#define HTTP_MIME_TYPES_HH - -#include "core/sstring.hh" - -namespace httpd { - -namespace mime_types { - -/** - * Convert a file extension into a MIME type. - * - * @param extension the file extension - * @return the mime type as a string - */ -const char* extension_to_type(const sstring& extension); - -} // namespace mime_types - -} // namespace httpd - -#endif // HTTP_MIME_TYPES_HH diff --git a/http/reply.cc b/http/reply.cc deleted file mode 100644 index 5cbd42a8d3..0000000000 --- a/http/reply.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -// -// response.cpp -// ~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -#include "reply.hh" - -namespace httpd { - -namespace status_strings { - -const sstring ok = " 200 OK\r\n"; -const sstring created = " 201 Created\r\n"; -const sstring accepted = " 202 Accepted\r\n"; -const sstring no_content = " 204 No Content\r\n"; -const sstring multiple_choices = " 300 Multiple Choices\r\n"; -const sstring moved_permanently = " 301 Moved Permanently\r\n"; -const sstring moved_temporarily = " 302 Moved Temporarily\r\n"; -const sstring not_modified = " 304 Not Modified\r\n"; -const sstring bad_request = " 400 Bad Request\r\n"; -const sstring unauthorized = " 401 Unauthorized\r\n"; -const sstring forbidden = " 403 Forbidden\r\n"; -const sstring not_found = " 404 Not Found\r\n"; -const sstring internal_server_error = " 500 Internal Server Error\r\n"; -const sstring not_implemented = " 501 Not Implemented\r\n"; -const sstring bad_gateway = " 502 Bad Gateway\r\n"; -const sstring service_unavailable = " 503 Service Unavailable\r\n"; - -static const sstring& to_string(reply::status_type status) { - switch (status) { - case reply::status_type::ok: - return ok; - case reply::status_type::created: - return created; - case reply::status_type::accepted: - return accepted; - case reply::status_type::no_content: - return no_content; - case reply::status_type::multiple_choices: - return multiple_choices; - case reply::status_type::moved_permanently: - return moved_permanently; - case reply::status_type::moved_temporarily: - return moved_temporarily; - case reply::status_type::not_modified: - return not_modified; - case reply::status_type::bad_request: - return bad_request; - case reply::status_type::unauthorized: - return unauthorized; - case reply::status_type::forbidden: - return forbidden; - case reply::status_type::not_found: - return not_found; - case reply::status_type::internal_server_error: - return internal_server_error; - case reply::status_type::not_implemented: - return not_implemented; - case reply::status_type::bad_gateway: - return bad_gateway; - case reply::status_type::service_unavailable: - return service_unavailable; - default: - return internal_server_error; - } -} -} // namespace status_strings - -namespace misc_strings { - -const char name_value_separator[] = { ':', ' ' }; -const char crlf[] = { '\r', '\n' }; - -} // namespace misc_strings - -sstring reply::response_line() { - return "HTTP/" + _version + status_strings::to_string(_status); -} - -} // namespace server diff --git a/http/reply.hh b/http/reply.hh deleted file mode 100644 index 4361ba578e..0000000000 --- a/http/reply.hh +++ /dev/null @@ -1,132 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -// This file was modified from boost http example -// -// reply.hpp -// ~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -#pragma once - -#include "core/sstring.hh" -#include -#include "http/mime_types.hh" - -namespace httpd { -/** - * A reply to be sent to a client. - */ -struct reply { - /** - * The status of the reply. - */ - enum class status_type { - ok = 200, //!< ok - created = 201, //!< created - accepted = 202, //!< accepted - no_content = 204, //!< no_content - multiple_choices = 300, //!< multiple_choices - moved_permanently = 301, //!< moved_permanently - moved_temporarily = 302, //!< moved_temporarily - not_modified = 304, //!< not_modified - bad_request = 400, //!< bad_request - unauthorized = 401, //!< unauthorized - forbidden = 403, //!< forbidden - not_found = 404, //!< not_found - internal_server_error = 500, //!< internal_server_error - not_implemented = 501, //!< not_implemented - bad_gateway = 502, //!< bad_gateway - service_unavailable = 503 //!< service_unavailable - } _status; - - /** - * The headers to be included in the reply. - */ - std::unordered_map _headers; - - sstring _version; - /** - * The content to be sent in the reply. - */ - sstring _content; - - sstring _response_line; - reply() - : _status(status_type::ok) { - } - - reply& add_header(const sstring& h, const sstring& value) { - _headers[h] = value; - return *this; - } - - reply& set_version(const sstring& version) { - _version = version; - return *this; - } - - reply& set_status(status_type status, const sstring& content = "") { - _status = status; - if (content != "") { - _content = content; - } - return *this; - } - - /** - * Set the content type mime type. - * Used when the mime type is known. - * For most cases, use the set_content_type - */ - reply& set_mime_type(const sstring& mime) { - _headers["Content-Type"] = mime; - return *this; - } - - /** - * Set the content type mime type according to the file extension - * that would have been used if it was a file: e.g. html, txt, json etc' - */ - reply& set_content_type(const sstring& content_type = "html") { - set_mime_type(httpd::mime_types::extension_to_type(content_type)); - return *this; - } - - reply& done(const sstring& content_type) { - return set_content_type(content_type).done(); - } - /** - * Done should be called before using the reply. - * It would set the response line - */ - reply& done() { - _response_line = response_line(); - return *this; - } - sstring response_line(); -}; - -} // namespace httpd diff --git a/http/request.hh b/http/request.hh deleted file mode 100644 index 2c3417f915..0000000000 --- a/http/request.hh +++ /dev/null @@ -1,119 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -// -// request.hpp -// ~~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -#ifndef HTTP_REQUEST_HPP -#define HTTP_REQUEST_HPP - -#include "core/sstring.hh" -#include -#include -#include -#include "common.hh" - -namespace httpd { -class connection; - -/** - * A request received from a client. - */ -struct request { - enum class ctclass - : char { - other, multipart, app_x_www_urlencoded, - }; - - sstring _method; - sstring _url; - sstring _version; - int http_version_major; - int http_version_minor; - ctclass content_type_class; - size_t content_length = 0; - std::unordered_map _headers; - std::unordered_map query_parameters; - connection* connection_ptr; - parameters param; - sstring content; - sstring protocol_name; - - /** - * Search for the first header of a given name - * @param name the header name - * @return a pointer to the header value, if it exists or empty string - */ - sstring get_header(const sstring& name) const { - auto res = _headers.find(name); - if (res == _headers.end()) { - return ""; - } - return res->second; - } - - /** - * Search for the first header of a given name - * @param name the header name - * @return a pointer to the header value, if it exists or empty string - */ - sstring get_query_param(const sstring& name) const { - auto res = query_parameters.find(name); - if (res == query_parameters.end()) { - return ""; - } - return res->second; - } - - /** - * Get the request protocol name. Can be either "http" or "https". - */ - sstring get_protocol_name() const { - return "http"; - } - - /** - * Get the request url. - * @return the request url - */ - sstring get_url() const { - return get_protocol_name() + "://" + get_header("Host") + _url; - } - - bool is_multi_part() const { - return content_type_class == ctclass::multipart; - } - - bool is_form_post() const { - return content_type_class == ctclass::app_x_www_urlencoded; - } - -}; - -} // namespace httpd - -#endif // HTTP_REQUEST_HPP diff --git a/http/request_parser.rl b/http/request_parser.rl deleted file mode 100644 index b06c82b65b..0000000000 --- a/http/request_parser.rl +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#pragma once - -#include "core/ragel.hh" -#include -#include -#include "http/request.hh" - -using namespace httpd; - -%% machine request; - -%%{ - -access _fsm_; - -action mark { - g.mark_start(p); -} - -action store_method { - _req->_method = str(); -} - -action store_uri { - _req->_url = str(); -} - -action store_version { - _req->_version = str(); -} - -action store_field_name { - _field_name = str(); -} - -action store_value { - _value = str(); -} - -action assign_field { - _req->_headers[_field_name] = std::move(_value); -} - -action extend_field { - _req->_headers[_field_name] += sstring(" ") + std::move(_value); -} - -action done { - done = true; - fbreak; -} - -crlf = '\r\n'; -tchar = alpha | digit | '-' | '!' | '#' | '$' | '%' | '&' | '\'' | '*' - | '+' | '.' | '^' | '_' | '`' | '|' | '~'; - -sp = ' '; -ht = '\t'; - -sp_ht = sp | ht; - -op_char = upper; - -operation = op_char+ >mark %store_method; -uri = (any - sp)+ >mark %store_uri; -http_version = 'HTTP/' (digit '.' digit) >mark %store_version; - -field = tchar+ >mark %store_field_name; -value = any* >mark %store_value; -start_line = ((operation sp uri sp http_version) -- crlf) crlf; -header_1st = (field sp_ht* ':' sp_ht* value :> crlf) %assign_field; -header_cont = (sp_ht+ value sp_ht* crlf) %extend_field; -header = header_1st header_cont*; -main := start_line header* :> (crlf @done); - -}%% - -class http_request_parser : public ragel_parser_base { - %% write data nofinal noprefix; -public: - enum class state { - error, - eof, - done, - }; - std::unique_ptr _req; - sstring _field_name; - sstring _value; - state _state; -public: - void init() { - init_base(); - _req.reset(new httpd::request()); - _state = state::eof; - %% write init; - } - char* parse(char* p, char* pe, char* eof) { - sstring_builder::guard g(_builder, p, pe); - auto str = [this, &g, &p] { g.mark_end(p); return get_str(); }; - bool done = false; - if (p != pe) { - _state = state::error; - } - %% write exec; - if (!done) { - p = nullptr; - } else { - _state = state::done; - } - return p; - } - auto get_parsed_request() { - return std::move(_req); - } - bool eof() const { - return _state == state::eof; - } -}; diff --git a/http/routes.cc b/http/routes.cc deleted file mode 100644 index 1d7d3e179c..0000000000 --- a/http/routes.cc +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "routes.hh" -#include "reply.hh" -#include "exception.hh" - -namespace httpd { - -using namespace std; - -void verify_param(const request& req, const sstring& param) { - if (req.get_query_param(param) == "") { - throw missing_param_exception(param); - } -} - -routes::~routes() { - for (int i = 0; i < NUM_OPERATION; i++) { - for (auto kv : _map[i]) { - delete kv.second; - } - } - for (int i = 0; i < NUM_OPERATION; i++) { - for (auto r : _rules[i]) { - delete r; - } - } - -} - -future > routes::handle(const sstring& path, std::unique_ptr req, std::unique_ptr rep) { - handler_base* handler = get_handler(str2type(req->_method), - normalize_url(path), req->param); - if (handler != nullptr) { - try { - for (auto& i : handler->_mandatory_param) { - verify_param(*req.get(), i); - } - auto r = handler->handle(path, std::move(req), std::move(rep)); - return r; - } catch (const redirect_exception& _e) { - rep.reset(new reply()); - rep->add_header("Location", _e.url).set_status(_e.status()).done( - "json"); - - } catch (const base_exception& _e) { - rep.reset(new reply()); - json_exception e(_e); - rep->set_status(_e.status(), e.to_json()).done("json"); - } catch (exception& _e) { - rep.reset(new reply()); - json_exception e(_e); - cerr << "exception was caught for " << path << ": " << _e.what() - << endl; - rep->set_status(reply::status_type::internal_server_error, - e.to_json()).done("json"); - } - } else { - rep.reset(new reply()); - json_exception ex(not_found_exception("Not found")); - rep->set_status(reply::status_type::not_found, ex.to_json()).done( - "json"); - } - cerr << "Failed with " << path << " " << rep->_content << endl; - return make_ready_future>(std::move(rep)); -} - -sstring routes::normalize_url(const sstring& url) { - if (url.length() < 2 || url.at(url.length() - 1) != '/') { - return url; - } - return url.substr(0, url.length() - 1); -} - -handler_base* routes::get_handler(operation_type type, const sstring& url, - parameters& params) { - handler_base* handler = get_exact_match(type, url); - if (handler != nullptr) { - return handler; - } - - for (auto rule = _rules[type].cbegin(); rule != _rules[type].cend(); - ++rule) { - handler = (*rule)->get(url, params); - if (handler != nullptr) { - return handler; - } - params.clear(); - } - return nullptr; -} - -routes& routes::add(operation_type type, const url& url, - handler_base* handler) { - match_rule* rule = new match_rule(handler); - rule->add_str(url._path); - if (url._param != "") { - rule->add_param(url._param, true); - } - return add(rule, type); -} - -} diff --git a/http/routes.hh b/http/routes.hh deleted file mode 100644 index 8d1541aa2e..0000000000 --- a/http/routes.hh +++ /dev/null @@ -1,178 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef ROUTES_HH_ -#define ROUTES_HH_ - -#include "matchrules.hh" -#include "handlers.hh" -#include "common.hh" -#include "reply.hh" - -#include -#include -#include -#include "core/future-util.hh" - -namespace httpd { - -/** - * The url helps defining a route. - */ -class url { -public: - /** - * Move constructor - */ - url(url&&) = default; - - /** - * Construct with a url path as it's parameter - * @param path the url path to be used - */ - url(const sstring& path) - : _path(path) { - } - - /** - * Adds a parameter that matches untill the end of the URL. - * @param param the parmaeter name - * @return the current url - */ - url& remainder(const sstring& param) { - this->_param = param; - return *this; - } - - sstring _path; - sstring _param; -}; - -/** - * routes object do the request dispatching according to the url. - * It uses two decision mechanism exact match, if a url matches exactly - * (an optional leading slash is permitted) it is choosen - * If not, the matching rules are used. - * matching rules are evaluated by their insertion order - */ -class routes { -public: - /** - * The destructor deletes the match rules and handlers - */ - ~routes(); - - /** - * adding a handler as an exact match - * @param url the url to match (note that url should start with /) - * @param handler the desire handler - * @return it self - */ - routes& put(operation_type type, const sstring& url, - handler_base* handler) { - //FIXME if a handler is already exists, it need to be - // deleted to prevent memory leak - _map[type][url] = handler; - return *this; - } - - /** - * add a rule to be used. - * rules are search only if an exact match was not found. - * rules are search by the order they were added. - * First in higher priority - * @param rule a rule to add - * @param type the operation type - * @return it self - */ - routes& add(match_rule* rule, operation_type type = GET) { - _rules[type].push_back(rule); - return *this; - } - - /** - * Add a url match to a handler: - * Example routes.add(GET, url("/api").remainder("path"), handler); - * @param type - * @param url - * @param handler - * @return - */ - routes& add(operation_type type, const url& url, handler_base* handler); - - /** - * the main entry point. - * the general handler calls this method with the request - * the method takes the headers from the request and find the - * right handler. - * It then call the handler with the parameters (if they exists) found in the url - * @param path the url path found - * @param req the http request - * @param rep the http reply - */ - future > handle(const sstring& path, std::unique_ptr req, std::unique_ptr rep); - - /** - * Search and return an exact match - * @param url the request url - * @return the handler if exists or nullptr if it does not - */ - handler_base* get_exact_match(operation_type type, const sstring& url) { - return (_map[type].find(url) == _map[type].end()) ? - nullptr : _map[type][url]; - } - -private: - - /** - * Search and return a handler by the operation type and url - * @param type the http operation type - * @param url the request url - * @param params a parameter object that will be filled during the match - * @return a handler based on the type/url match - */ - handler_base* get_handler(operation_type type, const sstring& url, - parameters& params); - - /** - * Normalize the url to remove the last / if exists - * and get the parameter part - * @param url the full url path - * @param param_part will hold the string with the parameters - * @return the url from the request without the last / - */ - sstring normalize_url(const sstring& url); - - std::unordered_map _map[NUM_OPERATION]; - std::vector _rules[NUM_OPERATION]; -}; - -/** - * A helper function that check if a parameter is found in the params object - * if it does not the function would throw a parameter not found exception - * @param params the parameters object - * @param param the parameter to look for - */ -void verify_param(const httpd::request& req, const sstring& param); - -} - -#endif /* ROUTES_HH_ */ diff --git a/http/transformers.cc b/http/transformers.cc deleted file mode 100644 index a08991a6da..0000000000 --- a/http/transformers.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include -#include "transformers.hh" -#include - -namespace httpd { - -using namespace std; - -void content_replace::transform(sstring& content, const request& req, - const sstring& extension) { - sstring host = req.get_header("Host"); - if (host == "" || (this->extension != "" && extension != this->extension)) { - return; - } - boost::replace_all(content, "{{Host}}", host); - boost::replace_all(content, "{{Protocol}}", req.get_protocol_name()); -} - -} diff --git a/http/transformers.hh b/http/transformers.hh deleted file mode 100644 index 52a3488c4b..0000000000 --- a/http/transformers.hh +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef TRANSFORMERS_HH_ -#define TRANSFORMERS_HH_ - -#include "handlers.hh" -#include "file_handler.hh" - -namespace httpd { - -/** - * content_replace replaces variable in a file with a dynamic value. - * It would take the host from request and will replace the variable - * in a file - * - * The replacement can be restricted to an extension. - * - * We are currently support only one file type for replacement. - * It could be extend if we will need it - * - */ -class content_replace : public file_transformer { -public: - virtual void transform(sstring& content, const request& req, - const sstring& extension) override; - /** - * the constructor get the file extension the replace would work on. - * @param extension file extension, when not set all files extension - */ - explicit content_replace(const sstring& extension = "") - : extension(extension) { - } -private: - sstring extension; -}; - -} -#endif /* TRANSFORMERS_HH_ */ diff --git a/json/formatter.cc b/json/formatter.cc deleted file mode 100644 index 6efd3f7580..0000000000 --- a/json/formatter.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "formatter.hh" -#include "json_elements.hh" - -using namespace std; - -namespace json { - -sstring formatter::to_json(const sstring& str) { - return to_json(str.c_str()); -} - -sstring formatter::to_json(const char* str) { - sstring res = "\""; - res += str; - res += "\""; - return res; -} - -sstring formatter::to_json(int n) { - return to_string(n); -} - -sstring formatter::to_json(long n) { - return to_string(n); -} - -sstring formatter::to_json(float f) { - if (std::isinf(f)) { - throw out_of_range("Infinite float value is not supported"); - } else if (std::isnan(f)) { - throw invalid_argument("Invalid float value"); - } - return to_sstring(f); -} - -sstring formatter::to_json(double d) { - if (std::isinf(d)) { - throw out_of_range("Infinite double value is not supported"); - } else if (std::isnan(d)) { - throw invalid_argument("Invalid double value"); - } - return to_sstring(d); -} - -sstring formatter::to_json(bool b) { - return (b) ? "true" : "false"; -} - -sstring formatter::to_json(const date_time& d) { - char buff[50]; - sstring res = "\""; - strftime(buff, 50, TIME_FORMAT, &d); - res += buff; - return res + "\""; -} - -sstring formatter::to_json(const jsonable& obj) { - return obj.to_json(); -} - -sstring formatter::to_json(unsigned long l) { - return to_string(l); -} - -} diff --git a/json/formatter.hh b/json/formatter.hh deleted file mode 100644 index 3463adf4e7..0000000000 --- a/json/formatter.hh +++ /dev/null @@ -1,144 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef FORMATTER_HH_ -#define FORMATTER_HH_ - -#include -#include -#include -#include -#include "core/sstring.hh" - -namespace json { - -struct jsonable; - -typedef struct tm date_time; - -/** - * The formatter prints json values in a json format - * it overload to_json method for each of the supported format - * all to_json parameters are passed as a pointer - */ -class formatter { -public: - - /** - * return a json formated string - * @param str the string to format - * @return the given string in a json format - */ - static sstring to_json(const sstring& str); - - /** - * return a json formated int - * @param n the int to format - * @return the given int in a json format - */ - static sstring to_json(int n); - - /** - * return a json formated long - * @param n the long to format - * @return the given long in a json format - */ - static sstring to_json(long n); - - /** - * return a json formated float - * @param n the float to format - * @return the given float in a json format - */ - static sstring to_json(float f); - - /** - * return a json formated double - * @param d the double to format - * @return the given double in a json format - */ - static sstring to_json(double d); - - /** - * return a json formated char* (treated as string) - * @param str the char* to foramt - * @return the given char* in a json foramt - */ - static sstring to_json(const char* str); - - /** - * return a json formated bool - * @param d the bool to format - * @return the given bool in a json format - */ - static sstring to_json(bool d); - - /** - * return a json formated list of a given vector of params - * @param vec the vector to format - * @return the given vector in a json format - */ - template - static sstring to_json(const std::vector& vec) { - std::stringstream res; - res << "["; - bool first = true; - for (auto i : vec) { - if (first) { - first = false; - } else { - res << ","; - } - res << to_json(i); - } - res << "]"; - return res.str(); - } - - /** - * return a json formated date_time - * @param d the date_time to format - * @return the given date_time in a json format - */ - static sstring to_json(const date_time& d); - - /** - * return a json formated json object - * @param obj the date_time to format - * @return the given json object in a json format - */ - static sstring to_json(const jsonable& obj); - - /** - * return a json formated unsigned long - * @param l unsigned long to format - * @return the given unsigned long in a json format - */ - static sstring to_json(unsigned long l); - -private: - - static constexpr const char* TIME_FORMAT = "%a %b %d %I:%M:%S %Z %Y"; - -}; - -} -#endif /* FORMATTER_HH_ */ diff --git a/json/json2code.py b/json/json2code.py deleted file mode 100755 index 0dbbfb36b9..0000000000 --- a/json/json2code.py +++ /dev/null @@ -1,444 +0,0 @@ -#!/usr/bin/env python -import json -import sys -import re -import glob -import argparse -import os - -parser = argparse.ArgumentParser(description="""Generate C++ class for json -handling from swagger definition""") - -parser.add_argument('--outdir', help='the output directory', default='autogen') -parser.add_argument('-o', help='Output file', default='') -parser.add_argument('-f', help='input file', default='api-java.json') -parser.add_argument('-ns', help="""namespace when set struct will be created -under the namespace""", default='') -parser.add_argument('-jsoninc', help='relative path to the jsaon include', - default='json/') -parser.add_argument('-jsonns', help='set the json namespace', default='json') -parser.add_argument('-indir', help="""when set all json file in the given -directory will be parsed, do not use with -f""", default='') -parser.add_argument('-debug', help='debug level 0 -quite,1-error,2-verbose', - default='1', type=int) -parser.add_argument('-combined', help='set the name of the combined file', - default='autogen/pathautogen.ee') -config = parser.parse_args() - - -valid_vars = {'string': 'sstring', 'int': 'int', 'double': 'double', - 'float': 'float', 'long': 'long', 'boolean': 'bool', 'char': 'char', - 'datetime': 'json::date_time'} - -current_file = '' - -spacing = " " -def getitem(d, key, name): - if key in d: - return d[key] - else: - raise Exception("'" + key + "' not found in " + name) - -def fprint(f, *args): - for arg in args: - f.write(arg) - -def fprintln(f, *args): - for arg in args: - f.write(arg) - f.write('\n') - - -def open_namespace(f, ns=config.ns): - fprintln(f, "namespace ", ns , ' {\n') - - -def close_namespace(f): - fprintln(f, '}') - - -def add_include(f, includes): - for include in includes: - fprintln(f, '#include ', include) - fprintln(f, "") - -def trace_verbose(*params): - if config.debug > 1: - print(''.join(params)) - - -def trace_err(*params): - if config.debug > 0: - print(current_file + ':' + ''.join(params)) - - -def valid_type(param): - if param in valid_vars: - return valid_vars[param] - trace_err("Type [", param, "] not defined") - return param - - -def type_change(param, member): - if param == "array": - if "items" not in member: - trace_err("array without item declaration in ", param) - return "" - item = member["items"] - if "type" in item: - t = item["type"] - elif "$ref" in item: - t = item["$ref"] - else: - trace_err("array items with no type or ref declaration ", param) - return "" - return "json_list< " + valid_type(t) + " >" - return "json_element< " + valid_type(param) + " >" - - - -def print_ind_comment(f, ind, *params): - fprintln(f, ind, "/**") - for s in params: - fprintln(f, ind, " * ", s) - fprintln(f, ind, " */") - -def print_comment(f, *params): - print_ind_comment(f, spacing, *params) - -def print_copyrights(f): - fprintln(f, "/*") - fprintln(f, "* Copyright (C) 2014 Cloudius Systems, Ltd.") - fprintln(f, "*") - fprintln(f, "* This work is open source software, licensed under the", - " terms of the") - fprintln(f, "* BSD license as described in the LICENSE f in the top-", - "level directory.") - fprintln(f, "*") - fprintln(f, "* This is an Auto-Generated-code ") - fprintln(f, "* Changes you do in this file will be erased on next", - " code generation") - fprintln(f, "*/\n") - - -def print_h_file_headers(f, name): - print_copyrights(f) - fprintln(f, "#ifndef __JSON_AUTO_GENERATED_" + name) - fprintln(f, "#define __JSON_AUTO_GENERATED_" + name + "\n") - - -def clean_param(param): - match = re.match(r"(^[^\}]+)\s*}", param) - if match: - return match.group(1) - return param - - -def get_parameter_by_name(obj, name): - for p in obj["parameters"]: - if p["name"] == name: - return p - trace_err ("No Parameter declaration found for ", name) - - -def clear_path_ending(path): - if not path or path[-1] != '/': - return path - return path[0:-1] - - -def add_path(f, path, details): - if "summary" in details: - print_comment(f, details["summary"]) - - if "{" in path: - vals = path.split("{") - vals.reverse() - fprintln(f, spacing, 'path_description::add_path("', clear_path_ending(vals.pop()), - '",', details["method"], ',"', details["nickname"], '")') - while vals: - param = clean_param(vals.pop()) - param_type = get_parameter_by_name(details, param) - if ("allowMultiple" in param_type and - param_type["allowMultiple"] == True): - fprintln(f, spacing, ' ->pushparam("', param, '",true)') - else: - fprintln(f, spacing, ' ->pushparam("', param, '")') - else: - fprintln(f, spacing, 'path_description::add_path("', clear_path_ending(path), '",', - details["method"], ',"', details["nickname"], '")') - if "parameters" in details: - for param in details["parameters"]: - if "required" in param and param["required"] and param["paramType"] == "query": - fprintln(f, spacing, ' ->pushmandatory_param("', param["name"], '")') - fprintln(f, spacing, ";") - - -def get_base_name(param): - return os.path.basename(param) - - -def is_model_valid(name, model): - if name in valid_vars: - return "" - properties = getitem(model[name], "properties", name) - for var in properties: - type = getitem(properties[var], "type", name + ":" + var) - if type == "array": - type = getitem(getitem(properties[var], "items", name + ":" + var), "type", name + ":" + var + ":items") - if type not in valid_vars: - if type not in model: - raise Exception("Unknown type '" + type + "' in Model '" + name + "'") - return type - valid_vars[name] = name - return "" - -def resolve_model_order(data): - res = [] - models = set() - for model_name in data: - visited = set(model_name) - missing = is_model_valid(model_name, data) - resolved = missing == '' - if not resolved: - stack = [model_name] - while not resolved: - if missing in visited: - raise Exception("Cyclic dependency found: " + missing) - missing_depends = is_model_valid(missing, data) - if missing_depends == '': - if missing not in models: - res.append(missing) - models.add(missing) - resolved = len(stack) == 0 - if not resolved: - missing = stack.pop() - else: - stack.append(missing) - missing = missing_depends - elif model_name not in models: - res.append(model_name) - models.add(model_name) - return res - -def create_h_file(data, hfile_name, api_name, init_method, base_api): - if config.o != '': - hfile = open(config.o, "w") - else: - hfile = open(config.outdir + "/" + hfile_name, "w") - print_h_file_headers(hfile, api_name) - add_include(hfile, ['"core/sstring.hh"', '"' + config.jsoninc + - 'json_elements.hh"', '"http/json_path.hh"']) - - add_include(hfile, ['', '']) - open_namespace(hfile, "httpd") - open_namespace(hfile, api_name) - - if "models" in data: - models_order = resolve_model_order(data["models"]) - for model_name in models_order: - model = data["models"][model_name] - if 'description' in model: - print_ind_comment(hfile, "", model["description"]) - fprintln(hfile, "struct ", model_name, " : public json::json_base {") - member_init = '' - member_assignment = '' - member_copy = '' - for member_name in model["properties"]: - member = model["properties"][member_name] - if "description" in member: - print_comment(hfile, member["description"]) - if "enum" in member: - enum_name = model_name + "_" + member_name - fprintln(hfile, " enum class ", enum_name, " {") - for enum_entry in member["enum"]: - fprintln(hfile, " ", enum_entry, ", ") - fprintln(hfile, "NUM_ITEMS};") - wrapper = member_name + "_wrapper" - fprintln(hfile, " struct ", wrapper, " : public jsonable {") - fprintln(hfile, " ", wrapper, "() = default;") - fprintln(hfile, " virtual std::string to_json() const {") - fprintln(hfile, " switch(v) {") - for enum_entry in member["enum"]: - fprintln(hfile, " case ", enum_name, "::", enum_entry, ": return \"\\\"", enum_entry, "\\\"\";") - fprintln(hfile, " default: return \"Unknown\";") - fprintln(hfile, " }") - fprintln(hfile, " }") - fprintln(hfile, " template") - fprintln(hfile, " ", wrapper, "(const T& _v) {") - fprintln(hfile, " switch(_v) {") - for enum_entry in member["enum"]: - fprintln(hfile, " case T::", enum_entry, ": v = ", enum_name, "::", enum_entry, "; break;") - fprintln(hfile, " default: v = ", enum_name, "::NUM_ITEMS;") - fprintln(hfile, " }") - fprintln(hfile, " }") - fprintln(hfile, " template") - fprintln(hfile, " operator T() const {") - fprintln(hfile, " switch(v) {") - for enum_entry in member["enum"]: - fprintln(hfile, " case ", enum_name, "::", enum_entry, ": return T::", enum_entry, ";") - fprintln(hfile, " default: return T::", member["enum"][0], ";") - fprintln(hfile, " }") - fprintln(hfile, " }") - fprintln(hfile, " typedef typename std::underlying_type<", enum_name, ">::type pos_type;") - fprintln(hfile, " ", wrapper,"& operator++() {") - fprintln(hfile, " v = static_cast<", enum_name,">(static_cast(v) + 1);") - fprintln(hfile, " return *this;") - fprintln(hfile, " }") - fprintln(hfile, " ", wrapper, "& operator++(int) {") - fprintln(hfile, " return ++(*this);") - fprintln(hfile, " }") - fprintln(hfile, " bool operator==(const ", wrapper, "& c) const {") - fprintln(hfile, " return v == c.v;") - fprintln(hfile, " }") - fprintln(hfile, " bool operator!=(const ", wrapper, "& c) const {") - fprintln(hfile, " return v != c.v;") - fprintln(hfile, " }") - fprintln(hfile, " bool operator<=(const ", wrapper, "& c) const {") - fprintln(hfile, " return static_cast(v) <= static_cast(c.v);") - fprintln(hfile, " }") - fprintln(hfile, " static ", wrapper, " begin() {") - fprintln(hfile, " return ", wrapper, "(", enum_name, "::", member["enum"][0], ");") - fprintln(hfile, " }") - fprintln(hfile, " static ", wrapper, " end() {") - fprintln(hfile, " return ", wrapper, "(", enum_name, "::NUM_ITEMS);") - fprintln(hfile, " }") - fprintln(hfile, " static boost::integer_range<", wrapper, "> all_items() {") - fprintln(hfile, " return boost::irange(begin(), end());") - fprintln(hfile, " }") - fprintln(hfile, " ", enum_name, " v;") - fprintln(hfile, " };") - fprintln(hfile, " ", config.jsonns, "::json_element<", - member_name, "_wrapper> ", - member_name, ";\n") - else: - fprintln(hfile, " ", config.jsonns, "::", - type_change(member["type"], member), " ", - member_name, ";\n") - member_init += " add(&" + member_name + ',"' - member_init += member_name + '");\n' - member_assignment += " " + member_name + " = " + "e." + member_name + ";\n" - member_copy += " e." + member_name + " = " + member_name + ";\n" - fprintln(hfile, "void register_params() {") - fprintln(hfile, member_init) - fprintln(hfile, '}') - - fprintln(hfile, model_name, '() {') - fprintln(hfile, ' register_params();') - fprintln(hfile, '}') - fprintln(hfile, model_name, '(const ' + model_name + ' & e) {') - fprintln(hfile, ' register_params();') - fprintln(hfile, member_assignment) - fprintln(hfile, '}') - fprintln(hfile, "template") - fprintln(hfile, model_name, "& operator=(const ", "T& e) {") - fprintln(hfile, member_assignment) - fprintln(hfile, " return *this;") - fprintln(hfile, "}") - fprintln(hfile, model_name, "& operator=(const ", model_name, "& e) {") - fprintln(hfile, member_assignment) - fprintln(hfile, " return *this;") - fprintln(hfile, "}") - fprintln(hfile, "template") - fprintln(hfile, model_name, "& update(T& e) {") - fprintln(hfile, member_copy) - fprintln(hfile, " return *this;") - fprintln(hfile, "}") - fprintln(hfile, "};\n\n") - - # print_ind_comment(hfile, "", "Initialize the path") -# fprintln(hfile, init_method + "(const std::string& description);") - fprintln(hfile, 'static const sstring name = "', base_api, '";') - for item in data["apis"]: - path = item["path"] - if "operations" in item: - for oper in item["operations"]: - if "summary" in oper: - print_comment(hfile, oper["summary"]) - vals = path.split("{") - vals.reverse() - - fprintln(hfile, 'static const path_description ', getitem(oper, "nickname", oper), '("', clear_path_ending(vals.pop()), - '",', oper["method"], ',"', oper["nickname"], '",') - fprint(hfile, '{') - first = True - while vals: - path_param = clean_param(vals.pop()) - path_param_type = get_parameter_by_name(oper, path_param) - if first == True: - first = False - else: - fprint(hfile, "\n,") - if ("allowMultiple" in path_param_type and - path_param_type["allowMultiple"] == True): - fprint(hfile, '{', '"', path_param , '", true', '}') - else: - fprint(hfile, '{', '"', path_param , '", false', '}') - fprint(hfile, '}') - fprint(hfile, ',{') - first = True - if "parameters" in oper: - enum_definitions = "" - for param in oper["parameters"]: - if "required" in param and param["required"] and param["paramType"] == "query": - if first == True: - first = False - else: - fprint(hfile, "\n,") - fprint(hfile, '"', param["name"], '"') - if "enum" in param: - enum_definitions = enum_definitions + 'namespace ns_' + oper["nickname"] + '{\n' - enm = param["name"] - enum_definitions = enum_definitions + 'enum class ' + enm + ' {' - for val in param["enum"]: - enum_definitions = enum_definitions + val + ", " - enum_definitions = enum_definitions + 'NUM_ITEMS};\n' - enum_definitions = enum_definitions + enm + ' str2' + enm + '(const sstring& str) {\n' - enum_definitions = enum_definitions + ' static const sstring arr[] = {"' + '","'.join(param["enum"]) + '"};\n' - enum_definitions = enum_definitions + ' int i;\n' - enum_definitions = enum_definitions + ' for (i=0; i < ' + str(len(param["enum"])) + '; i++) {\n' - enum_definitions = enum_definitions + ' if (arr[i] == str) {return (' + enm + ')i;}\n}\n' - enum_definitions = enum_definitions + ' return (' + enm + ')i;\n' - enum_definitions = enum_definitions + '}\n}\n' - - fprintln(hfile, '});') - fprintln(hfile, enum_definitions) - - close_namespace(hfile) - close_namespace(hfile) - hfile.write("#endif //__JSON_AUTO_GENERATED_HEADERS\n") - hfile.close() - -def parse_file(param, combined): - global current_file - trace_verbose("parsing ", param, " file") - try: - json_data = open(param) - data = json.load(json_data) - json_data.close() - except: - type, value, tb = sys.exc_info() - print("Bad formatted JSON file '" + param + "' error ", value.message) - sys.exit(-1) - try: - base_file_name = get_base_name(param) - current_file = base_file_name - hfile_name = base_file_name + ".hh" - api_name = base_file_name.replace('.', '_') - base_api = base_file_name.replace('.json', '') - init_method = "void " + api_name + "_init_path" - trace_verbose("creating ", hfile_name) - if (combined): - fprintln(combined, '#include "', base_file_name, ".cc", '"') - create_h_file(data, hfile_name, api_name, init_method, base_api) - except: - type, value, tb = sys.exc_info() - print("Error while parsing JSON file '" + param + "' error ", value.message) - sys.exit(-1) - -if "indir" in config and config.indir != '': - combined = open(config.combined, "w") - for f in glob.glob(os.path.join(config.indir, "*.json")): - parse_file(f, combined) -else: - parse_file(config.f, None) diff --git a/json/json_elements.cc b/json/json_elements.cc deleted file mode 100644 index 2274f78d3d..0000000000 --- a/json/json_elements.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#include "json_elements.hh" -#include -#include -#include -#include - -using namespace std; - -namespace json { - -/** - * The json builder is a helper class - * To help create a json object - * - */ -class json_builder { -public: - json_builder() - : first(true) { - result << OPEN; - } - - /** - * add a name value to an object - * @param name the name of the element - * @param str the value already formated - */ - void add(const string& name, const string& str) { - if (first) { - first = false; - } else { - result << ", "; - } - result << '"' << name << "\": " << str; - } - - /** - * add a json element to the an object - * @param element - */ - void add(json_base_element* element) { - if (element == nullptr || element->_set == false) { - return; - } - add(element->_name, element->to_string()); - } - - /** - * Get the string representation of the object - * @return a string of accumulative object - */ - string as_json() { - result << CLOSE; - return result.str(); - } - -private: - static const string OPEN; - static const string CLOSE; - - stringstream result; - bool first; - -}; - -const string json_builder::OPEN("{"); -const string json_builder::CLOSE("}"); - -void json_base::add(json_base_element* element, string name, bool mandatory) { - element->_mandatory = mandatory; - element->_name = name; - _elements.push_back(element); -} - -string json_base::to_json() const { - json_builder res; - for (auto i : _elements) { - res.add(i); - } - return res.as_json(); -} - -bool json_base::is_verify() const { - for (auto i : _elements) { - if (!i->is_verify()) { - return false; - } - } - return true; -} - -} diff --git a/json/json_elements.hh b/json/json_elements.hh deleted file mode 100644 index b6cda8925b..0000000000 --- a/json/json_elements.hh +++ /dev/null @@ -1,259 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2015 Cloudius Systems - */ - -#ifndef JSON_ELEMENTS_HH_ -#define JSON_ELEMENTS_HH_ - -#include -#include -#include -#include -#include "formatter.hh" -#include "core/sstring.hh" - -namespace json { - -/** - * The base class for all json element. - * Every json element has a name - * An indication if it was set or not - * And is this element is mandatory. - * When a mandatory element is not set - * this is not a valid object - */ -class json_base_element { -public: - /** - * The constructors - */ - json_base_element() - : _mandatory(false), _set(false) { - } - - virtual ~json_base_element() = default; - - /** - * Check if it's a mandatory parameter - * and if it's set. - * @return true if this is not a mandatory parameter - * or if it is and it's value is set - */ - virtual bool is_verify() { - return !(_mandatory && !_set); - } - - /** - * returns the internal value in a json format - * Each inherit class must implement this method - * @return formated internal value - */ - virtual std::string to_string() = 0; - - std::string _name; - bool _mandatory; - bool _set; -}; - -/** - * Basic json element instantiate - * the json_element template. - * it adds a value to the base definition - * and the to_string implementation using the formatter - */ -template -class json_element : public json_base_element { -public: - - /** - * the assignment operator also set - * the set value to true. - * @param new_value the new value - * @return the value itself - */ - json_element &operator=(const T& new_value) { - _value = new_value; - _set = true; - return *this; - } - /** - * the assignment operator also set - * the set value to true. - * @param new_value the new value - * @return the value itself - */ - template - json_element &operator=(const C& new_value) { - _value = new_value; - _set = true; - return *this; - } - /** - * The brackets operator - * @return the value - */ - const T& operator()() const { - return _value; - } - - /** - * The to_string return the value - * formated as a json value - * @return the value foramted for json - */ - virtual std::string to_string() override - { - return formatter::to_json(_value); - } - -private: - T _value; -}; - -/** - * json_list is based on std vector implementation. - * - * When values are added with push it is set the "set" flag to true - * hence will be included in the parsed object - */ -template -class json_list : public json_base_element { -public: - - /** - * Add an element to the list. - * @param element a new element that will be added to the list - */ - void push(const T& element) { - _set = true; - _elements.push_back(element); - } - - virtual std::string to_string() override - { - return formatter::to_json(_elements); - } - - /** - * Assignment can be done from any object that support const range - * iteration and that it's elements can be assigned to the list elements - */ - template - json_list& operator=(const C& list) { - _elements.clear(); - for (auto i : list) { - push(i); - } - return *this; - } - - std::vector _elements; -}; - -class jsonable { -public: - virtual ~jsonable() = default; - /** - * create a foramted string of the object. - * @return the object formated. - */ - virtual std::string to_json() const = 0; -}; - -/** - * The base class for all json objects - * It holds a list of all the element in it, - * allowing it implement the to_json method. - * - * It also allows iterating over the element - * in the object, even if not all the member - * are known in advance and in practice mimic - * reflection - */ -struct json_base : public jsonable { - - virtual ~json_base() = default; - - json_base() = default; - - json_base(const json_base&) = delete; - - json_base operator=(const json_base&) = delete; - - /** - * create a foramted string of the object. - * @return the object formated. - */ - virtual std::string to_json() const; - - /** - * Check that all mandatory elements are set - * @return true if all mandatory parameters are set - */ - virtual bool is_verify() const; - - /** - * Register an element in an object - * @param element the element to be added - * @param name the element name - * @param mandatory is this element mandatory. - */ - virtual void add(json_base_element* element, std::string name, - bool mandatory = false); - - std::vector _elements; -}; - -/** - * There are cases where a json request needs to return a successful - * empty reply. - * The json_void class will be used to mark that the reply should be empty. - * - */ -struct json_void : public jsonable{ - virtual std::string to_json() const { - return ""; - } -}; - - -/** - * The json return type, is a helper class to return a json - * formatted string. - * It uses autoboxing in its constructor so when a function return - * type is json_return_type, it could return a type that would be converted - * ie. - * json_return_type foo() { - * return "hello"; - * } - * - * would return a json formatted string: "hello" (rather then hello) - */ -struct json_return_type { - sstring _res; - template - json_return_type(const T& res) { - _res = formatter::to_json(res); - } -}; - -} - -#endif /* JSON_ELEMENTS_HH_ */ diff --git a/licenses/freebsd.txt b/licenses/freebsd.txt deleted file mode 100644 index 644acc97a1..0000000000 --- a/licenses/freebsd.txt +++ /dev/null @@ -1,20 +0,0 @@ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. diff --git a/net/api.hh b/net/api.hh deleted file mode 100644 index 9d5dc60881..0000000000 --- a/net/api.hh +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef NET_API_HH_ -#define NET_API_HH_ - -#include -#include -#include -#include "core/future.hh" -#include "net/byteorder.hh" -#include "net/packet.hh" -#include "core/print.hh" -#include "core/temporary_buffer.hh" -#include -#include -#include - -class socket_address { -public: - union { - ::sockaddr_storage sas; - ::sockaddr sa; - ::sockaddr_in in; - } u; - socket_address(sockaddr_in sa) { - u.in = sa; - } - socket_address() = default; - ::sockaddr& as_posix_sockaddr() { return u.sa; } - ::sockaddr_in& as_posix_sockaddr_in() { return u.in; } - const ::sockaddr& as_posix_sockaddr() const { return u.sa; } - const ::sockaddr_in& as_posix_sockaddr_in() const { return u.in; } -}; - -struct listen_options { - bool reuse_address = false; -}; - -struct ipv4_addr { - uint32_t ip; - uint16_t port; - - ipv4_addr() : ip(0), port(0) {} - ipv4_addr(uint32_t ip, uint16_t port) : ip(ip), port(port) {} - ipv4_addr(uint16_t port) : ip(0), port(port) {} - ipv4_addr(const std::string &addr); - ipv4_addr(const std::string &addr, uint16_t port); - - ipv4_addr(const socket_address &sa) { - ip = net::ntoh(sa.u.in.sin_addr.s_addr); - port = net::ntoh(sa.u.in.sin_port); - } - - ipv4_addr(socket_address &&sa) : ipv4_addr(sa) {} -}; - -static inline -bool is_ip_unspecified(ipv4_addr &addr) { - return addr.ip == 0; -} - -static inline -bool is_port_unspecified(ipv4_addr &addr) { - return addr.port == 0; -} - -static inline -std::ostream& operator<<(std::ostream &os, ipv4_addr addr) { - fprint(os, "%d.%d.%d.%d", - (addr.ip >> 24) & 0xff, - (addr.ip >> 16) & 0xff, - (addr.ip >> 8) & 0xff, - (addr.ip) & 0xff); - return os << ":" << addr.port; -} - -static inline -socket_address make_ipv4_address(ipv4_addr addr) { - socket_address sa; - sa.u.in.sin_family = AF_INET; - sa.u.in.sin_port = htons(addr.port); - sa.u.in.sin_addr.s_addr = htonl(addr.ip); - return sa; -} - -namespace net { - -class udp_datagram_impl { -public: - virtual ~udp_datagram_impl() {}; - virtual ipv4_addr get_src() = 0; - virtual ipv4_addr get_dst() = 0; - virtual uint16_t get_dst_port() = 0; - virtual packet& get_data() = 0; -}; - -class udp_datagram final { -private: - std::unique_ptr _impl; -public: - udp_datagram(std::unique_ptr&& impl) : _impl(std::move(impl)) {}; - ipv4_addr get_src() { return _impl->get_src(); } - ipv4_addr get_dst() { return _impl->get_dst(); } - uint16_t get_dst_port() { return _impl->get_dst_port(); } - packet& get_data() { return _impl->get_data(); } -}; - -class udp_channel_impl { -public: - virtual ~udp_channel_impl() {}; - virtual future receive() = 0; - virtual future<> send(ipv4_addr dst, const char* msg) = 0; - virtual future<> send(ipv4_addr dst, packet p) = 0; - virtual bool is_closed() const = 0; - virtual void close() = 0; -}; - -class udp_channel { -private: - std::unique_ptr _impl; -public: - udp_channel() {} - udp_channel(std::unique_ptr impl) : _impl(std::move(impl)) {} - future receive() { return _impl->receive(); } - future<> send(ipv4_addr dst, const char* msg) { return _impl->send(std::move(dst), msg); } - future<> send(ipv4_addr dst, packet p) { return _impl->send(std::move(dst), std::move(p)); } - bool is_closed() const { return _impl->is_closed(); } - void close() { return _impl->close(); } -}; - -} /* namespace net */ - -#endif diff --git a/net/arp.cc b/net/arp.cc deleted file mode 100644 index 93546bbc60..0000000000 --- a/net/arp.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "arp.hh" - -namespace net { - -arp_for_protocol::arp_for_protocol(arp& a, uint16_t proto_num) - : _arp(a), _proto_num(proto_num) { - _arp.add(proto_num, this); -} - -arp_for_protocol::~arp_for_protocol() { - _arp.del(_proto_num); -} - -arp::arp(interface* netif) : _netif(netif), _proto(netif, eth_protocol_num::arp, [this] { return get_packet(); }) - , _rx_packets(_proto.receive([this] (packet p, ethernet_address ea) { - return process_packet(std::move(p), ea); - }, - [this](forward_hash& out_hash_data, packet& p, size_t off) { - return forward(out_hash_data, p, off); - })) { -} - -std::experimental::optional arp::get_packet() { - std::experimental::optional p; - if (!_packetq.empty()) { - p = std::move(_packetq.front()); - _packetq.pop_front(); - } - return p; -} - -bool arp::forward(forward_hash& out_hash_data, packet& p, size_t off) { - auto ah = p.get_header(off); - auto i = _arp_for_protocol.find(ntoh(ah->ptype)); - if (i != _arp_for_protocol.end()) { - return i->second->forward(out_hash_data, p, off); - } - return false; -} - -void arp::add(uint16_t proto_num, arp_for_protocol* afp) { - _arp_for_protocol[proto_num] = afp; -} - -void arp::del(uint16_t proto_num) { - _arp_for_protocol.erase(proto_num); -} - -future<> -arp::process_packet(packet p, ethernet_address from) { - auto ah = ntoh(*p.get_header()); - auto i = _arp_for_protocol.find(ah.ptype); - if (i != _arp_for_protocol.end()) { - i->second->received(std::move(p)); - } - return make_ready_future<>(); -} - -} diff --git a/net/arp.hh b/net/arp.hh deleted file mode 100644 index bbd29e7b74..0000000000 --- a/net/arp.hh +++ /dev/null @@ -1,261 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - * - */ - -#ifndef ARP_HH_ -#define ARP_HH_ - -#include "net.hh" -#include "core/reactor.hh" -#include "byteorder.hh" -#include "ethernet.hh" -#include "core/print.hh" -#include - -namespace net { - -class arp; -class arp_for_protocol; -template -class arp_for; - -class arp_for_protocol { -protected: - arp& _arp; - uint16_t _proto_num; -public: - arp_for_protocol(arp& a, uint16_t proto_num); - virtual ~arp_for_protocol(); - virtual future<> received(packet p) = 0; - virtual bool forward(forward_hash& out_hash_data, packet& p, size_t off) { return false; } -}; - -class arp { - interface* _netif; - l3_protocol _proto; - subscription _rx_packets; - std::unordered_map _arp_for_protocol; - circular_buffer _packetq; -private: - struct arp_hdr { - packed htype; - packed ptype; - - template - void adjust_endianness(Adjuster a) { return a(htype, ptype); } - }; -public: - explicit arp(interface* netif); - void add(uint16_t proto_num, arp_for_protocol* afp); - void del(uint16_t proto_num); -private: - ethernet_address l2self() { return _netif->hw_address(); } - future<> process_packet(packet p, ethernet_address from); - bool forward(forward_hash& out_hash_data, packet& p, size_t off); - std::experimental::optional get_packet(); - template - friend class arp_for; -}; - -template -class arp_for : public arp_for_protocol { -public: - using l2addr = ethernet_address; - using l3addr = typename L3::address_type; -private: - static constexpr auto max_waiters = 512; - enum oper { - op_request = 1, - op_reply = 2, - }; - struct arp_hdr { - packed htype; - packed ptype; - uint8_t hlen; - uint8_t plen; - packed oper; - l2addr sender_hwaddr; - l3addr sender_paddr; - l2addr target_hwaddr; - l3addr target_paddr; - - template - void adjust_endianness(Adjuster a) { - a(htype, ptype, oper, sender_hwaddr, sender_paddr, target_hwaddr, target_paddr); - } - }; - struct resolution { - std::vector> _waiters; - timer<> _timeout_timer; - }; -private: - l3addr _l3self = L3::broadcast_address(); - std::unordered_map _table; - std::unordered_map _in_progress; -private: - packet make_query_packet(l3addr paddr); - virtual future<> received(packet p) override; - future<> handle_request(arp_hdr* ah); - l2addr l2self() { return _arp.l2self(); } - void send(l2addr to, packet p); -public: - future<> send_query(const l3addr& paddr); - explicit arp_for(arp& a) : arp_for_protocol(a, L3::arp_protocol_type()) { - _table[L3::broadcast_address()] = ethernet::broadcast_address(); - } - future lookup(const l3addr& addr); - void learn(l2addr l2, l3addr l3); - void run(); - void set_self_addr(l3addr addr) { _l3self = addr; } - friend class arp; -}; - -template -packet -arp_for::make_query_packet(l3addr paddr) { - arp_hdr hdr; - hdr.htype = ethernet::arp_hardware_type(); - hdr.ptype = L3::arp_protocol_type(); - hdr.hlen = sizeof(l2addr); - hdr.plen = sizeof(l3addr); - hdr.oper = op_request; - hdr.sender_hwaddr = l2self(); - hdr.sender_paddr = _l3self; - hdr.target_hwaddr = ethernet::broadcast_address(); - hdr.target_paddr = paddr; - hdr = hton(hdr); - return packet(reinterpret_cast(&hdr), sizeof(hdr)); -} - -template -void arp_for::send(l2addr to, packet p) { - _arp._packetq.push_back(l3_protocol::l3packet{eth_protocol_num::arp, to, std::move(p)}); -} - -template -future<> -arp_for::send_query(const l3addr& paddr) { - send(ethernet::broadcast_address(), make_query_packet(paddr)); - return make_ready_future<>(); -} - -class arp_error : public std::runtime_error { -public: - arp_error(const std::string& msg) : std::runtime_error(msg) {} -}; - -class arp_timeout_error : public arp_error { -public: - arp_timeout_error() : arp_error("ARP timeout") {} -}; - -class arp_queue_full_error : public arp_error { -public: - arp_queue_full_error() : arp_error("ARP waiter's queue is full") {} -}; - -template -future -arp_for::lookup(const l3addr& paddr) { - auto i = _table.find(paddr); - if (i != _table.end()) { - return make_ready_future(i->second); - } - auto j = _in_progress.find(paddr); - auto first_request = j == _in_progress.end(); - auto& res = first_request ? _in_progress[paddr] : j->second; - - if (first_request) { - res._timeout_timer.set_callback([paddr, this, &res] { - send_query(paddr); - for (auto& w : res._waiters) { - w.set_exception(arp_timeout_error()); - } - res._waiters.clear(); - }); - res._timeout_timer.arm_periodic(std::chrono::seconds(1)); - send_query(paddr); - } - - if (res._waiters.size() >= max_waiters) { - return make_exception_future(arp_queue_full_error()); - } - - res._waiters.emplace_back(); - return res._waiters.back().get_future(); -} - -template -void -arp_for::learn(l2addr hwaddr, l3addr paddr) { - _table[paddr] = hwaddr; - auto i = _in_progress.find(paddr); - if (i != _in_progress.end()) { - auto& res = i->second; - res._timeout_timer.cancel(); - for (auto &&pr : res._waiters) { - pr.set_value(hwaddr); - } - _in_progress.erase(i); - } -} - -template -future<> -arp_for::received(packet p) { - auto ah = p.get_header(); - if (!ah) { - return make_ready_future<>(); - } - auto h = ntoh(*ah); - if (h.hlen != sizeof(l2addr) || h.plen != sizeof(l3addr)) { - return make_ready_future<>(); - } - switch (h.oper) { - case op_request: - return handle_request(&h); - case op_reply: - arp_learn(h.sender_hwaddr, h.sender_paddr); - return make_ready_future<>(); - default: - return make_ready_future<>(); - } -} - -template -future<> -arp_for::handle_request(arp_hdr* ah) { - if (ah->target_paddr == _l3self - && _l3self != L3::broadcast_address()) { - ah->oper = op_reply; - ah->target_hwaddr = ah->sender_hwaddr; - ah->target_paddr = ah->sender_paddr; - ah->sender_hwaddr = l2self(); - ah->sender_paddr = _l3self; - *ah = hton(*ah); - send(ah->target_hwaddr, packet(reinterpret_cast(ah), sizeof(*ah))); - } - return make_ready_future<>(); -} - -} - -#endif /* ARP_HH_ */ diff --git a/net/byteorder.hh b/net/byteorder.hh deleted file mode 100644 index 52f29f76b9..0000000000 --- a/net/byteorder.hh +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef BYTEORDER_HH_ -#define BYTEORDER_HH_ - -#include // for ntohs() and friends -#include -#include - -#include "core/unaligned.hh" - -inline uint64_t ntohq(uint64_t v) { - return __builtin_bswap64(v); -} -inline uint64_t htonq(uint64_t v) { - return __builtin_bswap64(v); -} - -namespace net { - -inline void ntoh() {} -inline void hton() {} - -inline uint8_t ntoh(uint8_t x) { return x; } -inline uint8_t hton(uint8_t x) { return x; } -inline uint16_t ntoh(uint16_t x) { return ntohs(x); } -inline uint16_t hton(uint16_t x) { return htons(x); } -inline uint32_t ntoh(uint32_t x) { return ntohl(x); } -inline uint32_t hton(uint32_t x) { return htonl(x); } -inline uint64_t ntoh(uint64_t x) { return ntohq(x); } -inline uint64_t hton(uint64_t x) { return htonq(x); } - -inline int8_t ntoh(int8_t x) { return x; } -inline int8_t hton(int8_t x) { return x; } -inline int16_t ntoh(int16_t x) { return ntohs(x); } -inline int16_t hton(int16_t x) { return htons(x); } -inline int32_t ntoh(int32_t x) { return ntohl(x); } -inline int32_t hton(int32_t x) { return htonl(x); } -inline int64_t ntoh(int64_t x) { return ntohq(x); } -inline int64_t hton(int64_t x) { return htonq(x); } - -// Deprecated alias net::packed<> for unaligned<> from unaligned.hh. -// TODO: get rid of this alias. -template using packed = unaligned; - -template -inline T ntoh(const packed& x) { - T v = x; - return ntoh(v); -} - -template -inline T hton(const packed& x) { - T v = x; - return hton(v); -} - -template -inline std::ostream& operator<<(std::ostream& os, const packed& v) { - auto x = v.raw; - return os << x; -} - -inline -void ntoh_inplace() {} -inline -void hton_inplace() {}; - -template -inline -void ntoh_inplace(First& first, Rest&... rest) { - first = ntoh(first); - ntoh_inplace(std::forward(rest)...); -} - -template -inline -void hton_inplace(First& first, Rest&... rest) { - first = hton(first); - hton_inplace(std::forward(rest)...); -} - -template -inline -T ntoh(const T& x) { - T tmp = x; - tmp.adjust_endianness([] (auto&&... what) { ntoh_inplace(std::forward(what)...); }); - return tmp; -} - -template -inline -T hton(const T& x) { - T tmp = x; - tmp.adjust_endianness([] (auto&&... what) { hton_inplace(std::forward(what)...); }); - return tmp; -} - -} - -#endif /* BYTEORDER_HH_ */ diff --git a/net/const.hh b/net/const.hh deleted file mode 100644 index 0b9dcaead5..0000000000 --- a/net/const.hh +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef CONST_HH_ -#define CONST_HH_ -namespace net { - -enum class ip_protocol_num : uint8_t { - icmp = 1, tcp = 6, udp = 17, unused = 255 -}; - -enum class eth_protocol_num : uint16_t { - ipv4 = 0x0800, arp = 0x0806, ipv6 = 0x86dd -}; - -const uint8_t eth_hdr_len = 14; -const uint8_t tcp_hdr_len_min = 20; -const uint8_t ipv4_hdr_len_min = 20; -const uint8_t ipv6_hdr_len_min = 40; -const uint16_t ip_packet_len_max = 65535; - -} -#endif diff --git a/net/dhcp.cc b/net/dhcp.cc deleted file mode 100644 index 3f56f12996..0000000000 --- a/net/dhcp.cc +++ /dev/null @@ -1,467 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#include -#include -#include -#include - -#include "dhcp.hh" -#include "ip.hh" -#include "udp.hh" - -using namespace std::literals::chrono_literals; - -class net::dhcp::impl : public ip_packet_filter { -public: - - decltype(std::cout) & log() { - return std::cout << "DHCP "; - } - - enum class state { - NONE, - DISCOVER, - REQUEST, - DONE, - FAIL, - }; - - enum class m_type : uint8_t { - BOOTREQUEST = 1, - BOOTREPLY = 2 - }; - - enum class htype : uint8_t { - ETHERNET = 1 - }; - - enum class opt_type : uint8_t { - PAD = 0, - SUBNET_MASK = 1, - ROUTER = 3, - DOMAIN_NAME_SERVERS = 6, - INTERFACE_MTU = 26, - BROADCAST_ADDRESS = 28, - REQUESTED_ADDRESS = 50, - LEASE_TIME = 51, - MESSAGE_TYPE = 53, - DHCP_SERVER = 54, - PARAMETER_REQUEST_LIST = 55, - RENEWAL_TIME = 58, - REBINDING_TIME = 59, - CLASSLESS_ROUTE = 121, - END = 255 - }; - - enum class msg_type : uint8_t { - DISCOVER = 1, - OFFER = 2, - REQUEST = 3, - DECLINE = 4, - ACK = 5, - NAK = 6, - RELEASE = 7, - INFORM = 8, - LEASEQUERY = 10, - LEASEUNASSIGNED = 11, - LEASEUNKNOWN = 12, - LEASEACTIVE = 13, - INVALID = 255 - }; - - struct dhcp_header { - m_type op = m_type::BOOTREQUEST; // Message op code / message type. - htype type = htype::ETHERNET; // Hardware address type - uint8_t hlen = 6; // Hardware address length - uint8_t hops = 0; // Client sets to zero, used by relay agents - packed xid = 0; // Client sets Transaction ID, a random number - packed secs = 0; // Client sets seconds elapsed since op start - packed flags = 0; // Flags - ipv4_address ciaddr; // Client IP address - ipv4_address yiaddr; // 'your' (client) IP address. - ipv4_address siaddr; // IP address of next server to use in bootstrap - ipv4_address giaddr; // Relay agent IP address - uint8_t chaddr[16] = { 0, }; // Client hardware address. - char sname[64] = { 0, }; // unused - char file[128] = { 0, }; // unused - - template - auto adjust_endianness(Adjuster a) { - return a(xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr); - } - } __attribute__((packed)); - - typedef std::array req_opt_type; - - static const req_opt_type requested_options; - - struct option_mark { - option_mark(opt_type t = opt_type::END) : type(t) {}; - opt_type type; - } __attribute__((packed)); - - struct option : public option_mark { - option(opt_type t, uint8_t l = 1) : option_mark(t), len(l) {}; - uint8_t len; - } __attribute__((packed)); - - struct type_option : public option { - type_option(msg_type t) : option(opt_type::MESSAGE_TYPE), type(t) {} - msg_type type; - } __attribute__((packed)); - - struct mtu_option : public option { - mtu_option(uint16_t v) : option(opt_type::INTERFACE_MTU, 2), mtu((::htons)(v)) {} - packed mtu; - } __attribute__((packed)); - - struct ip_option : public option { - ip_option(opt_type t = opt_type::BROADCAST_ADDRESS, const ipv4_address & ip = ipv4_address()) : option(t, sizeof(uint32_t)), ip(::htonl(ip.ip)) {} - packed ip; - } __attribute__((packed)); - - struct time_option : public option { - time_option(opt_type t, uint32_t v) : option(t, sizeof(uint32_t)), time(::htonl(v)) {} - packed time; - } __attribute__((packed)); - - - struct requested_option: public option { - requested_option() - : option(opt_type::PARAMETER_REQUEST_LIST, - uint8_t(requested_options.size())), req( - requested_options) { - } - req_opt_type req; - }__attribute__((packed)); - - static const uint16_t client_port = 68; - static const uint16_t server_port = 67; - - typedef std::array magic_tag; - - static const magic_tag options_magic; - - struct dhcp_payload { - dhcp_header bootp; - magic_tag magic = options_magic; - - template - auto adjust_endianness(Adjuster a) { - return a(bootp); - } - } __attribute__((packed)); - - struct dhcp_packet_base { - dhcp_payload dhp; - - template - auto adjust_endianness(Adjuster a) { - return a(dhp); - } - } __attribute__((packed)); - - struct ip_info : public lease { - msg_type type = msg_type(); - - void set(opt_type type, const ipv4_address & ip) { - switch (type) { - case opt_type::SUBNET_MASK: netmask = ip; break; - case opt_type::ROUTER: gateway = ip; break; - case opt_type::BROADCAST_ADDRESS: broadcast = ip; break; - case opt_type::DHCP_SERVER: dhcp_server = ip; break; - case opt_type::DOMAIN_NAME_SERVERS: - name_servers.emplace_back(ip); - break; - default: - break; - } - } - - void set(opt_type type, std::chrono::seconds s) { - switch (type) { - case opt_type::LEASE_TIME: lease_time = s; break; - case opt_type::RENEWAL_TIME: renew_time = s; break; - case opt_type::REBINDING_TIME: rebind_time = s; break; - default: - break; - } - } - - void parse_options(packet & p, size_t off) { - for (;;) { - auto * m = p.get_header(off); - if (m == nullptr || m->type == opt_type::END) { - break; - } - auto * o = p.get_header p = ::make_shared(); - { - auto p2 = dynamic_pointer_cast(p); - BOOST_ASSERT(!p2); - } - BOOST_ASSERT(!A::destroyed); -} - -class C : public enable_shared_from_this { -public: - shared_ptr dup() { return shared_from_this(); } - shared_ptr get() const { return shared_from_this(); } -}; - -BOOST_AUTO_TEST_CASE(test_const_ptr) { - shared_ptr a = make_shared(); - shared_ptr ca = a; - BOOST_REQUIRE(ca == a); - shared_ptr cca = ca->get(); - BOOST_REQUIRE(cca == ca); -} - -struct D {}; - -BOOST_AUTO_TEST_CASE(test_lw_const_ptr_1) { - auto pd1 = make_lw_shared(D()); - auto pd2 = make_lw_shared(D()); - lw_shared_ptr pd3 = pd2; - BOOST_REQUIRE(pd2 == pd3); -} - -struct E : enable_lw_shared_from_this {}; - -BOOST_AUTO_TEST_CASE(test_lw_const_ptr_2) { - auto pe1 = make_lw_shared(); - auto pe2 = make_lw_shared(); - lw_shared_ptr pe3 = pe2; - BOOST_REQUIRE(pe2 == pe3); -} - -struct F : enable_lw_shared_from_this { - auto const_method() const { - return shared_from_this(); - } -}; - -BOOST_AUTO_TEST_CASE(test_shared_from_this_called_on_const_object) { - auto ptr = make_lw_shared(); - ptr->const_method(); -} - -BOOST_AUTO_TEST_CASE(test_exception_thrown_from_constructor_is_propagated) { - struct X { - X() { - throw expected_exception(); - } - }; - try { - auto ptr = make_lw_shared(); - BOOST_FAIL("Constructor should have thrown"); - } catch (const expected_exception& e) { - BOOST_MESSAGE("Expected exception caught"); - } - try { - auto ptr = ::make_shared(); - BOOST_FAIL("Constructor should have thrown"); - } catch (const expected_exception& e) { - BOOST_MESSAGE("Expected exception caught"); - } -} diff --git a/tests/slab_test.cc b/tests/slab_test.cc deleted file mode 100644 index 94b4dd4e9a..0000000000 --- a/tests/slab_test.cc +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - * - * To compile: g++ -std=c++14 slab_test.cc - */ - -#include -#include -#include "core/slab.hh" - -static constexpr size_t max_object_size = 1024*1024; - -class item : public slab_item_base { -public: - bi::list_member_hook<> _cache_link; - uint32_t _slab_page_index; - - item(uint32_t slab_page_index) : _slab_page_index(slab_page_index) {} - - const uint32_t get_slab_page_index() { - return _slab_page_index; - } - const bool is_unlocked() { - return true; - } -}; - -template -static void free_vector(slab_allocator& slab, std::vector& items) { - for (auto item : items) { - slab.free(item); - } -} - -static void test_allocation_1(const double growth_factor, const unsigned slab_limit_size) { - slab_allocator slab(growth_factor, slab_limit_size, max_object_size); - size_t size = max_object_size; - - slab.print_slab_classes(); - - std::vector items; - - assert(slab_limit_size % size == 0); - for (auto i = 0u; i < (slab_limit_size / size); i++) { - auto item = slab.create(size); - items.push_back(item); - } - assert(slab.create(size) == nullptr); - - free_vector(slab, items); - std::cout << __FUNCTION__ << " done!\n"; -} - -static void test_allocation_2(const double growth_factor, const unsigned slab_limit_size) { - slab_allocator slab(growth_factor, slab_limit_size, max_object_size); - size_t size = 1024; - - std::vector items; - - auto allocations = 0u; - for (;;) { - auto item = slab.create(size); - if (!item) { - break; - } - items.push_back(item); - allocations++; - } - - auto class_size = slab.class_size(size); - auto per_slab_page = max_object_size / class_size; - auto available_slab_pages = slab_limit_size / max_object_size; - assert(allocations == (per_slab_page * available_slab_pages)); - - free_vector(slab, items); - std::cout << __FUNCTION__ << " done!\n"; -} - -static void test_allocation_with_lru(const double growth_factor, const unsigned slab_limit_size) { - bi::list, &item::_cache_link>> _cache; - unsigned evictions = 0; - - slab_allocator slab(growth_factor, slab_limit_size, max_object_size, - [&](item& item_ref) { _cache.erase(_cache.iterator_to(item_ref)); evictions++; }); - size_t size = max_object_size; - - auto max = slab_limit_size / max_object_size; - for (auto i = 0u; i < max * 1000; i++) { - auto item = slab.create(size); - assert(item != nullptr); - _cache.push_front(*item); - } - assert(evictions == max * 999); - - _cache.clear(); - - std::cout << __FUNCTION__ << " done!\n"; -} - -int main(int ac, char** av) { - test_allocation_1(1.25, 5*1024*1024); - test_allocation_2(1.07, 5*1024*1024); // 1.07 is the growth factor used by facebook. - test_allocation_with_lru(1.25, 5*1024*1024); - - return 0; -} diff --git a/tests/smp_test.cc b/tests/smp_test.cc deleted file mode 100644 index 3033649ab4..0000000000 --- a/tests/smp_test.cc +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "core/reactor.hh" -#include "core/app-template.hh" -#include "core/print.hh" - -future test_smp_call() { - return smp::submit_to(1, [] { - return make_ready_future(3); - }).then([] (int ret) { - return make_ready_future(ret == 3); - }); -} - -struct nasty_exception {}; - -future test_smp_exception() { - print("1\n"); - return smp::submit_to(1, [] { - print("2\n"); - auto x = make_exception_future(nasty_exception()); - print("3\n"); - return x; - }).then_wrapped([] (future result) { - print("4\n"); - try { - result.get(); - return make_ready_future(false); // expected an exception - } catch (nasty_exception&) { - // all is well - return make_ready_future(true); - } catch (...) { - // incorrect exception type - return make_ready_future(false); - } - }); -} - -int tests, fails; - -future<> -report(sstring msg, future&& result) { - return std::move(result).then([msg] (bool result) { - print("%s: %s\n", (result ? "PASS" : "FAIL"), msg); - tests += 1; - fails += !result; - }); -} - -int main(int ac, char** av) { - return app_template().run(ac, av, [] { - return report("smp call", test_smp_call()).then([] { - return report("smp exception", test_smp_exception()); - }).then([] { - print("\n%d tests / %d failures\n", tests, fails); - engine().exit(fails ? 1 : 0); - }); - }); -} diff --git a/tests/sstring_test.cc b/tests/sstring_test.cc deleted file mode 100644 index 21df8f4821..0000000000 --- a/tests/sstring_test.cc +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE core - -#include -#include "core/sstring.hh" -#include - -BOOST_AUTO_TEST_CASE(test_equality) { - BOOST_REQUIRE_EQUAL(sstring("aaa"), sstring("aaa")); -} - -BOOST_AUTO_TEST_CASE(test_to_sstring) { - BOOST_REQUIRE_EQUAL(to_sstring(1234567), sstring("1234567")); -} - -BOOST_AUTO_TEST_CASE(test_add_literal_to_sstring) { - BOOST_REQUIRE_EQUAL("x" + sstring("y"), sstring("xy")); -} - -BOOST_AUTO_TEST_CASE(test_find_sstring) { - BOOST_REQUIRE_EQUAL(sstring("abcde").find('b'), 1); - BOOST_REQUIRE_EQUAL(sstring("babcde").find('b',1), 2); -} - -BOOST_AUTO_TEST_CASE(test_not_find_sstring) { - BOOST_REQUIRE_EQUAL(sstring("abcde").find('x'), sstring::npos); -} - -BOOST_AUTO_TEST_CASE(test_str_find_sstring) { - BOOST_REQUIRE_EQUAL(sstring("abcde").find("bc"), 1); - BOOST_REQUIRE_EQUAL(sstring("abcbcde").find("bc", 2), 3); -} - -BOOST_AUTO_TEST_CASE(test_str_not_find_sstring) { - BOOST_REQUIRE_EQUAL(sstring("abcde").find("x"), sstring::npos); -} - -BOOST_AUTO_TEST_CASE(test_substr_sstring) { - BOOST_REQUIRE_EQUAL(sstring("abcde").substr(1,2), "bc"); - BOOST_REQUIRE_EQUAL(sstring("abc").substr(1,2), "bc"); - BOOST_REQUIRE_EQUAL(sstring("abc").substr(1,3), "bc"); - BOOST_REQUIRE_EQUAL(sstring("abc").substr(0, 2), "ab"); - BOOST_REQUIRE_EQUAL(sstring("abc").substr(3, 2), ""); - BOOST_REQUIRE_EQUAL(sstring("abc").substr(1), "bc"); -} - -BOOST_AUTO_TEST_CASE(test_substr_eor_sstring) { - BOOST_REQUIRE_THROW(sstring("abcde").substr(6,1), std::out_of_range); -} - -BOOST_AUTO_TEST_CASE(test_at_sstring) { - BOOST_REQUIRE_EQUAL(sstring("abcde").at(1), 'b'); - BOOST_REQUIRE_THROW(sstring("abcde").at(6), std::out_of_range); - sstring s("abcde"); - s.at(1) = 'd'; - BOOST_REQUIRE_EQUAL(s, "adcde"); -} - -BOOST_AUTO_TEST_CASE(test_find_last_sstring) { - BOOST_REQUIRE_EQUAL(sstring("ababa").find_last_of('a'), 4); - BOOST_REQUIRE_EQUAL(sstring("ababa").find_last_of('a',5), 4); - BOOST_REQUIRE_EQUAL(sstring("ababa").find_last_of('a',4), 4); - BOOST_REQUIRE_EQUAL(sstring("ababa").find_last_of('a',3), 2); - BOOST_REQUIRE_EQUAL(sstring("ababa").find_last_of('x'), sstring::npos); - BOOST_REQUIRE_EQUAL(sstring("").find_last_of('a'), sstring::npos); -} - - -BOOST_AUTO_TEST_CASE(test_append) { - BOOST_REQUIRE_EQUAL(sstring("aba").append("1234", 3), "aba123"); - BOOST_REQUIRE_EQUAL(sstring("aba").append("1234", 4), "aba1234"); - BOOST_REQUIRE_EQUAL(sstring("aba").append("1234", 0), "aba"); -} - -BOOST_AUTO_TEST_CASE(test_replace) { - BOOST_REQUIRE_EQUAL(sstring("abc").replace(1,1, "xyz", 1), "axc"); - BOOST_REQUIRE_EQUAL(sstring("abc").replace(3,2, "xyz", 2), "abcxy"); - BOOST_REQUIRE_EQUAL(sstring("abc").replace(2,2, "xyz", 2), "abxy"); - BOOST_REQUIRE_EQUAL(sstring("abc").replace(0,2, "", 0), "c"); - BOOST_REQUIRE_THROW(sstring("abc").replace(4,1, "xyz", 1), std::out_of_range); - const char* s = "xyz"; - sstring str("abcdef"); - BOOST_REQUIRE_EQUAL(str.replace(str.begin() + 1 , str.begin() + 3, s + 1, s + 3), "ayzdef"); - BOOST_REQUIRE_THROW(sstring("abc").replace(4,1, "xyz", 1), std::out_of_range); - -} - -BOOST_AUTO_TEST_CASE(test_insert) { - sstring str("abc"); - const char* s = "xyz"; - str.insert(str.begin() +1, s + 1, s + 2); - BOOST_REQUIRE_EQUAL(str, "aybc"); - str = "abc"; - BOOST_REQUIRE_THROW(str.insert(str.begin() + 5, s + 1, s + 2), std::out_of_range); -} - -BOOST_AUTO_TEST_CASE(test_erase) { - sstring str("abcdef"); - auto i = str.erase(str.begin() + 1, str.begin() + 3); - BOOST_REQUIRE_EQUAL(*i, 'd'); - BOOST_REQUIRE_EQUAL(str, "adef"); - BOOST_REQUIRE_THROW(str.erase(str.begin() + 5, str.begin() + 6), std::out_of_range); -} - -BOOST_AUTO_TEST_CASE(test_ctor_iterator) { - std::list data{{'a', 'b', 'c'}}; - sstring s(data.begin(), data.end()); - BOOST_REQUIRE_EQUAL(s, "abc"); -} diff --git a/tests/tcp_client.cc b/tests/tcp_client.cc deleted file mode 100644 index b77ad7b6c8..0000000000 --- a/tests/tcp_client.cc +++ /dev/null @@ -1,261 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "core/app-template.hh" -#include "core/future-util.hh" -#include "core/distributed.hh" - -using namespace net; -using namespace std::chrono_literals; - -static int rx_msg_size = 4 * 1024; -static int tx_msg_total_size = 100 * 1024 * 1024; -static int tx_msg_size = 4 * 1024; -static int tx_msg_nr = tx_msg_total_size / tx_msg_size; -static std::string str_txbuf(tx_msg_size, 'X'); - -class client; -distributed clients; - -class client { -private: - static constexpr unsigned _pings_per_connection = 10000; - unsigned _total_pings; - unsigned _concurrent_connections; - ipv4_addr _server_addr; - std::string _test; - lowres_clock::time_point _earliest_started; - lowres_clock::time_point _latest_finished; - size_t _processed_bytes; - unsigned _num_reported; -public: - class connection { - connected_socket _fd; - input_stream _read_buf; - output_stream _write_buf; - size_t _bytes_read = 0; - size_t _bytes_write = 0; - public: - connection(connected_socket&& fd) - : _fd(std::move(fd)) - , _read_buf(_fd.input()) - , _write_buf(_fd.output()) {} - - future<> do_read() { - return _read_buf.read_exactly(rx_msg_size).then([this] (temporary_buffer buf) { - _bytes_read += buf.size(); - if (buf.size() == 0) { - return make_ready_future(); - } else { - return do_read(); - } - }); - } - - future<> do_write(int end) { - if (end == 0) { - return make_ready_future(); - } - return _write_buf.write(str_txbuf).then([this, end] { - _bytes_write += tx_msg_size; - return _write_buf.flush(); - }).then([this, end] { - return do_write(end - 1); - }); - } - - future<> ping(int times) { - return _write_buf.write("ping").then([this] { - return _write_buf.flush(); - }).then([this, times] { - return _read_buf.read_exactly(4).then([this, times] (temporary_buffer buf) { - if (buf.size() != 4) { - fprint(std::cerr, "illegal packet received: %d\n", buf.size()); - return make_ready_future(); - } - auto str = std::string(buf.get(), buf.size()); - if (str != "pong") { - fprint(std::cerr, "illegal packet received: %d\n", buf.size()); - return make_ready_future(); - } - if (times > 0) { - return ping(times - 1); - } else { - return make_ready_future(); - } - }); - }); - } - - future rxrx() { - return _write_buf.write("rxrx").then([this] { - return _write_buf.flush(); - }).then([this] { - return do_write(tx_msg_nr).then([this] { - return _write_buf.close(); - }).then([this] { - return make_ready_future(_bytes_write); - }); - }); - } - - future txtx() { - return _write_buf.write("txtx").then([this] { - return _write_buf.flush(); - }).then([this] { - return do_read().then([this] { - return make_ready_future(_bytes_read); - }); - }); - } - }; - - future<> ping_test(connection *conn) { - auto started = lowres_clock::now(); - return conn->ping(_pings_per_connection).then([started] { - auto finished = lowres_clock::now(); - clients.invoke_on(0, &client::ping_report, started, finished); - }); - } - - future<> rxrx_test(connection *conn) { - auto started = lowres_clock::now(); - return conn->rxrx().then([started] (size_t bytes) { - auto finished = lowres_clock::now(); - clients.invoke_on(0, &client::rxtx_report, started, finished, bytes); - }); - } - - future<> txtx_test(connection *conn) { - auto started = lowres_clock::now(); - return conn->txtx().then([started] (size_t bytes) { - auto finished = lowres_clock::now(); - clients.invoke_on(0, &client::rxtx_report, started, finished, bytes); - }); - } - - void ping_report(lowres_clock::time_point started, lowres_clock::time_point finished) { - if (_earliest_started > started) - _earliest_started = started; - if (_latest_finished < finished) - _latest_finished = finished; - if (++_num_reported == _concurrent_connections) { - auto elapsed = _latest_finished - _earliest_started; - auto usecs = std::chrono::duration_cast(elapsed).count(); - auto secs = static_cast(usecs) / static_cast(1000 * 1000); - fprint(std::cout, "========== ping ============\n"); - fprint(std::cout, "Server: %s\n", _server_addr); - fprint(std::cout,"Connections: %u\n", _concurrent_connections); - fprint(std::cout, "Total PingPong: %u\n", _total_pings); - fprint(std::cout, "Total Time(Secs): %f\n", secs); - fprint(std::cout, "Requests/Sec: %f\n", - static_cast(_total_pings) / secs); - clients.stop().then([] { - engine().exit(0); - }); - } - } - - void rxtx_report(lowres_clock::time_point started, lowres_clock::time_point finished, size_t bytes) { - if (_earliest_started > started) - _earliest_started = started; - if (_latest_finished < finished) - _latest_finished = finished; - _processed_bytes += bytes; - if (++_num_reported == _concurrent_connections) { - auto elapsed = _latest_finished - _earliest_started; - auto usecs = std::chrono::duration_cast(elapsed).count(); - auto secs = static_cast(usecs) / static_cast(1000 * 1000); - fprint(std::cout, "========== %s ============\n", _test); - fprint(std::cout, "Server: %s\n", _server_addr); - fprint(std::cout, "Connections: %u\n", _concurrent_connections); - fprint(std::cout, "Bytes Received(MiB): %u\n", _processed_bytes/1024/1024); - fprint(std::cout, "Total Time(Secs): %f\n", secs); - fprint(std::cout, "Bandwidth(Gbits/Sec): %f\n", - static_cast((_processed_bytes * 8)) / (1000 * 1000 * 1000) / secs); - clients.stop().then([] { - engine().exit(0); - }); - } - } - - future<> start(ipv4_addr server_addr, std::string test, unsigned ncon) { - _server_addr = server_addr; - _concurrent_connections = ncon * smp::count; - _total_pings = _pings_per_connection * _concurrent_connections; - _test = test; - - for (unsigned i = 0; i < ncon; i++) { - engine().net().connect(make_ipv4_address(server_addr)).then([this, server_addr, test] (connected_socket fd) { - auto conn = new connection(std::move(fd)); - (this->*tests.at(test))(conn).then_wrapped([this, conn] (auto&& f) { - delete conn; - try { - f.get(); - } catch (std::exception& ex) { - fprint(std::cerr, "request error: %s\n", ex.what()); - } - }); - }); - } - return make_ready_future(); - } - future<> stop() { - return make_ready_future(); - } - - typedef future<> (client::*test_fn)(connection *conn); - static const std::map tests; -}; - -namespace bpo = boost::program_options; - -int main(int ac, char ** av) { - app_template app; - app.add_options() - ("server", bpo::value(), "Server address") - ("test", bpo::value()->default_value("ping"), "test type(ping | rxrx | txtx)") - ("conn", bpo::value()->default_value(16), "nr connections per cpu") - ; - - return app.run(ac, av, [&app] { - auto&& config = app.configuration(); - auto server = config["server"].as(); - auto test = config["test"].as(); - auto ncon = config["conn"].as(); - - if (!client::tests.count(test)) { - fprint(std::cerr, "Error: -test=ping | rxrx | txtx\n"); - return engine().exit(1); - } - - clients.start().then([server, test, ncon] () { - clients.invoke_on_all(&client::start, ipv4_addr{server}, test, ncon); - }); - }); -} - -const std::map client::tests = { - {"ping", &client::ping_test}, - {"rxrx", &client::rxrx_test}, - {"txtx", &client::txtx_test}, -}; - diff --git a/tests/tcp_server.cc b/tests/tcp_server.cc deleted file mode 100644 index 9bb681d876..0000000000 --- a/tests/tcp_server.cc +++ /dev/null @@ -1,166 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright 2014 Cloudius Systems - */ - -#include "core/reactor.hh" -#include "core/app-template.hh" -#include "core/temporary_buffer.hh" -#include "core/distributed.hh" -#include -#include - -static std::string str_ping{"ping"}; -static std::string str_txtx{"txtx"}; -static std::string str_rxrx{"rxrx"}; -static std::string str_pong{"pong"}; -static std::string str_unknow{"unknow cmd"}; -static int tx_msg_total_size = 100 * 1024 * 1024; -static int tx_msg_size = 4 * 1024; -static int tx_msg_nr = tx_msg_total_size / tx_msg_size; -static int rx_msg_size = 4 * 1024; -static std::string str_txbuf(tx_msg_size, 'X'); - -class tcp_server { - std::vector _listeners; -public: - future<> listen(ipv4_addr addr) { - listen_options lo; - lo.reuse_address = true; - _listeners.push_back(engine().listen(make_ipv4_address(addr), lo)); - do_accepts(_listeners.size() - 1); - return make_ready_future<>(); - } - void do_accepts(int which) { - _listeners[which].accept().then([this, which] (connected_socket fd, socket_address addr) mutable { - auto conn = new connection(*this, std::move(fd), addr); - conn->process().then_wrapped([this, conn] (auto&& f) { - delete conn; - try { - f.get(); - } catch (std::exception& ex) { - std::cout << "request error " << ex.what() << "\n"; - } - }); - do_accepts(which); - }).then_wrapped([] (auto&& f) { - try { - f.get(); - } catch (std::exception& ex) { - std::cout << "accept failed: " << ex.what() << "\n"; - } - }); - } - class connection { - connected_socket _fd; - input_stream _read_buf; - output_stream _write_buf; - public: - connection(tcp_server& server, connected_socket&& fd, socket_address addr) - : _fd(std::move(fd)) - , _read_buf(_fd.input()) - , _write_buf(_fd.output()) {} - future<> process() { - return read(); - } - future<> read() { - if (_read_buf.eof()) { - return make_ready_future(); - } - // Expect 4 bytes cmd from client - size_t n = 4; - return _read_buf.read_exactly(n).then([this] (temporary_buffer buf) { - if (buf.size() == 0) { - return make_ready_future(); - } - auto cmd = std::string(buf.get(), buf.size()); - // pingpong test - if (cmd == str_ping) { - return _write_buf.write(str_pong).then([this] { - return _write_buf.flush(); - }).then([this] { - return this->read(); - }); - // server tx test - } else if (cmd == str_txtx) { - return tx_test(); - // server tx test - } else if (cmd == str_rxrx) { - return rx_test(); - // unknow test - } else { - return _write_buf.write(str_unknow).then([this] { - return _write_buf.flush(); - }).then([this] { - return make_ready_future(); - }); - } - }); - } - future<> do_write(int end) { - if (end == 0) { - return make_ready_future<>(); - } - return _write_buf.write(str_txbuf).then([this, end] { - return _write_buf.flush(); - }).then([this, end] { - return do_write(end - 1); - }); - } - future<> tx_test() { - return do_write(tx_msg_nr).then([this] { - return _write_buf.close(); - }).then([this] { - return make_ready_future<>(); - }); - } - future<> do_read() { - return _read_buf.read_exactly(rx_msg_size).then([this] (temporary_buffer buf) { - if (buf.size() == 0) { - return make_ready_future(); - } else { - return do_read(); - } - }); - } - future<> rx_test() { - return do_read().then([] { - return make_ready_future<>(); - }); - } - }; -}; - -namespace bpo = boost::program_options; - -int main(int ac, char** av) { - app_template app; - app.add_options() - ("port", bpo::value()->default_value(10000), "TCP server port") ; - return app.run(ac, av, [&] { - auto&& config = app.configuration(); - uint16_t port = config["port"].as(); - auto server = new distributed; - server->start().then([server = std::move(server), port] () mutable { - server->invoke_on_all(&tcp_server::listen, ipv4_addr{port}); - }).then([port] { - std::cout << "Seastar TCP server listening on port " << port << " ...\n"; - }); - }); -} diff --git a/tests/tcp_test.cc b/tests/tcp_test.cc deleted file mode 100644 index e31dd58d6e..0000000000 --- a/tests/tcp_test.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "net/ip.hh" -#include "net/virtio.hh" -#include "net/tcp.hh" - -using namespace net; - -struct tcp_test { - ipv4& inet; - using tcp = net::tcp; - tcp::listener _listener; - struct connection { - tcp::connection tcp_conn; - explicit connection(tcp::connection tc) : tcp_conn(std::move(tc)) {} - void run() { - tcp_conn.wait_for_data().then([this] { - auto p = tcp_conn.read(); - if (!p.len()) { - tcp_conn.close_write(); - return; - } - print("read %d bytes\n", p.len()); - tcp_conn.send(std::move(p)); - run(); - }); - } - }; - tcp_test(ipv4& inet) : inet(inet), _listener(inet.get_tcp().listen(10000)) {} - void run() { - _listener.accept().then([this] (tcp::connection conn) { - (new connection(std::move(conn)))->run(); - run(); - }); - } -}; - -int main(int ac, char** av) { - boost::program_options::variables_map opts; - opts.insert(std::make_pair("tap-device", boost::program_options::variable_value(std::string("tap0"), false))); - - auto vnet = create_virtio_net_device(opts); - interface netif(std::move(vnet)); - ipv4 inet(&netif); - inet.set_host_address(ipv4_address("192.168.122.2")); - tcp_test tt(inet); - engine().when_started().then([&tt] { tt.run(); }); - engine().run(); -} - - diff --git a/tests/test-utils.cc b/tests/test-utils.cc deleted file mode 100644 index a586c39907..0000000000 --- a/tests/test-utils.cc +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include - -#include "tests/test-utils.hh" -#include "core/future.hh" -#include "core/app-template.hh" - -void seastar_test::run() { - // HACK: please see https://github.com/cloudius-systems/seastar/issues/10 - BOOST_REQUIRE(true); - - // HACK: please see https://github.com/cloudius-systems/seastar/issues/10 - boost::program_options::variables_map()["dummy"]; - - global_test_runner().run_sync([this] { - return run_test_case(); - }); -} - -// We store a pointer because tests are registered from dynamic initializers, -// so we must ensure that 'tests' is initialized before any dynamic initializer. -// I use a primitive type, which is guaranteed to be initialized before any -// dynamic initializer and lazily allocate the factor. - -static std::vector* tests; - -seastar_test::seastar_test() { - if (!tests) { - tests = new std::vector(); - } - tests->push_back(this); -} - -bool init_unit_test_suite() { - auto&& ts = boost::unit_test::framework::master_test_suite(); - ts.p_name.set(tests->size() ? (*tests)[0]->get_test_file() : "seastar-tests"); - - for (seastar_test* test : *tests) { - ts.add(boost::unit_test::make_test_case([test] { test->run(); }, test->get_name()), 0, 0); - } - - global_test_runner().start(ts.argc, ts.argv); - return true; -} - -int main(int ac, char** av) { - return ::boost::unit_test::unit_test_main(&init_unit_test_suite, ac, av); -} diff --git a/tests/test-utils.hh b/tests/test-utils.hh deleted file mode 100644 index 2bfd45b1fa..0000000000 --- a/tests/test-utils.hh +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#pragma once - -#define BOOST_TEST_DYN_LINK - -#include - -#include "core/future.hh" -#include "test_runner.hh" - -class seastar_test { -public: - seastar_test(); - virtual ~seastar_test() {} - virtual const char* get_test_file() = 0; - virtual const char* get_name() = 0; - virtual future<> run_test_case() = 0; - void run(); -}; - -#define SEASTAR_TEST_CASE(name) \ - struct name : public seastar_test { \ - const char* get_test_file() override { return __FILE__; } \ - const char* get_name() override { return #name; } \ - future<> run_test_case() override; \ - }; \ - static name name ## _instance; \ - future<> name::run_test_case() - diff --git a/tests/test_runner.cc b/tests/test_runner.cc deleted file mode 100644 index b544869331..0000000000 --- a/tests/test_runner.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include - -#include "core/app-template.hh" -#include "core/future-util.hh" -#include "core/reactor.hh" -#include "test_runner.hh" - -static test_runner instance; - -struct stop_execution : public std::exception {}; - -test_runner::~test_runner() { - if (_thread) { - _task.interrupt(stop_execution()); - _thread->join(); - } -} - -void -test_runner::start(int ac, char** av) { - bool expected = false; - if (!_started.compare_exchange_strong(expected, true, std::memory_order_acquire)) { - return; - } - - _thread = std::make_unique([this, ac, av]() mutable { - app_template app; - auto exit_code = app.run(ac, av, [this] { - do_until([this] { return _done; }, [this] { - // this will block the reactor briefly, but we don't care - try { - auto func = _task.take(); - return func(); - } catch (const stop_execution&) { - _done = true; - engine().exit(0); - return make_ready_future<>(); - } - }).or_terminate(); - }); - - if (exit_code) { - exit(exit_code); - } - }); -} - -void -test_runner::run_sync(std::function()> task) { - exchanger> e; - _task.give([task = std::move(task), &e] { - try { - return task().then_wrapped([&e](auto&& f) { - try { - f.get(); - e.give({}); - } catch (...) { - e.give({std::current_exception()}); - } - }); - } catch (...) { - e.give({std::current_exception()}); - return make_ready_future<>(); - } - }); - auto maybe_exception = e.take(); - if (maybe_exception) { - std::rethrow_exception(*maybe_exception); - } -} - -test_runner& global_test_runner() { - return instance; -} diff --git a/tests/test_runner.hh b/tests/test_runner.hh deleted file mode 100644 index 818e817f42..0000000000 --- a/tests/test_runner.hh +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include -#include -#include -#include "core/future.hh" -#include "core/posix.hh" -#include "exchanger.hh" - -class posix_thread; - -class test_runner { -private: - std::unique_ptr _thread; - std::atomic _started{false}; - exchanger()>> _task; - bool _done = false; -public: - void start(int argc, char** argv); - ~test_runner(); - void run_sync(std::function()> task); -}; - -test_runner& global_test_runner(); diff --git a/tests/thread_context_switch.cc b/tests/thread_context_switch.cc deleted file mode 100644 index f462aa22d5..0000000000 --- a/tests/thread_context_switch.cc +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include -#include "core/thread.hh" -#include "core/semaphore.hh" -#include "core/app-template.hh" -#include "core/do_with.hh" -#include "core/distributed.hh" -#include "core/sleep.hh" - -using namespace seastar; -using namespace std::chrono_literals; - -class context_switch_tester { - uint64_t _switches{0}; - semaphore _s1{0}; - semaphore _s2{0}; - bool _done1{false}; - bool _done2{false}; - thread _t1{[this] { main1(); }}; - thread _t2{[this] { main2(); }}; -private: - void main1() { - while (!_done1) { - _s1.wait().get(); - ++_switches; - _s2.signal(); - } - _done2 = true; - } - void main2() { - while (!_done2) { - _s2.wait().get(); - ++_switches; - _s1.signal(); - } - } -public: - void begin_measurement() { - _s1.signal(); - } - future measure() { - _done1 = true; - return _t1.join().then([this] { - return _t2.join(); - }).then([this] { - return _switches; - }); - } - future<> stop() { - return make_ready_future<>(); - } -}; - -int main(int ac, char** av) { - static const auto test_time = 5s; - return app_template().run(ac, av, [] { - return do_with(distributed(), [] (distributed& dcst) { - return dcst.start().then([&dcst] { - return dcst.invoke_on_all(&context_switch_tester::begin_measurement); - }).then([] { - return sleep(test_time); - }).then([&dcst] { - return dcst.map_reduce0(std::mem_fn(&context_switch_tester::measure), uint64_t(), std::plus()); - }).then([] (uint64_t switches) { - switches /= smp::count; - print("context switch time: %5.1f ns\n", - double(std::chrono::duration_cast(test_time).count()) / switches); - }).then([&dcst] { - return dcst.stop(); - }).then([] { - engine_exit(0); - }); - }); - }); -} diff --git a/tests/thread_test.cc b/tests/thread_test.cc deleted file mode 100644 index 79b877f731..0000000000 --- a/tests/thread_test.cc +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#include "core/thread.hh" -#include "core/do_with.hh" -#include "test-utils.hh" -#include "core/sstring.hh" -#include "core/reactor.hh" -#include "core/semaphore.hh" -#include "core/do_with.hh" -#include "core/future-util.hh" -#include "core/sleep.hh" - -using namespace seastar; -using namespace std::chrono_literals; - -SEASTAR_TEST_CASE(test_thread_1) { - return do_with(sstring(), [] (sstring& x) { - auto t1 = new thread([&x] { - x = "abc"; - }); - return t1->join().then([&x] { - BOOST_REQUIRE_EQUAL(x, "abc"); - }); - }); -} - -SEASTAR_TEST_CASE(test_thread_2) { - struct tmp { - std::vector threads; - semaphore sem1{0}; - semaphore sem2{0}; - int counter = 0; - void thread_fn() { - sem1.wait(1).get(); - ++counter; - sem2.signal(1); - } - }; - return do_with(tmp(), [] (tmp& x) { - auto n = 10; - for (int i = 0; i < n; ++i) { - x.threads.emplace_back(std::bind(&tmp::thread_fn, &x)); - } - BOOST_REQUIRE_EQUAL(x.counter, 0); - x.sem1.signal(n); - return x.sem2.wait(n).then([&x, n] { - BOOST_REQUIRE_EQUAL(x.counter, n); - return parallel_for_each(x.threads.begin(), x.threads.end(), std::mem_fn(&thread::join)); - }); - }); -} - -SEASTAR_TEST_CASE(test_thread_async) { - sstring x = "x"; - sstring y = "y"; - auto concat = [] (sstring x, sstring y) { - sleep(10ms).get(); - return x + y; - }; - return async(concat, x, y).then([] (sstring xy) { - BOOST_REQUIRE_EQUAL(xy, "xy"); - }); -} - -SEASTAR_TEST_CASE(test_thread_async_immed) { - return async([] { return 3; }).then([] (int three) { - BOOST_REQUIRE_EQUAL(three, 3); - }); -} - -SEASTAR_TEST_CASE(test_thread_async_nested) { - return async([] { - return async([] { - return 3; - }).get0(); - }).then([] (int three) { - BOOST_REQUIRE_EQUAL(three, 3); - }); -} - diff --git a/tests/timertest.cc b/tests/timertest.cc deleted file mode 100644 index 5dca144110..0000000000 --- a/tests/timertest.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "core/app-template.hh" -#include "core/reactor.hh" -#include "core/print.hh" -#include - -using namespace std::chrono_literals; - -#define BUG() do { \ - std::cerr << "ERROR @ " << __FILE__ << ":" << __LINE__ << std::endl; \ - throw std::runtime_error("test failed"); \ - } while (0) - -#define OK() do { \ - std::cerr << "OK @ " << __FILE__ << ":" << __LINE__ << std::endl; \ - } while (0) - -template -struct timer_test { - timer t1; - timer t2; - timer t3; - timer t4; - timer t5; - promise<> pr1; - promise<> pr2; - - future<> run() { - t1.set_callback([this] { - OK(); - print(" 500ms timer expired\n"); - if (!t4.cancel()) { - BUG(); - } - if (!t5.cancel()) { - BUG(); - } - t5.arm(1100ms); - }); - t2.set_callback([this] { OK(); print(" 900ms timer expired\n"); }); - t3.set_callback([this] { OK(); print("1000ms timer expired\n"); }); - t4.set_callback([this] { OK(); print(" BAD cancelled timer expired\n"); }); - t5.set_callback([this] { OK(); print("1600ms rearmed timer expired\n"); pr1.set_value(); }); - - t1.arm(500ms); - t2.arm(900ms); - t3.arm(1000ms); - t4.arm(700ms); - t5.arm(800ms); - - return pr1.get_future().then([this] { test_timer_cancelling(); }); - } - - future<> test_timer_cancelling() { - timer& t1 = *new timer(); - t1.set_callback([] { BUG(); }); - t1.arm(100ms); - t1.cancel(); - - t1.arm(100ms); - t1.cancel(); - - t1.set_callback([this] { OK(); pr2.set_value(); }); - t1.arm(100ms); - return pr2.get_future(); - } -}; - -int main(int ac, char** av) { - app_template app; - timer_test t1; - timer_test t2; - return app.run(ac, av, [&t1, &t2] { - print("=== Start High res clock test\n"); - t1.run().then([&t2] { - print("=== Start Low res clock test\n"); - return t2.run(); - }).then([] { - print("Done\n"); - engine().exit(0); - }); - }); -} diff --git a/tests/udp_client.cc b/tests/udp_client.cc deleted file mode 100644 index 68b0cea09e..0000000000 --- a/tests/udp_client.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "core/app-template.hh" -#include "core/future-util.hh" -#include "core/reactor.hh" -#include "net/api.hh" - -using namespace net; -using namespace std::chrono_literals; - -class client { -private: - udp_channel _chan; - uint64_t n_sent {}; - uint64_t n_received {}; - uint64_t n_failed {}; - timer<> _stats_timer; -public: - void start(ipv4_addr server_addr) { - std::cout << "Sending to " << server_addr << std::endl; - - _chan = engine().net().make_udp_channel(); - - _stats_timer.set_callback([this] { - std::cout << "Out: " << n_sent << " pps, \t"; - std::cout << "Err: " << n_failed << " pps, \t"; - std::cout << "In: " << n_received << " pps" << std::endl; - n_sent = 0; - n_received = 0; - n_failed = 0; - }); - _stats_timer.arm_periodic(1s); - - keep_doing([this, server_addr] { - return _chan.send(server_addr, "hello!\n") - .then_wrapped([this] (auto&& f) { - try { - f.get(); - n_sent++; - } catch (...) { - n_failed++; - } - }); - }); - - keep_doing([this] { - return _chan.receive().then([this] (auto) { - n_received++; - }); - }); - } -}; - -namespace bpo = boost::program_options; - -int main(int ac, char ** av) { - client _client; - app_template app; - app.add_options() - ("server", bpo::value(), "Server address") - ; - return app.run(ac, av, [&_client, &app] { - auto&& config = app.configuration(); - _client.start(config["server"].as()); - }); -} diff --git a/tests/udp_server.cc b/tests/udp_server.cc deleted file mode 100644 index ff9c2439f1..0000000000 --- a/tests/udp_server.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "core/distributed.hh" -#include "core/app-template.hh" -#include "core/future-util.hh" - -using namespace net; -using namespace std::chrono_literals; - -class udp_server { -private: - udp_channel _chan; - timer<> _stats_timer; - uint64_t _n_sent {}; -public: - void start(uint16_t port) { - ipv4_addr listen_addr{port}; - _chan = engine().net().make_udp_channel(listen_addr); - - _stats_timer.set_callback([this] { - std::cout << "Out: " << _n_sent << " pps" << std::endl; - _n_sent = 0; - }); - _stats_timer.arm_periodic(1s); - - keep_doing([this] { - return _chan.receive().then([this] (udp_datagram dgram) { - return _chan.send(dgram.get_src(), std::move(dgram.get_data())).then([this] { - _n_sent++; - }); - }); - }); - } -}; - -namespace bpo = boost::program_options; - -int main(int ac, char ** av) { - app_template app; - app.add_options() - ("port", bpo::value()->default_value(10000), "UDP server port") ; - return app.run(ac, av, [&] { - auto&& config = app.configuration(); - uint16_t port = config["port"].as(); - auto server = new distributed; - server->start().then([server = std::move(server), port] () mutable { - server->invoke_on_all(&udp_server::start, port); - }).then([port] { - std::cout << "Seastar UDP server listening on port " << port << " ...\n"; - }); - }); -} diff --git a/tests/udp_zero_copy.cc b/tests/udp_zero_copy.cc deleted file mode 100644 index a5695a3881..0000000000 --- a/tests/udp_zero_copy.cc +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#include "core/app-template.hh" -#include "core/future-util.hh" -#include "core/scattered_message.hh" -#include "core/vector-data-sink.hh" -#include "core/shared_ptr.hh" -#include "core/units.hh" -#include -#include - -using namespace net; -using namespace std::chrono_literals; -namespace bpo = boost::program_options; - -template -typename Duration::rep to_seconds(Duration d) { - return std::chrono::duration_cast(d).count(); -} - -class server { -private: - udp_channel _chan; - timer<> _stats_timer; - uint64_t _n_sent {}; - size_t _chunk_size; - bool _copy; - std::vector _packets; - std::unique_ptr> _out; - clock_type::time_point _last; - sstring _key; - size_t _packet_size = 8*KB; - char* _mem; - size_t _mem_size; - std::mt19937 _rnd; - std::random_device _randem_dev; - std::uniform_int_distribution _chunk_distribution; -private: - char* next_chunk() { - return _mem + _chunk_distribution(_rnd); - } -public: - server() - : _rnd(std::random_device()()) { - } - future<> send(ipv4_addr dst, packet p) { - return _chan.send(dst, std::move(p)).then([this] { - _n_sent++; - }); - } - void start(int chunk_size, bool copy, size_t mem_size) { - ipv4_addr listen_addr{10000}; - _chan = engine().net().make_udp_channel(listen_addr); - - std::cout << "Listening on " << listen_addr << std::endl; - - _last = clock_type::now(); - _stats_timer.set_callback([this] { - auto now = clock_type::now(); - std::cout << "Out: " - << std::setprecision(2) << std::fixed - << (double)_n_sent / to_seconds(now - _last) - << " pps" << std::endl; - _last = now; - _n_sent = 0; - }); - _stats_timer.arm_periodic(1s); - - _chunk_size = chunk_size; - _copy = copy; - _key = sstring(new char[64], 64); - - _out = std::make_unique>( - data_sink(std::make_unique(_packets)), _packet_size); - - _mem = new char[mem_size]; - _mem_size = mem_size; - - _chunk_distribution = std::uniform_int_distribution(0, _mem_size - _chunk_size * 3); - - assert(3 * _chunk_size <= _packet_size); - - keep_doing([this] { - return _chan.receive().then([this] (udp_datagram dgram) { - auto chunk = next_chunk(); - lw_shared_ptr item; - if (_copy) { - _packets.clear(); - _out->write(chunk, _chunk_size); - chunk += _chunk_size; - _out->write(chunk, _chunk_size); - chunk += _chunk_size; - _out->write(chunk, _chunk_size); - _out->flush(); - assert(_packets.size() == 1); - return send(dgram.get_src(), std::move(_packets[0])); - } else { - auto chunk = next_chunk(); - scattered_message msg; - msg.reserve(3); - msg.append_static(chunk, _chunk_size); - msg.append_static(chunk, _chunk_size); - msg.append_static(chunk, _chunk_size); - return send(dgram.get_src(), std::move(msg).release()); - } - }); - }); - } -}; - -int main(int ac, char ** av) { - server s; - app_template app; - app.add_options() - ("chunk-size", bpo::value()->default_value(1024), - "Chunk size") - ("mem-size", bpo::value()->default_value(512), - "Memory pool size in MiB") - ("copy", "Copy data rather than send via zero-copy") - ; - return app.run(ac, av, [&app, &s] { - auto&& config = app.configuration(); - auto chunk_size = config["chunk-size"].as(); - auto mem_size = (size_t)config["mem-size"].as() * MB; - auto copy = config.count("copy"); - s.start(chunk_size, copy, mem_size); - }); -} diff --git a/util/conversions.cc b/util/conversions.cc deleted file mode 100644 index 11042d836b..0000000000 --- a/util/conversions.cc +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef CONVERSIONS_CC_ -#define CONVERSIONS_CC_ - -#include "conversions.hh" -#include "core/print.hh" -#include - -size_t parse_memory_size(std::string s) { - size_t factor = 1; - if (s.size()) { - auto c = s[s.size() - 1]; - static std::string suffixes = "kMGT"; - auto pos = suffixes.find(c); - if (pos == suffixes.npos) { - throw std::runtime_error(sprint("Cannot parse memory size '%s'", s)); - } - factor <<= (pos + 1) * 10; - s = s.substr(0, s.size() - 1); - } - return boost::lexical_cast(s) * factor; -} - - -#endif /* CONVERSIONS_CC_ */ diff --git a/util/conversions.hh b/util/conversions.hh deleted file mode 100644 index 960085d557..0000000000 --- a/util/conversions.hh +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef CONVERSIONS_HH_ -#define CONVERSIONS_HH_ - -#include -#include -#include - -// Convert a string to a memory size, allowing binary SI -// suffixes (intentionally, even though SI suffixes are -// decimal, to follow existing usage). -// -// "5" -> 5 -// "4k" -> (4 << 10) -// "8M" -> (8 << 20) -// "7G" -> (7 << 30) -// "1T" -> (1 << 40) -// anything else: exception -size_t parse_memory_size(std::string s); - -static inline std::vector string2vector(std::string str) { - auto v = std::vector(str.begin(), str.end()); - v.push_back('\0'); - return v; -} - -#endif /* CONVERSIONS_HH_ */ diff --git a/util/defer.hh b/util/defer.hh deleted file mode 100644 index a36778c30b..0000000000 --- a/util/defer.hh +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef UTIL_DEFER_HH_ -#define UTIL_DEFER_HH_ - -template -class deferred_action { - Func _func; - bool _cancelled = false; -public: - deferred_action(Func&& func) : _func(std::move(func)) {} - ~deferred_action() { _func(); } - void cancel() { _cancelled = true; } -}; - -template -inline -deferred_action -defer(Func&& func) { - return deferred_action(std::forward(func)); -} - -#endif /* UTIL_DEFER_HH_ */ diff --git a/util/eclipse.hh b/util/eclipse.hh deleted file mode 100644 index 26b72a081b..0000000000 --- a/util/eclipse.hh +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - * - */ - -#ifndef ECLIPSE_HH_ -#define ECLIPSE_HH_ - -// Workarounds for deficiencies in Eclipse's C++ parser -// -// Tell Eclipse that IN_ECLIPSE is defined so it will ignore all the unknown syntax. - -#ifndef IN_ECLIPSE - -#else - -// Eclipse doesn't grok alignof -#define alignof sizeof - -#endif - -#endif /* ECLIPSE_HH_ */ diff --git a/util/function_input_iterator.hh b/util/function_input_iterator.hh deleted file mode 100644 index 4ffbebca2b..0000000000 --- a/util/function_input_iterator.hh +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef UTIL_FUNCTION_INPUT_ITERATOR_HH_ -#define UTIL_FUNCTION_INPUT_ITERATOR_HH_ - -template -struct function_input_iterator { - Function _func; - State _state; -public: - function_input_iterator(Function func, State state) - : _func(func), _state(state) { - } - function_input_iterator(const function_input_iterator&) = default; - function_input_iterator(function_input_iterator&&) = default; - function_input_iterator& operator=(const function_input_iterator&) = default; - function_input_iterator& operator=(function_input_iterator&&) = default; - auto operator*() const { - return _func(); - } - function_input_iterator& operator++() { - ++_state; - return *this; - } - function_input_iterator operator++(int) { - function_input_iterator ret{*this}; - ++_state; - return ret; - } - bool operator==(const function_input_iterator& x) const { - return _state == x._state; - } - bool operator!=(const function_input_iterator& x) const { - return !operator==(x); - } -}; - -template -inline -function_input_iterator -make_function_input_iterator(Function func, State state) { - return function_input_iterator(func, state); -} - -template -inline -function_input_iterator -make_function_input_iterator(Function&& func) { - return function_input_iterator(func, State{}); -} - -#endif /* UTIL_FUNCTION_INPUT_ITERATOR_HH_ */ diff --git a/util/is_smart_ptr.hh b/util/is_smart_ptr.hh deleted file mode 100644 index 9a45bc1573..0000000000 --- a/util/is_smart_ptr.hh +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2015 Cloudius Systems, Ltd. - */ - -#pragma once - -#include // for std::unique_ptr - -template -struct is_smart_ptr : std::false_type {}; - -template -struct is_smart_ptr> : std::true_type {}; diff --git a/util/transform_iterator.hh b/util/transform_iterator.hh deleted file mode 100644 index e726c1c29e..0000000000 --- a/util/transform_iterator.hh +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (C) 2014 Cloudius Systems, Ltd. - */ - -#ifndef UTIL_TRANSFORM_ITERATOR_HH_ -#define UTIL_TRANSFORM_ITERATOR_HH_ - -template -class transform_iterator { - Iterator _i; - Func _f; -public: - transform_iterator(Iterator i, Func f) : _i(i), _f(f) {} - auto operator*() { return _f(*_i); } - transform_iterator& operator++() { - ++_i; - return *this; - } - transform_iterator operator++(int) { - transform_iterator ret(*this); - _i++; - return ret; - } - bool operator==(const transform_iterator& x) const { - return _i == x._i; - } - bool operator!=(const transform_iterator& x) const { - return !operator==(x); - } -}; - -template -inline -transform_iterator -make_transform_iterator(Iterator i, Func f) { - return transform_iterator(i, f); -} - -#endif /* UTIL_TRANSFORM_ITERATOR_HH_ */