Move seastar to a submodule

Instead of urchin being layered on top of seastar, move seastar to a
subdirectory (as a submodule) and use seastar.pc/libseastar.a to link
with it.
This commit is contained in:
Avi Kivity
2015-03-17 16:16:01 +02:00
parent 04b4047cac
commit cc17c44640
206 changed files with 98 additions and 42145 deletions

6
.gitmodules vendored
View File

@@ -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

View File

@@ -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

11
NOTICE
View File

@@ -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.

View File

@@ -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
<html><head><title>this is the future</title></head><body><p>Future!!</p></body></html>
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).

348
README.md
View File

@@ -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<int> 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<int> 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<int> 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<buffer> receive();
request parse(buffer buf);
future<response> 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_type> my_future();
void f() {
receive().then_wrapped([] (future<my_type> 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.

View File

@@ -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"]
}
}
}
}
}

View File

@@ -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<std::unique_ptr<reply> > handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> rep) {
rep->_content = "hello";
rep->done("html");
return make_ready_future<std::unique_ptr<reply>>(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<request> req) {
return make_ready_future<json::json_return_type>("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<uint16_t>()->default_value(10000),
"HTTP Server port");
return app.run(ac, av, [&] {
auto&& config = app.configuration();
uint16_t port = config["port"].as<uint16_t>();
auto server = new http_server_control();
auto rb = make_shared<api_registry_builder>("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();
});
});
});
}

View File

@@ -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 <memory>
#include <algorithm>
#include <functional>
%%{
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<memcache_ascii_parser> {
%% 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<memcache::item_key> _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;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -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<sstring>()(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<memcache::item_key> {
size_t operator()(const memcache::item_key& key) {
return key.hash();
}
};
} /* namespace std */
#endif

View File

@@ -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 <memory>
#include <unordered_map>
struct http_response {
sstring _version;
std::unordered_map<sstring, sstring> _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<http_response_parser> {
%% write data nofinal noprefix;
public:
enum class state {
error,
eof,
done,
};
std::unique_ptr<http_response> _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;
}
};

View File

@@ -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 <chrono>
template <typename... Args>
void http_debug(const char* fmt, Args&&... args) {
#if HTTP_DEBUG
print(fmt, std::forward<Args>(args)...);
#endif
}
class http_client {
private:
unsigned _duration;
unsigned _conn_per_core;
unsigned _reqs_per_conn;
std::vector<connected_socket> _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<char> _read_buf;
output_stream<char> _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<char> buf) {
_nr_done++;
http_debug("%s\n", buf.get());
if (_http_client->done(_nr_done)) {
return make_ready_future();
} else {
return do_req();
}
});
});
});
}
};
future<uint64_t> total_reqs() {
print("Requests on cpu %2d: %ld\n", engine().cpu_id(), _total_reqs);
return make_ready_future<uint64_t>(_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<std::string>()->default_value("192.168.66.100:10000"), "Server address")
("conn,c", bpo::value<unsigned>()->default_value(100), "total connections")
("reqs,r", bpo::value<unsigned>()->default_value(0), "reqs per connection")
("duration,d", bpo::value<unsigned>()->default_value(10), "duration of the test in seconds)");
return app.run(ac, av, [&app] {
auto& config = app.configuration();
auto server = config["server"].as<std::string>();
auto reqs_per_conn = config["reqs"].as<unsigned>();
auto total_conn= config["conn"].as<unsigned>();
auto duration = config["duration"].as<unsigned>();
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<http_client>;
// 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<uint64_t>(), &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<double>(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<double>(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);
});
});
});
}

View File

@@ -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 <stdint.h>\n'
source += '#include <xen/xen.h>\n'
source += '#include <xen/sys/evtchn.h>\n'
source += '#include <xen/sys/gntdev.h>\n'
source += '#include <xen/sys/gntalloc.h>\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=<path>" 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 <hwloc.h>\n#include <numa.h>')
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

View File

@@ -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 <cstdint>
#include <cstdlib>
template <typename T>
inline constexpr
T align_up(T v, T align) {
return (v + align - 1) & ~(align - 1);
}
template <typename T>
inline constexpr
T* align_up(T* v, size_t align) {
static_assert(sizeof(T) == 1, "align byte pointers only");
return reinterpret_cast<T*>(align_up(reinterpret_cast<uintptr_t>(v), align));
}
template <typename T>
inline constexpr
T align_down(T v, T align) {
return v & ~(align - 1);
}
template <typename T>
inline constexpr
T* align_down(T* v, size_t align) {
static_assert(sizeof(T) == 1, "align byte pointers only");
return reinterpret_cast<T*>(align_down(reinterpret_cast<uintptr_t>(v), align));
}
#endif /* ALIGN_HH_ */

View File

@@ -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 <boost/program_options.hpp>
#include <boost/make_shared.hpp>
#include <fstream>
#include <cstdlib>
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<positional_option> options) {
for (auto&& o : options) {
_opts.add(boost::make_shared<bpo::option_description>(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<void ()>&& 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;
}

View File

@@ -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 <boost/program_options.hpp>
#include <boost/optional.hpp>
#include <functional>
class app_template {
private:
boost::program_options::options_description _opts;
boost::program_options::positional_options_description _pos_opts;
boost::optional<boost::program_options::variables_map> _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<positional_option> options);
boost::program_options::variables_map& configuration();
int run(int ac, char ** av, std::function<void ()>&& func);
};
#endif

View File

@@ -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 <tuple>
#include <utility>
template <typename Func, typename Args, typename IndexList>
struct apply_helper;
template <typename Func, typename Tuple, size_t... I>
struct apply_helper<Func, Tuple, std::index_sequence<I...>> {
static auto apply(Func&& func, Tuple args) {
return func(std::get<I>(std::forward<Tuple>(args))...);
}
};
template <typename Func, typename... T>
inline
auto apply(Func&& func, std::tuple<T...>&& args) {
using helper = apply_helper<Func, std::tuple<T...>&&, std::index_sequence_for<T...>>;
return helper::apply(std::forward<Func>(func), std::move(args));
}
template <typename Func, typename... T>
inline
auto apply(Func&& func, std::tuple<T...>& args) {
using helper = apply_helper<Func, std::tuple<T...>&, std::index_sequence_for<T...>>;
return helper::apply(std::forward<Func>(func), args);
}
template <typename Func, typename... T>
inline
auto apply(Func&& func, const std::tuple<T...>& args) {
using helper = apply_helper<Func, const std::tuple<T...>&, std::index_sequence_for<T...>>;
return helper::apply(std::forward<Func>(func), args);
}
#endif /* APPLY_HH_ */

View File

@@ -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 <array>
// unordered_map implemented as a simple array
template <typename Value, size_t Max>
class array_map {
std::array<Value, Max> _a {};
public:
array_map(std::initializer_list<std::pair<size_t, Value>> 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_ */

View File

@@ -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_ */

View File

@@ -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 <bitset>
#include <limits>
namespace bitsets {
static constexpr int ulong_bits = std::numeric_limits<unsigned long>::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<T>::digits - 1,
* which is returned when value == 1.
*/
template<typename T>
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<T>::digits - 1.
*/
template<typename T>
static inline size_t count_trailing_zeros(T value);
template<>
inline size_t count_leading_zeros<unsigned long>(unsigned long value)
{
return __builtin_clzl(value);
}
template<>
inline size_t count_leading_zeros<long>(long value)
{
return __builtin_clzl((unsigned long)value) - 1;
}
template<>
inline
size_t count_trailing_zeros<unsigned long>(unsigned long value)
{
return __builtin_ctzl(value);
}
template<>
inline
size_t count_trailing_zeros<long>(long value)
{
return __builtin_ctzl((unsigned long)value);
}
/**
* Returns the index of the first set bit.
* Result is undefined if bitset.any() == false.
*/
template<size_t N>
static inline size_t get_first_set(const std::bitset<N>& 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<size_t N>
static inline size_t get_last_set(const std::bitset<N>& bitset)
{
static_assert(N <= ulong_bits, "bitset too large");
return ulong_bits - 1 - count_leading_zeros(bitset.to_ulong());
}
template<size_t N>
class set_iterator : public std::iterator<std::input_iterator_tag, int>
{
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<N> 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<N> _bitset;
int _index;
};
template<size_t N>
class set_range
{
public:
using iterator = set_iterator<N>;
using value_type = int;
set_range(std::bitset<N> bitset, int offset = 0)
: _bitset(bitset)
, _offset(offset)
{
}
iterator begin() const { return iterator(_bitset, _offset); }
iterator end() const { return iterator(0); }
private:
std::bitset<N> _bitset;
int _offset;
};
template<size_t N>
static inline set_range<N> for_each_set(std::bitset<N> bitset, int offset = 0)
{
return set_range<N>(bitset, offset);
}
}
#endif

View File

@@ -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 <memory>
#include <algorithm>
template <typename T, typename Alloc = std::allocator<T>>
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 <typename... A>
void emplace_front(A&&... args);
void push_back(const T& data);
void push_back(T&& data);
template <typename... A>
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 <typename Func>
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<typename CB, typename ValueType>
struct cbiterator : std::iterator<std::random_access_iterator_tag, ValueType> {
typedef std::iterator<std::random_access_iterator_tag, ValueType> super_t;
ValueType& operator*() const { return cb->_impl.storage[cb->mask(idx)]; }
ValueType* operator->() const { return &cb->_impl.storage[cb->mask(idx)]; }
// prefix
cbiterator<CB, ValueType>& operator++() {
idx++;
return *this;
}
// postfix
cbiterator<CB, ValueType> operator++(int unused) {
auto v = *this;
idx++;
return v;
}
// prefix
cbiterator<CB, ValueType>& operator--() {
idx--;
return *this;
}
// postfix
cbiterator<CB, ValueType> operator--(int unused) {
auto v = *this;
idx--;
return v;
}
cbiterator<CB, ValueType> operator+(typename super_t::difference_type n) const {
return cbiterator<CB, ValueType>(cb, idx + n);
}
cbiterator<CB, ValueType> operator-(typename super_t::difference_type n) const {
return cbiterator<CB, ValueType>(cb, idx - n);
}
cbiterator<CB, ValueType>& operator+=(typename super_t::difference_type n) {
idx += n;
return *this;
}
cbiterator<CB, ValueType>& operator-=(typename super_t::difference_type n) {
idx -= n;
return *this;
}
bool operator==(const cbiterator<CB, ValueType>& rhs) const {
return idx == rhs.idx;
}
bool operator!=(const cbiterator<CB, ValueType>& rhs) const {
return idx != rhs.idx;
}
bool operator<(const cbiterator<CB, ValueType>& rhs) const {
return idx < rhs.idx;
}
bool operator>(const cbiterator<CB, ValueType>& rhs) const {
return idx > rhs.idx;
}
bool operator>=(const cbiterator<CB, ValueType>& rhs) const {
return idx >= rhs.idx;
}
bool operator<=(const cbiterator<CB, ValueType>& rhs) const {
return idx <= rhs.idx;
}
typename super_t::difference_type operator-(const cbiterator<CB, ValueType>& rhs) const {
return idx - rhs.idx;
}
private:
CB* cb;
size_t idx;
cbiterator<CB, ValueType>(CB* b, size_t i) : cb(b), idx(i) {}
friend class circular_buffer;
};
friend class iterator;
public:
typedef cbiterator<circular_buffer, T> iterator;
typedef cbiterator<const circular_buffer, const T> 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 <typename T, typename Alloc>
inline
size_t
circular_buffer<T, Alloc>::mask(size_t idx) const {
return idx & (_impl.capacity - 1);
}
template <typename T, typename Alloc>
inline
bool
circular_buffer<T, Alloc>::empty() const {
return _impl.begin == _impl.end;
}
template <typename T, typename Alloc>
inline
size_t
circular_buffer<T, Alloc>::size() const {
return _impl.end - _impl.begin;
}
template <typename T, typename Alloc>
inline
size_t
circular_buffer<T, Alloc>::capacity() const {
return _impl.capacity;
}
template <typename T, typename Alloc>
inline
circular_buffer<T, Alloc>::circular_buffer(circular_buffer&& x)
: _impl(std::move(x._impl)) {
x._impl = {};
}
template <typename T, typename Alloc>
template <typename Func>
inline
void
circular_buffer<T, Alloc>::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 <typename T, typename Alloc>
inline
circular_buffer<T, Alloc>::~circular_buffer() {
for_each([this] (T& obj) {
_impl.destroy(&obj);
});
_impl.deallocate(_impl.storage, _impl.capacity);
}
template <typename T, typename Alloc>
void
circular_buffer<T, Alloc>::expand() {
auto new_cap = std::max<size_t>(_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 <typename T, typename Alloc>
inline
void
circular_buffer<T, Alloc>::maybe_expand(size_t nr) {
if (_impl.end - _impl.begin + nr > _impl.capacity) {
expand();
}
}
template <typename T, typename Alloc>
inline
void
circular_buffer<T, Alloc>::push_front(const T& data) {
maybe_expand();
auto p = &_impl.storage[mask(_impl.begin - 1)];
_impl.construct(p, data);
--_impl.begin;
}
template <typename T, typename Alloc>
inline
void
circular_buffer<T, Alloc>::push_front(T&& data) {
maybe_expand();
auto p = &_impl.storage[mask(_impl.begin - 1)];
_impl.construct(p, std::move(data));
--_impl.begin;
}
template <typename T, typename Alloc>
template <typename... Args>
inline
void
circular_buffer<T, Alloc>::emplace_front(Args&&... args) {
maybe_expand();
auto p = &_impl.storage[mask(_impl.begin - 1)];
_impl.construct(p, std::forward<Args>(args)...);
--_impl.begin;
}
template <typename T, typename Alloc>
inline
void
circular_buffer<T, Alloc>::push_back(const T& data) {
maybe_expand();
auto p = &_impl.storage[mask(_impl.end)];
_impl.construct(p, data);
++_impl.end;
}
template <typename T, typename Alloc>
inline
void
circular_buffer<T, Alloc>::push_back(T&& data) {
maybe_expand();
auto p = &_impl.storage[mask(_impl.end)];
_impl.construct(p, std::move(data));
++_impl.end;
}
template <typename T, typename Alloc>
template <typename... Args>
inline
void
circular_buffer<T, Alloc>::emplace_back(Args&&... args) {
maybe_expand();
auto p = &_impl.storage[mask(_impl.end)];
_impl.construct(p, std::forward<Args>(args)...);
++_impl.end;
}
template <typename T, typename Alloc>
inline
T&
circular_buffer<T, Alloc>::front() {
return _impl.storage[mask(_impl.begin)];
}
template <typename T, typename Alloc>
inline
T&
circular_buffer<T, Alloc>::back() {
return _impl.storage[mask(_impl.end - 1)];
}
template <typename T, typename Alloc>
inline
void
circular_buffer<T, Alloc>::pop_front() {
_impl.destroy(&front());
++_impl.begin;
}
template <typename T, typename Alloc>
inline
void
circular_buffer<T, Alloc>::pop_back() {
_impl.destroy(&back());
--_impl.end;
}
template <typename T, typename Alloc>
inline
T&
circular_buffer<T, Alloc>::operator[](size_t idx) {
return _impl.storage[mask(_impl.begin + idx)];
}
template <typename T, typename Alloc>
inline
T&
circular_buffer<T, Alloc>::access_element_unsafe(size_t idx) {
return _impl.storage[mask(_impl.begin + idx)];
}
#endif /* CIRCULAR_BUFFER_HH_ */

View File

@@ -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 <memory>
#include <cstdlib>
#include <assert.h>
#include <type_traits>
/// \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<uintptr_t>(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<uintptr_t>(i);
return reinterpret_cast<void*>(x & ~uintptr_t(1));
}
void* to_raw_object() const {
return to_raw_object(_impl);
}
impl* from_raw_object(void* object) {
auto x = reinterpret_cast<uintptr_t>(object);
return reinterpret_cast<impl*>(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 <typename Deleter>
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 <typename Object>
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 <typename Object>
inline
object_deleter_impl<Object>* make_object_deleter_impl(deleter next, Object obj) {
return new object_deleter_impl<Object>(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 <typename Object>
deleter
make_deleter(deleter next, Object o) {
return deleter(new lambda_deleter_impl<Object>(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 <typename Object>
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 <typename T>
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 <typename T>
inline
deleter
make_object_deleter(deleter d, T&& obj) {
return deleter{make_object_deleter_impl(std::move(d), std::move(obj))};
}
/// @}
#endif /* DELETER_HH_ */

View File

@@ -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 <typename Service>
using distributed = seastar::sharded<Service>;

View File

@@ -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 <utility>
#include <memory>
#include <tuple>
/// \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<typename T, typename F>
inline
auto do_with(T&& rvalue, F&& f) {
auto obj = std::make_unique<T>(std::forward<T>(rvalue));
auto fut = f(*obj);
return fut.finally([obj = std::move(obj)] () {});
}
/// \cond internal
template <typename Tuple, size_t... Idx>
inline
auto
cherry_pick_tuple(std::index_sequence<Idx...>, Tuple&& tuple) {
return std::make_tuple(std::get<Idx>(std::forward<Tuple>(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 <typename T1, typename T2, typename T3_or_F, typename... More>
inline
auto
do_with(T1&& rv1, T2&& rv2, T3_or_F&& rv3, More&&... more) {
auto all = std::forward_as_tuple(
std::forward<T1>(rv1),
std::forward<T2>(rv2),
std::forward<T3_or_F>(rv3),
std::forward<More>(more)...);
constexpr size_t nr = std::tuple_size<decltype(all)>::value - 1;
using idx = std::make_index_sequence<nr>;
auto&& just_values = cherry_pick_tuple(idx(), std::move(all));
auto&& just_func = std::move(std::get<nr>(std::move(all)));
auto obj = std::make_unique<std::remove_reference_t<decltype(just_values)>>(std::move(just_values));
auto fut = apply(just_func, *obj);
return fut.finally([obj = std::move(obj)] () {});
}
/// @}

View File

@@ -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 <experimental/optional>
#include <rte_pci.h>
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<std::vector<char>> args {
string2vector("dpdk_args"),
string2vector("-c"), string2vector(mask.str()),
string2vector("-n"), string2vector("1")
};
std::experimental::optional<std::string> hugepages_path;
if (opts.count("hugepages")) {
hugepages_path = opts["hugepages"].as<std::string>();
}
// 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<char*> 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

View File

@@ -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 <bitset>
#include <rte_config.h>
#include <rte_ethdev.h>
#include <rte_version.h>
#include <boost/program_options.hpp>
/*********************** 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<RTE_MAX_LCORE>;
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_

View File

@@ -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 <type_traits>
#include <functional>
#include <cstddef>
template <typename T>
class enum_hash {
static_assert(std::is_enum<T>::value, "must be an enum");
public:
std::size_t operator()(const T& e) const {
using utype = typename std::underlying_type<T>::type;
return std::hash<utype>()(static_cast<utype>(e));
}
};

View File

@@ -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 <experimental/optional>
#include <system_error>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <sys/uio.h>
#include <unistd.h>
/// \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<directory_entry_type> type;
};
/// \cond internal
class file_impl {
public:
virtual ~file_impl() {}
virtual future<size_t> write_dma(uint64_t pos, const void* buffer, size_t len) = 0;
virtual future<size_t> write_dma(uint64_t pos, std::vector<iovec> iov) = 0;
virtual future<size_t> read_dma(uint64_t pos, void* buffer, size_t len) = 0;
virtual future<size_t> read_dma(uint64_t pos, std::vector<iovec> iov) = 0;
virtual future<> flush(void) = 0;
virtual future<struct stat> 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<uint64_t> size(void) = 0;
virtual future<> close() = 0;
virtual subscription<directory_entry> list_directory(std::function<future<> (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<size_t> write_dma(uint64_t pos, const void* buffer, size_t len);
future<size_t> write_dma(uint64_t pos, std::vector<iovec> iov);
future<size_t> read_dma(uint64_t pos, void* buffer, size_t len);
future<size_t> read_dma(uint64_t pos, std::vector<iovec> iov);
future<> flush(void);
future<struct stat> 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_t> size(void);
virtual future<> close() override;
virtual subscription<directory_entry> list_directory(std::function<future<> (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_t> size(void) override;
virtual future<> allocate(uint64_t position, uint64_t length) override;
};
inline
shared_ptr<file_impl>
make_file_impl(int fd) {
auto r = ::ioctl(fd, BLKGETSIZE);
if (r != -1) {
return make_shared<blockdev_file_impl>(fd);
} else {
return make_shared<posix_file_impl>(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> _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 <typename CharType>
future<size_t>
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 <typename CharType>
future<temporary_buffer<CharType>> dma_read(uint64_t pos, size_t len) {
return dma_read_bulk<CharType>(pos, len).then(
[len] (temporary_buffer<CharType> 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 <typename CharType>
future<temporary_buffer<CharType>>
dma_read_exactly(uint64_t pos, size_t len) {
return dma_read<CharType>(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<size_t> dma_read(uint64_t pos, std::vector<iovec> 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 <typename CharType>
future<size_t> 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<size_t> dma_write(uint64_t pos, std::vector<iovec> 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<struct stat> 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<uint64_t> 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<directory_entry> list_directory(std::function<future<> (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 <typename CharType>
future<temporary_buffer<CharType>>
dma_read_bulk(uint64_t offset, size_t range_size);
private:
template <typename CharType>
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 <typename CharType>
future<temporary_buffer<CharType>>
read_maybe_eof(uint64_t pos, size_t len);
friend class reactor;
};
/// \cond internal
template <typename CharType>
struct file::read_state {
typedef temporary_buffer<CharType> 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 <typename CharType>
future<temporary_buffer<CharType>>
file::dma_read_bulk(uint64_t offset, size_t range_size) {
using tmp_buf_type = typename read_state<CharType>::tmp_buf_type;
auto front = offset & (dma_alignment - 1);
offset -= front;
range_size += front;
auto rstate = make_lw_shared<read_state<CharType>>(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<CharType>(
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<tmp_buf_type>(std::move(rstate->buf));
});
});
}
template <typename CharType>
future<temporary_buffer<CharType>>
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<CharType> buf = temporary_buffer<CharType>::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<size_t> 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_ */

View File

@@ -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 <malloc.h>
#include <string.h>
class file_data_source_impl : public data_source_impl {
lw_shared_ptr<file> _file;
uint64_t _pos;
size_t _buffer_size;
public:
file_data_source_impl(lw_shared_ptr<file> f, uint64_t pos, size_t buffer_size)
: _file(std::move(f)), _pos(pos), _buffer_size(buffer_size) {}
virtual future<temporary_buffer<char>> get() override {
using buf_type = temporary_buffer<char>;
return _file->dma_read_bulk<char>(_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<file> f, uint64_t offset, size_t buffer_size)
: data_source(std::make_unique<file_data_source_impl>(
std::move(f), offset, buffer_size)) {}
};
input_stream<char> make_file_input_stream(
lw_shared_ptr<file> f, uint64_t offset, size_t buffer_size) {
return input_stream<char>(file_data_source(std::move(f), offset, buffer_size));
}
class file_data_sink_impl : public data_sink_impl {
lw_shared_ptr<file> _file;
file_output_stream_options _options;
uint64_t _pos = 0;
uint64_t _last_preallocation = 0;
public:
file_data_sink_impl(lw_shared_ptr<file> f, file_output_stream_options options)
: _file(std::move(f)), _options(options) {}
future<> put(net::packet data) { abort(); }
virtual temporary_buffer<char> allocate_buffer(size_t size) override {
return temporary_buffer<char>::aligned(file::dma_alignment, size);
}
virtual future<> put(temporary_buffer<char> 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<const char*>(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<uint64_t>(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<file> f, file_output_stream_options options)
: data_sink(std::make_unique<file_data_sink_impl>(
std::move(f), options)) {}
};
output_stream<char> make_file_output_stream(lw_shared_ptr<file> 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<char> make_file_output_stream(lw_shared_ptr<file> f, file_output_stream_options options) {
return output_stream<char>(file_data_sink(std::move(f), options), options.buffer_size, true);
}

View File

@@ -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<char> make_file_input_stream(
lw_shared_ptr<file> 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<char> make_file_output_stream(
lw_shared_ptr<file> 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<char> make_file_output_stream(
lw_shared_ptr<file> file,
file_output_stream_options options);

View File

@@ -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 <tuple>
template<typename T>
struct function_traits;
template<typename Ret, typename... Args>
struct function_traits<Ret(Args...)>
{
using return_type = Ret;
using args_as_tuple = std::tuple<Args...>;
using signature = Ret (Args...);
static constexpr std::size_t arity = sizeof...(Args);
template <std::size_t N>
struct arg
{
static_assert(N < arity, "no such parameter index.");
using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
};
template<typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...)> : public function_traits<Ret(Args...)>
{};
template <typename T, typename Ret, typename... Args>
struct function_traits<Ret(T::*)(Args...)> : public function_traits<Ret(Args...)>
{};
template <typename T, typename Ret, typename... Args>
struct function_traits<Ret(T::*)(Args...) const> : public function_traits<Ret(Args...)>
{};
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template<typename T>
struct function_traits<T&> : public function_traits<std::remove_reference_t<T>>
{};

View File

@@ -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 <tuple>
#include <iterator>
#include <vector>
/// \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 <typename Iterator, typename Func>
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 <typename Range, typename Func>
inline
future<>
parallel_for_each(Range&& range, Func&& func) {
return parallel_for_each(std::begin(range), std::end(range),
std::forward<Func>(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<typename AsyncAction, typename StopCondition>
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<AsyncAction>(action),
stop_cond = std::forward<StopCondition>(stop_cond), p = std::move(p)](std::result_of_t<AsyncAction()> fut) mutable {
try {
fut.get();
do_until_continued(stop_cond, std::forward<AsyncAction>(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<stop_iteration>. 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<typename AsyncAction>
static inline
future<> repeat(AsyncAction&& action) {
using futurator = futurize<std::result_of_t<AsyncAction()>>;
static_assert(std::is_same<future<stop_iteration>, 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<AsyncAction>(action)] (stop_iteration stop) mutable {
if (stop == stop_iteration::yes) {
return make_ready_future<>();
} else {
return repeat(std::forward<AsyncAction>(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<AsyncAction>(action), p = std::move(p)] () mutable {
repeat(std::forward<AsyncAction>(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<typename AsyncAction, typename StopCondition>
static inline
future<> do_until(StopCondition&& stop_cond, AsyncAction&& action) {
promise<> p;
auto f = p.get_future();
do_until_continued(std::forward<StopCondition>(stop_cond),
std::forward<AsyncAction>(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<typename AsyncAction>
static inline
future<> keep_doing(AsyncAction&& action) {
return repeat([action = std::forward<AsyncAction>(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<typename Iterator, typename AsyncAction>
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<AsyncAction>(action),
begin = std::move(begin), end = std::move(end)] () mutable {
return do_for_each(std::move(begin), std::move(end), std::forward<AsyncAction>(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<typename Container, typename AsyncAction>
static inline
future<> do_for_each(Container& c, AsyncAction&& action) {
return do_for_each(std::begin(c), std::end(c), std::forward<AsyncAction>(action));
}
/// \cond internal
inline
future<std::tuple<>>
when_all() {
return make_ready_future<std::tuple<>>();
}
// 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 <typename... Future>
future<std::tuple<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 <typename... FutureArgs, typename... Rest>
inline
future<std::tuple<future<FutureArgs...>, Rest...>>
when_all(future<FutureArgs...>&& fut, Rest&&... rest) {
using Future = future<FutureArgs...>;
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<std::tuple<Rest...>>&& rest) mutable {
return make_ready_future<std::tuple<Future, Rest...>>(
std::tuple_cat(std::make_tuple(std::move(fut)), std::get<0>(rest.get())));
});
});
}
/// \cond internal
template <typename Iterator, typename IteratorCategory>
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 <typename Iterator>
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 <typename Future>
inline
future<std::vector<Future>>
complete_when_all(std::vector<Future>&& futures, typename std::vector<Future>::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::vector<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 <typename FutureIterator>
inline
future<std::vector<typename std::iterator_traits<FutureIterator>::value_type>>
when_all(FutureIterator begin, FutureIterator end) {
using itraits = std::iterator_traits<FutureIterator>;
std::vector<typename itraits::value_type> 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 <typename T>
struct reducer_with_get_traits {
using result_type = decltype(std::declval<T>().get());
using future_type = future<result_type>;
static future_type maybe_call_get(future<> f, lw_shared_ptr<T> r) {
return f.then([r = std::move(r)] () mutable {
return make_ready_future<result_type>(std::move(*r).get());
});
}
};
template <typename T, typename V = void>
struct reducer_traits {
using future_type = future<>;
static future_type maybe_call_get(future<> f, lw_shared_ptr<T> r) {
return f.then([r = std::move(r)] {});
}
};
template <typename T>
struct reducer_traits<T, decltype(std::declval<T>().get(), void())> : public reducer_with_get_traits<T> {};
// @Mapper is a callable which transforms values from the iterator range
// into a future<T>. @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 <typename Iterator, typename Mapper, typename Reducer>
inline
auto
map_reduce(Iterator begin, Iterator end, Mapper&& mapper, Reducer&& r)
-> typename reducer_traits<Reducer>::future_type
{
auto r_ptr = make_lw_shared(std::forward<Reducer>(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<Reducer>::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<size_t>())
/// \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<Initial>
///
/// \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 <typename Iterator, typename Mapper, typename Initial, typename Reduce>
inline
future<Initial>
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<Initial>(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<file> files = ...;
/// map_reduce(files,
/// std::mem_fn(file::size),
/// size_t(0),
/// std::plus<size_t>())
/// \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<Initial>
///
/// \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 <typename Range, typename Mapper, typename Initial, typename Reduce>
inline
future<Initial>
map_reduce(Range&& range, Mapper&& mapper, Initial initial, Reduce reduce) {
return map_reduce(std::begin(range), std::end(range), std::forward<Mapper>(mapper),
std::move(initial), std::move(reduce));
}
// Implements @Reducer concept. Calculates the result by
// adding elements to the accumulator.
template <typename Result, typename Addend = Result>
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_ */

File diff suppressed because it is too large Load Diff

View File

@@ -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 <experimental/optional>
#include <exception>
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<promise<>> _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 <typename Func>
inline
auto
with_gate(gate& g, Func&& func) {
g.enter();
return func().finally([&g] { g.leave(); });
}
/// @}
}

View File

@@ -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<typename CharType>
inline
future<> output_stream<CharType>::write(const char_type* buf) {
return write(buf, strlen(buf));
}
template<typename CharType>
template<typename StringChar, typename SizeType, SizeType MaxSize>
inline
future<> output_stream<CharType>::write(const basic_sstring<StringChar, SizeType, MaxSize>& s) {
return write(reinterpret_cast<const CharType *>(s.c_str()), s.size());
}
template<typename CharType>
inline
future<> output_stream<CharType>::write(const std::basic_string<CharType>& s) {
return write(s.c_str(), s.size());
}
template<typename CharType>
future<> output_stream<CharType>::write(scattered_message<CharType> msg) {
return write(std::move(msg).release());
}
template<typename CharType>
future<> output_stream<CharType>::write(net::packet p) {
static_assert(std::is_same<CharType, char>::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 <typename CharType>
future<temporary_buffer<CharType>>
input_stream<CharType>::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<tmp_buf>(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<tmp_buf>(std::move(buf));
}
_buf = std::move(buf);
return this->read_exactly_part(n, std::move(out), completed);
});
}
template <typename CharType>
future<temporary_buffer<CharType>>
input_stream<CharType>::read_exactly(size_t n) {
if (_buf.size() == n) {
// easy case: steal buffer, return to caller
return make_ready_future<tmp_buf>(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<tmp_buf>(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<tmp_buf>(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 <typename CharType>
template <typename Consumer>
future<>
input_stream<CharType>::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_remainder> 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 <typename CharType>
future<>
output_stream<CharType>::split_and_put(temporary_buffer<CharType> 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 <typename CharType>
future<>
output_stream<CharType>::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<char> 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<char> 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<char> 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 <typename CharType>
future<>
output_stream<CharType>::flush() {
if (!_end) {
return make_ready_future<>();
}
_buf.trim(_end);
_end = 0;
return _fd.put(std::move(_buf));
}

View File

@@ -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<temporary_buffer<char>> get() = 0;
};
class data_source {
std::unique_ptr<data_source_impl> _dsi;
protected:
data_source_impl* impl() const { return _dsi.get(); }
public:
data_source() = default;
explicit data_source(std::unique_ptr<data_source_impl> dsi) : _dsi(std::move(dsi)) {}
data_source(data_source&& x) = default;
data_source& operator=(data_source&& x) = default;
future<temporary_buffer<char>> get() { return _dsi->get(); }
};
class data_sink_impl {
public:
virtual ~data_sink_impl() {}
virtual temporary_buffer<char> allocate_buffer(size_t size) {
return temporary_buffer<char>(size);
}
virtual future<> put(net::packet data) = 0;
virtual future<> put(std::vector<temporary_buffer<char>> 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<char> buf) {
return put(net::packet(net::fragment{buf.get_write(), buf.size()}, buf.release()));
}
virtual future<> close() = 0;
};
class data_sink {
std::unique_ptr<data_sink_impl> _dsi;
public:
data_sink() = default;
explicit data_sink(std::unique_ptr<data_sink_impl> dsi) : _dsi(std::move(dsi)) {}
data_sink(data_sink&& x) = default;
data_sink& operator=(data_sink&& x) = default;
temporary_buffer<char> allocate_buffer(size_t size) {
return _dsi->allocate_buffer(size);
}
future<> put(std::vector<temporary_buffer<char>> data) {
return _dsi->put(std::move(data));
}
future<> put(temporary_buffer<char> data) {
return _dsi->put(std::move(data));
}
future<> put(net::packet p) {
return _dsi->put(std::move(p));
}
future<> close() { return _dsi->close(); }
};
template <typename CharType>
class input_stream final {
static_assert(sizeof(CharType) == 1, "must buffer stream of bytes");
data_source _fd;
temporary_buffer<CharType> _buf;
bool _eof = false;
private:
using tmp_buf = temporary_buffer<CharType>;
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<tmp_buf>;
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<unconsumed_remainder> 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<temporary_buffer<CharType>> read_exactly(size_t n);
template <typename Consumer>
future<> consume(Consumer& c);
bool eof() { return _eof; }
private:
future<temporary_buffer<CharType>> 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 <typename CharType>
class output_stream final {
static_assert(sizeof(CharType) == 1, "must buffer stream of bytes");
data_sink _fd;
temporary_buffer<CharType> _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<CharType> 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 <typename StringChar, typename SizeType, SizeType MaxSize>
future<> write(const basic_sstring<StringChar, SizeType, MaxSize>& s);
future<> write(const std::basic_string<char_type>& s);
future<> write(net::packet p);
future<> write(scattered_message<char_type> msg);
future<> flush();
future<> close() { return flush().then([this] { return _fd.close(); }); }
private:
};
#include "iostream-impl.hh"

File diff suppressed because it is too large Load Diff

View File

@@ -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 <new>
#include <functional>
#include <vector>
/// \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<resource::memory> m,
std::experimental::optional<std::string> hugetlbfs_path = {});
void* allocate_reclaimable(size_t size);
class reclaimer {
std::function<void ()> _reclaim;
public:
reclaimer(std::function<void ()> 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<void (std::function<void ()>)> 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 <typename T, size_t Align>
class aligned_allocator {
public:
using value_type = T;
T* allocate(size_t n) const {
// FIXME: multiply can overflow?
return reinterpret_cast<T*>(::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_ */

View File

@@ -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 <experimental/optional>
/// \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 <typename T>
class pipe_buffer {
private:
queue<std::experimental::optional<T>> _buf;
bool _read_open = true;
bool _write_open = true;
public:
pipe_buffer(size_t size) : _buf(size) {}
future<std::experimental::optional<T>> 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 <typename T>
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 <typename T>
class pipe_reader {
private:
internal::pipe_buffer<T> *_bufp;
pipe_reader(internal::pipe_buffer<T> *bufp) : _bufp(bufp) { }
friend class pipe<T>;
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<T>, which is disengaged to mark and end of file
/// (i.e., the write side was closed, and we've read everything it sent).
future<std::experimental::optional<T>> read() {
if (_bufp->readable()) {
return _bufp->read();
} else {
return make_ready_future<std::experimental::optional<T>>();
}
}
~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 <typename T>
class pipe_writer {
private:
internal::pipe_buffer<T> *_bufp;
pipe_writer(internal::pipe_buffer<T> *bufp) : _bufp(bufp) { }
friend class pipe<T>;
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<T> 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<T> 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<T> 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 <typename T>
class pipe {
public:
pipe_reader<T> reader;
pipe_writer<T> writer;
explicit pipe(size_t size) : pipe(new internal::pipe_buffer<T>(size)) { }
private:
pipe(internal::pipe_buffer<T> *bufp) : reader(bufp), writer(bufp) { }
};
/// @}
} // namespace seastar

View File

@@ -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 <sys/mman.h>
file_desc
file_desc::temporary(sstring directory) {
// FIXME: add O_TMPFILE support one day
directory += "/XXXXXX";
std::vector<char> 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<char*>(ret), mmap_deleter{length});
}
void* posix_thread::start_routine(void* arg) {
auto pfunc = reinterpret_cast<std::function<void ()>*>(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<void ()> func)
: posix_thread(attr{}, std::move(func)) {
}
posix_thread::posix_thread(attr a, std::function<void ()> func)
: _func(std::make_unique<std::function<void ()>>(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);
}

View File

@@ -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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <utility>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <signal.h>
#include <system_error>
#include <boost/optional.hpp>
#include <pthread.h>
#include <signal.h>
#include <memory>
#include "net/api.hh"
inline void throw_system_error_on(bool condition);
template <typename T>
inline void throw_kernel_error(T r);
struct mmap_deleter {
size_t _size;
void operator()(void* ptr) const;
};
using mmap_area = std::unique_ptr<char[], mmap_deleter>;
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 <class X>
int ioctl(int request, X& data) {
int r = ::ioctl(_fd, request, &data);
throw_system_error_on(r == -1);
return r;
}
template <class X>
int ioctl(int request, X&& data) {
int r = ::ioctl(_fd, request, &data);
throw_system_error_on(r == -1);
return r;
}
template <class X>
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 <class X>
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<size_t> 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<ssize_t> 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<size_t> 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<size_t> 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<size_t> 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<size_t> 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<size_t> 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<size_t> 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<char*>(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<std::function<void ()>> _func;
pthread_t _pthread;
bool _valid = true;
mmap_area _stack;
private:
static void* start_routine(void* arg);
public:
posix_thread(std::function<void ()> func);
posix_thread(attr a, std::function<void ()> func);
posix_thread(posix_thread&& x);
~posix_thread();
void join();
public:
class attr {
public:
struct stack_size { size_t size = 0; };
attr() = default;
template <typename... A>
attr(A... a) {
set(std::forward<A>(a)...);
}
void set() {}
template <typename A, typename... Rest>
void set(A a, Rest... rest) {
set(std::forward<A>(a));
set(std::forward<Rest>(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 <typename T>
inline
void throw_kernel_error(T r) {
static_assert(std::is_signed<T>::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_ */

View File

@@ -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 <atomic>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>
#include "align.hh"
static constexpr size_t cacheline_size = 64;
template <size_t N, int RW, int LOC>
struct prefetcher;
template<int RW, int LOC>
struct prefetcher<0, RW, LOC> {
prefetcher(uintptr_t ptr) {}
};
template <size_t N, int RW, int LOC>
struct prefetcher {
prefetcher(uintptr_t ptr) {
__builtin_prefetch(reinterpret_cast<void*>(ptr), RW, LOC);
std::atomic_signal_fence(std::memory_order_seq_cst);
prefetcher<N-64, RW, LOC>(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<typename T, int LOC = 3>
void prefetch(T* ptr) {
prefetcher<align_up(sizeof(T), cacheline_size), 0, LOC>(reinterpret_cast<uintptr_t>(ptr));
}
template<typename Iterator, int LOC = 3>
void prefetch(Iterator begin, Iterator end) {
std::for_each(begin, end, [] (auto v) { prefetch<decltype(*v), LOC>(v); });
}
template<size_t C, typename T, int LOC = 3>
void prefetch_n(T** pptr) {
boost::mpl::for_each< boost::mpl::range_c<size_t,0,C> >( [pptr] (size_t x) { prefetch<T, LOC>(*(pptr + x)); } );
}
template<size_t L, int LOC = 3>
void prefetch(void* ptr) {
prefetcher<L*cacheline_size, 0, LOC>(reinterpret_cast<uintptr_t>(ptr));
}
template<size_t L, typename Iterator, int LOC = 3>
void prefetch_n(Iterator begin, Iterator end) {
std::for_each(begin, end, [] (auto v) { prefetch<L, LOC>(v); });
}
template<size_t L, size_t C, typename T, int LOC = 3>
void prefetch_n(T** pptr) {
boost::mpl::for_each< boost::mpl::range_c<size_t,0,C> >( [pptr] (size_t x) { prefetch<L, LOC>(*(pptr + x)); } );
}
template<typename T, int LOC = 3>
void prefetchw(T* ptr) {
prefetcher<align_up(sizeof(T), cacheline_size), 1, LOC>(reinterpret_cast<uintptr_t>(ptr));
}
template<typename Iterator, int LOC = 3>
void prefetchw_n(Iterator begin, Iterator end) {
std::for_each(begin, end, [] (auto v) { prefetchw<decltype(*v), LOC>(v); });
}
template<size_t C, typename T, int LOC = 3>
void prefetchw_n(T** pptr) {
boost::mpl::for_each< boost::mpl::range_c<size_t,0,C> >( [pptr] (size_t x) { prefetchw<T, LOC>(*(pptr + x)); } );
}
template<size_t L, int LOC = 3>
void prefetchw(void* ptr) {
prefetcher<L*cacheline_size, 1, LOC>(reinterpret_cast<uintptr_t>(ptr));
}
template<size_t L, typename Iterator, int LOC = 3>
void prefetchw_n(Iterator begin, Iterator end) {
std::for_each(begin, end, [] (auto v) { prefetchw<L, LOC>(v); });
}
template<size_t L, size_t C, typename T, int LOC = 3>
void prefetchw_n(T** pptr) {
boost::mpl::for_each< boost::mpl::range_c<size_t,0,C> >( [pptr] (size_t x) { prefetchw<L, LOC>(*(pptr + x)); } );
}
#endif

View File

@@ -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 <boost/format.hpp>
#include <iostream>
#include <iomanip>
#include <chrono>
#include "core/sstring.hh"
inline void
apply_format(boost::format& fmt) {
}
template <typename A0, typename... Arest>
inline void
apply_format(boost::format& fmt, A0&& a0, Arest&&... arest) {
apply_format(fmt % std::forward<A0>(a0), std::forward<Arest>(arest)...);
}
template <typename... A>
std::ostream&
fprint(std::ostream& os, boost::format& fmt, A&&... a) {
apply_format(fmt, std::forward<A>(a)...);
return os << fmt;
}
template <typename... A>
void
print(boost::format& fmt, A&&... a) {
fprint(std::cout, fmt, std::forward<A>(a)...);
}
template <typename... A>
std::ostream&
fprint(std::ostream& os, const char* fmt, A&&... a) {
boost::format bfmt(fmt);
return fprint(os, bfmt, std::forward<A>(a)...);
}
template <typename... A>
void
print(const char* fmt, A&&... a) {
boost::format bfmt(fmt);
return print(bfmt, std::forward<A>(a)...);
}
template <typename... A>
std::string
sprint(const char* fmt, A&&... a) {
boost::format bfmt(fmt);
apply_format(bfmt, std::forward<A>(a)...);
return bfmt.str();
}
template <typename... A>
std::string
sprint(const sstring& fmt, A&&... a) {
return sprint(fmt.c_str(), std::forward<A>(a)...);
}
template <typename Iterator>
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 <typename TimePoint>
struct usecfmt_wrapper {
TimePoint val;
};
template <typename TimePoint>
inline
usecfmt_wrapper<TimePoint>
usecfmt(TimePoint tp) {
return { tp };
};
template <typename Clock, typename Rep, typename Period>
std::ostream&
operator<<(std::ostream& os, usecfmt_wrapper<std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>> tp) {
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(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 <typename... A>
void
log(A&&... a) {
std::cout << usecfmt(std::chrono::high_resolution_clock::now()) << " ";
print(std::forward<A>(a)...);
}
#endif /* PRINT_HH_ */

View File

@@ -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 <queue>
#include <experimental/optional>
template <typename T>
class queue {
std::queue<T, circular_buffer<T>> _q;
size_t _max;
std::experimental::optional<promise<>> _not_empty;
std::experimental::optional<promise<>> _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 <typename Func>
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<T> 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 <typename T>
inline
queue<T>::queue(size_t size)
: _max(size) {
}
template <typename T>
inline
void queue<T>::notify_not_empty() {
if (_not_empty) {
_not_empty->set_value();
_not_empty = std::experimental::optional<promise<>>();
}
}
template <typename T>
inline
void queue<T>::notify_not_full() {
if (_not_full) {
_not_full->set_value();
_not_full = std::experimental::optional<promise<>>();
}
}
template <typename T>
inline
bool queue<T>::push(T&& data) {
if (_q.size() < _max) {
_q.push(std::move(data));
notify_not_empty();
return true;
} else {
return false;
}
}
template <typename T>
inline
T queue<T>::pop() {
if (_q.size() == _max) {
notify_not_full();
}
T data = std::move(_q.front());
_q.pop();
return data;
}
template <typename T>
inline
future<T> queue<T>::pop_eventually() {
if (empty()) {
return not_empty().then([this] {
return make_ready_future<T>(pop());
});
} else {
return make_ready_future<T>(pop());
}
}
template <typename T>
inline
future<> queue<T>::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 <typename T>
template <typename Func>
inline
bool queue<T>::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 <typename T>
inline
bool queue<T>::empty() const {
return _q.empty();
}
template <typename T>
inline
bool queue<T>::full() const {
return _q.size() == _max;
}
template <typename T>
inline
future<> queue<T>::not_empty() {
if (!empty()) {
return make_ready_future<>();
} else {
_not_empty = promise<>();
return _not_empty->get_future();
}
}
template <typename T>
inline
future<> queue<T>::not_full() {
if (!full()) {
return make_ready_future<>();
} else {
_not_full = promise<>();
return _not_full->get_future();
}
}
#endif /* QUEUE_HH_ */

View File

@@ -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 <algorithm>
#include <memory>
#include <cassert>
#include <experimental/optional>
#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 <typename ConcreteParser>
class ragel_parser_base {
protected:
int _fsm_cs;
std::unique_ptr<int[]> _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<int[]> 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<temporary_buffer<char>>;
future<unconsumed_remainder> operator()(temporary_buffer<char> buf) {
char* p = buf.get_write();
char* pe = p + buf.size();
char* eof = buf.empty() ? pe : nullptr;
char* parsed = static_cast<ConcreteParser*>(this)->parse(p, pe, eof);
if (parsed) {
buf.trim_front(parsed - p);
return make_ready_future<unconsumed_remainder>(std::move(buf));
}
return make_ready_future<unconsumed_remainder>();
}
};
#endif /* RAGEL_HH_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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 <hwloc.h>
#include <unordered_map>
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<hwloc_obj_t, size_t>& 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<cpu> 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<size_t>(mem / procs, 2 << 20);
std::vector<hwloc_cpuset_t> cpu_sets{procs};
auto root = hwloc_get_root_obj(topology);
hwloc_distribute(topology, root, cpu_sets.data(), cpu_sets.size(), INT_MAX);
std::vector<cpu> ret;
std::unordered_map<hwloc_obj_t, size_t> topo_used_mem;
std::vector<std::pair<cpu, size_t>> 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 <unistd.h>
namespace resource {
std::vector<cpu> 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<cpu> 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

View File

@@ -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 <cstdlib>
#include <experimental/optional>
#include <vector>
#include <set>
cpu_set_t cpuid_to_cpuset(unsigned cpuid);
namespace resource {
using std::experimental::optional;
using cpuset = std::set<unsigned>;
struct configuration {
optional<size_t> total_memory;
optional<size_t> reserve_memory; // if total_memory not specified
optional<size_t> cpus;
optional<cpuset> cpu_set;
};
struct memory {
size_t bytes;
unsigned nodeid;
};
struct cpu {
unsigned cpu_id;
std::vector<memory> mem;
};
std::vector<cpu> allocate(configuration c);
unsigned nr_processing_units();
}
#endif /* RESOURCE_HH_ */

View File

@@ -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<size_t>::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_ */

View File

@@ -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 <memory>
#include <vector>
#include <experimental/string_view>
template <typename CharType>
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 <size_t N>
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 <typename size_type, size_type max_size>
void append_static(const basic_sstring<char_type, size_type, max_size>& s) {
append_static(s.begin(), s.size());
}
void append_static(const std::experimental::string_view& s) {
append_static(s.data(), s.size());
}
template <typename size_type, size_type max_size>
void append(basic_sstring<char_type, size_type, max_size> s) {
if (s.size()) {
_p = packet(std::move(_p), std::move(s).release());
}
}
template <typename size_type, size_type max_size, typename Callback>
void append(const basic_sstring<char_type, size_type, max_size>& 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 <typename Callback>
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

View File

@@ -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 <functional>
#include <unordered_map>
#include <forward_list>
#include <utility>
#include <string>
#include <map>
#include <iostream>
#include <unordered_map>
#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<type_instance_id, shared_ptr<value_list> > value_list_map;
typedef value_list_map::value_type value_list_pair;
void add_polled(const type_instance_id & id,
const shared_ptr<value_list> & 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 230 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 230 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<uint64_t, std::ratio<1, 0x40000000>> collectd_hres_duration;
// yet another writer type, this one to construct collectd network
// protocol data.
struct cpwriter {
typedef std::array<char, payload_size> 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<uint16_t, sstring> _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<typename _Iter>
cpwriter & write(_Iter s, _Iter e) {
if (check(std::distance(s, e))) {
_pos = std::copy(s, e, _pos);
}
return *this;
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, cpwriter &>::type write(
const T & t) {
T tmp = net::hton(t);
auto * p = reinterpret_cast<const uint8_t *>(&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 T>
typename std::enable_if<std::is_integral<T>::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<data_type *>(&(*_pos)));
_pos += s;
v.values(reinterpret_cast<net::packed<uint64_t> *>(&(*_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<std::chrono::seconds>(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<collectd_hres_duration>(
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<iterator, cpwriter> context;
auto ctxt = make_lw_shared<context>();
// 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<iterator>(*ctxt) = _values.begin();
auto stop_when = [this, ctxt]() {
auto done = std::get<iterator>(*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<iterator>(*ctxt);
auto & out = std::get<cpwriter>(*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<cpwriter>(*ctxt);
auto now = std::chrono::high_resolution_clock::now();
// dogfood stats
++_num_packets;
_millis += std::chrono::duration_cast<std::chrono::milliseconds>(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<value_list> get_values(const type_instance_id & id) {
return _values[id];
}
std::vector<type_instance_id> get_instance_ids() {
std::vector<type_instance_id> 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<value_list> & 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<bool>();
if (!enable) {
return;
}
auto addr = ipv4_addr(opts["collectd-address"].as<std::string>());
auto period = std::chrono::duration_cast<clock_type::duration>(
std::chrono::milliseconds(
opts["collectd-poll-period"].as<unsigned>()));
auto host = opts["collectd-hostname"].as<std::string>();
// 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<bool>()->default_value(true),
"enable collectd daemon")("collectd-address",
bpo::value<std::string>()->default_value("239.192.74.66:25826"),
"address to send/broadcast metrics to")("collectd-poll-period",
bpo::value<unsigned>()->default_value(1000),
"poll period - frequency of sending counter metrics (default: 1000ms, 0 disables)")(
"collectd-hostname",
bpo::value<std::string>()->default_value(hostname),
"collectd host name");
return opts;
}
std::vector<collectd_value> get_collectd_value(
const scollectd::type_instance_id& id) {
std::vector<collectd_value> res_values;
auto raw_types = get_impl().get_values(id);
if (raw_types == nullptr) {
return res_values;
}
std::vector<data_type> types(raw_types->size());
raw_types->types(&(*types.begin()));
std::vector<net::packed<uint64_t>> 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<data_type> get_collectd_types(
const scollectd::type_instance_id& id) {
auto res = get_impl().get_values(id);
if (res == nullptr) {
return std::vector<data_type>();
}
std::vector<data_type> vals(res->size());
res->types(&(*vals.begin()));
return vals;
}
std::vector<scollectd::type_instance_id> get_collectd_ids() {
return get_impl().get_instance_ids();
}
}

View File

@@ -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 <type_traits>
#include <utility>
#include <functional>
#include <array>
#include <iterator>
#include <stdint.h>
#include <memory>
#include <string>
#include <tuple>
#include <chrono>
#include <boost/program_options.hpp>
#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("<pluginname>", "<instance_name>", "<type_name>", "<instance_name>");
* scollectd::add_polled_metric(typ, [<metric var> | scollectd::make_typed(<data_type>, <metric_var>) [, ...]);
*
* Where
* <pluginname> would be the overall 'module', e.g. "cpu"
* <instance_name> -> 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)
* <type_name> is the 'type' of metric collected, for ex. "usage" (cpu/0/usage)
* <type_instance> 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<typename T>
struct typed {
typed(data_type t, T && v)
: type(t), value(std::forward<T>(v)) {
}
data_type type;
T value;
};
template<typename T>
static inline typed<T> make_typed(data_type type, T&& t) {
return typed<T>(type, std::forward<T>(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;
* <scope end, above dies>
*/
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<registration>
{
public:
typedef std::vector<registration> vector_type;
registrations()
{}
registrations(vector_type&& v) : vector_type(std::move(v))
{}
registrations(const std::initializer_list<type_instance_id>& 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<type_instance_id>& l) {
return registrations::operator=(registrations(l));
}
};
// lots of template junk to build typed value list tuples
// for registered values.
template<typename T, typename En = void>
struct data_type_for;
template<typename T, typename En = void>
struct is_callable;
template<typename T>
struct is_callable<T,
typename std::enable_if<
!std::is_void<typename std::result_of<T()>::type>::value,
void>::type> : public std::true_type {
};
template<typename T>
struct is_callable<T,
typename std::enable_if<std::is_fundamental<T>::value, void>::type> : public std::false_type {
};
template<typename T>
struct data_type_for<T,
typename std::enable_if<
std::is_integral<T>::value && std::is_unsigned<T>::value,
void>::type> : public std::integral_constant<data_type,
data_type::COUNTER> {
};
template<typename T>
struct data_type_for<T,
typename std::enable_if<
std::is_integral<T>::value && std::is_signed<T>::value, void>::type> : public std::integral_constant<
data_type, data_type::DERIVE> {
};
template<typename T>
struct data_type_for<T,
typename std::enable_if<std::is_floating_point<T>::value, void>::type> : public std::integral_constant<
data_type, data_type::GAUGE> {
};
template<typename T>
struct data_type_for<T,
typename std::enable_if<is_callable<T>::value, void>::type> : public data_type_for<
typename std::result_of<T()>::type> {
};
template<typename T>
struct data_type_for<typed<T>> : public data_type_for<T> {
};
template<typename T>
class value {
public:
template<typename W>
struct wrap {
wrap(const W & v)
: _v(v) {
}
const W & operator()() const {
return _v;
}
const W & _v;
};
typedef typename std::remove_reference<T>::type value_type;
typedef typename std::conditional<
is_callable<typename std::remove_reference<T>::type>::value,
value_type, wrap<value_type> >::type stored_type;
value(const value_type & t)
: value<T>(data_type_for<value_type>::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<typename _Iter>
void bpack(_Iter s, _Iter e, uint64_t v) const {
while (s != e) {
*s++ = (v & 0xff);
v >>= 8;
}
}
template<typename V>
typename std::enable_if<std::is_integral<V>::value, uint64_t>::type convert(
V v) const {
uint64_t i = v;
// network byte order
return ntohq(i);
}
template<typename V>
typename std::enable_if<std::is_floating_point<V>::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<typename T>
class value<typed<T>> : public value<T> {
public:
value(const typed<T> & args)
: value<T>(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<uint64_t> *) const = 0;
bool empty() const {
return size() == 0;
}
};
template<typename ... Args>
class values_impl: public value_list {
public:
static const size_t num_values = sizeof...(Args);
values_impl(Args&& ...args)
: _values(std::forward<Args>(args)...)
{}
values_impl(values_impl<Args...>&& a) = default;
values_impl(const values_impl<Args...>& 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<data_type> tmp = { args... };
std::copy(tmp.begin(), tmp.end(), p);
});
}
void values(net::packed<uint64_t> * p) const override {
unpack(_values, [p](Args... args) {
std::initializer_list<uint64_t> tmp = { args... };
std::copy(tmp.begin(), tmp.end(), p);
});
}
private:
template<typename _Op>
void unpack(const std::tuple<Args...>& t, _Op&& op) const {
do_unpack(t, std::index_sequence_for<Args...> {}, std::forward<_Op>(op));
}
template<size_t ...S, typename _Op>
void do_unpack(const std::tuple<Args...>& t, const std::index_sequence<S...> &, _Op&& op) const {
op(std::get<S>(t)...);
}
std::tuple < Args... > _values;
};
void add_polled(const type_instance_id &, const shared_ptr<value_list> &);
typedef std::function<void()> notify_function;
template<typename... _Args>
static auto make_type_instance(_Args && ... args) -> values_impl < decltype(value<_Args>(std::forward<_Args>(args)))... >
{
return values_impl<decltype(value<_Args>(std::forward<_Args>(args)))... >
(value<_Args>(std::forward<_Args>(args))...);
}
template<typename ... _Args>
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<typename ... _Args>
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<typename ... _Args>
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<typename ... _Args>
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<impl_type>(
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<typename ... _Args>
static future<> send_explicit_metric(const type_instance_id & id,
_Args&& ... args) {
return send_metric(id, make_type_instance(std::forward<_Args>(args)...));
}
template<typename ... _Args>
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_ */

View File

@@ -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<collectd_value> get_collectd_value(
const scollectd::type_instance_id& id);
std::vector<scollectd::type_instance_id> get_collectd_ids();
}
#endif /* CORE_SCOLLECTD_API_HH_ */

View File

@@ -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 CharType> class input_stream;
template <class CharType> 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<connected_socket> 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<file> 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<file> 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);
/// @}

View File

@@ -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 <list>
#include <stdexcept>
#include <exception>
#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<entry> _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 <typename Exception>
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_ */

View File

@@ -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 <typename Service>
class sharded {
std::vector<Service*> _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 <typename... Args>
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 <typename... Args>
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 <typename... Args>
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 <typename... Args>
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 <typename Func>
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 <typename Reducer, typename Ret, typename... FuncArgs, typename... Args>
inline
auto
map_reduce(Reducer&& r, Ret (Service::*func)(FuncArgs...), Args&&... args)
-> typename reducer_traits<Reducer>::future_type
{
unsigned c = 0;
return ::map_reduce(_instances.begin(), _instances.end(),
[&c, func, args = std::make_tuple(std::forward<Args>(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>(args)...);
}, std::move(args));
});
}, std::forward<Reducer>(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 <typename Reducer, typename Func>
inline
auto map_reduce(Reducer&& r, Func&& func) -> typename reducer_traits<Reducer>::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<Reducer>(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<Value> (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 <typename Mapper, typename Initial, typename Reduce>
inline
future<Initial>
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 <typename Ret, typename... FuncArgs, typename... Args, typename FutureRet = futurize_t<Ret>>
FutureRet
invoke_on(unsigned id, Ret (Service::*func)(FuncArgs...), Args&&... args) {
using futurator = futurize<Ret>;
auto inst = _instances[id];
return smp::submit_to(id, [func, args = std::make_tuple(inst, std::forward<Args>(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<Value> (Service&)` (for some `Value` type)
/// \return result of calling `func(instance)` on the designated instance
template <typename Func, typename Ret = futurize_t<std::result_of_t<Func(Service&)>>>
Ret
invoke_on(unsigned id, Func&& func) {
auto inst = _instances[id];
return smp::submit_to(id, [inst, func = std::forward<Func>(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 <typename Service>
sharded<Service>::~sharded() {
assert(_instances.empty());
}
template <typename Service>
template <typename... Args>
future<>
sharded<Service>::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>(args)...)] (Service*& inst) mutable {
return smp::submit_to(c++, [&inst, args] () mutable {
inst = apply([] (Args&&... args) {
return new Service(std::forward<Args>(args)...);
}, std::move(args));
});
});
}
template <typename Service>
template <typename... Args>
future<>
sharded<Service>::start_single(Args&&... args) {
assert(_instances.empty());
_instances.resize(1);
return smp::submit_to(0, [this, args = std::make_tuple(std::forward<Args>(args)...)] () mutable {
_instances[0] = apply([] (Args&&... args) {
return new Service(std::forward<Args>(args)...);
}, std::move(args));
});
}
template <typename Service>
future<>
sharded<Service>::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<Service*>();
});
}
template <typename Service>
template <typename... Args>
inline
future<>
sharded<Service>::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 <typename Service>
template <typename... Args>
inline
future<>
sharded<Service>::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 <typename Service>
template <typename Func>
inline
future<>
sharded<Service>::invoke_on_all(Func&& func) {
static_assert(std::is_same<futurize_t<std::result_of_t<Func(Service&)>>, 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 <typename Service>
Service& sharded<Service>::local() {
assert(local_is_initialized());
return *_instances[engine().cpu_id()];
}
template <typename Service>
inline bool sharded<Service>::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 <typename PtrType>
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<PtrType>::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<bool>(_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 <typename T>
foreign_ptr<T> make_foreign(T ptr) {
return foreign_ptr<T>(std::move(ptr));
}
template<typename T>
struct is_smart_ptr<::foreign_ptr<T>> : std::true_type {};
/// @}

View File

@@ -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<waiter> _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 <typename Func>
inline
futurize_t<std::result_of_t<Func ()>>
with_shared(shared_mutex& sm, Func&& func) {
return sm.lock_shared().then([func = std::forward<Func>(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 <typename Func>
inline
futurize_t<std::result_of_t<Func ()>>
with_lock(shared_mutex& sm, Func&& func) {
return sm.lock().then([func = std::forward<Func>(func)] () mutable {
return func();
}).finally([&sm] {
sm.unlock();
});
}
/// @}
}

View File

@@ -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 <utility>
#include <type_traits>
#include <functional>
#include <iostream>
#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 <typename T>
class lw_shared_ptr;
template <typename T>
class shared_ptr;
template <typename T>
class enable_lw_shared_from_this;
template <typename T>
class enable_shared_from_this;
template <typename T, typename... A>
lw_shared_ptr<T> make_lw_shared(A&&... a);
template <typename T>
lw_shared_ptr<T> make_lw_shared(T&& a);
template <typename T>
lw_shared_ptr<T> make_lw_shared(T& a);
template <typename T, typename... A>
shared_ptr<T> make_shared(A&&... a);
template <typename T>
shared_ptr<T> make_shared(T&& a);
template <typename T, typename U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& p);
template <typename T, typename U>
shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& p);
template <typename T, typename U>
shared_ptr<T> const_pointer_cast(const shared_ptr<U>& p);
// We want to support two use cases for shared_ptr<T>:
//
// 1. T is any type (primitive or class type)
//
// 2. T is a class type that inherits from enable_shared_from_this<T>.
//
// 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 <typename T>
class enable_lw_shared_from_this {
shared_ptr_counter_type _count = 0;
using ctor = T;
T* to_value() { return static_cast<T*>(this); }
T* to_internal_object() { return static_cast<T*>(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<T> shared_from_this();
lw_shared_ptr<const T> shared_from_this() const;
template <typename X>
friend class lw_shared_ptr;
};
template <typename T>
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 <typename... A>
shared_ptr_no_esft(A&&... a) : _value(std::forward<A>(a)...) {}
template <typename X>
friend class lw_shared_ptr;
};
template <typename T>
using shared_ptr_impl
= std::conditional_t<
std::is_base_of<enable_lw_shared_from_this<std::remove_const_t<T>>, T>::value,
enable_lw_shared_from_this<std::remove_const_t<T>>,
shared_ptr_no_esft<std::remove_const_t<T>>
>;
template <typename T>
class lw_shared_ptr {
mutable shared_ptr_impl<T>* _p = nullptr;
private:
lw_shared_ptr(shared_ptr_impl<T>* p) noexcept : _p(p) {
if (_p) {
++_p->_count;
}
}
template <typename... A>
static lw_shared_ptr make(A&&... a) {
return lw_shared_ptr(new typename shared_ptr_impl<T>::ctor(std::forward<A>(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<T>(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 T>() const noexcept {
return lw_shared_ptr<const T>(_p);
}
explicit operator bool() const noexcept {
return _p;
}
bool owned() const noexcept {
return _p->_count == 1;
}
bool operator==(const lw_shared_ptr<const T>& x) const {
return _p == x._p;
}
bool operator!=(const lw_shared_ptr<const T>& x) const {
return !operator==(x);
}
bool operator==(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
return _p == x._p;
}
bool operator!=(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
return !operator==(x);
}
template <typename U>
friend class lw_shared_ptr;
template <typename X, typename... A>
friend lw_shared_ptr<X> make_lw_shared(A&&...);
template <typename U>
friend lw_shared_ptr<U> make_lw_shared(U&&);
template <typename U>
friend lw_shared_ptr<U> make_lw_shared(U&);
template <typename U>
friend class enable_lw_shared_from_this;
};
template <typename T, typename... A>
inline
lw_shared_ptr<T> make_lw_shared(A&&... a) {
return lw_shared_ptr<T>::make(std::forward<A>(a)...);
}
template <typename T>
inline
lw_shared_ptr<T> make_lw_shared(T&& a) {
return lw_shared_ptr<T>::make(std::move(a));
}
template <typename T>
inline
lw_shared_ptr<T> make_lw_shared(T& a) {
return lw_shared_ptr<T>::make(a);
}
template <typename T>
inline
lw_shared_ptr<T>
enable_lw_shared_from_this<T>::shared_from_this() {
return lw_shared_ptr<T>(this);
}
template <typename T>
inline
lw_shared_ptr<const T>
enable_lw_shared_from_this<T>::shared_from_this() const {
return lw_shared_ptr<const T>(const_cast<enable_lw_shared_from_this*>(this));
}
template <typename T>
static inline
std::ostream& operator<<(std::ostream& out, const lw_shared_ptr<T>& 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 <typename T>
struct shared_ptr_count_for : shared_ptr_count_base {
T data;
template <typename... A>
shared_ptr_count_for(A&&... a) : data(std::forward<A>(a)...) {}
};
template <typename T>
class enable_shared_from_this : private shared_ptr_count_base {
public:
shared_ptr<T> shared_from_this();
shared_ptr<const T> shared_from_this() const;
template <typename U>
friend class shared_ptr;
template <typename U, bool esft>
friend struct shared_ptr_make_helper;
};
template <typename T>
class shared_ptr {
mutable shared_ptr_count_base* _b = nullptr;
mutable T* _p = nullptr;
private:
explicit shared_ptr(shared_ptr_count_for<T>* 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<std::remove_const_t<T>>* p) noexcept : _b(p), _p(static_cast<T*>(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 <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
shared_ptr(const shared_ptr<U>& x) noexcept
: _b(x._b)
, _p(x._p) {
if (_b) {
++_b->count;
}
}
template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
shared_ptr(shared_ptr<U>&& 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 <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
shared_ptr& operator=(const shared_ptr<U>& x) noexcept {
if (*this != x) {
this->~shared_ptr();
new (this) shared_ptr(x);
}
return *this;
}
template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
shared_ptr& operator=(shared_ptr<U>&& 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 <bool esft>
struct make_helper;
template <typename U, typename... A>
friend shared_ptr<U> make_shared(A&&... a);
template <typename U>
friend shared_ptr<U> make_shared(U&& a);
template <typename V, typename U>
friend shared_ptr<V> static_pointer_cast(const shared_ptr<U>& p);
template <typename V, typename U>
friend shared_ptr<V> dynamic_pointer_cast(const shared_ptr<U>& p);
template <typename V, typename U>
friend shared_ptr<V> const_pointer_cast(const shared_ptr<U>& p);
template <bool esft, typename... A>
static shared_ptr make(A&&... a);
template <typename U>
friend class enable_shared_from_this;
template <typename U, bool esft>
friend struct shared_ptr_make_helper;
template <typename U>
friend class shared_ptr;
};
template <typename U, bool esft>
struct shared_ptr_make_helper;
template <typename T>
struct shared_ptr_make_helper<T, false> {
template <typename... A>
static shared_ptr<T> make(A&&... a) {
return shared_ptr<T>(new shared_ptr_count_for<T>(std::forward<A>(a)...));
}
};
template <typename T>
struct shared_ptr_make_helper<T, true> {
template <typename... A>
static shared_ptr<T> make(A&&... a) {
auto p = new T(std::forward<A>(a)...);
return shared_ptr<T>(p, p);
}
};
template <typename T, typename... A>
inline
shared_ptr<T>
make_shared(A&&... a) {
using helper = shared_ptr_make_helper<T, std::is_base_of<shared_ptr_count_base, T>::value>;
return helper::make(std::forward<A>(a)...);
}
template <typename T>
inline
shared_ptr<T>
make_shared(T&& a) {
using helper = shared_ptr_make_helper<T, std::is_base_of<shared_ptr_count_base, T>::value>;
return helper::make(std::forward<T>(a));
}
template <typename T, typename U>
inline
shared_ptr<T>
static_pointer_cast(const shared_ptr<U>& p) {
return shared_ptr<T>(p._b, static_cast<T*>(p._p));
}
template <typename T, typename U>
inline
shared_ptr<T>
dynamic_pointer_cast(const shared_ptr<U>& p) {
auto q = dynamic_cast<T*>(p._p);
return shared_ptr<T>(q ? p._b : nullptr, q);
}
template <typename T, typename U>
inline
shared_ptr<T>
const_pointer_cast(const shared_ptr<U>& p) {
return shared_ptr<T>(p._b, const_cast<T*>(p._p));
}
template <typename T>
inline
shared_ptr<T>
enable_shared_from_this<T>::shared_from_this() {
auto unconst = reinterpret_cast<enable_shared_from_this<std::remove_const_t<T>>*>(this);
return shared_ptr<T>(unconst);
}
template <typename T>
inline
shared_ptr<const T>
enable_shared_from_this<T>::shared_from_this() const {
auto esft = const_cast<enable_shared_from_this*>(this);
auto unconst = reinterpret_cast<enable_shared_from_this<std::remove_const_t<T>>*>(esft);
return shared_ptr<const T>(unconst);
}
template <typename T, typename U>
inline
bool
operator==(const shared_ptr<T>& x, const shared_ptr<U>& y) {
return x.get() == y.get();
}
template <typename T>
inline
bool
operator==(const shared_ptr<T>& x, std::nullptr_t) {
return x.get() == nullptr;
}
template <typename T>
inline
bool
operator==(std::nullptr_t, const shared_ptr<T>& y) {
return nullptr == y.get();
}
template <typename T, typename U>
inline
bool
operator!=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
return x.get() != y.get();
}
template <typename T>
inline
bool
operator!=(const shared_ptr<T>& x, std::nullptr_t) {
return x.get() != nullptr;
}
template <typename T>
inline
bool
operator!=(std::nullptr_t, const shared_ptr<T>& y) {
return nullptr != y.get();
}
template <typename T, typename U>
inline
bool
operator<(const shared_ptr<T>& x, const shared_ptr<U>& y) {
return x.get() < y.get();
}
template <typename T>
inline
bool
operator<(const shared_ptr<T>& x, std::nullptr_t) {
return x.get() < nullptr;
}
template <typename T>
inline
bool
operator<(std::nullptr_t, const shared_ptr<T>& y) {
return nullptr < y.get();
}
template <typename T, typename U>
inline
bool
operator<=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
return x.get() <= y.get();
}
template <typename T>
inline
bool
operator<=(const shared_ptr<T>& x, std::nullptr_t) {
return x.get() <= nullptr;
}
template <typename T>
inline
bool
operator<=(std::nullptr_t, const shared_ptr<T>& y) {
return nullptr <= y.get();
}
template <typename T, typename U>
inline
bool
operator>(const shared_ptr<T>& x, const shared_ptr<U>& y) {
return x.get() > y.get();
}
template <typename T>
inline
bool
operator>(const shared_ptr<T>& x, std::nullptr_t) {
return x.get() > nullptr;
}
template <typename T>
inline
bool
operator>(std::nullptr_t, const shared_ptr<T>& y) {
return nullptr > y.get();
}
template <typename T, typename U>
inline
bool
operator>=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
return x.get() >= y.get();
}
template <typename T>
inline
bool
operator>=(const shared_ptr<T>& x, std::nullptr_t) {
return x.get() >= nullptr;
}
template <typename T>
inline
bool
operator>=(std::nullptr_t, const shared_ptr<T>& y) {
return nullptr >= y.get();
}
template <typename T>
static inline
std::ostream& operator<<(std::ostream& out, const shared_ptr<T>& p) {
if (!p) {
return out << "null";
}
return out << *p;
}
template<typename T>
struct shared_ptr_equal_by_value {
bool operator()(const shared_ptr<T>& i1, const shared_ptr<T>& i2) const {
if (bool(i1) ^ bool(i2)) {
return false;
}
return !i1 || *i1 == *i2;
}
};
template<typename T>
struct shared_ptr_value_hash {
size_t operator()(const shared_ptr<T>& p) const {
if (p) {
return std::hash<T>()(*p);
}
return 0;
}
};
namespace std {
template <typename T>
struct hash<lw_shared_ptr<T>> : private hash<T*> {
size_t operator()(const lw_shared_ptr<T>& p) const {
return hash<T*>::operator()(p.get());
}
};
template <typename T>
struct hash<::shared_ptr<T>> : private hash<T*> {
size_t operator()(const ::shared_ptr<T>& p) const {
return hash<T*>::operator()(p.get());
}
};
}
template<typename T>
struct is_smart_ptr<::shared_ptr<T>> : std::true_type {};
template<typename T>
struct is_smart_ptr<::lw_shared_ptr<T>> : std::true_type {};
#endif /* SHARED_PTR_HH_ */

View File

@@ -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 <thread>
#include <cassert>
// 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

View File

@@ -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 <boost/intrusive/unordered_set.hpp>
#include <boost/intrusive/list.hpp>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <memory>
#include <vector>
#include <algorithm>
#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<uintptr_t> _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<uintptr_t>(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<uintptr_t>& free_objects() {
return _free_objects;
}
void* allocate_object() {
assert(!_free_objects.empty());
auto object = reinterpret_cast<void*>(_free_objects.back());
_free_objects.pop_back();
return object;
}
void free_object(void *object) {
_free_objects.push_back(reinterpret_cast<uintptr_t>(object));
}
template<typename Item>
friend class slab_class;
template<typename Item>
friend class slab_allocator;
};
class slab_item_base {
bi::list_member_hook<> _lru_link;
template<typename Item>
friend class slab_class;
};
template<typename Item>
class slab_class {
private:
bi::list<slab_page_desc,
bi::member_hook<slab_page_desc, bi::list_member_hook<>,
&slab_page_desc::_free_pages_link>> _free_slab_pages;
bi::list<slab_item_base,
bi::member_hook<slab_item_base, bi::list_member_hook<>,
&slab_item_base::_lru_link>> _lru;
size_t _size; // size of objects
uint8_t _slab_class_id;
private:
template<typename... Args>
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>(args)...);
_lru.push_front(reinterpret_cast<slab_item_base&>(*new_item));
return new_item;
}
inline
std::pair<void *, uint32_t> evict_lru_item(std::function<void (Item& item_ref)>& erase_func) {
if (_lru.empty()) {
return { nullptr, 0U };
}
Item& victim = reinterpret_cast<Item&>(_lru.back());
uint32_t index = victim.get_slab_page_index();
assert(victim.is_unlocked());
_lru.erase(_lru.iterator_to(reinterpret_cast<slab_item_base&>(victim)));
// WARNING: You need to make sure that erase_func will not release victim back to slab.
erase_func(victim);
return { reinterpret_cast<void*>(&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<typename... Args>
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>(args)...);
}
template<typename... Args>
Item *create_from_new_page(uint64_t max_object_size, uint32_t slab_page_index,
std::function<void (slab_page_desc& desc)> insert_slab_page_desc,
Args&&... args) {
// allocate slab page.
constexpr size_t alignment = std::alignment_of<Item>::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>(args)...);
}
template<typename... Args>
Item *create_from_lru(std::function<void (Item& item_ref)>& 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>(args)...);
}
void free_item(Item *item, slab_page_desc& desc) {
void *object = item;
_lru.erase(_lru.iterator_to(reinterpret_cast<slab_item_base&>(*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<slab_item_base&>(*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<slab_item_base&>(*item);
_lru.erase(_lru.iterator_to(item_ref));
}
void insert_item_into_lru(Item *item) {
auto& item_ref = reinterpret_cast<slab_item_base&>(*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<typename Item>
class slab_allocator {
private:
std::vector<size_t> _slab_class_sizes;
std::vector<slab_class<Item>> _slab_classes;
std::vector<scollectd::registration> _registrations;
// erase_func() is used to remove the item from the cache using slab.
std::function<void (Item& item_ref)> _erase_func;
std::vector<slab_page_desc*> _slab_pages_vector;
bi::list<slab_page_desc,
bi::member_hook<slab_page_desc, bi::list_member_hook<>,
&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<uintptr_t>(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<Item*>(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<Item>::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<uint8_t>::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<Item>* 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<Item>* 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<Item>& 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<void (Item& item_ref)> 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<typename... Args>
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>(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>(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>(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__ */

View File

@@ -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 <chrono>
#include <functional>
#include "core/shared_ptr.hh"
#include "core/reactor.hh"
#include "core/future.hh"
template <typename Clock = std::chrono::high_resolution_clock, typename Rep, typename Period>
future<> sleep(std::chrono::duration<Rep, Period> dur) {
struct sleeper {
promise<> done;
timer<Clock> tmr;
sleeper(std::chrono::duration<Rep, Period> 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; });
}

View File

@@ -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 <stdint.h>
#include <algorithm>
#include <string>
#include <cstring>
#include <stdexcept>
#include <initializer_list>
#include <iostream>
#include <functional>
#include <cstdio>
#include <type_traits>
#include <experimental/string_view>
#include "core/temporary_buffer.hh"
template <typename char_type, typename Size, Size max_size>
class basic_sstring {
static_assert(
(std::is_same<char_type, char>::value
|| std::is_same<char_type, signed char>::value
|| std::is_same<char_type, unsigned char>::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<char_type>;
using allocator_type = std::allocator<char_type>;
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<Size> can be too small
using size_type = Size;
static constexpr size_type npos = static_cast<size_type>(-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<char_type*>(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<char_type*>(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<char_type*>(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<const char_type*>(x), std::strlen(x)) {}
basic_sstring(std::basic_string<char_type>& x) : basic_sstring(x.c_str(), x.size()) {}
basic_sstring(std::initializer_list<char_type> 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<char_type>& s)
: basic_sstring(s.data(), s.size()) {}
template <typename InputIterator>
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<char_type>() 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 <class InputIterator>
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<char_type*>(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 <class InputIterator>
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<char_type> 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<char_type>(ptr, size, make_free_deleter(ptr));
} else {
auto buf = temporary_buffer<char_type>(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<char_type>() const {
return std::experimental::basic_string_view<char_type>(str(), size());
}
};
template <typename char_type, typename Size, Size max_size>
constexpr Size basic_sstring<char_type, Size, max_size>::npos;
template <typename char_type, typename size_type, size_type Max, size_type N>
inline
basic_sstring<char_type, size_type, Max>
operator+(const char(&s)[N], const basic_sstring<char_type, size_type, Max>& t) {
using sstring = basic_sstring<char_type, size_type, Max>;
// 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 <size_t N>
static inline
size_t str_len(const char(&s)[N]) { return N - 1; }
template <size_t N>
static inline
const char* str_begin(const char(&s)[N]) { return s; }
template <size_t N>
static inline
const char* str_end(const char(&s)[N]) { return str_begin(s) + str_len(s); }
template <typename char_type, typename size_type, size_type max_size>
static inline
const char_type* str_begin(const basic_sstring<char_type, size_type, max_size>& s) { return s.begin(); }
template <typename char_type, typename size_type, size_type max_size>
static inline
const char_type* str_end(const basic_sstring<char_type, size_type, max_size>& s) { return s.end(); }
template <typename char_type, typename size_type, size_type max_size>
static inline
size_type str_len(const basic_sstring<char_type, size_type, max_size>& s) { return s.size(); }
template <typename First, typename Second, typename... Tail>
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 <typename char_type, typename size_type, size_type max_size>
inline
void swap(basic_sstring<char_type, size_type, max_size>& x,
basic_sstring<char_type, size_type, max_size>& y) noexcept
{
return x.swap(y);
}
template <typename char_type, typename size_type, size_type max_size, typename char_traits>
inline
std::basic_ostream<char_type, char_traits>&
operator<<(std::basic_ostream<char_type, char_traits>& os,
const basic_sstring<char_type, size_type, max_size>& s) {
return os.write(s.begin(), s.size());
}
template <typename char_type, typename size_type, size_type max_size, typename char_traits>
inline
std::basic_istream<char_type, char_traits>&
operator>>(std::basic_istream<char_type, char_traits>& is,
basic_sstring<char_type, size_type, max_size>& s) {
std::string tmp;
is >> tmp;
s = tmp;
return is;
}
namespace std {
template <typename char_type, typename size_type, size_type max_size>
struct hash<basic_sstring<char_type, size_type, max_size>> {
size_t operator()(const basic_sstring<char_type, size_type, max_size>& s) const {
return std::hash<std::experimental::basic_string_view<char_type>>()(s);
}
};
}
using sstring = basic_sstring<char, uint32_t, 15>;
static inline
char* copy_str_to(char* dst) {
return dst;
}
template <typename Head, typename... Tail>
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 <typename String = sstring, typename... Args>
static String make_sstring(Args&&... args)
{
String ret(sstring::initialized_later(), str_len(args...));
copy_str_to(ret.begin(), args...);
return ret;
}
template <typename string_type, typename T>
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<char_type*>(tmp), len);
}
template <typename T>
inline
sstring
to_sstring_sprintf(T value, const char* fmt) {
return to_sstring_sprintf<sstring>(value, fmt);
}
template <typename string_type = sstring>
inline
string_type to_sstring(int value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%d");
}
template <typename string_type = sstring>
inline
string_type to_sstring(unsigned value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%u");
}
template <typename string_type = sstring>
inline
string_type to_sstring(long value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%ld");
}
template <typename string_type = sstring>
inline
string_type to_sstring(unsigned long value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%lu");
}
template <typename string_type = sstring>
inline
string_type to_sstring(long long value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%lld");
}
template <typename string_type = sstring>
inline
string_type to_sstring(unsigned long long value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%llu");
}
template <typename string_type = sstring>
inline
string_type to_sstring(float value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%f");
}
template <typename string_type = sstring>
inline
string_type to_sstring(double value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%f");
}
template <typename string_type = sstring>
inline
string_type to_sstring(long double value, void* = nullptr) {
return to_sstring_sprintf<string_type>(value, "%Lf");
}
template <typename string_type = sstring>
inline
string_type to_sstring(const char* value, void* = nullptr) {
return string_type(value);
}
template <typename string_type = sstring>
inline
string_type to_sstring(sstring value, void* = nullptr) {
return value;
}
template <typename string_type = sstring>
static string_type to_sstring(const temporary_buffer<char>& buf) {
return string_type(buf.get(), buf.size());
}
template <typename T>
inline
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
bool first = true;
os << "{";
for (auto&& elem : v) {
if (!first) {
os << ", ";
} else {
first = false;
}
os << elem;
}
os << "}";
return os;
}
#endif /* SSTRING_HH_ */

View File

@@ -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 <exception>
#include <cassert>
// 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 <typename... T>
class stream;
template <typename... T>
class subscription;
template <typename... T>
class stream {
subscription<T...>* _sub = nullptr;
promise<> _done;
promise<> _ready;
public:
using next_fn = std::function<future<> (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<T...> listen();
// Returns a subscription that reads value from this
// stream, and also sets up the listen function.
subscription<T...> 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 <typename E>
void set_exception(E ex);
private:
void pause(future<> can_continue);
void start();
friend class subscription<T...>;
};
template <typename... T>
class subscription {
public:
using next_fn = typename stream<T...>::next_fn;
private:
stream<T...>* _stream;
next_fn _next;
private:
explicit subscription(stream<T...>* 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<future<> (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<T...>;
};
template <typename... T>
inline
stream<T...>::~stream() {
if (_sub) {
_sub->_stream = nullptr;
}
}
template <typename... T>
inline
subscription<T...>
stream<T...>::listen() {
return subscription<T...>(this);
}
template <typename... T>
inline
subscription<T...>
stream<T...>::listen(next_fn next) {
auto sub = subscription<T...>(this);
sub.start(std::move(next));
return sub;
}
template <typename... T>
inline
future<>
stream<T...>::started() {
return _ready.get_future();
}
template <typename... T>
inline
future<>
stream<T...>::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 <typename... T>
inline
void
stream<T...>::close() {
_done.set_value();
}
template <typename... T>
template <typename E>
inline
void
stream<T...>::set_exception(E ex) {
_sub->_done.set_exception(ex);
}
template <typename... T>
inline
subscription<T...>::subscription(stream<T...>* s)
: _stream(s) {
assert(!_stream->_sub);
_stream->_sub = this;
}
template <typename... T>
inline
void
subscription<T...>::start(std::function<future<> (T...)> next) {
_next = std::move(next);
_stream->_ready.set_value();
}
template <typename... T>
inline
subscription<T...>::~subscription() {
if (_stream) {
_stream->_sub = nullptr;
}
}
template <typename... T>
inline
subscription<T...>::subscription(subscription&& x)
: _stream(x._stream), _next(std::move(x._next)) {
x._stream = nullptr;
if (_stream) {
_stream->_sub = this;
}
}
template <typename... T>
inline
future<>
subscription<T...>::done() {
return _stream->_done.get_future();
}
#endif /* STREAM_HH_ */

View File

@@ -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 <memory>
class task {
public:
virtual ~task() noexcept {}
virtual void run() noexcept = 0;
};
void schedule(std::unique_ptr<task> t);
template <typename Func>
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 <typename Func>
inline
std::unique_ptr<task>
make_task(Func&& func) {
return std::make_unique<lambda_task<Func>>(std::forward<Func>(func));
}

View File

@@ -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 <malloc.h>
/// \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<char[]>,
/// 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 <typename CharType>
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<CharType*>(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<CharType*>(ptr);
if (ret) {
throw std::bad_alloc();
}
return temporary_buffer(buf, size, make_free_deleter(buf));
}
};
/// @}
#endif /* TEMPORARY_BUFFER_HH_ */

View File

@@ -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 <ucontext.h>
/// \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<void ()> 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<uintptr_t>(this));
auto main = reinterpret_cast<void (*)()>(&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<thread_context*>(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

View File

@@ -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 <memory>
#include <setjmp.h>
#include <type_traits>
/// \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<char[]> _stack{new char[_stack_size]};
std::function<void ()> _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<void ()> 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<thread_context> _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 <typename Func>
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 <typename Func>
inline
thread::thread(Func func)
: _context(std::make_unique<thread_context>(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<int> compute_sum(int a, int b) {
/// return seastar::async([a, b] {
/// // some blocking code:
/// sleep(1s).get();
/// return a + b;
/// });
/// }
/// \endcode
template <typename Func, typename... Args>
inline
futurize_t<std::result_of_t<std::decay_t<Func>(std::decay_t<Args>...)>>
async(Func&& func, Args&&... args) {
using return_type = std::result_of_t<std::decay_t<Func>(std::decay_t<Args>...)>;
struct work {
Func func;
std::tuple<Args...> args;
promise<return_type> pr;
thread th;
};
return do_with(work{std::forward<Func>(func), std::forward_as_tuple(std::forward<Args>(args)...)}, [] (work& w) {
auto ret = w.pr.get_future();
w.th = thread([&w] {
futurize<return_type>::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);
});
});
}
/// @}
}

View File

@@ -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 <chrono>
#include <limits>
#include <bitset>
#include <array>
#include <boost/intrusive/list.hpp>
#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<typename Timer, bi::list_member_hook<> Timer::*link>
class timer_set {
public:
using time_point = typename Timer::time_point;
using timer_list_t = bi::list<Timer, bi::member_hook<Timer, bi::list_member_hook<>, link>>;
private:
using duration = typename Timer::duration;
using timestamp_t = typename Timer::duration::rep;
static constexpr timestamp_t max_timestamp = std::numeric_limits<timestamp_t>::max();
static constexpr int timestamp_bits = std::numeric_limits<timestamp_t>::digits;
// The last bucket is reserved for active timers with timeout <= _last.
static constexpr int n_buckets = timestamp_bits + 1;
std::array<timer_list_t, n_buckets> _buckets;
timestamp_t _last;
timestamp_t _next;
std::bitset<n_buckets> _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

View File

@@ -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 <experimental/optional>
#include <atomic>
#include "future.hh"
#include "timer-set.hh"
using clock_type = std::chrono::high_resolution_clock;
template <typename Clock = std::chrono::high_resolution_clock>
class timer {
public:
typedef typename Clock::time_point time_point;
typedef typename Clock::duration duration;
typedef Clock clock;
private:
using callback_t = std::function<void()>;
boost::intrusive::list_member_hook<> _link;
callback_t _callback;
time_point _expiry;
std::experimental::optional<duration> _period;
bool _armed = false;
bool _queued = false;
bool _expired = false;
void readd_periodic();
void arm_state(time_point until, std::experimental::optional<duration> 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<duration> period = {});
void rearm(time_point until, std::experimental::optional<duration> 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<timer, &timer::_link>;
};

View File

@@ -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 <type_traits>
template <typename T, typename Alloc>
inline
void
transfer_pass1(Alloc& a, T* from, T* to,
typename std::enable_if<std::is_nothrow_move_constructible<T>::value>::type* = nullptr) {
a.construct(to, std::move(*from));
a.destroy(from);
}
template <typename T, typename Alloc>
inline
void
transfer_pass2(Alloc& a, T* from, T* to,
typename std::enable_if<std::is_nothrow_move_constructible<T>::value>::type* = nullptr) {
}
template <typename T, typename Alloc>
inline
void
transfer_pass1(Alloc& a, T* from, T* to,
typename std::enable_if<!std::is_nothrow_move_constructible<T>::value>::type* = nullptr) {
a.construct(to, *from);
}
template <typename T, typename Alloc>
inline
void
transfer_pass2(Alloc& a, T* from, T* to,
typename std::enable_if<!std::is_nothrow_move_constructible<T>::value>::type* = nullptr) {
a.destroy(from);
}
#endif /* TRANSFER_HH_ */

View File

@@ -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<T*>(p) is a portable replacement for
// reinterpret_cast<T*>(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<int32_t*> 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 <typename T>
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 <typename T, typename F>
inline auto unaligned_cast(F* p) {
return reinterpret_cast<unaligned<std::remove_pointer_t<T>>*>(p);
}
template <typename T, typename F>
inline auto unaligned_cast(const F* p) {
return reinterpret_cast<const unaligned<std::remove_pointer_t<T>>*>(p);
}

View File

@@ -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

View File

@@ -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<net::packet>;
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

View File

@@ -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 <memory>
#include <new>
#include <assert.h>
#include <type_traits>
// 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 <class S, typename E>
inline
std::unique_ptr<S>
make_struct_with_vla(E S::*last, size_t nr) {
auto fake = reinterpret_cast<S*>(0);
size_t offset = reinterpret_cast<uintptr_t>(&(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<S>(new (p) S());
}
#endif /* VLA_HH_ */

View File

@@ -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 <stdint.h>
#include <unistd.h>
#include <xen/xen.h>
#include <xen/event_channel.h> // kernel interface
#include <xen/sys/evtchn.h> // 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<char *>(&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<struct ioctl_evtchn_bind_unbound_port>(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<struct ioctl_evtchn_notify>(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<reactor_notifier> _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<reactor_notifier *>(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<void *>(_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;
}
}

View File

@@ -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<int, port*> _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

View File

@@ -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 <stdint.h>
#include <unistd.h>
#include <xen/xen.h>
#include <xen/grant_table.h> // Kernel interface
#include <xen/sys/gntalloc.h> // Userspace interface
#include <atomic>
#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<int> _ref_head = { 0 };
std::vector<gntref> _refs;
public:
userspace_grant_head(std::vector<gntref> 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<struct ioctl_gntalloc_alloc_gref *>(malloc(sizeof(*gref) + nr_ents * sizeof(gref->gref_ids[0])));
gref->domid = _otherend;
gref->flags = GNTALLOC_FLAG_WRITABLE;
gref->count = nr_ents;
_gntalloc.ioctl<struct ioctl_gntalloc_alloc_gref>(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<gntref> 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<kernel_gntalloc *>(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<kernel_gntalloc *>(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<gntref> 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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 <stdlib.h>
#include <string>
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<std::string> xenstore::ls(std::string path, xenstore_transaction &t)
{
unsigned int num;
char **dir = xs_directory(_h, t.t(), path.c_str(), &num);
std::list<std::string> names;
for (unsigned int i = 0; i < num; ++i) {
names.push_back(dir[i]);
}
free(dir);
return names;
}

View File

@@ -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 <list>
extern "C" {
#include <xenstore.h>
}
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<std::string> ls(std::string path, xenstore_transaction &t = _xs_null);
template <typename T>
T read(std::string path, xenstore_transaction &t = _xs_null) { return boost::lexical_cast<T>(read(path, t)); }
template <typename T>
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<T>(val);
}
}
template <typename T>
void write(std::string path, T val, xenstore_transaction &t = _xs_null) { return write(path, std::to_string(val), t); }
};
#endif /* XENSTORE_HH_ */

View File

@@ -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

1
dpdk

Submodule dpdk deleted from 3b5e1551b3

View File

@@ -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 = ".";
}

View File

@@ -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 <string>
namespace httpd {
struct api_doc : public json::json_base {
json::json_element<std::string> path;
json::json_element<std::string> 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<class T>
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<std::string> apiVersion;
json::json_element<std::string> swaggerVersion;
json::json_list<api_doc> 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<class T>
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<std::unique_ptr<reply>> handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
rep->_content = json::formatter::to_json(_docs);
rep->done("json");
return make_ready_future<std::unique_ptr<reply>>(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<api_registry*>(h)->reg(api, description, alternative_path);
};
}
};
}
#endif /* API_DOCS_HH_ */

View File

@@ -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;
}
}

View File

@@ -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 <unordered_map>
#include "core/sstring.hh"
namespace httpd {
class parameters {
std::unordered_map<sstring, sstring> 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_ */

View File

@@ -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<std::string> _msg;
json::json_element<int> _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_ */

View File

@@ -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 <algorithm>
#include <iostream>
#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<std::unique_ptr<reply>> directory_handler::handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> 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::unique_ptr<reply>>(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::unique_ptr<reply>>(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<reply> rep)
: is(
make_file_input_stream(make_lw_shared<file>(std::move(f)),
0, 4096)), _rep(std::move(rep)) {
}
input_stream<char> is;
std::unique_ptr<reply> _rep;
// for input_stream::consume():
using unconsumed_remainder = std::experimental::optional<temporary_buffer<char>>;
future<unconsumed_remainder> operator()(temporary_buffer<char> data) {
if (data.empty()) {
_rep->done();
return make_ready_future<unconsumed_remainder>(std::move(data));
} else {
_rep->_content.append(data.get(), data.size());
return make_ready_future<unconsumed_remainder>();
}
}
};
future<std::unique_ptr<reply>> file_interaction_handler::read(
const sstring& file_name, std::unique_ptr<request> req,
std::unique_ptr<reply> 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<reader> r = std::make_shared<reader>(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::unique_ptr<reply>>(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<std::unique_ptr<reply>> file_handler::handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> rep) {
if (force_path && redirect_if_needed(*req.get(), *rep.get())) {
return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
}
return read(file, std::move(req), std::move(rep));
}
}

View File

@@ -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<std::unique_ptr<reply> > read(const sstring& file,
std::unique_ptr<request> req, std::unique_ptr<reply> 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<std::unique_ptr<reply>> handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> 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<std::unique_ptr<reply>> handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> rep) override;
private:
sstring file;
bool force_path;
};
}
#endif /* HTTP_FILE_HANDLER_HH_ */

View File

@@ -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 <functional>
#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<sstring(const_req req)> request_function;
/**
* A handle function is a lambda expression that gets request and reply
*/
typedef std::function<sstring(const_req req, reply&)> 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::json_return_type(const_req req)> 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<json::json_return_type>(std::unique_ptr<request> 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<request> req, std::unique_ptr<reply> rep) {
rep->_content += f_handle(*req.get(), *rep.get());
return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
}), _type(type) {
}
function_handler(const request_function & _handle, const sstring& type)
: _f_handle(
[_handle](std::unique_ptr<request> req, std::unique_ptr<reply> rep) {
rep->_content += _handle(*req.get());
return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
}), _type(type) {
}
function_handler(const json_request_function& _handle)
: _f_handle(
[_handle](std::unique_ptr<request> req, std::unique_ptr<reply> rep) {
json::json_return_type res = _handle(*req.get());
rep->_content += res._res;
return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
}), _type("json") {
}
function_handler(const future_json_function& _handle)
: _f_handle(
[_handle](std::unique_ptr<request> req, std::unique_ptr<reply> 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::unique_ptr<reply>>(std::move(rep));
}
);
}), _type("json") {
}
future<std::unique_ptr<reply>> handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
return _f_handle(std::move(req), std::move(rep)).then(
[this](std::unique_ptr<reply> rep) {
rep->done(_type);
return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
});
}
protected:
std::function<
future<std::unique_ptr<reply>>(std::unique_ptr<request> req,
std::unique_ptr<reply> rep)> _f_handle;
sstring _type;
};
}

View File

@@ -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 <unordered_map>
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<std::unique_ptr<reply> > handle(const sstring& path,
std::unique_ptr<request> req, std::unique_ptr<reply> 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<sstring> _mandatory_param;
};
}
#endif /* HANDLERS_HH_ */

View File

@@ -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 <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#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(); })),
} {
}
}

View File

@@ -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 <experimental/string_view>
#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 <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#include <boost/intrusive/list.hpp>
#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<server_socket> _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<connected_socket, socket_address> 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<char> _read_buf;
output_stream<char> _write_buf;
static constexpr size_t limit = 4096;
using tmp_buf = temporary_buffer<char>;
http_request_parser _parser;
std::unique_ptr<request> _req;
std::unique_ptr<reply> _resp;
// null element marks eof
queue<std::unique_ptr<reply>> _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<>, 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<httpd::request> 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<reply> 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<sstring, sstring>::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<char>(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<bool> generate_reply(std::unique_ptr<request> req) {
auto resp = std::make_unique<reply>();
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<reply> rep) {
this->_replies.push(std::move(rep));
return make_ready_future<bool>(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<connection> _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<http_server>* _server_dist;
public:
http_server_control() : _server_dist(new distributed<http_server>) {
}
future<> start() {
return _server_dist->start();
}
future<> stop() {
return _server_dist->stop();
}
future<> set_routes(std::function<void(routes& r)> 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<http_server>& server() {
return *_server_dist;
}
};
}
#endif /* APPS_HTTPD_HTTPD_HH_ */

View File

@@ -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<std::pair<sstring, bool>>& path_parameters,
const std::vector<sstring>& mandatory_params)
: path(path), operations(method, nickname) {
for (auto man : mandatory_params) {
pushmandatory_param(man);
}
for (auto param : path_parameters) {
params.push_back(param);
}
}
}

View File

@@ -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 <vector>
#include <unordered_map>
#include <tuple>
#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<std::pair<sstring, bool>>& path_parameters,
const std::vector<sstring>& 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<std::pair<sstring, bool>> params;
sstring path;
json_operation operations;
std::vector<sstring> 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_ */

View File

@@ -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 <iostream>
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;
}
}

View File

@@ -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_ */

View File

@@ -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 <vector>
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<matcher*> _match_list;
handler_base* _handler;
};
}
#endif /* MATCH_RULES_HH_ */

Some files were not shown because too many files have changed in this diff Show More