diff options
66 files changed, 3 insertions, 10928 deletions
diff --git a/.gitmodules b/.gitmodules index 24a8ba53de6..a945b90796c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -49,3 +49,6 @@ [submodule "src/rapidjson"] path = src/rapidjson url = https://github.com/ceph/rapidjson +[submodule "src/dmclock"] + path = src/dmclock + url = https://github.com/ceph/dmclock.git diff --git a/README.git-subtree b/README.git-subtree deleted file mode 100644 index 7219bee8d31..00000000000 --- a/README.git-subtree +++ /dev/null @@ -1,48 +0,0 @@ -Some libraries that Ceph uses are incorporated into the build tree -through a technique known as git subtrees. This is an alternative to -git submodules, which is also used in Ceph. - -One such library is the dmclock library. Here are some basic notes on -the use of git subtrees. - -When a subtree is added to the repo, commands such as these were run -from the top-level ceph directory: - - git subtree add --prefix src/dmclock \ - git@github.com:ceph/dmclock.git master --squash - -That essentially brings in a full copy of the library into the -subdirectory src/dmclock, but squashes all the commits into a single -one. - -If in time the library is updated and you'd like to bring the updates -in, you could run: - - git subtree pull --prefix src/dmclock \ - git@github.com:ceph/dmclock.git master --squash - -WARNINGS - -1. A rebase should NEVER include the commits by which a subtree is -added or pulled. Those commits do not include the prefix that was used -for the subtree add/pull, and therefore the commits are applied to the -wrong files or, more likely, to non-existant files. If something like -this must be done, a workable approach is to a) do an interactive -rebase, b) remove the commits for the former subtree add/pull, and -either c) replace those commits with executions (x/exec) of the -commands used to add/pull the subtrees, or d) do those commands from -the command-line by using (e/edit) on preceding commits. - -2. If you'd like to modify the library contained in a subtree you'll -need to choose whether to just change your subtree and maintain those -differences into the future (until the library incorporates them) or, -if you're able, modify the library and use a "git subtree pull ..." to -bring them in. - -3. If you modify the library within the ceph tree, then it's best not -to combine changes within the subtree and outside the subtree in a -single commit. Each commit should either only contain changes within -the subtree or outside the subtree. That gives you the option to -cleanly push those changes back to the library's repo. That way if you -ultimately decide to make the changes to the library, you can easily -remove the subtree commits. diff --git a/src/dmclock b/src/dmclock new file mode 160000 +Subproject c4334e5688e73ac8a855ea77c1b9a9a3450f68e diff --git a/src/dmclock/.gitignore b/src/dmclock/.gitignore deleted file mode 100644 index c6ddef2752b..00000000000 --- a/src/dmclock/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*~ -*.dSYM -*.o -build* diff --git a/src/dmclock/.travis.yml b/src/dmclock/.travis.yml deleted file mode 100644 index 587bccb5683..00000000000 --- a/src/dmclock/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: cpp -cache: ccache -dist: trusty -sudo: false -branches: - only: - - master -os: - - linux -compiler: - - clang - - gcc -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - cmake - - libgtest-dev - - libboost-dev -before_script: - - mkdir build - - cd build -script: - - cmake .. - - cmake --build . -- -j2 && ctest -V -j2 -env: - global: - - LANG="en_US.UTF-8" diff --git a/src/dmclock/CMakeLists.txt b/src/dmclock/CMakeLists.txt deleted file mode 100644 index d133bc6b015..00000000000 --- a/src/dmclock/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -cmake_minimum_required(VERSION 2.8.11) - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules") - -set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-write-strings -Wall -pthread") - -if(DO_NOT_DELAY_TAG_CALC) - add_definitions(-DDO_NOT_DELAY_TAG_CALC) -endif() - -if (NOT(TARGET gtest AND TARGET gtest_main)) - if (NOT GTEST_FOUND) - find_package(GTest QUIET) - if (NOT GTEST_FOUND) - include(BuildGTest) - endif() - endif() -endif() - -if (NOT(BOOST_FOUND)) - find_package(Boost REQUIRED) -endif() - -add_subdirectory(src) -add_subdirectory(sim) - -enable_testing() -add_subdirectory(test) -add_subdirectory(support/test) -add_test(NAME dmclock-tests - COMMAND $<TARGET_FILE:dmclock-tests>) -add_test(NAME dmclock-data-struct-tests - COMMAND $<TARGET_FILE:dmclock-data-struct-tests>) diff --git a/src/dmclock/COPYING b/src/dmclock/COPYING deleted file mode 100644 index 6f1dfff1f68..00000000000 --- a/src/dmclock/COPYING +++ /dev/null @@ -1,3 +0,0 @@ -Files: * -Copyright: (C) 2016-2018 by Red Hat Inc. -License: LGPL2.1 (see COPYING-LGPL2.1) diff --git a/src/dmclock/COPYING-LGPL2.1 b/src/dmclock/COPYING-LGPL2.1 deleted file mode 100644 index 5ab7695ab8c..00000000000 --- a/src/dmclock/COPYING-LGPL2.1 +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/src/dmclock/README.before-modifying-files-here-or-below b/src/dmclock/README.before-modifying-files-here-or-below deleted file mode 100644 index 2226512d722..00000000000 --- a/src/dmclock/README.before-modifying-files-here-or-below +++ /dev/null @@ -1,29 +0,0 @@ -The dmclock is an EXTERNAL library is not wholly owned by the ceph -repo. Instead, it is brought into ceph repo via a "git subtree". - -If you are unfamiliar with this technique, PLEASE READ -../../README.git-subtree. - -IT'S VERY LIKELY YOU DO *NOT* WANT TO DIRECTLY MODIFY FILES IN OR -BELOW src/dmclock, AND THAT IF YOU DO MODIFY SUCH FILES YOUR -MODIFICATIONS WILL LIKELY BE REJECTED. - -You should only modify files below src/dmclock if: - - 1) you intend to diverge the ceph version of the dmclock library - from the external library, and - - 2) you intend for ceph developers to maintain those divergences - for the foreseeable future, even as the dmclock library evolves - and those changes are pulled into ceph. - -[Note: this file is an example of such a modification; this file is -such a divergence.] - -If you would like to submit a PR to the dmclock library itself, then -you'll want to submit a PR to: - - https://github.com/ceph/dmclock - -After that PR is merged, you would then submit a PR to ceph that does -a "git subtree pull" (see ../../README.git-subtree for details). diff --git a/src/dmclock/README.md b/src/dmclock/README.md deleted file mode 100644 index ab67295b153..00000000000 --- a/src/dmclock/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# dmclock - -This repository contains C++ 11 code that implements the dmclock -distributed quality of service algorithm. See __mClock: Handling -Throughput Variability for Hypervisor IO Scheduling__ by Gulati, -Merchant, and Varman for a description of the algorithm. - -## Running cmake - -When running cmake, set the build type with either: - - -DCMAKE_BUILD_TYPE=Debug - -DCMAKE_BUILD_TYPE=Release - -To turn on profiling, run cmake with an additional: - - -DPROFILE=yes - -An optimization/fix to the published algorithm has been added and is -on by default. To disable this optimization/fix run cmake with: - - -DDO_NOT_DELAY_TAG_CALC=yes - -## Running make - -### Building the dmclock library - -The `make` command builds a library libdmclock.a. That plus the header -files in the src directory allow one to use the implementation in -their code. - -### Building unit tests - -The `make dmclock-tests` command builds unit tests. - -### Building simulations - -The `make dmclock-sims` command builds two simulations -- *dmc_sim* -and *ssched_sim* -- which incorporate, respectively, the dmclock -priority queue or a very simple scheduler for comparison. Other -priority queue implementations could be added in the future. - -## dmclock API - -To be written.... diff --git a/src/dmclock/benchmark/README.md b/src/dmclock/benchmark/README.md deleted file mode 100644 index d945e986fc1..00000000000 --- a/src/dmclock/benchmark/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# dmclock benchmarking - -**IMPORTANT**: now that K_WAY_HEAP is no longer allowed to have the -value 1, the shell and Python scripts that generate the PDFs no longer -work exactly correctly. Some effort to debug is necessary. - -This directory contains scripts to evaluate effects of different -branching-factors (k=1 to k=11) in the IndirectIntrusiveHeap -data-structure. IndirectIntrusiveHeap is now a k-way heap, so finding -an ideal value for k (i.e., k=2 or k=3) for a particular work-load is -important. Also, it is well-documented that the right choice of -k-value improves the caching behaviour [Syed -- citation needed -here]. As a result, the overall performance of an application using -k-way heap increases significantly [Syed -- citation needed here]. - -A rule of thumb is the following: - if number of elements are <= 6, use k=1 - otherwise, use k=3. - -## Prerequisites - -requires python 2.7, gnuplot, and awk. - -## Running benchmark - -./run.sh [name_of_the_output] [k_way] [repeat] # [Syed -- last two command line args do not work] - -The "run.sh" script looks for config files in the "configs" directory, -and the final output is generated as -"name_of_the_output.pdf". Internally, "run.sh" calls other scripts -such as data_gen.sh, data_parser.py, and plot_gen.sh. - -## Modifying parameters - -To modify k-value and/or the amount of times each simulation is -repeated, modify the following two variables in "run.sh" file: - - k_way=[your_value] - repeat=[your_value] - -For example, k_way=3 means, the benchmark will compare simulations -using 1-way, 2-way, and 3-way heaps. diff --git a/src/dmclock/benchmark/configs/dmc_sim_100_100.conf b/src/dmclock/benchmark/configs/dmc_sim_100_100.conf deleted file mode 100644 index c93d4c71f6d..00000000000 --- a/src/dmclock/benchmark/configs/dmc_sim_100_100.conf +++ /dev/null @@ -1,31 +0,0 @@ -[global] -server_groups = 1 -client_groups = 2 -server_random_selection = true -server_soft_limit = true - -[server.0] -server_count = 100 -server_iops = 160 - -[client.0] -client_count = 99 -client_wait = 0 -client_total_ops = 10000 -client_server_select_range = 100 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 100.0 -client_limit = 0.0 -client_weight = 1.0 - -[client.1] -client_count = 1 -client_wait = 10 -client_total_ops = 10000 -client_server_select_range = 100 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 100.0 -client_limit = 0.0 -client_weight = 1.0 diff --git a/src/dmclock/benchmark/configs/dmc_sim_8_6.conf b/src/dmclock/benchmark/configs/dmc_sim_8_6.conf deleted file mode 100644 index 28aeb401d44..00000000000 --- a/src/dmclock/benchmark/configs/dmc_sim_8_6.conf +++ /dev/null @@ -1,43 +0,0 @@ -[global] -server_groups = 1 -client_groups = 3 -server_random_selection = true -server_soft_limit = true - -[client.0] -client_count = 2 -client_wait = 0 -client_total_ops = 1000 -client_server_select_range = 8 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 0.0 -client_limit = 0.0 -client_weight = 1.0 - -[client.1] -client_count = 2 -client_wait = 5 -client_total_ops = 1000 -client_server_select_range = 8 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 20.0 -client_limit = 40.0 -client_weight = 1.0 - -[client.2] -client_count = 2 -client_wait = 10 -client_total_ops = 1000 -client_server_select_range = 8 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 0.0 -client_limit = 50.0 -client_weight = 2.0 - - -[server.0] -server_count = 8 -server_iops = 160 diff --git a/src/dmclock/benchmark/data_gen.sh b/src/dmclock/benchmark/data_gen.sh deleted file mode 100755 index b4fdab3deed..00000000000 --- a/src/dmclock/benchmark/data_gen.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -config_dir="configs" -repeat=2 #5 - -# parameter check -- output_file name -if [ "$1" != "" ]; then - output_file="$1" -else - echo "Please provide the name of the output file" - exit -fi - -# parameter check -- k-value -if [ "$2" != "" ]; then - k_way="$2" -else - echo "Please provide the maximum K_WAY value" - exit -fi - -# parameter check --repeat -if [ "$3" != "" ]; then - repeat="$3" -fi - -echo "k-way:$k_way, num_repeat:$repeat" - -# create simulators in different directories -k=2 -while [ $k -le $k_way ] -do - mkdir "build_$k" - cd "build_$k" - rm -rf * - cmake -DCMAKE_BUILD_TYPE=Release -DK_WAY_HEAP=$k ../../. - make dmclock-sims - cd .. - - k=$(( $k + 1 )) -done - -# run simulators -echo '' > $output_file -for config in "$config_dir"/*.conf -do - k=2 - while [ $k -le $k_way ] - do - cd "build_$k" - - # repeat same experiment - i=0 - while [ $i -lt $repeat ] - do - i=$(( $i + 1 )) - - # clear cache first - sync - #sudo sh -c 'echo 1 >/proc/sys/vm/drop_caches' - #sudo sh -c 'echo 2 >/proc/sys/vm/drop_caches' - #sudo sh -c 'echo 3 >/proc/sys/vm/drop_caches' - - # run with heap - msg="file_name:$k:$config" - echo $msg >> ../$output_file - echo "running $msg ..." - ./sim/dmc_sim -c ../$config | awk '(/average/)' >> ../$output_file - done # end repeat - cd .. - k=$(( $k + 1 )) - done # end k_way -done # end config - diff --git a/src/dmclock/benchmark/data_parser.py b/src/dmclock/benchmark/data_parser.py deleted file mode 100755 index c90d85fd9ab..00000000000 --- a/src/dmclock/benchmark/data_parser.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python - -class DataPoint: - def __init__(self): - self.nserver = 0; - self.nclient = 0; - self.heap_type = 0; - self.total_time_to_add_req = 0; - self.total_time_to_complete_req = 0; - self.config = '' - - def set_name(self, config, heap_type): - self.config = config; - self.heap_type = heap_type - - def get_conig(self): - import re - return re.split(r"/|\.", self.config)[1] - - def __str__(self): - return "s:%d, c:%d,h:%d,config:%s"%(self.nserver, self.nclient, self.heap_type, self.config); -# end DataPoint - - -def isFloat(elem): - try: - float(elem) - return True - except ValueError: - return False -#end isFloat - - -def parse_config_params(fname): - nclient = 0; - nserver = 0; - # read config file property - with open(fname, 'r') as f: - for line in f: - line = line.strip('\n \t') - if not line: continue; - if line.startswith("client_count"): - nclient += int(line.split('=')[-1]); - if line.startswith("server_count"): - nserver += int(line.split('=')[-1]); - # end of file - return [nserver, nclient]; -# parse_config_params - -def make_aggregate_data_point(dps, config, heap_type): - # create new aggregate point - dp = DataPoint(); - # set set and k_way_heap property - dp.set_name(config, heap_type); - - num_run = 0 - for _dp in dps: - if _dp.config == config and _dp.heap_type == heap_type: - # print _dp, config, heap_type - dp.nserver =_dp.nserver - dp.nclient = _dp.nclient - num_run += 1 - dp.total_time_to_add_req += _dp.total_time_to_add_req - dp.total_time_to_complete_req += _dp.total_time_to_complete_req - - # average - dp.total_time_to_add_req /= num_run; - dp.total_time_to_complete_req /= num_run - #print dp - return dp; - -def parse_data_points(filename): - dps = []; #data-points - dp = None; - state = 0; - configs = {} - k_ways = {} - - with open(filename, 'r') as f: - for line in f: - line = line.strip('\n \t') - if not line: continue; - - # file_name:1:configs/dmc_sim_8_6.conf - if line.startswith("file_name"): - if dp: - dps.append(dp); - state = 0; - - # new data-point - dp = DataPoint(); - parts = line.split(':') - fname = parts[-1]; - dp.heap_type = int(parts[1]); - if dp.heap_type not in k_ways: - k_ways[dp.heap_type] = 1; - - # add to the dictionary - configs[fname] = 1; - - dp.config = fname; - params = parse_config_params(fname) - dp.nserver = params[0]; - dp.nclient = params[-1]; - - elif line.startswith("average"): # take last 2 averages - r = [float(s) for s in line.split(' ') if isFloat(s)] - state +=1; - #print r, dp #if isFloat(s) - if state == 3: - dp.total_time_to_add_req = r[0] - elif state == 4: - dp.total_time_to_complete_req = r[0] - else: pass - - else: - pass; - # final entry - dps.append(dp) - - # compute average of multiple runs - dps_avg = [] - for config in configs: - data_per_config = [] - for k in k_ways: - aggr_dp = make_aggregate_data_point(dps, config , k); - data_per_config.append(aggr_dp); - dps_avg.append(data_per_config); - # end for - return dps_avg; -# end parse_data_points - - -def create_header(num_cols): - fields = ['nserver_nclient(config_file)','add_req', 'complete_req']; - header = fields[0] - #write add_req_{1, ...} - for i in range(num_cols): - header = '%s %s_%i'%(header, fields[1], i+2) - #write complete_req_{1, ...} - for i in range(num_cols): - header = '%s %s_%i'%(header, fields[2], i+2) - # new-line - header = '%s\n'%(header) - return header -# end create_header - - -def create_data_line(aggr_dp): - # get common info - dp = aggr_dp[0] - data_line = "s:%d_c:%d "%(dp.nserver, dp.nclient); - # get the point-count - num_cols = len(aggr_dp); - # write add_req_{1, ...} - for i in range(num_cols): - data_line = '%s %f'%(data_line, aggr_dp[i].total_time_to_add_req) - # write complete_req_{1, ...} - for i in range(num_cols): - data_line = '%s %f'%(data_line, aggr_dp[i].total_time_to_complete_req) - # new-line - data_line = '%s\n'%(data_line) - return data_line -# end create_data_line - - -def make_data(filename): - # write the aggregated point in space separated file - dps = parse_data_points(filename); - if not len(dps) : return - print "total points: ", len(dps) - # open file - with open('%s.dat'%(filename), 'w+') as f: - # write header - f.write(create_header(len(dps[0]))); - # write data-line - for aggr_dp in dps: - f.write(create_data_line(aggr_dp)); - - -def main(output_file): - print output_file - make_data(output_file); - -import sys -if __name__ == "__main__": - file_name="result" - if len(sys.argv) > 1: - file_name=sys.argv[1].strip() - main(file_name) - diff --git a/src/dmclock/benchmark/plot_gen.sh b/src/dmclock/benchmark/plot_gen.sh deleted file mode 100755 index 21ae03ddf4a..00000000000 --- a/src/dmclock/benchmark/plot_gen.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash - -if [ "$1" != "" ]; then - output_file="$1" -else - echo "Please provide the name of the output file" - exit -fi - -# parameter check -- k-value -if [ "$2" != "" ]; then - k_way="$2" -else - echo "Please provide the maximum K_WAY value" - exit -fi -#echo "k-way: $k_way" -#exit - -gnuplot << EOF - -# Note you need gnuplot 4.4 for the pdfcairo terminal. -clear -reset - -set terminal pdfcairo size 7in,5in font "Gill Sans,5" linewidth 1 rounded fontscale .8 noenhanced -set output "${output_file}.pdf" - -# starts multiplot -set multiplot layout 2,1 - -# Line style for axes -set style line 80 lt rgb "#808080" - -# Line style for grid -set style line 81 lt 0 # dashed -set style line 81 lt rgb "#808080" # grey - -set grid back linestyle 81 -set border 3 back linestyle 80 - -#set xtics rotate out -set style data histogram -set style histogram clustered - -set style fill solid border -set xlabel 'Heap Timing for different K values' -set ylabel 'Time (nanosec)' -set key top right - -set yrange [0:*] - -# plot 1 -set title 'Request Addition Time' -plot for [COL=2:($k_way + 1)] '${output_file}.dat' using COL:xticlabels(1) title columnheader - -# plot 2 -set title 'Request Completion Time' -plot for [COL=($k_way + 2):(2 * $k_way + 1)] '${output_file}.dat' using COL:xticlabels(1) title columnheader -EOF diff --git a/src/dmclock/benchmark/run.sh b/src/dmclock/benchmark/run.sh deleted file mode 100755 index ae76d82ca2a..00000000000 --- a/src/dmclock/benchmark/run.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -# default value -k_way=3 #11 -repeat=2 #5 - -output_file="" -if [ "$1" != "" ]; then - output_file="$1" -else - echo "Please provide the name of the output file" - exit -fi - -echo "generating file ${output_file}" -sh data_gen.sh ${output_file} ${k_way} ${repeat} - -echo "converting ${output_file} to ${output_file}.dat" -python data_parser.py ${output_file} - -echo "now generating bar-chart" -#gnuplot -e 'output_file=value' plot_gen.gnuplot -sh plot_gen.sh ${output_file} ${k_way} -echo "done! check ${output_file}.pdf" diff --git a/src/dmclock/cmake/modules/BuildGTest.cmake b/src/dmclock/cmake/modules/BuildGTest.cmake deleted file mode 100644 index 6d0690d5e63..00000000000 --- a/src/dmclock/cmake/modules/BuildGTest.cmake +++ /dev/null @@ -1,71 +0,0 @@ -macro(_build_gtest gtest_root) - include(ExternalProject) - ExternalProject_Add(googletest - SOURCE_DIR ${gtest_root} - CMAKE_ARGS -DBUILD_GMOCK=OFF -DBUILD_GTEST=ON - INSTALL_COMMAND "" - LOG_CONFIGURE ON - LOG_BUILD ON) - - ExternalProject_Get_Property(googletest source_dir) - find_path(GTEST_INCLUDE_DIRS - NAMES gtest/gtest.h - PATHS ${source_dir}/googletest/include /usr/include) - find_path(GMOCK_INCLUDE_DIRS - NAMES gmock/gmock.h - PATHS ${source_dir}/googlemock/include /usr/include) - - find_package(Threads REQUIRED) - - ExternalProject_Get_Property(googletest binary_dir) - set(GTEST_LIBRARY_PATH ${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}gtest.a) - set(GTEST_LIBRARY gtest) - add_library(${GTEST_LIBRARY} STATIC IMPORTED) - set_target_properties(${GTEST_LIBRARY} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIRS}" - IMPORTED_LOCATION ${GTEST_LIBRARY_PATH} - IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" - IMPORTED_LINK_INTERFACE_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - add_dependencies(${GTEST_LIBRARY} googletest) - set(GTEST_LIBRARIES ${GTEST_LIBRARY}) - - set(GTEST_MAIN_LIBRARY_PATH ${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}gtest_main.a) - set(GTEST_MAIN_LIBRARY gtest_main) - add_library(${GTEST_MAIN_LIBRARY} STATIC IMPORTED) - set_target_properties(${GTEST_MAIN_LIBRARY} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIRS}" - IMPORTED_LOCATION ${GTEST_MAIN_LIBRARY_PATH} - IMPORTED_LINK_INTERFACE_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - add_dependencies(${GTEST_MAIN_LIBRARY} googletest) - - set(GMOCK_LIBRARY_PATH ${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}gmock.a) - set(GMOCK_LIBRARY gmock) - add_library(${GMOCK_LIBRARY} STATIC IMPORTED) - set_target_properties(${GMOCK_LIBRARY} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${GMOCK_INCLUDE_DIRS}" - IMPORTED_LOCATION "${GMOCK_LIBRARY_PATH}" - IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" - IMPORTED_LINK_INTERFACE_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - add_dependencies(${GMOCK_LIBRARY} googletest) - - set(GMOCK_MAIN_LIBRARY_PATH ${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}gmock_main.a) - set(GMOCK_MAIN_LIBRARY gmock_main) - add_library(${GMOCK_MAIN_LIBRARY} STATIC IMPORTED) - set_target_properties(${GMOCK_MAIN_LIBRARY} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${GMOCK_INCLUDE_DIRS}" - IMPORTED_LOCATION ${GMOCK_MAIN_LIBRARY_PATH} - IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" - IMPORTED_LINK_INTERFACE_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - add_dependencies(${GMOCK_MAIN_LIBRARY} ${GTEST_LIBRARY}) -endmacro() - -find_path(GTEST_ROOT src/gtest.cc - HINTS $ENV{GTEST_ROOT} - PATHS /usr/src/googletest/googletest /usr/src/gtest) - -if(EXISTS ${GTEST_ROOT}) - message(STATUS "Found googletest: ${GTEST_ROOT}") - _build_gtest(${GTEST_ROOT}) -else() - message(SEND_ERROR "Could NOT find googletest") -endif() diff --git a/src/dmclock/dmclock-config.cmake.in b/src/dmclock/dmclock-config.cmake.in deleted file mode 100644 index 01636532c1d..00000000000 --- a/src/dmclock/dmclock-config.cmake.in +++ /dev/null @@ -1,17 +0,0 @@ -# - Config file for the FooBar package -# It defines the following variables -# DMCLOCK_INCLUDE_DIRS - include directories for FooBar -# DMCLOCK_LIBRARIES - libraries to link against - -# Compute paths -get_filename_component(DMCLOCK_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -set(DMCLOCK_INCLUDE_DIRS "${DMCLOCK_CMAKE_DIR}/src") -# set(DMCLOCK_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@") - -# Our library dependencies (contains definitions for IMPORTED targets) -if(NOT TARGET dmclock AND NOT dmclock_BINARY_DIR) - include("${DMCLOCK_CMAKE_DIR}/dmclock-targets.cmake") -endif() - -# These are IMPORTED targets created by FooBarTargets.cmake -set(DMCLOCK_LIBRARIES dmclock) diff --git a/src/dmclock/dmclock-targets.cmake b/src/dmclock/dmclock-targets.cmake deleted file mode 100644 index 2c84f34a142..00000000000 --- a/src/dmclock/dmclock-targets.cmake +++ /dev/null @@ -1 +0,0 @@ -export(PACKAGE dmclock) diff --git a/src/dmclock/sim/CMakeLists.txt b/src/dmclock/sim/CMakeLists.txt deleted file mode 100644 index c088d2f1cd7..00000000000 --- a/src/dmclock/sim/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -if(K_WAY_HEAP) - if(K_WAY_HEAP LESS 2) - message(FATAL_ERROR "K_WAY_HEAP value should be at least 2") - else() - set(CMAKE_CXX_SIM_FLAGS "-DK_WAY_HEAP=${K_WAY_HEAP}") - endif() -endif() - -add_subdirectory(src) diff --git a/src/dmclock/sim/dmc_sim_100th.conf b/src/dmclock/sim/dmc_sim_100th.conf deleted file mode 100644 index 17d0043548e..00000000000 --- a/src/dmclock/sim/dmc_sim_100th.conf +++ /dev/null @@ -1,32 +0,0 @@ -[global] -server_groups = 1 -client_groups = 2 -server_random_selection = true -server_soft_limit = true - -[client.0] -client_count = 99 -client_wait = 0 -client_total_ops = 1000 -client_server_select_range = 10 -client_iops_goal = 50 -client_outstanding_ops = 100 -client_reservation = 20.0 -client_limit = 60.0 -client_weight = 1.0 - -[client.1] -client_count = 1 -client_wait = 10 -client_total_ops = 1000 -client_server_select_range = 10 -client_iops_goal = 50 -client_outstanding_ops = 100 -client_reservation = 20.0 -client_limit = 60.0 -client_weight = 1.0 - -[server.0] -server_count = 100 -server_iops = 40 -server_threads = 1 diff --git a/src/dmclock/sim/dmc_sim_example.conf b/src/dmclock/sim/dmc_sim_example.conf deleted file mode 100644 index 989f2f08281..00000000000 --- a/src/dmclock/sim/dmc_sim_example.conf +++ /dev/null @@ -1,43 +0,0 @@ -[global] -server_groups = 1 -client_groups = 3 -server_random_selection = false -server_soft_limit = false - -[client.0] -client_count = 1 -client_wait = 0 -client_total_ops = 2000 -client_server_select_range = 1 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 0.0 -client_limit = 0.0 -client_weight = 1.0 - -[client.1] -client_count = 1 -client_wait = 5 -client_total_ops = 2000 -client_server_select_range = 1 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 0.0 -client_limit = 40.0 -client_weight = 1.0 - -[client.2] -client_count = 1 -client_wait = 10 -client_total_ops = 2000 -client_server_select_range = 1 -client_iops_goal = 200 -client_outstanding_ops = 32 -client_reservation = 0.0 -client_limit = 50.0 -client_weight = 2.0 - -[server.0] -server_count = 1 -server_iops = 160 -server_threads = 1 diff --git a/src/dmclock/sim/src/CMakeLists.txt b/src/dmclock/sim/src/CMakeLists.txt deleted file mode 100644 index d7bb1c81610..00000000000 --- a/src/dmclock/sim/src/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -include_directories(ssched) # ssched code -include_directories(../../src) # dmclock code -include_directories(../../support/src) -include_directories(SYSTEM ${BOOST_INCLUDE_DIR}) - -set(local_flags "-Wall -pthread ${CMAKE_CXX_SIM_FLAGS}") - -set(ssched_sim_srcs test_ssched.cc test_ssched_main.cc) -set(dmc_sim_srcs test_dmclock.cc test_dmclock_main.cc) -set(config_srcs config.cc str_list.cc ConfUtils.cc) - -set_source_files_properties(${ssched_sim_srcs} ${dmc_sim_srcs} ${dmc_srcs} ${config_srcs} - PROPERTIES - COMPILE_FLAGS "${local_flags}" - ) - -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(warnings_off " -Wno-unused-variable -Wno-unused-function") -elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(warnings_off " -Wno-unused-but-set-variable -Wno-unused-function") -endif() - -# append warning flags to certain source files -set_property( - SOURCE ${ssched_sim_srcs} ${dmc_sim_srcs} ${config_srcs} - APPEND_STRING - PROPERTY COMPILE_FLAGS "${warnings_off}" - ) - -add_executable(ssched_sim EXCLUDE_FROM_ALL ${ssched_sim_srcs}) -add_executable(dmc_sim EXCLUDE_FROM_ALL ${dmc_sim_srcs} ${config_srcs}) - -set_target_properties(ssched_sim dmc_sim - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ..) - -add_dependencies(dmc_sim dmclock) - -target_link_libraries(ssched_sim LINK_PRIVATE pthread) -target_link_libraries(dmc_sim LINK_PRIVATE pthread $<TARGET_FILE:dmclock>) - -add_custom_target(dmclock-sims DEPENDS ssched_sim dmc_sim) diff --git a/src/dmclock/sim/src/ConfUtils.cc b/src/dmclock/sim/src/ConfUtils.cc deleted file mode 100644 index 74ddb06ee29..00000000000 --- a/src/dmclock/sim/src/ConfUtils.cc +++ /dev/null @@ -1,574 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab -/* - * Ceph - scalable distributed file system - * - * Copyright (C) 2011 New Dream Network - * - * This is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software - * Foundation. See file COPYING. - * - */ - -#include <algorithm> -#include <errno.h> -#include <list> -#include <map> -#include <sstream> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <string> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> -#include <iostream> - -#include <assert.h> -#include "ConfUtils.h" - -using std::cerr; -using std::ostringstream; -using std::pair; -using std::string; - -#define MAX_CONFIG_FILE_SZ 0x40000000 - -////////////////////////////// ConfLine ////////////////////////////// -ConfLine:: -ConfLine(const std::string &key_, const std::string val_, - const std::string newsection_, const std::string comment_, int line_no_) - : key(key_), val(val_), newsection(newsection_) -{ - // If you want to implement writable ConfFile support, you'll need to save - // the comment and line_no arguments here. -} - -bool ConfLine:: -operator<(const ConfLine &rhs) const -{ - // We only compare keys. - // If you have more than one line with the same key in a given section, the - // last one wins. - if (key < rhs.key) - return true; - else - return false; -} - -std::ostream &operator<<(std::ostream& oss, const ConfLine &l) -{ - oss << "ConfLine(key = '" << l.key << "', val='" - << l.val << "', newsection='" << l.newsection << "')"; - return oss; -} -///////////////////////// ConfFile ////////////////////////// -ConfFile:: -ConfFile() -{ -} - -ConfFile:: -~ConfFile() -{ -} - -void ConfFile:: -clear() -{ - sections.clear(); -} - -/* We load the whole file into memory and then parse it. Although this is not - * the optimal approach, it does mean that most of this code can be shared with - * the bufferlist loading function. Since bufferlists are always in-memory, the - * load_from_buffer interface works well for them. - * In general, configuration files should be a few kilobytes at maximum, so - * loading the whole configuration into memory shouldn't be a problem. - */ -int ConfFile:: -parse_file(const std::string &fname, std::deque<std::string> *errors, - std::ostream *warnings) -{ - clear(); - - int ret = 0; - size_t sz; - char *buf = NULL; - char buf2[128]; - FILE *fp = fopen(fname.c_str(), "r"); - if (!fp) { - ret = -errno; - return ret; - } - - struct stat st_buf; - if (fstat(fileno(fp), &st_buf)) { - ret = -errno; - ostringstream oss; - oss << "read_conf: failed to fstat '" << fname << "': " << strerror_r(ret, buf2, sizeof(buf2)); - errors->push_back(oss.str()); - goto done; - } - - if (st_buf.st_size > MAX_CONFIG_FILE_SZ) { - ostringstream oss; - oss << "read_conf: config file '" << fname << "' is " << st_buf.st_size - << " bytes, but the maximum is " << MAX_CONFIG_FILE_SZ; - errors->push_back(oss.str()); - ret = -EINVAL; - goto done; - } - - sz = (size_t)st_buf.st_size; - buf = (char*)malloc(sz); - if (!buf) { - ret = -ENOMEM; - goto done; - } - - if (fread(buf, 1, sz, fp) != sz) { - if (ferror(fp)) { - ret = -errno; - ostringstream oss; - oss << "read_conf: fread error while reading '" << fname << "': " - << strerror_r(ret, buf2, sizeof(buf2)); - errors->push_back(oss.str()); - goto done; - } - else { - ostringstream oss; - oss << "read_conf: unexpected EOF while reading '" << fname << "': " - << "possible concurrent modification?"; - errors->push_back(oss.str()); - ret = -EIO; - goto done; - } - } - - load_from_buffer(buf, sz, errors, warnings); - ret = 0; - -done: - free(buf); - fclose(fp); - return ret; -} - -int ConfFile:: -read(const std::string §ion, const std::string &key, std::string &val) const -{ - string k(normalize_key_name(key)); - - const_section_iter_t s = sections.find(section); - if (s == sections.end()) - return -ENOENT; - ConfLine exemplar(k, "", "", "", 0); - ConfSection::const_line_iter_t l = s->second.lines.find(exemplar); - if (l == s->second.lines.end()) - return -ENOENT; - val = l->val; - return 0; -} - -ConfFile::const_section_iter_t ConfFile:: -sections_begin() const -{ - return sections.begin(); -} - -ConfFile::const_section_iter_t ConfFile:: -sections_end() const -{ - return sections.end(); -} - -void ConfFile:: -trim_whitespace(std::string &str, bool strip_internal) -{ - // strip preceding - const char *in = str.c_str(); - while (true) { - char c = *in; - if ((!c) || (!isspace(c))) - break; - ++in; - } - char output[strlen(in) + 1]; - strcpy(output, in); - - // strip trailing - char *o = output + strlen(output); - while (true) { - if (o == output) - break; - --o; - if (!isspace(*o)) { - ++o; - *o = '\0'; - break; - } - } - - if (!strip_internal) { - str.assign(output); - return; - } - - // strip internal - char output2[strlen(output) + 1]; - char *out2 = output2; - bool prev_was_space = false; - for (char *u = output; *u; ++u) { - char c = *u; - if (isspace(c)) { - if (!prev_was_space) - *out2++ = c; - prev_was_space = true; - } - else { - *out2++ = c; - prev_was_space = false; - } - } - *out2++ = '\0'; - str.assign(output2); -} - -/* Normalize a key name. - * - * Normalized key names have no leading or trailing whitespace, and all - * whitespace is stored as underscores. The main reason for selecting this - * normal form is so that in common/config.cc, we can use a macro to stringify - * the field names of md_config_t and get a key in normal form. - */ -std::string ConfFile:: -normalize_key_name(const std::string &key) -{ - string k(key); - ConfFile::trim_whitespace(k, true); - std::replace(k.begin(), k.end(), ' ', '_'); - return k; -} - -std::ostream &operator<<(std::ostream &oss, const ConfFile &cf) -{ - for (ConfFile::const_section_iter_t s = cf.sections_begin(); - s != cf.sections_end(); ++s) { - oss << "[" << s->first << "]\n"; - for (ConfSection::const_line_iter_t l = s->second.lines.begin(); - l != s->second.lines.end(); ++l) { - if (!l->key.empty()) { - oss << "\t" << l->key << " = \"" << l->val << "\"\n"; - } - } - } - return oss; -} - -void ConfFile:: -load_from_buffer(const char *buf, size_t sz, std::deque<std::string> *errors, - std::ostream *warnings) -{ - errors->clear(); - - section_iter_t::value_type vt("global", ConfSection()); - pair < section_iter_t, bool > vr(sections.insert(vt)); - assert(vr.second); - section_iter_t cur_section = vr.first; - std::string acc; - - const char *b = buf; - int line_no = 0; - size_t line_len = -1; - size_t rem = sz; - while (1) { - b += line_len + 1; - rem -= line_len + 1; - if (rem == 0) - break; - line_no++; - - // look for the next newline - const char *end = (const char*)memchr(b, '\n', rem); - if (!end) { - ostringstream oss; - oss << "read_conf: ignoring line " << line_no << " because it doesn't " - << "end with a newline! Please end the config file with a newline."; - errors->push_back(oss.str()); - break; - } - - // find length of line, and search for NULLs - line_len = 0; - bool found_null = false; - for (const char *tmp = b; tmp != end; ++tmp) { - line_len++; - if (*tmp == '\0') { - found_null = true; - } - } - - if (found_null) { - ostringstream oss; - oss << "read_conf: ignoring line " << line_no << " because it has " - << "an embedded null."; - errors->push_back(oss.str()); - acc.clear(); - continue; - } - - if ((line_len >= 1) && (b[line_len-1] == '\\')) { - // A backslash at the end of a line serves as a line continuation marker. - // Combine the next line with this one. - // Remove the backslash itself from the text. - acc.append(b, line_len - 1); - continue; - } - - acc.append(b, line_len); - - //cerr << "acc = '" << acc << "'" << std::endl; - ConfLine *cline = process_line(line_no, acc.c_str(), errors); - acc.clear(); - if (!cline) - continue; - const std::string &csection(cline->newsection); - if (!csection.empty()) { - std::map <std::string, ConfSection>::value_type nt(csection, ConfSection()); - pair < section_iter_t, bool > nr(sections.insert(nt)); - cur_section = nr.first; - } - else { - if (cur_section->second.lines.count(*cline)) { - // replace an existing key/line in this section, so that - // [mysection] - // foo = 1 - // foo = 2 - // will result in foo = 2. - cur_section->second.lines.erase(*cline); - if (cline->key.length() && warnings) - *warnings << "warning: line " << line_no << ": '" << cline->key << "' in section '" - << cur_section->first << "' redefined " << std::endl; - } - // add line to current section - //std::cerr << "cur_section = " << cur_section->first << ", " << *cline << std::endl; - cur_section->second.lines.insert(*cline); - } - delete cline; - } - - if (!acc.empty()) { - ostringstream oss; - oss << "read_conf: don't end with lines that end in backslashes!"; - errors->push_back(oss.str()); - } -} - -/* - * A simple state-machine based parser. - * This probably could/should be rewritten with something like boost::spirit - * or yacc if the grammar ever gets more complex. - */ -ConfLine* ConfFile:: -process_line(int line_no, const char *line, std::deque<std::string> *errors) -{ - enum acceptor_state_t { - ACCEPT_INIT, - ACCEPT_SECTION_NAME, - ACCEPT_KEY, - ACCEPT_VAL_START, - ACCEPT_UNQUOTED_VAL, - ACCEPT_QUOTED_VAL, - ACCEPT_COMMENT_START, - ACCEPT_COMMENT_TEXT, - }; - const char *l = line; - acceptor_state_t state = ACCEPT_INIT; - string key, val, newsection, comment; - bool escaping = false; - while (true) { - char c = *l++; - switch (state) { - case ACCEPT_INIT: - if (c == '\0') - return NULL; // blank line. Not an error, but not interesting either. - else if (c == '[') - state = ACCEPT_SECTION_NAME; - else if ((c == '#') || (c == ';')) - state = ACCEPT_COMMENT_TEXT; - else if (c == ']') { - ostringstream oss; - oss << "unexpected right bracket at char " << (l - line) - << ", line " << line_no; - errors->push_back(oss.str()); - return NULL; - } - else if (isspace(c)) { - // ignore whitespace here - } - else { - // try to accept this character as a key - state = ACCEPT_KEY; - --l; - } - break; - case ACCEPT_SECTION_NAME: - if (c == '\0') { - ostringstream oss; - oss << "error parsing new section name: expected right bracket " - << "at char " << (l - line) << ", line " << line_no; - errors->push_back(oss.str()); - return NULL; - } - else if ((c == ']') && (!escaping)) { - trim_whitespace(newsection, true); - if (newsection.empty()) { - ostringstream oss; - oss << "error parsing new section name: no section name found? " - << "at char " << (l - line) << ", line " << line_no; - errors->push_back(oss.str()); - return NULL; - } - state = ACCEPT_COMMENT_START; - } - else if (((c == '#') || (c == ';')) && (!escaping)) { - ostringstream oss; - oss << "unexpected comment marker while parsing new section name, at " - << "char " << (l - line) << ", line " << line_no; - errors->push_back(oss.str()); - return NULL; - } - else if ((c == '\\') && (!escaping)) { - escaping = true; - } - else { - escaping = false; - newsection += c; - } - break; - case ACCEPT_KEY: - if ((((c == '#') || (c == ';')) && (!escaping)) || (c == '\0')) { - ostringstream oss; - if (c == '\0') { - oss << "end of key=val line " << line_no - << " reached, no \"=val\" found...missing =?"; - } else { - oss << "unexpected character while parsing putative key value, " - << "at char " << (l - line) << ", line " << line_no; - } - errors->push_back(oss.str()); - return NULL; - } - else if ((c == '=') && (!escaping)) { - key = normalize_key_name(key); - if (key.empty()) { - ostringstream oss; - oss << "error parsing key name: no key name found? " - << "at char " << (l - line) << ", line " << line_no; - errors->push_back(oss.str()); - return NULL; - } - state = ACCEPT_VAL_START; - } - else if ((c == '\\') && (!escaping)) { - escaping = true; - } - else { - escaping = false; - key += c; - } - break; - case ACCEPT_VAL_START: - if (c == '\0') - return new ConfLine(key, val, newsection, comment, line_no); - else if ((c == '#') || (c == ';')) - state = ACCEPT_COMMENT_TEXT; - else if (c == '"') - state = ACCEPT_QUOTED_VAL; - else if (isspace(c)) { - // ignore whitespace - } - else { - // try to accept character as a val - state = ACCEPT_UNQUOTED_VAL; - --l; - } - break; - case ACCEPT_UNQUOTED_VAL: - if (c == '\0') { - if (escaping) { - ostringstream oss; - oss << "error parsing value name: unterminated escape sequence " - << "at char " << (l - line) << ", line " << line_no; - errors->push_back(oss.str()); - return NULL; - } - trim_whitespace(val, false); - return new ConfLine(key, val, newsection, comment, line_no); - } - else if (((c == '#') || (c == ';')) && (!escaping)) { - trim_whitespace(val, false); - state = ACCEPT_COMMENT_TEXT; - } - else if ((c == '\\') && (!escaping)) { - escaping = true; - } - else { - escaping = false; - val += c; - } - break; - case ACCEPT_QUOTED_VAL: - if (c == '\0') { - ostringstream oss; - oss << "found opening quote for value, but not the closing quote. " - << "line " << line_no; - errors->push_back(oss.str()); - return NULL; - } - else if ((c == '"') && (!escaping)) { - state = ACCEPT_COMMENT_START; - } - else if ((c == '\\') && (!escaping)) { - escaping = true; - } - else { - escaping = false; - // Add anything, including whitespace. - val += c; - } - break; - case ACCEPT_COMMENT_START: - if (c == '\0') { - return new ConfLine(key, val, newsection, comment, line_no); - } - else if ((c == '#') || (c == ';')) { - state = ACCEPT_COMMENT_TEXT; - } - else if (isspace(c)) { - // ignore whitespace - } - else { - ostringstream oss; - oss << "unexpected character at char " << (l - line) << " of line " - << line_no; - errors->push_back(oss.str()); - return NULL; - } - break; - case ACCEPT_COMMENT_TEXT: - if (c == '\0') - return new ConfLine(key, val, newsection, comment, line_no); - else - comment += c; - break; - default: - assert(0); - break; - } - assert(c != '\0'); // We better not go past the end of the input string. - } -} diff --git a/src/dmclock/sim/src/ConfUtils.h b/src/dmclock/sim/src/ConfUtils.h deleted file mode 100644 index 6c9c2c6c9c8..00000000000 --- a/src/dmclock/sim/src/ConfUtils.h +++ /dev/null @@ -1,83 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab -/* - * Ceph - scalable distributed file system - * - * Copyright (C) 2011 New Dream Network - * - * This is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software - * Foundation. See file COPYING. - * - */ - -#ifndef CEPH_CONFUTILS_H -#define CEPH_CONFUTILS_H - -#include <deque> -#include <map> -#include <set> -#include <string> - -/* - * Ceph configuration file support. - * - * This class loads an INI-style configuration from a file or bufferlist, and - * holds it in memory. In general, an INI configuration file is composed of - * sections, which contain key/value pairs. You can put comments on the end of - * lines by using either a hash mark (#) or the semicolon (;). - * - * You can get information out of ConfFile by calling get_key or by examining - * individual sections. - * - * This class could be extended to support modifying configuration files and - * writing them back out without too much difficulty. Currently, this is not - * implemented, and the file is read-only. - */ -class ConfLine { -public: - ConfLine(const std::string &key_, const std::string val_, - const std::string newsection_, const std::string comment_, int line_no_); - bool operator<(const ConfLine &rhs) const; - friend std::ostream &operator<<(std::ostream& oss, const ConfLine &l); - - std::string key, val, newsection; -}; - -class ConfSection { -public: - typedef std::set <ConfLine>::const_iterator const_line_iter_t; - - std::set <ConfLine> lines; -}; - -class ConfFile { -public: - typedef std::map <std::string, ConfSection>::iterator section_iter_t; - typedef std::map <std::string, ConfSection>::const_iterator const_section_iter_t; - - ConfFile(); - ~ConfFile(); - void clear(); - int parse_file(const std::string &fname, std::deque<std::string> *errors, std::ostream *warnings); - int read(const std::string §ion, const std::string &key, - std::string &val) const; - - const_section_iter_t sections_begin() const; - const_section_iter_t sections_end() const; - - static void trim_whitespace(std::string &str, bool strip_internal); - static std::string normalize_key_name(const std::string &key); - friend std::ostream &operator<<(std::ostream &oss, const ConfFile &cf); - -private: - void load_from_buffer(const char *buf, size_t sz, - std::deque<std::string> *errors, std::ostream *warnings); - static ConfLine* process_line(int line_no, const char *line, - std::deque<std::string> *errors); - - std::map <std::string, ConfSection> sections; -}; - -#endif diff --git a/src/dmclock/sim/src/config.cc b/src/dmclock/sim/src/config.cc deleted file mode 100644 index 79a7b284651..00000000000 --- a/src/dmclock/sim/src/config.cc +++ /dev/null @@ -1,182 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <unistd.h> -#include <string.h> -#include <stdarg.h> - -#include <iostream> -#include <vector> -#include <list> - -#include "config.h" -#include "str_list.h" - - -static void dashes_to_underscores(const char *input, char *output) { - char c = 0; - char *o = output; - const char *i = input; - // first two characters are copied as-is - *o = *i++; - if (*o++ == '\0') - return; - *o = *i++; - if (*o++ == '\0') - return; - for (; ((c = *i)); ++i) { - if (c == '=') { - strcpy(o, i); - return; - } - if (c == '-') - *o++ = '_'; - else - *o++ = c; - } - *o++ = '\0'; -} - -static int va_ceph_argparse_witharg(std::vector<const char*> &args, - std::vector<const char*>::iterator &i, std::string *ret, - std::ostream &oss, va_list ap) { - const char *first = *i; - char tmp[strlen(first)+1]; - dashes_to_underscores(first, tmp); - first = tmp; - - // does this argument match any of the possibilities? - while (1) { - const char *a = va_arg(ap, char*); - if (a == NULL) - return 0; - int strlen_a = strlen(a); - char a2[strlen_a+1]; - dashes_to_underscores(a, a2); - if (strncmp(a2, first, strlen(a2)) == 0) { - if (first[strlen_a] == '=') { - *ret = first + strlen_a + 1; - i = args.erase(i); - return 1; - } - else if (first[strlen_a] == '\0') { - // find second part (or not) - if (i+1 == args.end()) { - oss << "Option " << *i << " requires an argument." << std::endl; - i = args.erase(i); - return -EINVAL; - } - i = args.erase(i); - *ret = *i; - i = args.erase(i); - return 1; - } - } - } -} - -bool crimson::qos_simulation::ceph_argparse_witharg(std::vector<const char*> &args, - std::vector<const char*>::iterator &i, std::string *ret, ...) { - int r; - va_list ap; - va_start(ap, ret); - r = va_ceph_argparse_witharg(args, i, ret, std::cerr, ap); - va_end(ap); - if (r < 0) - _exit(1); - return r != 0; -} - -void crimson::qos_simulation::ceph_argparse_early_args(std::vector<const char*>& args, std::string *conf_file_list) { - std::string val; - - std::vector<const char *> orig_args = args; - - for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) { - if (ceph_argparse_witharg(args, i, &val, "--conf", "-c", (char*)NULL)) { - *conf_file_list = val; - } - else { - // ignore - ++i; - } - } - return; -} - -static bool stobool(const std::string & v) { - return !v.empty () && - (strcasecmp (v.c_str (), "true") == 0 || - atoi (v.c_str ()) != 0); -} - -int crimson::qos_simulation::parse_config_file(const std::string &fname, sim_config_t &g_conf) { - ConfFile cf; - std::deque<std::string> err; - std::ostringstream warn; - int ret = cf.parse_file(fname.c_str(), &err, &warn); - if (ret) { - // error - return ret; - } - - std::string val; - if (!cf.read("global", "server_groups", val)) - g_conf.server_groups = std::stoul(val); - if (!cf.read("global", "client_groups", val)) - g_conf.client_groups = std::stoul(val); - if (!cf.read("global", "server_random_selection", val)) - g_conf.server_random_selection = stobool(val); - if (!cf.read("global", "server_soft_limit", val)) - g_conf.server_soft_limit = stobool(val); - if (!cf.read("global", "anticipation_timeout", val)) - g_conf.anticipation_timeout = stod(val); - - for (uint i = 0; i < g_conf.server_groups; i++) { - srv_group_t st; - std::string section = "server." + std::to_string(i); - if (!cf.read(section, "server_count", val)) - st.server_count = std::stoul(val); - if (!cf.read(section, "server_iops", val)) - st.server_iops = std::stoul(val); - if (!cf.read(section, "server_threads", val)) - st.server_threads = std::stoul(val); - g_conf.srv_group.push_back(st); - } - - for (uint i = 0; i < g_conf.client_groups; i++) { - cli_group_t ct; - std::string section = "client." + std::to_string(i); - if (!cf.read(section, "client_count", val)) - ct.client_count = std::stoul(val); - if (!cf.read(section, "client_wait", val)) - ct.client_wait = std::chrono::seconds(std::stoul(val)); - if (!cf.read(section, "client_total_ops", val)) - ct.client_total_ops = std::stoul(val); - if (!cf.read(section, "client_server_select_range", val)) - ct.client_server_select_range = std::stoul(val); - if (!cf.read(section, "client_iops_goal", val)) - ct.client_iops_goal = std::stoul(val); - if (!cf.read(section, "client_outstanding_ops", val)) - ct.client_outstanding_ops = std::stoul(val); - if (!cf.read(section, "client_reservation", val)) - ct.client_reservation = std::stod(val); - if (!cf.read(section, "client_limit", val)) - ct.client_limit = std::stod(val); - if (!cf.read(section, "client_weight", val)) - ct.client_weight = std::stod(val); - g_conf.cli_group.push_back(ct); - } - - return 0; -} diff --git a/src/dmclock/sim/src/config.h b/src/dmclock/sim/src/config.h deleted file mode 100644 index 41a675e714c..00000000000 --- a/src/dmclock/sim/src/config.h +++ /dev/null @@ -1,152 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <string.h> - -#include <chrono> -#include <vector> -#include <sstream> -#include <iomanip> - -#include "ConfUtils.h" - - -namespace crimson { - namespace qos_simulation { - - struct cli_group_t { - uint client_count; - std::chrono::seconds client_wait; - uint client_total_ops; - uint client_server_select_range; - uint client_iops_goal; - uint client_outstanding_ops; - double client_reservation; - double client_limit; - double client_weight; - - cli_group_t(uint _client_count = 100, - uint _client_wait = 0, - uint _client_total_ops = 1000, - uint _client_server_select_range = 10, - uint _client_iops_goal = 50, - uint _client_outstanding_ops = 100, - double _client_reservation = 20.0, - double _client_limit = 60.0, - double _client_weight = 1.0) : - client_count(_client_count), - client_wait(std::chrono::seconds(_client_wait)), - client_total_ops(_client_total_ops), - client_server_select_range(_client_server_select_range), - client_iops_goal(_client_iops_goal), - client_outstanding_ops(_client_outstanding_ops), - client_reservation(_client_reservation), - client_limit(_client_limit), - client_weight(_client_weight) - { - // empty - } - - friend std::ostream& operator<<(std::ostream& out, - const cli_group_t& cli_group) { - out << - "client_count = " << cli_group.client_count << "\n" << - "client_wait = " << cli_group.client_wait.count() << "\n" << - "client_total_ops = " << cli_group.client_total_ops << "\n" << - "client_server_select_range = " << cli_group.client_server_select_range << "\n" << - "client_iops_goal = " << cli_group.client_iops_goal << "\n" << - "client_outstanding_ops = " << cli_group.client_outstanding_ops << "\n" << - std::fixed << std::setprecision(1) << - "client_reservation = " << cli_group.client_reservation << "\n" << - "client_limit = " << cli_group.client_limit << "\n" << - "client_weight = " << cli_group.client_weight; - return out; - } - }; // class cli_group_t - - - struct srv_group_t { - uint server_count; - uint server_iops; - uint server_threads; - - srv_group_t(uint _server_count = 100, - uint _server_iops = 40, - uint _server_threads = 1) : - server_count(_server_count), - server_iops(_server_iops), - server_threads(_server_threads) - { - // empty - } - - friend std::ostream& operator<<(std::ostream& out, - const srv_group_t& srv_group) { - out << - "server_count = " << srv_group.server_count << "\n" << - "server_iops = " << srv_group.server_iops << "\n" << - "server_threads = " << srv_group.server_threads; - return out; - } - }; // class srv_group_t - - - struct sim_config_t { - uint server_groups; - uint client_groups; - bool server_random_selection; - bool server_soft_limit; - double anticipation_timeout; - - std::vector<cli_group_t> cli_group; - std::vector<srv_group_t> srv_group; - - sim_config_t(uint _server_groups = 1, - uint _client_groups = 1, - bool _server_random_selection = false, - bool _server_soft_limit = true, - double _anticipation_timeout = 0.0) : - server_groups(_server_groups), - client_groups(_client_groups), - server_random_selection(_server_random_selection), - server_soft_limit(_server_soft_limit), - anticipation_timeout(_anticipation_timeout) - { - srv_group.reserve(server_groups); - cli_group.reserve(client_groups); - } - - friend std::ostream& operator<<(std::ostream& out, - const sim_config_t& sim_config) { - out << - "server_groups = " << sim_config.server_groups << "\n" << - "client_groups = " << sim_config.client_groups << "\n" << - "server_random_selection = " << sim_config.server_random_selection << "\n" << - "server_soft_limit = " << sim_config.server_soft_limit << "\n" << - std::fixed << std::setprecision(3) << - "anticipation_timeout = " << sim_config.anticipation_timeout; - return out; - } - }; // class sim_config_t - - - bool ceph_argparse_witharg(std::vector<const char*> &args, - std::vector<const char*>::iterator &i, std::string *ret, ...); - void ceph_argparse_early_args(std::vector<const char*>& args, std::string *conf_file_list); - int parse_config_file(const std::string &fname, sim_config_t &g_conf); - - }; // namespace qos_simulation -}; // namespace crimson diff --git a/src/dmclock/sim/src/sim_client.h b/src/dmclock/sim/src/sim_client.h deleted file mode 100644 index 328fe184745..00000000000 --- a/src/dmclock/sim/src/sim_client.h +++ /dev/null @@ -1,337 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <atomic> -#include <mutex> -#include <condition_variable> -#include <thread> -#include <chrono> -#include <vector> -#include <deque> -#include <iostream> - -#include "sim_recs.h" - - -namespace crimson { - namespace qos_simulation { - - struct req_op_t {}; - struct wait_op_t {}; - constexpr struct req_op_t req_op {}; - constexpr struct wait_op_t wait_op {}; - - - enum class CliOp { req, wait }; - struct CliInst { - CliOp op; - union { - std::chrono::milliseconds wait_time; - struct { - uint32_t count; - std::chrono::microseconds time_bw_reqs; - uint16_t max_outstanding; - } req_params; - } args; - - // D is a duration type - template<typename D> - CliInst(wait_op_t, D duration) : - op(CliOp::wait) - { - args.wait_time = - std::chrono::duration_cast<std::chrono::milliseconds>(duration); - } - - CliInst(req_op_t, - uint32_t count, double ops_per_sec, uint16_t max_outstanding) : - op(CliOp::req) - { - args.req_params.count = count; - args.req_params.max_outstanding = max_outstanding; - uint32_t us = uint32_t(0.5 + 1.0 / ops_per_sec * 1000000); - args.req_params.time_bw_reqs = std::chrono::microseconds(us); - } - }; - - - using ServerSelectFunc = std::function<const ServerId&(uint64_t seed)>; - - - template<typename SvcTrk, typename ReqPm, typename RespPm, typename Accum> - class SimulatedClient { - public: - - struct InternalStats { - std::mutex mtx; - std::chrono::nanoseconds track_resp_time; - std::chrono::nanoseconds get_req_params_time; - uint32_t track_resp_count; - uint32_t get_req_params_count; - - InternalStats() : - track_resp_time(0), - get_req_params_time(0), - track_resp_count(0), - get_req_params_count(0) - { - // empty - } - }; - - using SubmitFunc = - std::function<void(const ServerId&, - TestRequest&&, - const ClientId&, - const ReqPm&)>; - - using ClientAccumFunc = std::function<void(Accum&,const RespPm&)>; - - typedef std::chrono::time_point<std::chrono::steady_clock> TimePoint; - - static TimePoint now() { return std::chrono::steady_clock::now(); } - - protected: - - struct RespQueueItem { - TestResponse response; - ServerId server_id; - RespPm resp_params; - }; - - const ClientId id; - const SubmitFunc submit_f; - const ServerSelectFunc server_select_f; - const ClientAccumFunc accum_f; - - std::vector<CliInst> instructions; - - SvcTrk service_tracker; - - // TODO: use lock rather than atomic??? - std::atomic_ulong outstanding_ops; - std::atomic_bool requests_complete; - - std::deque<RespQueueItem> resp_queue; - - std::mutex mtx_req; - std::condition_variable cv_req; - - std::mutex mtx_resp; - std::condition_variable cv_resp; - - using RespGuard = std::lock_guard<decltype(mtx_resp)>; - using Lock = std::unique_lock<std::mutex>; - - // data collection - - std::vector<TimePoint> op_times; - Accum accumulator; - InternalStats internal_stats; - - std::thread thd_req; - std::thread thd_resp; - - public: - - SimulatedClient(ClientId _id, - const SubmitFunc& _submit_f, - const ServerSelectFunc& _server_select_f, - const ClientAccumFunc& _accum_f, - const std::vector<CliInst>& _instrs) : - id(_id), - submit_f(_submit_f), - server_select_f(_server_select_f), - accum_f(_accum_f), - instructions(_instrs), - service_tracker(), - outstanding_ops(0), - requests_complete(false) - { - size_t op_count = 0; - for (auto i : instructions) { - if (CliOp::req == i.op) { - op_count += i.args.req_params.count; - } - } - op_times.reserve(op_count); - - thd_resp = std::thread(&SimulatedClient::run_resp, this); - thd_req = std::thread(&SimulatedClient::run_req, this); - } - - - SimulatedClient(ClientId _id, - const SubmitFunc& _submit_f, - const ServerSelectFunc& _server_select_f, - const ClientAccumFunc& _accum_f, - uint16_t _ops_to_run, - double _iops_goal, - uint16_t _outstanding_ops_allowed) : - SimulatedClient(_id, - _submit_f, _server_select_f, _accum_f, - {{req_op, _ops_to_run, _iops_goal, _outstanding_ops_allowed}}) - { - // empty - } - - - SimulatedClient(const SimulatedClient&) = delete; - SimulatedClient(SimulatedClient&&) = delete; - SimulatedClient& operator=(const SimulatedClient&) = delete; - SimulatedClient& operator=(SimulatedClient&&) = delete; - - virtual ~SimulatedClient() { - wait_until_done(); - } - - void receive_response(const TestResponse& resp, - const ServerId& server_id, - const RespPm& resp_params) { - RespGuard g(mtx_resp); - resp_queue.push_back(RespQueueItem{resp, server_id, resp_params}); - cv_resp.notify_one(); - } - - const std::vector<TimePoint>& get_op_times() const { return op_times; } - - void wait_until_done() { - if (thd_req.joinable()) thd_req.join(); - if (thd_resp.joinable()) thd_resp.join(); - } - - const Accum& get_accumulator() const { return accumulator; } - - const InternalStats& get_internal_stats() const { return internal_stats; } - - protected: - - void run_req() { - size_t ops_count = 0; - for (auto i : instructions) { - if (CliOp::wait == i.op) { - std::this_thread::sleep_for(i.args.wait_time); - } else if (CliOp::req == i.op) { - Lock l(mtx_req); - for (uint64_t o = 0; o < i.args.req_params.count; ++o) { - while (outstanding_ops >= i.args.req_params.max_outstanding) { - cv_req.wait(l); - } - - l.unlock(); - auto now = std::chrono::steady_clock::now(); - const ServerId& server = server_select_f(o); - - ReqPm rp = - time_stats_w_return<decltype(internal_stats.get_req_params_time), - ReqPm>(internal_stats.mtx, - internal_stats.get_req_params_time, - [&]() -> ReqPm { - return service_tracker.get_req_params(server); - }); - count_stats(internal_stats.mtx, - internal_stats.get_req_params_count); - - submit_f(server, - TestRequest{server, static_cast<uint32_t>(o), 12}, - id, rp); - ++outstanding_ops; - l.lock(); // lock for return to top of loop - - auto delay_time = now + i.args.req_params.time_bw_reqs; - while (std::chrono::steady_clock::now() < delay_time) { - cv_req.wait_until(l, delay_time); - } // while - } // for - ops_count += i.args.req_params.count; - } else { - assert(false); - } - } // for loop - - requests_complete = true; - - // all requests made, thread ends - } - - - void run_resp() { - std::chrono::milliseconds delay(1000); - int op = 0; - - Lock l(mtx_resp); - - // since the following code would otherwise be repeated (except for - // the call to notify_one) in the two loops below; let's avoid - // repetition and define it once. - const auto proc_resp = [this, &op, &l](const bool notify_req_cv) { - if (!resp_queue.empty()) { - RespQueueItem item = resp_queue.front(); - resp_queue.pop_front(); - - l.unlock(); - - // data collection - - op_times.push_back(now()); - accum_f(accumulator, item.resp_params); - - // processing - -#if 0 // not needed - TestResponse& resp = item.response; -#endif - - time_stats(internal_stats.mtx, - internal_stats.track_resp_time, - [&](){ - service_tracker.track_resp(item.server_id, item.resp_params); - }); - count_stats(internal_stats.mtx, - internal_stats.track_resp_count); - - --outstanding_ops; - if (notify_req_cv) { - cv_req.notify_one(); - } - - l.lock(); - } - }; - - while(!requests_complete.load()) { - while(resp_queue.empty() && !requests_complete.load()) { - cv_resp.wait_for(l, delay); - } - proc_resp(true); - } - - while(outstanding_ops.load() > 0) { - while(resp_queue.empty() && outstanding_ops.load() > 0) { - cv_resp.wait_for(l, delay); - } - proc_resp(false); // don't call notify_one as all requests are complete - } - - // all responses received, thread ends - } - }; // class SimulatedClient - - - }; // namespace qos_simulation -}; // namespace crimson diff --git a/src/dmclock/sim/src/sim_recs.h b/src/dmclock/sim/src/sim_recs.h deleted file mode 100644 index 29369a7226e..00000000000 --- a/src/dmclock/sim/src/sim_recs.h +++ /dev/null @@ -1,129 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <stdint.h> -#include <stdlib.h> -#include <assert.h> -#include <signal.h> - -#include <sys/time.h> - -#include <cmath> -#include <limits> -#include <string> -#include <mutex> -#include <iostream> -#include <functional> - - -using ClientId = uint; -using ServerId = uint; - - -namespace crimson { - namespace qos_simulation { - - inline void debugger() { - raise(SIGCONT); - } - - template<typename T> - void time_stats(std::mutex& mtx, - T& time_accumulate, - std::function<void()> code) { - auto t1 = std::chrono::steady_clock::now(); - code(); - auto t2 = std::chrono::steady_clock::now(); - auto duration = t2 - t1; - auto cast_duration = std::chrono::duration_cast<T>(duration); - std::lock_guard<std::mutex> lock(mtx); - time_accumulate += cast_duration; - } - - // unfortunately it's hard for the compiler to infer the types, - // and therefore when called the template params might have to be - // explicit - template<typename T, typename R> - R time_stats_w_return(std::mutex& mtx, - T& time_accumulate, - std::function<R()> code) { - auto t1 = std::chrono::steady_clock::now(); - R result = code(); - auto t2 = std::chrono::steady_clock::now(); - auto duration = t2 - t1; - auto cast_duration = std::chrono::duration_cast<T>(duration); - std::lock_guard<std::mutex> lock(mtx); - time_accumulate += cast_duration; - return result; - } - - template<typename T> - void count_stats(std::mutex& mtx, - T& counter) { - std::lock_guard<std::mutex> lock(mtx); - ++counter; - } - - struct TestRequest { - ServerId server; // allows debugging - uint32_t epoch; - uint32_t op; - - TestRequest(ServerId _server, - uint32_t _epoch, - uint32_t _op) : - server(_server), - epoch(_epoch), - op(_op) - { - // empty - } - - TestRequest(const TestRequest& r) : - TestRequest(r.server, r.epoch, r.op) - { - // empty - } - }; // struct TestRequest - - - struct TestResponse { - uint32_t epoch; - - TestResponse(uint32_t _epoch) : - epoch(_epoch) - { - // empty - } - - TestResponse(const TestResponse& r) : - epoch(r.epoch) - { - // empty - } - - friend std::ostream& operator<<(std::ostream& out, const TestResponse& resp) { - out << "{ "; - out << "epoch:" << resp.epoch; - out << " }"; - return out; - } - }; // class TestResponse - - }; // namespace qos_simulation -}; // namespace crimson diff --git a/src/dmclock/sim/src/sim_server.h b/src/dmclock/sim/src/sim_server.h deleted file mode 100644 index aecaa8ed336..00000000000 --- a/src/dmclock/sim/src/sim_server.h +++ /dev/null @@ -1,234 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <thread> -#include <mutex> -#include <condition_variable> -#include <chrono> -#include <deque> - -#include "sim_recs.h" - - -namespace crimson { - namespace qos_simulation { - - template<typename Q, typename ReqPm, typename RespPm, typename Accum> - class SimulatedServer { - - struct QueueItem { - ClientId client; - std::unique_ptr<TestRequest> request; - RespPm additional; - - QueueItem(const ClientId& _client, - std::unique_ptr<TestRequest>&& _request, - const RespPm& _additional) : - client(_client), - request(std::move(_request)), - additional(_additional) - { - // empty - } - }; // QueueItem - - public: - - struct InternalStats { - std::mutex mtx; - std::chrono::nanoseconds add_request_time; - std::chrono::nanoseconds request_complete_time; - uint32_t add_request_count; - uint32_t request_complete_count; - - InternalStats() : - add_request_time(0), - request_complete_time(0), - add_request_count(0), - request_complete_count(0) - { - // empty - } - }; - - using ClientRespFunc = std::function<void(ClientId, - const TestResponse&, - const ServerId&, - const RespPm&)>; - - using ServerAccumFunc = std::function<void(Accum& accumulator, - const RespPm& additional)>; - - protected: - - const ServerId id; - Q* priority_queue; - ClientRespFunc client_resp_f; - int iops; - size_t thread_pool_size; - - bool finishing; - std::chrono::microseconds op_time; - - std::mutex inner_queue_mtx; - std::condition_variable inner_queue_cv; - std::deque<QueueItem> inner_queue; - - std::thread* threads; - - using InnerQGuard = std::lock_guard<decltype(inner_queue_mtx)>; - using Lock = std::unique_lock<std::mutex>; - - // data collection - - ServerAccumFunc accum_f; - Accum accumulator; - - InternalStats internal_stats; - - public: - - using CanHandleRequestFunc = std::function<bool(void)>; - using HandleRequestFunc = - std::function<void(const ClientId&,std::unique_ptr<TestRequest>,const RespPm&)>; - using CreateQueueF = std::function<Q*(CanHandleRequestFunc,HandleRequestFunc)>; - - - SimulatedServer(ServerId _id, - int _iops, - size_t _thread_pool_size, - const ClientRespFunc& _client_resp_f, - const ServerAccumFunc& _accum_f, - CreateQueueF _create_queue_f) : - id(_id), - priority_queue(_create_queue_f(std::bind(&SimulatedServer::has_avail_thread, - this), - std::bind(&SimulatedServer::inner_post, - this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3))), - client_resp_f(_client_resp_f), - iops(_iops), - thread_pool_size(_thread_pool_size), - finishing(false), - accum_f(_accum_f) - { - op_time = - std::chrono::microseconds((int) (0.5 + - thread_pool_size * 1000000.0 / iops)); - std::chrono::milliseconds delay(1000); - threads = new std::thread[thread_pool_size]; - for (size_t i = 0; i < thread_pool_size; ++i) { - threads[i] = std::thread(&SimulatedServer::run, this, delay); - } - } - - virtual ~SimulatedServer() { - Lock l(inner_queue_mtx); - finishing = true; - inner_queue_cv.notify_all(); - l.unlock(); - - for (size_t i = 0; i < thread_pool_size; ++i) { - threads[i].join(); - } - - delete[] threads; - - delete priority_queue; - } - - void post(TestRequest&& request, - const ClientId& client_id, - const ReqPm& req_params) - { - time_stats(internal_stats.mtx, - internal_stats.add_request_time, - [&](){ - priority_queue->add_request(std::move(request), - client_id, req_params); - }); - count_stats(internal_stats.mtx, - internal_stats.add_request_count); - } - - bool has_avail_thread() { - InnerQGuard g(inner_queue_mtx); - return inner_queue.size() <= thread_pool_size; - } - - const Accum& get_accumulator() const { return accumulator; } - const Q& get_priority_queue() const { return *priority_queue; } - const InternalStats& get_internal_stats() const { return internal_stats; } - - protected: - - void inner_post(const ClientId& client, - std::unique_ptr<TestRequest> request, - const RespPm& additional) { - Lock l(inner_queue_mtx); - assert(!finishing); - accum_f(accumulator, additional); - inner_queue.emplace_back(QueueItem(client, - std::move(request), - additional)); - inner_queue_cv.notify_one(); - } - - void run(std::chrono::milliseconds check_period) { - Lock l(inner_queue_mtx); - while(true) { - while(inner_queue.empty() && !finishing) { - inner_queue_cv.wait_for(l, check_period); - } - if (!inner_queue.empty()) { - auto& front = inner_queue.front(); - auto client = front.client; - auto req = std::move(front.request); - auto additional = front.additional; - inner_queue.pop_front(); - - l.unlock(); - - // simulation operation by sleeping; then call function to - // notify server of completion - std::this_thread::sleep_for(op_time); - - // TODO: rather than assuming this constructor exists, perhaps - // pass in a function that does this mapping? - client_resp_f(client, TestResponse{req->epoch}, id, additional); - - time_stats(internal_stats.mtx, - internal_stats.request_complete_time, - [&](){ - priority_queue->request_completed(); - }); - count_stats(internal_stats.mtx, - internal_stats.request_complete_count); - - l.lock(); // in prep for next iteration of loop - } else { - break; - } - } - } - }; // class SimulatedServer - - }; // namespace qos_simulation -}; // namespace crimson diff --git a/src/dmclock/sim/src/simulate.h b/src/dmclock/sim/src/simulate.h deleted file mode 100644 index 4d61ddf9231..00000000000 --- a/src/dmclock/sim/src/simulate.h +++ /dev/null @@ -1,448 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <assert.h> - -#include <memory> -#include <chrono> -#include <map> -#include <random> -#include <iostream> -#include <iomanip> -#include <string> - - -namespace crimson { - namespace qos_simulation { - - template<typename ServerId, typename ClientId, typename TS, typename TC> - class Simulation { - - public: - - using TimePoint = std::chrono::time_point<std::chrono::steady_clock>; - - protected: - - using ClientMap = std::map<ClientId,TC*>; - using ServerMap = std::map<ServerId,TS*>; - - uint server_count = 0; - uint client_count = 0; - - ServerMap servers; - ClientMap clients; - std::vector<ServerId> server_ids; - - TimePoint early_time; - TimePoint servers_created_time; - TimePoint clients_created_time; - TimePoint clients_finished_time; - TimePoint late_time; - - std::default_random_engine prng; - - bool has_run = false; - - - public: - - double fmt_tp(const TimePoint& t) { - auto c = t.time_since_epoch().count(); - return uint64_t(c / 1000000.0 + 0.5) % 100000 / 1000.0; - } - - TimePoint now() { - return std::chrono::steady_clock::now(); - } - - using ClientBasedServerSelectFunc = - std::function<const ServerId&(uint64_t, uint16_t)>; - - using ClientFilter = std::function<bool(const ClientId&)>; - - using ServerFilter = std::function<bool(const ServerId&)>; - - using ServerDataOutF = - std::function<void(std::ostream& out, - Simulation* sim, ServerFilter, - int header_w, int data_w, int data_prec)>; - - using ClientDataOutF = - std::function<void(std::ostream& out, - Simulation* sim, ClientFilter, - int header_w, int data_w, int data_prec)>; - - Simulation() : - early_time(now()), - prng(std::chrono::system_clock::now().time_since_epoch().count()) - { - // empty - } - - ~Simulation() { - for (auto c : clients) { - TC* cp = c.second; - delete cp; - } - - for (auto s : servers) { - delete s.second; - } - } - - uint get_client_count() const { return client_count; } - uint get_server_count() const { return server_count; } - TC& get_client(ClientId id) { return *clients[id]; } - TS& get_server(ServerId id) { return *servers[id]; } - const ServerId& get_server_id(uint index) const { - return server_ids[index]; - } - - - void add_servers(uint count, - std::function<TS*(ServerId)> create_server_f) { - uint i = server_count; - - // increment server_count before creating servers since they - // will start running immediately and may use the server_count - // value; NB: this could still be an issue if servers are - // added with multiple add_servers calls; consider using a - // separate start function after all servers (and clients?) - // have been added - server_count += count; - - for (; i < server_count; ++i) { - server_ids.push_back(i); - servers[i] = create_server_f(i); - } - - servers_created_time = now(); - } - - - void add_clients(uint count, - std::function<TC*(ClientId)> create_client_f) { - uint i = client_count; - - // increment client_count before creating clients since they - // will start running immediately and may use the client_count - // value (e.g., in the server selection function); NB: this could - // still be an issue if clients are added with multiple - // add_clients calls; consider using a separate start function - // after all clients have been added - client_count += count; - - for (; i < client_count; ++i) { - clients[i] = create_client_f(i); - } - - clients_created_time = now(); - } - - - void run() { - assert(server_count > 0); - assert(client_count > 0); - - std::cout << "simulation started" << std::endl; - - // clients are now running; wait for all to finish - - for (auto const &i : clients) { - i.second->wait_until_done(); - } - - late_time = clients_finished_time = now(); - - std::cout << "simulation completed in " << - std::chrono::duration_cast<std::chrono::milliseconds>(clients_finished_time - servers_created_time).count() << - " millisecs" << std::endl; - - has_run = true; - } // run - - - void display_stats(std::ostream& out, - ServerDataOutF server_out_f, ClientDataOutF client_out_f, - ServerFilter server_filter = - [] (const ServerId&) { return true; }, - ClientFilter client_filter = - [] (const ClientId&) { return true; }, - int head_w = 12, int data_w = 7, int data_prec = 2) { - assert(has_run); - - // skip first 2 secondsd of data - const std::chrono::seconds skip_amount(0); - // calculate in groups of 5 seconds - const std::chrono::seconds measure_unit(2); - // unit to output reports in - const std::chrono::seconds report_unit(1); - - // compute and display stats - - TimePoint earliest_start = late_time; - TimePoint latest_start = early_time; - TimePoint earliest_finish = late_time; - TimePoint latest_finish = early_time; - - for (auto const &c : clients) { - auto start = c.second->get_op_times().front(); - auto end = c.second->get_op_times().back(); - - if (start < earliest_start) { earliest_start = start; } - if (start > latest_start) { latest_start = start; } - if (end < earliest_finish) { earliest_finish = end; } - if (end > latest_finish) { latest_finish = end; } - } - - double ops_factor = - std::chrono::duration_cast<std::chrono::duration<double>>(measure_unit) / - std::chrono::duration_cast<std::chrono::duration<double>>(report_unit); - - const auto start_edge = clients_created_time + skip_amount; - - std::map<ClientId,std::vector<double>> ops_data; - - for (auto const &c : clients) { - auto it = c.second->get_op_times().begin(); - const auto end = c.second->get_op_times().end(); - while (it != end && *it < start_edge) { ++it; } - - for (auto time_edge = start_edge + measure_unit; - time_edge <= latest_finish + measure_unit; - time_edge += measure_unit) { - int count = 0; - for (; it != end && *it < time_edge; ++count, ++it) { /* empty */ } - double ops_per_second = double(count) / ops_factor; - ops_data[c.first].push_back(ops_per_second); - } - } - - out << "==== Client Data ====" << std::endl; - - out << std::setw(head_w) << "client:"; - for (auto const &c : clients) { - if (!client_filter(c.first)) continue; - out << " " << std::setw(data_w) << c.first; - } - out << std::setw(data_w) << "total" << std::endl; - - { - bool has_data; - size_t i = 0; - do { - std::string line_header = "t_" + std::to_string(i) + ":"; - out << std::setw(head_w) << line_header; - has_data = false; - double total = 0.0; - for (auto const &c : clients) { - double data = 0.0; - if (i < ops_data[c.first].size()) { - data = ops_data[c.first][i]; - has_data = true; - } - total += data; - - if (!client_filter(c.first)) continue; - - out << " " << std::setw(data_w) << std::setprecision(data_prec) << - std::fixed << data; - } - out << " " << std::setw(data_w) << std::setprecision(data_prec) << - std::fixed << total << std::endl; - ++i; - } while(has_data); - } - - client_out_f(out, this, client_filter, head_w, data_w, data_prec); - - display_client_internal_stats<std::chrono::nanoseconds>(out, - "nanoseconds"); - - out << std::endl << "==== Server Data ====" << std::endl; - - out << std::setw(head_w) << "server:"; - for (auto const &s : servers) { - if (!server_filter(s.first)) continue; - out << " " << std::setw(data_w) << s.first; - } - out << " " << std::setw(data_w) << "total" << std::endl; - - server_out_f(out, this, server_filter, head_w, data_w, data_prec); - - display_server_internal_stats<std::chrono::nanoseconds>(out, - "nanoseconds"); - - // clean up clients then servers - - for (auto i = clients.begin(); i != clients.end(); ++i) { - delete i->second; - i->second = nullptr; - } - - for (auto i = servers.begin(); i != servers.end(); ++i) { - delete i->second; - i->second = nullptr; - } - } // display_stats - - - template<typename T> - void display_server_internal_stats(std::ostream& out, - std::string time_unit) { - T add_request_time(0); - T request_complete_time(0); - uint32_t add_request_count = 0; - uint32_t request_complete_count = 0; - - for (uint i = 0; i < get_server_count(); ++i) { - const auto& server = get_server(i); - const auto& is = server.get_internal_stats(); - add_request_time += - std::chrono::duration_cast<T>(is.add_request_time); - request_complete_time += - std::chrono::duration_cast<T>(is.request_complete_time); - add_request_count += is.add_request_count; - request_complete_count += is.request_complete_count; - } - - double add_request_time_per_unit = - double(add_request_time.count()) / add_request_count ; - out << "total time to add requests: " << - std::fixed << add_request_time.count() << " " << time_unit << - ";" << std::endl << - " count: " << add_request_count << ";" << std::endl << - " average: " << add_request_time_per_unit << - " " << time_unit << " per request/response" << std::endl; - - double request_complete_time_unit = - double(request_complete_time.count()) / request_complete_count ; - out << "total time to note requests complete: " << std::fixed << - request_complete_time.count() << " " << time_unit << ";" << - std::endl << - " count: " << request_complete_count << ";" << std::endl << - " average: " << request_complete_time_unit << - " " << time_unit << " per request/response" << std::endl; - - out << std::endl; - - assert(add_request_count == request_complete_count); - out << "server timing for QOS algorithm: " << - add_request_time_per_unit + request_complete_time_unit << - " " << time_unit << " per request/response" << std::endl; - } - - - template<typename T> - void display_client_internal_stats(std::ostream& out, - std::string time_unit) { - T track_resp_time(0); - T get_req_params_time(0); - uint32_t track_resp_count = 0; - uint32_t get_req_params_count = 0; - - for (uint i = 0; i < get_client_count(); ++i) { - const auto& client = get_client(i); - const auto& is = client.get_internal_stats(); - track_resp_time += - std::chrono::duration_cast<T>(is.track_resp_time); - get_req_params_time += - std::chrono::duration_cast<T>(is.get_req_params_time); - track_resp_count += is.track_resp_count; - get_req_params_count += is.get_req_params_count; - } - - double track_resp_time_unit = - double(track_resp_time.count()) / track_resp_count; - out << "total time to track responses: " << - std::fixed << track_resp_time.count() << " " << time_unit << ";" << - std::endl << - " count: " << track_resp_count << ";" << std::endl << - " average: " << track_resp_time_unit << " " << time_unit << - " per request/response" << std::endl; - - double get_req_params_time_unit = - double(get_req_params_time.count()) / get_req_params_count; - out << "total time to get request parameters: " << - std::fixed << get_req_params_time.count() << " " << time_unit << - ";" << std::endl << - " count: " << get_req_params_count << ";" << std::endl << - " average: " << get_req_params_time_unit << " " << time_unit << - " per request/response" << std::endl; - - out << std::endl; - - assert(track_resp_count == get_req_params_count); - out << "client timing for QOS algorithm: " << - track_resp_time_unit + get_req_params_time_unit << " " << - time_unit << " per request/response" << std::endl; - } - - - // **** server selection functions **** - - - const ServerId& server_select_alternate(uint64_t seed, - uint16_t client_idx) { - uint index = (client_idx + seed) % server_count; - return server_ids[index]; - } - - - // returns a lambda using the range specified as servers_per (client) - ClientBasedServerSelectFunc - make_server_select_alt_range(uint16_t servers_per) { - return [servers_per,this](uint64_t seed, uint16_t client_idx) - -> const ServerId& { - double factor = double(server_count) / client_count; - uint offset = seed % servers_per; - uint index = (uint(0.5 + client_idx * factor) + offset) % server_count; - return server_ids[index]; - }; - } - - - // function to choose a server randomly - const ServerId& server_select_random(uint64_t seed, uint16_t client_idx) { - uint index = prng() % server_count; - return server_ids[index]; - } - - - // function to choose a server randomly - ClientBasedServerSelectFunc - make_server_select_ran_range(uint16_t servers_per) { - return [servers_per,this](uint64_t seed, uint16_t client_idx) - -> const ServerId& { - double factor = double(server_count) / client_count; - uint offset = prng() % servers_per; - uint index = (uint(0.5 + client_idx * factor) + offset) % server_count; - return server_ids[index]; - }; - } - - - // function to always choose the first server - const ServerId& server_select_0(uint64_t seed, uint16_t client_idx) { - return server_ids[0]; - } - }; // class Simulation - - }; // namespace qos_simulation -}; // namespace crimson diff --git a/src/dmclock/sim/src/ssched/ssched_client.h b/src/dmclock/sim/src/ssched/ssched_client.h deleted file mode 100644 index 89ff148fccf..00000000000 --- a/src/dmclock/sim/src/ssched/ssched_client.h +++ /dev/null @@ -1,51 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - -#include "ssched_recs.h" - - -namespace crimson { - namespace simple_scheduler { - - // S is server identifier type - template<typename S> - class ServiceTracker { - - public: - - // we have to start the counters at 1, as 0 is used in the - // cleaning process - ServiceTracker() - { - // emptry - } - - - void track_resp(const S& server_id, const NullData& ignore) { - // empty - } - - - /* - * Returns the ReqParams for the given server. - */ - ReqParams get_req_params(const S& server) { - return ReqParams(); - } // get_req_params - }; // class ServiceTracker - } // namespace simple_scheduler -} // namespace crimson diff --git a/src/dmclock/sim/src/ssched/ssched_recs.h b/src/dmclock/sim/src/ssched/ssched_recs.h deleted file mode 100644 index 935e678c1ef..00000000000 --- a/src/dmclock/sim/src/ssched/ssched_recs.h +++ /dev/null @@ -1,44 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <ostream> -#include <assert.h> - - -namespace crimson { - namespace simple_scheduler { - - // since we send no additional data out - // NOTE: Change name to RespParams? Is it used elsewhere? - struct NullData { - friend std::ostream& operator<<(std::ostream& out, const NullData& n) { - out << "NullData{ EMPTY }"; - return out; - } - }; // struct NullData - - - struct ReqParams { - friend std::ostream& operator<<(std::ostream& out, const ReqParams& rp) { - out << "ReqParams{ EMPTY }"; - return out; - } - }; - - } -} diff --git a/src/dmclock/sim/src/ssched/ssched_server.h b/src/dmclock/sim/src/ssched/ssched_server.h deleted file mode 100644 index dc496286a13..00000000000 --- a/src/dmclock/sim/src/ssched/ssched_server.h +++ /dev/null @@ -1,192 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - -#include <memory> -#include <mutex> -#include <deque> -#include <functional> - -#include "boost/variant.hpp" - -#include "ssched_recs.h" - -#ifdef PROFILE -#include "profile.h" -#endif - -namespace crimson { - - namespace simple_scheduler { - - template<typename C, typename R, typename Time> - class SimpleQueue { - - public: - - using RequestRef = std::unique_ptr<R>; - - // a function to see whether the server can handle another request - using CanHandleRequestFunc = std::function<bool(void)>; - - // a function to submit a request to the server; the second - // parameter is a callback when it's completed - using HandleRequestFunc = - std::function<void(const C&,RequestRef,NullData)>; - - struct PullReq { - enum class Type { returning, none }; - - struct Retn { - C client; - RequestRef request; - }; - - Type type; - boost::variant<Retn> data; - }; - - protected: - - enum class Mechanism { push, pull }; - - struct QRequest { - C client; - RequestRef request; - }; - - bool finishing = false; - Mechanism mechanism; - - CanHandleRequestFunc can_handle_f; - HandleRequestFunc handle_f; - - mutable std::mutex queue_mtx; - using DataGuard = std::lock_guard<decltype(queue_mtx)>; - - std::deque<QRequest> queue; - -#ifdef PROFILE - public: - ProfileTimer<std::chrono::nanoseconds> pull_request_timer; - ProfileTimer<std::chrono::nanoseconds> add_request_timer; - ProfileTimer<std::chrono::nanoseconds> request_complete_timer; - protected: -#endif - - public: - - // push full constructor - SimpleQueue(CanHandleRequestFunc _can_handle_f, - HandleRequestFunc _handle_f) : - mechanism(Mechanism::push), - can_handle_f(_can_handle_f), - handle_f(_handle_f) - { - // empty - } - - SimpleQueue() : - mechanism(Mechanism::pull) - { - // empty - } - - ~SimpleQueue() { - finishing = true; - } - - void add_request(R&& request, - const C& client_id, - const ReqParams& req_params) { - add_request(RequestRef(new R(std::move(request))), - client_id, req_params); - } - - void add_request(RequestRef&& request, - const C& client_id, - const ReqParams& req_params) { - DataGuard g(queue_mtx); - -#ifdef PROFILE - add_request_timer.start(); -#endif - queue.emplace_back(QRequest{client_id, std::move(request)}); - - if (Mechanism::push == mechanism) { - schedule_request(); - } - -#ifdef PROFILE - add_request_timer.stop(); -#endif - } // add_request - - void request_completed() { - assert(Mechanism::push == mechanism); - DataGuard g(queue_mtx); - -#ifdef PROFILE - request_complete_timer.start(); -#endif - schedule_request(); - -#ifdef PROFILE - request_complete_timer.stop(); -#endif - } // request_completed - - PullReq pull_request() { - assert(Mechanism::pull == mechanism); - PullReq result; - DataGuard g(queue_mtx); - -#ifdef PROFILE - pull_request_timer.start(); -#endif - - if (queue.empty()) { - result.type = PullReq::Type::none; - } else { - auto front = queue.front(); - result.type = PullReq::Type::returning; - result.data = - typename PullReq::Retn{front.client, std::move(front.request)}; - queue.pop(); - } - -#ifdef PROFILE - pull_request_timer.stop(); -#endif - - return result; - } - - protected: - - // queue_mtx should be held when called; should only be called - // when mechanism is push - void schedule_request() { - if (!queue.empty() && can_handle_f()) { - auto& front = queue.front(); - static NullData null_data; - handle_f(front.client, std::move(front.request), null_data); - queue.pop_front(); - } - } - }; - }; -}; diff --git a/src/dmclock/sim/src/str_list.cc b/src/dmclock/sim/src/str_list.cc deleted file mode 100644 index 22109e00840..00000000000 --- a/src/dmclock/sim/src/str_list.cc +++ /dev/null @@ -1,106 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab -/* - * Ceph - scalable distributed file system - * - * Copyright (C) 2009-2010 Dreamhost - * - * This is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software - * Foundation. See file COPYING. - * - */ - -#include "str_list.h" - -using std::string; -using std::vector; -using std::set; -using std::list; - -static bool get_next_token(const string &s, size_t& pos, const char *delims, string& token) -{ - int start = s.find_first_not_of(delims, pos); - int end; - - if (start < 0){ - pos = s.size(); - return false; - } - - end = s.find_first_of(delims, start); - if (end >= 0) - pos = end + 1; - else { - pos = end = s.size(); - } - - token = s.substr(start, end - start); - return true; -} - -void get_str_list(const string& str, const char *delims, list<string>& str_list) -{ - size_t pos = 0; - string token; - - str_list.clear(); - - while (pos < str.size()) { - if (get_next_token(str, pos, delims, token)) { - if (token.size() > 0) { - str_list.push_back(token); - } - } - } -} - -void get_str_list(const string& str, list<string>& str_list) -{ - const char *delims = ";,= \t"; - return get_str_list(str, delims, str_list); -} - -void get_str_vec(const string& str, const char *delims, vector<string>& str_vec) -{ - size_t pos = 0; - string token; - str_vec.clear(); - - while (pos < str.size()) { - if (get_next_token(str, pos, delims, token)) { - if (token.size() > 0) { - str_vec.push_back(token); - } - } - } -} - -void get_str_vec(const string& str, vector<string>& str_vec) -{ - const char *delims = ";,= \t"; - return get_str_vec(str, delims, str_vec); -} - -void get_str_set(const string& str, const char *delims, set<string>& str_set) -{ - size_t pos = 0; - string token; - - str_set.clear(); - - while (pos < str.size()) { - if (get_next_token(str, pos, delims, token)) { - if (token.size() > 0) { - str_set.insert(token); - } - } - } -} - -void get_str_set(const string& str, set<string>& str_set) -{ - const char *delims = ";,= \t"; - return get_str_set(str, delims, str_set); -} diff --git a/src/dmclock/sim/src/str_list.h b/src/dmclock/sim/src/str_list.h deleted file mode 100644 index 0d2e3bad71c..00000000000 --- a/src/dmclock/sim/src/str_list.h +++ /dev/null @@ -1,109 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2009 Red Hat Inc. - * - * Forked from Red Hat's Ceph project. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#ifndef CEPH_STRLIST_H -#define CEPH_STRLIST_H - -#include <list> -#include <set> -#include <sstream> -#include <string> -#include <vector> - -/** - * Split **str** into a list of strings, using the ";,= \t" delimiters and output the result in **str_list**. - * - * @param [in] str String to split and save as list - * @param [out] str_list List modified containing str after it has been split -**/ -extern void get_str_list(const std::string& str, - std::list<std::string>& str_list); - -/** - * Split **str** into a list of strings, using the **delims** delimiters and output the result in **str_list**. - * - * @param [in] str String to split and save as list - * @param [in] delims characters used to split **str** - * @param [out] str_list List modified containing str after it has been split -**/ -extern void get_str_list(const std::string& str, - const char *delims, - std::list<std::string>& str_list); - -/** - * Split **str** into a list of strings, using the ";,= \t" delimiters and output the result in **str_vec**. - * - * @param [in] str String to split and save as Vector - * @param [out] str_vec Vector modified containing str after it has been split -**/ -extern void get_str_vec(const std::string& str, - std::vector<std::string>& str_vec); - -/** - * Split **str** into a list of strings, using the **delims** delimiters and output the result in **str_vec**. - * - * @param [in] str String to split and save as Vector - * @param [in] delims characters used to split **str** - * @param [out] str_vec Vector modified containing str after it has been split -**/ -extern void get_str_vec(const std::string& str, - const char *delims, - std::vector<std::string>& str_vec); - -/** - * Split **str** into a list of strings, using the ";,= \t" delimiters and output the result in **str_list**. - * - * @param [in] str String to split and save as Set - * @param [out] str_list Set modified containing str after it has been split -**/ -extern void get_str_set(const std::string& str, - std::set<std::string>& str_list); - -/** - * Split **str** into a list of strings, using the **delims** delimiters and output the result in **str_list**. - * - * @param [in] str String to split and save as Set - * @param [in] delims characters used to split **str** - * @param [out] str_list Set modified containing str after it has been split -**/ -extern void get_str_set(const std::string& str, - const char *delims, - std::set<std::string>& str_list); - -/** - * Return a String containing the vector **v** joined with **sep** - * - * If **v** is empty, the function returns an empty string - * For each element in **v**, - * it will concatenate this element and **sep** with result - * - * @param [in] v Vector to join as a String - * @param [in] sep String used to join each element from **v** - * @return empty string if **v** is empty or concatenated string -**/ -inline std::string str_join(const std::vector<std::string>& v, std::string sep) -{ - if (v.empty()) - return std::string(); - std::vector<std::string>::const_iterator i = v.begin(); - std::string r = *i; - for (++i; i != v.end(); ++i) { - r += sep; - r += *i; - } - return r; -} - -#endif diff --git a/src/dmclock/sim/src/test_dmclock.cc b/src/dmclock/sim/src/test_dmclock.cc deleted file mode 100644 index db981da8ae6..00000000000 --- a/src/dmclock/sim/src/test_dmclock.cc +++ /dev/null @@ -1,47 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include "dmclock_recs.h" -#include "dmclock_server.h" -#include "dmclock_client.h" - -#include "sim_recs.h" -#include "sim_server.h" -#include "sim_client.h" - -#include "test_dmclock.h" - - -namespace test = crimson::test_dmc; - - -void test::dmc_server_accumulate_f(test::DmcAccum& a, - const test::dmc::PhaseType& phase) { - if (test::dmc::PhaseType::reservation == phase) { - ++a.reservation_count; - } else { - ++a.proportion_count; - } -} - - -void test::dmc_client_accumulate_f(test::DmcAccum& a, - const test::dmc::PhaseType& phase) { - if (test::dmc::PhaseType::reservation == phase) { - ++a.reservation_count; - } else { - ++a.proportion_count; - } -} diff --git a/src/dmclock/sim/src/test_dmclock.h b/src/dmclock/sim/src/test_dmclock.h deleted file mode 100644 index 1bbe5f7c98a..00000000000 --- a/src/dmclock/sim/src/test_dmclock.h +++ /dev/null @@ -1,64 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include "dmclock_recs.h" -#include "dmclock_server.h" -#include "dmclock_client.h" - -#include "sim_recs.h" -#include "sim_server.h" -#include "sim_client.h" - -#include "simulate.h" - - -namespace crimson { - namespace test_dmc { - - namespace dmc = crimson::dmclock; - namespace sim = crimson::qos_simulation; - - struct DmcAccum { - uint64_t reservation_count = 0; - uint64_t proportion_count = 0; - }; - - using DmcQueue = dmc::PushPriorityQueue<ClientId,sim::TestRequest>; - using DmcServiceTracker = dmc::ServiceTracker<ServerId,dmc::BorrowingTracker>; - - using DmcServer = sim::SimulatedServer<DmcQueue, - dmc::ReqParams, - dmc::PhaseType, - DmcAccum>; - - using DmcClient = sim::SimulatedClient<DmcServiceTracker, - dmc::ReqParams, - dmc::PhaseType, - DmcAccum>; - - using CreateQueueF = std::function<DmcQueue*(DmcQueue::CanHandleRequestFunc, - DmcQueue::HandleRequestFunc)>; - - using MySim = sim::Simulation<ServerId,ClientId,DmcServer,DmcClient>; - - using SubmitFunc = DmcClient::SubmitFunc; - - extern void dmc_server_accumulate_f(DmcAccum& a, - const dmc::PhaseType& phase); - - extern void dmc_client_accumulate_f(DmcAccum& a, - const dmc::PhaseType& phase); - } // namespace test_dmc -} // namespace crimson diff --git a/src/dmclock/sim/src/test_dmclock_main.cc b/src/dmclock/sim/src/test_dmclock_main.cc deleted file mode 100644 index 8983d0349c9..00000000000 --- a/src/dmclock/sim/src/test_dmclock_main.cc +++ /dev/null @@ -1,336 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include "test_dmclock.h" -#include "config.h" - -#ifdef PROFILE -#include "profile.h" -#endif - - -namespace dmc = crimson::dmclock; -namespace test = crimson::test_dmc; -namespace sim = crimson::qos_simulation; - -using namespace std::placeholders; - - -namespace crimson { - namespace test_dmc { - void server_data(std::ostream& out, - test::MySim* sim, - test::MySim::ServerFilter server_disp_filter, - int head_w, int data_w, int data_prec); - - void client_data(std::ostream& out, - test::MySim* sim, - test::MySim::ClientFilter client_disp_filter, - int head_w, int data_w, int data_prec); - } -} - - -int main(int argc, char* argv[]) { - std::vector<const char*> args; - for (int i = 1; i < argc; ++i) { - args.push_back(argv[i]); - } - - std::string conf_file_list; - sim::ceph_argparse_early_args(args, &conf_file_list); - - sim::sim_config_t g_conf; - std::vector<sim::cli_group_t> &cli_group = g_conf.cli_group; - std::vector<sim::srv_group_t> &srv_group = g_conf.srv_group; - - if (!conf_file_list.empty()) { - int ret; - ret = sim::parse_config_file(conf_file_list, g_conf); - if (ret) { - // error - _exit(1); - } - } else { - // default simulation parameter - g_conf.client_groups = 2; - - sim::srv_group_t st; - srv_group.push_back(st); - - sim::cli_group_t ct1(99, 0); - cli_group.push_back(ct1); - - sim::cli_group_t ct2(1, 10); - cli_group.push_back(ct2); - } - - const uint server_groups = g_conf.server_groups; - const uint client_groups = g_conf.client_groups; - const bool server_random_selection = g_conf.server_random_selection; - const bool server_soft_limit = g_conf.server_soft_limit; - const double anticipation_timeout = g_conf.anticipation_timeout; - uint server_total_count = 0; - uint client_total_count = 0; - - for (uint i = 0; i < client_groups; ++i) { - client_total_count += cli_group[i].client_count; - } - - for (uint i = 0; i < server_groups; ++i) { - server_total_count += srv_group[i].server_count; - } - - std::vector<test::dmc::ClientInfo> client_info; - for (uint i = 0; i < client_groups; ++i) { - client_info.push_back(test::dmc::ClientInfo - { cli_group[i].client_reservation, - cli_group[i].client_weight, - cli_group[i].client_limit } ); - } - - auto ret_client_group_f = [&](const ClientId& c) -> uint { - uint group_max = 0; - uint i = 0; - for (; i < client_groups; ++i) { - group_max += cli_group[i].client_count; - if (c < group_max) { - break; - } - } - return i; - }; - - auto ret_server_group_f = [&](const ServerId& s) -> uint { - uint group_max = 0; - uint i = 0; - for (; i < server_groups; ++i) { - group_max += srv_group[i].server_count; - if (s < group_max) { - break; - } - } - return i; - }; - - auto client_info_f = [=](const ClientId& c) -> const test::dmc::ClientInfo* { - return &client_info[ret_client_group_f(c)]; - }; - - auto client_disp_filter = [=] (const ClientId& i) -> bool { - return i < 3 || i >= (client_total_count - 3); - }; - - auto server_disp_filter = [=] (const ServerId& i) -> bool { - return i < 3 || i >= (server_total_count - 3); - }; - - - test::MySim *simulation; - - - // lambda to post a request to the identified server; called by client - test::SubmitFunc server_post_f = - [&simulation](const ServerId& server, - sim::TestRequest&& request, - const ClientId& client_id, - const test::dmc::ReqParams& req_params) { - test::DmcServer& s = simulation->get_server(server); - s.post(std::move(request), client_id, req_params); - }; - - std::vector<std::vector<sim::CliInst>> cli_inst; - for (uint i = 0; i < client_groups; ++i) { - if (cli_group[i].client_wait == std::chrono::seconds(0)) { - cli_inst.push_back( - { { sim::req_op, - (uint32_t)cli_group[i].client_total_ops, - (double)cli_group[i].client_iops_goal, - (uint16_t)cli_group[i].client_outstanding_ops } } ); - } else { - cli_inst.push_back( - { { sim::wait_op, cli_group[i].client_wait }, - { sim::req_op, - (uint32_t)cli_group[i].client_total_ops, - (double)cli_group[i].client_iops_goal, - (uint16_t)cli_group[i].client_outstanding_ops } } ); - } - } - - simulation = new test::MySim(); - - test::DmcServer::ClientRespFunc client_response_f = - [&simulation](ClientId client_id, - const sim::TestResponse& resp, - const ServerId& server_id, - const dmc::PhaseType& phase) { - simulation->get_client(client_id).receive_response(resp, - server_id, - phase); - }; - - test::CreateQueueF create_queue_f = - [&](test::DmcQueue::CanHandleRequestFunc can_f, - test::DmcQueue::HandleRequestFunc handle_f) -> test::DmcQueue* { - return new test::DmcQueue(client_info_f, - can_f, - handle_f, - server_soft_limit, - anticipation_timeout); - }; - - - auto create_server_f = [&](ServerId id) -> test::DmcServer* { - uint i = ret_server_group_f(id); - return new test::DmcServer(id, - srv_group[i].server_iops, - srv_group[i].server_threads, - client_response_f, - test::dmc_server_accumulate_f, - create_queue_f); - }; - - auto create_client_f = [&](ClientId id) -> test::DmcClient* { - uint i = ret_client_group_f(id); - test::MySim::ClientBasedServerSelectFunc server_select_f; - uint client_server_select_range = cli_group[i].client_server_select_range; - if (!server_random_selection) { - server_select_f = simulation->make_server_select_alt_range(client_server_select_range); - } else { - server_select_f = simulation->make_server_select_ran_range(client_server_select_range); - } - return new test::DmcClient(id, - server_post_f, - std::bind(server_select_f, _1, id), - test::dmc_client_accumulate_f, - cli_inst[i]); - }; - -#if 1 - std::cout << "[global]" << std::endl << g_conf << std::endl; - for (uint i = 0; i < client_groups; ++i) { - std::cout << std::endl << "[client." << i << "]" << std::endl; - std::cout << cli_group[i] << std::endl; - } - for (uint i = 0; i < server_groups; ++i) { - std::cout << std::endl << "[server." << i << "]" << std::endl; - std::cout << srv_group[i] << std::endl; - } - std::cout << std::endl; -#endif - - simulation->add_servers(server_total_count, create_server_f); - simulation->add_clients(client_total_count, create_client_f); - - simulation->run(); - simulation->display_stats(std::cout, - &test::server_data, &test::client_data, - server_disp_filter, client_disp_filter); - - delete simulation; -} // main - - -void test::client_data(std::ostream& out, - test::MySim* sim, - test::MySim::ClientFilter client_disp_filter, - int head_w, int data_w, int data_prec) { - // report how many ops were done by reservation and proportion for - // each client - - int total_r = 0; - out << std::setw(head_w) << "res_ops:"; - for (uint i = 0; i < sim->get_client_count(); ++i) { - const auto& client = sim->get_client(i); - auto r = client.get_accumulator().reservation_count; - total_r += r; - if (!client_disp_filter(i)) continue; - out << " " << std::setw(data_w) << r; - } - out << " " << std::setw(data_w) << std::setprecision(data_prec) << - std::fixed << total_r << std::endl; - - int total_p = 0; - out << std::setw(head_w) << "prop_ops:"; - for (uint i = 0; i < sim->get_client_count(); ++i) { - const auto& client = sim->get_client(i); - auto p = client.get_accumulator().proportion_count; - total_p += p; - if (!client_disp_filter(i)) continue; - out << " " << std::setw(data_w) << p; - } - out << " " << std::setw(data_w) << std::setprecision(data_prec) << - std::fixed << total_p << std::endl; -} - - -void test::server_data(std::ostream& out, - test::MySim* sim, - test::MySim::ServerFilter server_disp_filter, - int head_w, int data_w, int data_prec) { - out << std::setw(head_w) << "res_ops:"; - int total_r = 0; - for (uint i = 0; i < sim->get_server_count(); ++i) { - const auto& server = sim->get_server(i); - auto rc = server.get_accumulator().reservation_count; - total_r += rc; - if (!server_disp_filter(i)) continue; - out << " " << std::setw(data_w) << rc; - } - out << " " << std::setw(data_w) << std::setprecision(data_prec) << - std::fixed << total_r << std::endl; - - out << std::setw(head_w) << "prop_ops:"; - int total_p = 0; - for (uint i = 0; i < sim->get_server_count(); ++i) { - const auto& server = sim->get_server(i); - auto pc = server.get_accumulator().proportion_count; - total_p += pc; - if (!server_disp_filter(i)) continue; - out << " " << std::setw(data_w) << pc; - } - out << " " << std::setw(data_w) << std::setprecision(data_prec) << - std::fixed << total_p << std::endl; - - const auto& q = sim->get_server(0).get_priority_queue(); - out << std::endl << - " k-way heap: " << q.get_heap_branching_factor() << std::endl - << std::endl; - -#ifdef PROFILE - crimson::ProfileCombiner<std::chrono::nanoseconds> art_combiner; - crimson::ProfileCombiner<std::chrono::nanoseconds> rct_combiner; - for (uint i = 0; i < sim->get_server_count(); ++i) { - const auto& q = sim->get_server(i).get_priority_queue(); - const auto& art = q.add_request_timer; - art_combiner.combine(art); - const auto& rct = q.request_complete_timer; - rct_combiner.combine(rct); - } - out << "Server add_request_timer: count:" << art_combiner.get_count() << - ", mean:" << art_combiner.get_mean() << - ", std_dev:" << art_combiner.get_std_dev() << - ", low:" << art_combiner.get_low() << - ", high:" << art_combiner.get_high() << std::endl; - out << "Server request_complete_timer: count:" << rct_combiner.get_count() << - ", mean:" << rct_combiner.get_mean() << - ", std_dev:" << rct_combiner.get_std_dev() << - ", low:" << rct_combiner.get_low() << - ", high:" << rct_combiner.get_high() << std::endl; - out << "Server combined mean: " << - (art_combiner.get_mean() + rct_combiner.get_mean()) << - std::endl; -#endif -} diff --git a/src/dmclock/sim/src/test_ssched.cc b/src/dmclock/sim/src/test_ssched.cc deleted file mode 100644 index b06273dc0a8..00000000000 --- a/src/dmclock/sim/src/test_ssched.cc +++ /dev/null @@ -1,40 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include "ssched_recs.h" -#include "ssched_server.h" -#include "ssched_client.h" - -#include "sim_recs.h" -#include "sim_server.h" -#include "sim_client.h" - -#include "test_ssched.h" - - -namespace test = crimson::test_simple_scheduler; -namespace ssched = crimson::simple_scheduler; - - -void test::simple_server_accumulate_f(test::SimpleAccum& a, - const ssched::NullData& add_info) { - ++a.request_count; -} - - -void test::simple_client_accumulate_f(test::SimpleAccum& a, - const ssched::NullData& ignore) { - // empty -} diff --git a/src/dmclock/sim/src/test_ssched.h b/src/dmclock/sim/src/test_ssched.h deleted file mode 100644 index 0d778709afe..00000000000 --- a/src/dmclock/sim/src/test_ssched.h +++ /dev/null @@ -1,64 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include "ssched_server.h" -#include "ssched_client.h" - -#include "sim_recs.h" -#include "sim_server.h" -#include "sim_client.h" - -#include "simulate.h" - - -namespace crimson { - namespace test_simple_scheduler { - - namespace ssched = crimson::simple_scheduler; - namespace sim = crimson::qos_simulation; - - using Time = double; - - struct SimpleAccum { - uint32_t request_count = 0; - }; - - using SimpleQueue = ssched::SimpleQueue<ClientId,sim::TestRequest,Time>; - - using SimpleServer = sim::SimulatedServer<SimpleQueue, - ssched::ReqParams, - ssched::NullData, - SimpleAccum>; - using SimpleClient = sim::SimulatedClient<ssched::ServiceTracker<ServerId>, - ssched::ReqParams, - ssched::NullData, - SimpleAccum>; - - using CreateQueueF = - std::function<SimpleQueue*(SimpleQueue::CanHandleRequestFunc, - SimpleQueue::HandleRequestFunc)>; - - - using MySim = sim::Simulation<ServerId,ClientId,SimpleServer,SimpleClient>; - - using SubmitFunc = SimpleClient::SubmitFunc; - - extern void simple_server_accumulate_f(SimpleAccum& a, - const ssched::NullData& add_info); - - extern void simple_client_accumulate_f(SimpleAccum& a, - const ssched::NullData& ignore); - } // namespace test_simple -} // namespace crimson diff --git a/src/dmclock/sim/src/test_ssched_main.cc b/src/dmclock/sim/src/test_ssched_main.cc deleted file mode 100644 index 82bc381eba6..00000000000 --- a/src/dmclock/sim/src/test_ssched_main.cc +++ /dev/null @@ -1,194 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include "test_ssched.h" - - -#ifdef PROFILE -#include "profile.h" -#endif - - -namespace test = crimson::test_simple_scheduler; -namespace ssched = crimson::simple_scheduler; -namespace sim = crimson::qos_simulation; - -using namespace std::placeholders; - - -namespace crimson { - namespace test_simple_scheduler { - void client_data(std::ostream& out, - test::MySim* sim, - test::MySim::ClientFilter client_disp_filter, - int head_w, int data_w, int data_prec); - - void server_data(std::ostream& out, - test::MySim* sim, - test::MySim::ServerFilter server_disp_filter, - int head_w, int data_w, int data_prec); - } // namespace test_simple -} // namespace crimson - - -int main(int argc, char* argv[]) { - // server params - - const uint server_count = 100; - const uint server_iops = 40; - const uint server_threads = 1; - - // client params - - const uint client_total_ops = 1000; - const uint client_count = 100; - const uint client_server_select_range = 10; - const uint client_wait_count = 1; - const uint client_iops_goal = 50; - const uint client_outstanding_ops = 100; - const std::chrono::seconds client_wait(10); - - auto client_disp_filter = [=] (const ClientId& i) -> bool { - return i < 3 || i >= (client_count - 3); - }; - - auto server_disp_filter = [=] (const ServerId& i) -> bool { - return i < 3 || i >= (server_count - 3); - }; - - - test::MySim *simulation; - - // lambda to post a request to the identified server; called by client - test::SubmitFunc server_post_f = - [&simulation](const ServerId& server_id, - sim::TestRequest&& request, - const ClientId& client_id, - const ssched::ReqParams& req_params) { - auto& server = simulation->get_server(server_id); - server.post(std::move(request), client_id, req_params); - }; - - static std::vector<sim::CliInst> no_wait = - { { sim::req_op, client_total_ops, client_iops_goal, client_outstanding_ops } }; - static std::vector<sim::CliInst> wait = - { { sim::wait_op, client_wait }, - { sim::req_op, client_total_ops, client_iops_goal, client_outstanding_ops } }; - - simulation = new test::MySim(); - -#if 1 - test::MySim::ClientBasedServerSelectFunc server_select_f = - simulation->make_server_select_alt_range(client_server_select_range); -#elif 0 - test::MySim::ClientBasedServerSelectFunc server_select_f = - std::bind(&test::MySim::server_select_random, simulation, _1, _2); -#else - test::MySim::ClientBasedServerSelectFunc server_select_f = - std::bind(&test::MySim::server_select_0, simulation, _1, _2); -#endif - - test::SimpleServer::ClientRespFunc client_response_f = - [&simulation](ClientId client_id, - const sim::TestResponse& resp, - const ServerId& server_id, - const ssched::NullData& resp_params) { - simulation->get_client(client_id).receive_response(resp, - server_id, - resp_params); - }; - - test::CreateQueueF create_queue_f = - [&](test::SimpleQueue::CanHandleRequestFunc can_f, - test::SimpleQueue::HandleRequestFunc handle_f) -> test::SimpleQueue* { - return new test::SimpleQueue(can_f, handle_f); - }; - - auto create_server_f = [&](ServerId id) -> test::SimpleServer* { - return new test::SimpleServer(id, - server_iops, server_threads, - client_response_f, - test::simple_server_accumulate_f, - create_queue_f); - }; - - auto create_client_f = [&](ClientId id) -> test::SimpleClient* { - return new test::SimpleClient(id, - server_post_f, - std::bind(server_select_f, _1, id), - test::simple_client_accumulate_f, - id < (client_count - client_wait_count) - ? no_wait : wait); - }; - - simulation->add_servers(server_count, create_server_f); - simulation->add_clients(client_count, create_client_f); - - simulation->run(); - simulation->display_stats(std::cout, - &test::server_data, &test::client_data, - server_disp_filter, client_disp_filter); -} // main - - -void test::client_data(std::ostream& out, - test::MySim* sim, - test::MySim::ClientFilter client_disp_filter, - int head_w, int data_w, int data_prec) { - // empty -} - - -void test::server_data(std::ostream& out, - test::MySim* sim, - test::MySim::ServerFilter server_disp_filter, - int head_w, int data_w, int data_prec) { - out << std::setw(head_w) << "requests:"; - int total_req = 0; - for (uint i = 0; i < sim->get_server_count(); ++i) { - const auto& server = sim->get_server(i); - auto req_count = server.get_accumulator().request_count; - total_req += req_count; - if (!server_disp_filter(i)) continue; - out << std::setw(data_w) << req_count; - } - out << std::setw(data_w) << std::setprecision(data_prec) << - std::fixed << total_req << std::endl; - -#ifdef PROFILE - crimson::ProfileCombiner<std::chrono::nanoseconds> art_combiner; - crimson::ProfileCombiner<std::chrono::nanoseconds> rct_combiner; - for (uint i = 0; i < sim->get_server_count(); ++i) { - const auto& q = sim->get_server(i).get_priority_queue(); - const auto& art = q.add_request_timer; - art_combiner.combine(art); - const auto& rct = q.request_complete_timer; - rct_combiner.combine(rct); - } - out << "Server add_request_timer: count:" << art_combiner.get_count() << - ", mean:" << art_combiner.get_mean() << - ", std_dev:" << art_combiner.get_std_dev() << - ", low:" << art_combiner.get_low() << - ", high:" << art_combiner.get_high() << std::endl; - out << "Server request_complete_timer: count:" << rct_combiner.get_count() << - ", mean:" << rct_combiner.get_mean() << - ", std_dev:" << rct_combiner.get_std_dev() << - ", low:" << rct_combiner.get_low() << - ", high:" << rct_combiner.get_high() << std::endl; - out << "Server combined mean: " << - (art_combiner.get_mean() + rct_combiner.get_mean()) << - std::endl; -#endif -} diff --git a/src/dmclock/src/CMakeLists.txt b/src/dmclock/src/CMakeLists.txt deleted file mode 100644 index 36c03e887d0..00000000000 --- a/src/dmclock/src/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) -include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) -include_directories(../support/src) - -set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-write-strings -Wall -pthread") - -set(dmc_srcs dmclock_util.cc ../support/src/run_every.cc) - -add_library(dmclock STATIC ${dmc_srcs}) diff --git a/src/dmclock/src/dmclock_client.h b/src/dmclock/src/dmclock_client.h deleted file mode 100644 index 7cebd9e0108..00000000000 --- a/src/dmclock/src/dmclock_client.h +++ /dev/null @@ -1,281 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2017 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - -#include <map> -#include <deque> -#include <chrono> -#include <thread> -#include <mutex> -#include <condition_variable> - -#include "run_every.h" -#include "dmclock_util.h" -#include "dmclock_recs.h" - - -namespace crimson { - namespace dmclock { - - // OrigTracker is a best-effort implementation of the the original - // dmClock calculations of delta and rho. It adheres to an - // interface, implemented via a template type, that allows it to - // be replaced with an alternative. The interface consists of the - // static create, prepare_req, resp_update, and get_last_delta - // functions. - class OrigTracker { - Counter delta_prev_req; - Counter rho_prev_req; - uint32_t my_delta; - uint32_t my_rho; - - public: - - OrigTracker(Counter global_delta, - Counter global_rho) : - delta_prev_req(global_delta), - rho_prev_req(global_rho), - my_delta(0), - my_rho(0) - { /* empty */ } - - static inline OrigTracker create(Counter the_delta, Counter the_rho) { - return OrigTracker(the_delta, the_rho); - } - - inline ReqParams prepare_req(Counter& the_delta, Counter& the_rho) { - Counter delta_out = 1 + the_delta - delta_prev_req - my_delta; - Counter rho_out = 1 + the_rho - rho_prev_req - my_rho; - delta_prev_req = the_delta; - rho_prev_req = the_rho; - my_delta = 0; - my_rho = 0; - return ReqParams(uint32_t(delta_out), uint32_t(rho_out)); - } - - inline void resp_update(PhaseType phase, - Counter& the_delta, - Counter& the_rho) { - ++the_delta; - ++my_delta; - if (phase == PhaseType::reservation) { - ++the_rho; - ++my_rho; - } - } - - inline Counter get_last_delta() const { - return delta_prev_req; - } - }; // struct OrigTracker - - - // BorrowingTracker always returns a positive delta and rho. If - // not enough responses have come in to allow that, we will borrow - // a future response and repay it later. - class BorrowingTracker { - Counter delta_prev_req; - Counter rho_prev_req; - Counter delta_borrow; - Counter rho_borrow; - - public: - - BorrowingTracker(Counter global_delta, Counter global_rho) : - delta_prev_req(global_delta), - rho_prev_req(global_rho), - delta_borrow(0), - rho_borrow(0) - { /* empty */ } - - static inline BorrowingTracker create(Counter the_delta, - Counter the_rho) { - return BorrowingTracker(the_delta, the_rho); - } - - inline Counter calc_with_borrow(const Counter& global, - const Counter& previous, - Counter& borrow) { - Counter result = global - previous; - if (0 == result) { - // if no replies have come in, borrow one from the future - ++borrow; - return 1; - } else if (result > borrow) { - // if we can give back all of what we borrowed, do so - result -= borrow; - borrow = 0; - return result; - } else { - // can only return part of what was borrowed in order to - // return positive - borrow = borrow - result + 1; - return 1; - } - } - - inline ReqParams prepare_req(Counter& the_delta, Counter& the_rho) { - Counter delta_out = - calc_with_borrow(the_delta, delta_prev_req, delta_borrow); - Counter rho_out = - calc_with_borrow(the_rho, rho_prev_req, rho_borrow); - delta_prev_req = the_delta; - rho_prev_req = the_rho; - return ReqParams(uint32_t(delta_out), uint32_t(rho_out)); - } - - inline void resp_update(PhaseType phase, - Counter& the_delta, - Counter& the_rho) { - ++the_delta; - if (phase == PhaseType::reservation) { - ++the_rho; - } - } - - inline Counter get_last_delta() const { - return delta_prev_req; - } - }; // struct BorrowingTracker - - - // S is server identifier type - // T is the server info class that adheres to ServerTrackerIfc interface - template<typename S, typename T = BorrowingTracker> - class ServiceTracker { - // we don't want to include gtest.h just for FRIEND_TEST - friend class dmclock_client_server_erase_Test; - - using TimePoint = decltype(std::chrono::steady_clock::now()); - using Duration = std::chrono::milliseconds; - using MarkPoint = std::pair<TimePoint,Counter>; - - Counter delta_counter; // # reqs completed - Counter rho_counter; // # reqs completed via reservation - std::map<S,T> server_map; - mutable std::mutex data_mtx; // protects Counters and map - - using DataGuard = std::lock_guard<decltype(data_mtx)>; - - // clean config - - std::deque<MarkPoint> clean_mark_points; - Duration clean_age; // age at which server tracker cleaned - - // NB: All threads declared at end, so they're destructed firs! - - std::unique_ptr<RunEvery> cleaning_job; - - - public: - - // we have to start the counters at 1, as 0 is used in the - // cleaning process - template<typename Rep, typename Per> - ServiceTracker(std::chrono::duration<Rep,Per> _clean_every, - std::chrono::duration<Rep,Per> _clean_age) : - delta_counter(1), - rho_counter(1), - clean_age(std::chrono::duration_cast<Duration>(_clean_age)) - { - cleaning_job = - std::unique_ptr<RunEvery>( - new RunEvery(_clean_every, - std::bind(&ServiceTracker::do_clean, this))); - } - - - // the reason we're overloading the constructor rather than - // using default values for the arguments is so that callers - // have to either use all defaults or specify all timings; with - // default arguments they could specify some without others - ServiceTracker() : - ServiceTracker(std::chrono::minutes(5), std::chrono::minutes(10)) - { - // empty - } - - - /* - * Incorporates the RespParams received into the various counter. - */ - void track_resp(const S& server_id, const PhaseType& phase) { - DataGuard g(data_mtx); - - auto it = server_map.find(server_id); - if (server_map.end() == it) { - // this code can only run if a request did not precede the - // response or if the record was cleaned up b/w when - // the request was made and now - auto i = server_map.emplace(server_id, - T::create(delta_counter, rho_counter)); - it = i.first; - } - it->second.resp_update(phase, delta_counter, rho_counter); - } - - /* - * Returns the ReqParams for the given server. - */ - ReqParams get_req_params(const S& server) { - DataGuard g(data_mtx); - auto it = server_map.find(server); - if (server_map.end() == it) { - server_map.emplace(server, - T::create(delta_counter, rho_counter)); - return ReqParams(1, 1); - } else { - return it->second.prepare_req(delta_counter, rho_counter); - } - } - - private: - - /* - * This is being called regularly by RunEvery. Every time it's - * called it notes the time and delta counter (mark point) in a - * deque. It also looks at the deque to find the most recent - * mark point that is older than clean_age. It then walks the - * map and delete all server entries that were last used before - * that mark point. - */ - void do_clean() { - TimePoint now = std::chrono::steady_clock::now(); - DataGuard g(data_mtx); - clean_mark_points.emplace_back(MarkPoint(now, delta_counter)); - - Counter earliest = 0; - auto point = clean_mark_points.front(); - while (point.first <= now - clean_age) { - earliest = point.second; - clean_mark_points.pop_front(); - point = clean_mark_points.front(); - } - - if (earliest > 0) { - for (auto i = server_map.begin(); - i != server_map.end(); - /* empty */) { - auto i2 = i++; - if (i2->second.get_last_delta() <= earliest) { - server_map.erase(i2); - } - } - } - } // do_clean - }; // class ServiceTracker - } -} diff --git a/src/dmclock/src/dmclock_recs.h b/src/dmclock/src/dmclock_recs.h deleted file mode 100644 index 4b574f8dbe0..00000000000 --- a/src/dmclock/src/dmclock_recs.h +++ /dev/null @@ -1,68 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2017 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <ostream> -#include <assert.h> - - -namespace crimson { - namespace dmclock { - using Counter = uint64_t; - - enum class PhaseType : uint8_t { reservation, priority }; - - inline std::ostream& operator<<(std::ostream& out, const PhaseType& phase) { - out << (PhaseType::reservation == phase ? "reservation" : "priority"); - return out; - } - - struct ReqParams { - // count of all replies since last request; MUSTN'T BE 0 - uint32_t delta; - - // count of reservation replies since last request; MUSTN'T BE 0 - uint32_t rho; - - ReqParams(uint32_t _delta, uint32_t _rho) : - delta(_delta), - rho(_rho) - { - assert(0 != delta && 0 != rho && rho <= delta); - } - - ReqParams() : - ReqParams(1, 1) - { - // empty - } - - ReqParams(const ReqParams& other) : - delta(other.delta), - rho(other.rho) - { - // empty - } - - friend std::ostream& operator<<(std::ostream& out, const ReqParams& rp) { - out << "ReqParams{ delta:" << rp.delta << - ", rho:" << rp.rho << " }"; - return out; - } - }; // class ReqParams - } -} diff --git a/src/dmclock/src/dmclock_server.h b/src/dmclock/src/dmclock_server.h deleted file mode 100644 index 39a6b781d42..00000000000 --- a/src/dmclock/src/dmclock_server.h +++ /dev/null @@ -1,1667 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2017 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - -/* COMPILATION OPTIONS - * - * By default we include an optimization over the originally published - * dmclock algorithm using not the values of rho and delta that were - * sent in with a request but instead the most recent rho and delta - * values from the requests's client. To restore the algorithm's - * original behavior, define DO_NOT_DELAY_TAG_CALC (i.e., compiler - * argument -DDO_NOT_DELAY_TAG_CALC). - * - * The prop_heap does not seem to be necessary. The only thing it - * would help with is quickly finding the mininum proportion/prioity - * when an idle client became active. To have the code maintain the - * proportional heap, define USE_PROP_HEAP (i.e., compiler argument - * -DUSE_PROP_HEAP). - */ - -#include <assert.h> - -#include <cmath> -#include <memory> -#include <map> -#include <deque> -#include <queue> -#include <atomic> -#include <mutex> -#include <condition_variable> -#include <thread> -#include <iostream> -#include <sstream> -#include <limits> - -#include <boost/variant.hpp> - -#include "indirect_intrusive_heap.h" -#include "run_every.h" -#include "dmclock_util.h" -#include "dmclock_recs.h" - -#ifdef PROFILE -#include "profile.h" -#endif - - -namespace crimson { - - namespace dmclock { - - namespace c = crimson; - - constexpr double max_tag = std::numeric_limits<double>::is_iec559 ? - std::numeric_limits<double>::infinity() : - std::numeric_limits<double>::max(); - constexpr double min_tag = std::numeric_limits<double>::is_iec559 ? - -std::numeric_limits<double>::infinity() : - std::numeric_limits<double>::lowest(); - constexpr uint tag_modulo = 1000000; - - struct ClientInfo { - double reservation; // minimum - double weight; // proportional - double limit; // maximum - - // multiplicative inverses of above, which we use in calculations - // and don't want to recalculate repeatedly - double reservation_inv; - double weight_inv; - double limit_inv; - - // order parameters -- min, "normal", max - ClientInfo(double _reservation, double _weight, double _limit) : - reservation(_reservation), - weight(_weight), - limit(_limit), - reservation_inv(0.0 == reservation ? 0.0 : 1.0 / reservation), - weight_inv( 0.0 == weight ? 0.0 : 1.0 / weight), - limit_inv( 0.0 == limit ? 0.0 : 1.0 / limit) - { - // empty - } - - - friend std::ostream& operator<<(std::ostream& out, - const ClientInfo& client) { - out << - "{ ClientInfo:: r:" << client.reservation << - " w:" << std::fixed << client.weight << - " l:" << std::fixed << client.limit << - " 1/r:" << std::fixed << client.reservation_inv << - " 1/w:" << std::fixed << client.weight_inv << - " 1/l:" << std::fixed << client.limit_inv << - " }"; - return out; - } - }; // class ClientInfo - - - struct RequestTag { - double reservation; - double proportion; - double limit; - bool ready; // true when within limit - Time arrival; - - RequestTag(const RequestTag& prev_tag, - const ClientInfo& client, - const uint32_t delta, - const uint32_t rho, - const Time time, - const double cost = 0.0, - const double anticipation_timeout = 0.0) : - ready(false), - arrival(time) - { - Time max_time = time; - if (time - anticipation_timeout < prev_tag.arrival) - max_time -= anticipation_timeout; - - reservation = cost + tag_calc(max_time, - prev_tag.reservation, - client.reservation_inv, - rho, - true); - proportion = tag_calc(max_time, - prev_tag.proportion, - client.weight_inv, - delta, - true); - limit = tag_calc(max_time, - prev_tag.limit, - client.limit_inv, - delta, - false); - - assert(reservation < max_tag || proportion < max_tag); - } - - RequestTag(const RequestTag& prev_tag, - const ClientInfo& client, - const ReqParams req_params, - const Time time, - const double cost = 0.0, - const double anticipation_timeout = 0.0) : - RequestTag(prev_tag, client, req_params.delta, req_params.rho, time, - cost, anticipation_timeout) - { /* empty */ } - - RequestTag(double _res, double _prop, double _lim, const Time _arrival) : - reservation(_res), - proportion(_prop), - limit(_lim), - ready(false), - arrival(_arrival) - { - assert(reservation < max_tag || proportion < max_tag); - } - - RequestTag(const RequestTag& other) : - reservation(other.reservation), - proportion(other.proportion), - limit(other.limit), - ready(other.ready), - arrival(other.arrival) - { - // empty - } - - static std::string format_tag_change(double before, double after) { - if (before == after) { - return std::string("same"); - } else { - std::stringstream ss; - ss << format_tag(before) << "=>" << format_tag(after); - return ss.str(); - } - } - - static std::string format_tag(double value) { - if (max_tag == value) { - return std::string("max"); - } else if (min_tag == value) { - return std::string("min"); - } else { - return format_time(value, tag_modulo); - } - } - - private: - - static double tag_calc(const Time time, - double prev, - double increment, - uint32_t dist_req_val, - bool extreme_is_high) { - if (0.0 == increment) { - return extreme_is_high ? max_tag : min_tag; - } else { - if (0 != dist_req_val) { - increment *= dist_req_val; - } - return std::max(time, prev + increment); - } - } - - friend std::ostream& operator<<(std::ostream& out, - const RequestTag& tag) { - out << - "{ RequestTag:: ready:" << (tag.ready ? "true" : "false") << - " r:" << format_tag(tag.reservation) << - " p:" << format_tag(tag.proportion) << - " l:" << format_tag(tag.limit) << -#if 0 // try to resolve this to make sure Time is operator<<'able. -#ifndef DO_NOT_DELAY_TAG_CALC - " arrival:" << tag.arrival << -#endif -#endif - " }"; - return out; - } - }; // class RequestTag - - - // C is client identifier type, R is request type, - // U1 determines whether to use client information function dynamically, - // B is heap branching factor - template<typename C, typename R, bool U1, uint B> - class PriorityQueueBase { - // we don't want to include gtest.h just for FRIEND_TEST - friend class dmclock_server_client_idle_erase_Test; - - public: - - using RequestRef = std::unique_ptr<R>; - - protected: - - using TimePoint = decltype(std::chrono::steady_clock::now()); - using Duration = std::chrono::milliseconds; - using MarkPoint = std::pair<TimePoint,Counter>; - - enum class ReadyOption {ignore, lowers, raises}; - - // forward decl for friend decls - template<double RequestTag::*, ReadyOption, bool> - struct ClientCompare; - - class ClientReq { - friend PriorityQueueBase; - - RequestTag tag; - C client_id; - RequestRef request; - - public: - - ClientReq(const RequestTag& _tag, - const C& _client_id, - RequestRef&& _request) : - tag(_tag), - client_id(_client_id), - request(std::move(_request)) - { - // empty - } - - friend std::ostream& operator<<(std::ostream& out, const ClientReq& c) { - out << "{ ClientReq:: tag:" << c.tag << " client:" << - c.client_id << " }"; - return out; - } - }; // class ClientReq - - public: - - // NOTE: ClientRec is in the "public" section for compatibility - // with g++ 4.8.4, which complains if it's not. By g++ 6.3.1 - // ClientRec could be "protected" with no issue. [See comments - // associated with function submit_top_request.] - class ClientRec { - friend PriorityQueueBase<C,R,U1,B>; - - C client; - RequestTag prev_tag; - std::deque<ClientReq> requests; - - // amount added from the proportion tag as a result of - // an idle client becoming unidle - double prop_delta = 0.0; - - c::IndIntruHeapData reserv_heap_data {}; - c::IndIntruHeapData lim_heap_data {}; - c::IndIntruHeapData ready_heap_data {}; -#if USE_PROP_HEAP - c::IndIntruHeapData prop_heap_data {}; -#endif - - public: - - const ClientInfo* info; - bool idle; - Counter last_tick; - uint32_t cur_rho; - uint32_t cur_delta; - - ClientRec(C _client, - const ClientInfo* _info, - Counter current_tick) : - client(_client), - prev_tag(0.0, 0.0, 0.0, TimeZero), - info(_info), - idle(true), - last_tick(current_tick), - cur_rho(1), - cur_delta(1) - { - // empty - } - - inline const RequestTag& get_req_tag() const { - return prev_tag; - } - - static inline void assign_unpinned_tag(double& lhs, const double rhs) { - if (rhs != max_tag && rhs != min_tag) { - lhs = rhs; - } - } - - inline void update_req_tag(const RequestTag& _prev, - const Counter& _tick) { - assign_unpinned_tag(prev_tag.reservation, _prev.reservation); - assign_unpinned_tag(prev_tag.limit, _prev.limit); - assign_unpinned_tag(prev_tag.proportion, _prev.proportion); - prev_tag.arrival = _prev.arrival; - last_tick = _tick; - } - - inline void add_request(const RequestTag& tag, - const C& client_id, - RequestRef&& request) { - requests.emplace_back(ClientReq(tag, client_id, std::move(request))); - } - - inline const ClientReq& next_request() const { - return requests.front(); - } - - inline ClientReq& next_request() { - return requests.front(); - } - - inline void pop_request() { - requests.pop_front(); - } - - inline bool has_request() const { - return !requests.empty(); - } - - inline size_t request_count() const { - return requests.size(); - } - - // NB: because a deque is the underlying structure, this - // operation might be expensive - bool remove_by_req_filter_fw(std::function<bool(RequestRef&&)> filter_accum) { - bool any_removed = false; - for (auto i = requests.begin(); - i != requests.end(); - /* no inc */) { - if (filter_accum(std::move(i->request))) { - any_removed = true; - i = requests.erase(i); - } else { - ++i; - } - } - return any_removed; - } - - // NB: because a deque is the underlying structure, this - // operation might be expensive - bool remove_by_req_filter_bw(std::function<bool(RequestRef&&)> filter_accum) { - bool any_removed = false; - for (auto i = requests.rbegin(); - i != requests.rend(); - /* no inc */) { - if (filter_accum(std::move(i->request))) { - any_removed = true; - i = decltype(i){ requests.erase(std::next(i).base()) }; - } else { - ++i; - } - } - return any_removed; - } - - inline bool - remove_by_req_filter(std::function<bool(RequestRef&&)> filter_accum, - bool visit_backwards) { - if (visit_backwards) { - return remove_by_req_filter_bw(filter_accum); - } else { - return remove_by_req_filter_fw(filter_accum); - } - } - - friend std::ostream& - operator<<(std::ostream& out, - const typename PriorityQueueBase<C,R,U1,B>::ClientRec& e) { - out << "{ ClientRec::" << - " client:" << e.client << - " prev_tag:" << e.prev_tag << - " req_count:" << e.requests.size() << - " top_req:"; - if (e.has_request()) { - out << e.next_request(); - } else { - out << "none"; - } - out << " }"; - - return out; - } - }; // class ClientRec - - using ClientRecRef = std::shared_ptr<ClientRec>; - - // when we try to get the next request, we'll be in one of three - // situations -- we'll have one to return, have one that can - // fire in the future, or not have any - enum class NextReqType { returning, future, none }; - - // specifies which queue next request will get popped from - enum class HeapId { reservation, ready }; - - // this is returned from next_req to tell the caller the situation - struct NextReq { - NextReqType type; - union { - HeapId heap_id; - Time when_ready; - }; - - inline explicit NextReq() : - type(NextReqType::none) - { } - - inline NextReq(HeapId _heap_id) : - type(NextReqType::returning), - heap_id(_heap_id) - { } - - inline NextReq(Time _when_ready) : - type(NextReqType::future), - when_ready(_when_ready) - { } - - // calls to this are clearer than calls to the default - // constructor - static inline NextReq none() { - return NextReq(); - } - }; - - - // a function that can be called to look up client information - using ClientInfoFunc = std::function<const ClientInfo*(const C&)>; - - - bool empty() const { - DataGuard g(data_mtx); - return (resv_heap.empty() || ! resv_heap.top().has_request()); - } - - - size_t client_count() const { - DataGuard g(data_mtx); - return resv_heap.size(); - } - - - size_t request_count() const { - DataGuard g(data_mtx); - size_t total = 0; - for (auto i = resv_heap.cbegin(); i != resv_heap.cend(); ++i) { - total += i->request_count(); - } - return total; - } - - - bool remove_by_req_filter(std::function<bool(RequestRef&&)> filter_accum, - bool visit_backwards = false) { - bool any_removed = false; - DataGuard g(data_mtx); - for (auto i : client_map) { - bool modified = - i.second->remove_by_req_filter(filter_accum, visit_backwards); - if (modified) { - resv_heap.adjust(*i.second); - limit_heap.adjust(*i.second); - ready_heap.adjust(*i.second); -#if USE_PROP_HEAP - prop_heap.adjust(*i.second); -#endif - any_removed = true; - } - } - return any_removed; - } - - - // use as a default value when no accumulator is provide - static void request_sink(RequestRef&& req) { - // do nothing - } - - - void remove_by_client(const C& client, - bool reverse = false, - std::function<void (RequestRef&&)> accum = request_sink) { - DataGuard g(data_mtx); - - auto i = client_map.find(client); - - if (i == client_map.end()) return; - - if (reverse) { - for (auto j = i->second->requests.rbegin(); - j != i->second->requests.rend(); - ++j) { - accum(std::move(j->request)); - } - } else { - for (auto j = i->second->requests.begin(); - j != i->second->requests.end(); - ++j) { - accum(std::move(j->request)); - } - } - - i->second->requests.clear(); - - resv_heap.adjust(*i->second); - limit_heap.adjust(*i->second); - ready_heap.adjust(*i->second); -#if USE_PROP_HEAP - prop_heap.adjust(*i->second); -#endif - } - - - uint get_heap_branching_factor() const { - return B; - } - - - void update_client_info(const C& client_id) { - DataGuard g(data_mtx); - auto client_it = client_map.find(client_id); - if (client_map.end() != client_it) { - ClientRec& client = (*client_it->second); - client.info = client_info_f(client_id); - } - } - - - void update_client_infos() { - DataGuard g(data_mtx); - for (auto i : client_map) { - i.second->info = client_info_f(i.second->client); - } - } - - - friend std::ostream& operator<<(std::ostream& out, - const PriorityQueueBase& q) { - std::lock_guard<decltype(q.data_mtx)> guard(q.data_mtx); - - out << "{ PriorityQueue::"; - for (const auto& c : q.client_map) { - out << " { client:" << c.first << ", record:" << *c.second << - " }"; - } - if (!q.resv_heap.empty()) { - const auto& resv = q.resv_heap.top(); - out << " { reservation_top:" << resv << " }"; - const auto& ready = q.ready_heap.top(); - out << " { ready_top:" << ready << " }"; - const auto& limit = q.limit_heap.top(); - out << " { limit_top:" << limit << " }"; - } else { - out << " HEAPS-EMPTY"; - } - out << " }"; - - return out; - } - - // for debugging - void display_queues(std::ostream& out, - bool show_res = true, - bool show_lim = true, - bool show_ready = true, - bool show_prop = true) const { - auto filter = [](const ClientRec& e)->bool { return true; }; - DataGuard g(data_mtx); - if (show_res) { - resv_heap.display_sorted(out << "RESER:", filter); - } - if (show_lim) { - limit_heap.display_sorted(out << "LIMIT:", filter); - } - if (show_ready) { - ready_heap.display_sorted(out << "READY:", filter); - } -#if USE_PROP_HEAP - if (show_prop) { - prop_heap.display_sorted(out << "PROPO:", filter); - } -#endif - } // display_queues - - - protected: - - // The ClientCompare functor is essentially doing a precedes? - // operator, returning true if and only if the first parameter - // must precede the second parameter. If the second must precede - // the first, or if they are equivalent, false should be - // returned. The reason for this behavior is that it will be - // called to test if two items are out of order and if true is - // returned it will reverse the items. Therefore false is the - // default return when it doesn't matter to prevent unnecessary - // re-ordering. - // - // The template is supporting variations in sorting based on the - // heap in question and allowing these variations to be handled - // at compile-time. - // - // tag_field determines which tag is being used for comparison - // - // ready_opt determines how the ready flag influences the sort - // - // use_prop_delta determines whether the proportional delta is - // added in for comparison - template<double RequestTag::*tag_field, - ReadyOption ready_opt, - bool use_prop_delta> - struct ClientCompare { - bool operator()(const ClientRec& n1, const ClientRec& n2) const { - if (n1.has_request()) { - if (n2.has_request()) { - const auto& t1 = n1.next_request().tag; - const auto& t2 = n2.next_request().tag; - if (ReadyOption::ignore == ready_opt || t1.ready == t2.ready) { - // if we don't care about ready or the ready values are the same - if (use_prop_delta) { - return (t1.*tag_field + n1.prop_delta) < - (t2.*tag_field + n2.prop_delta); - } else { - return t1.*tag_field < t2.*tag_field; - } - } else if (ReadyOption::raises == ready_opt) { - // use_ready == true && the ready fields are different - return t1.ready; - } else { - return t2.ready; - } - } else { - // n1 has request but n2 does not - return true; - } - } else if (n2.has_request()) { - // n2 has request but n1 does not - return false; - } else { - // both have none; keep stable w false - return false; - } - } - }; - - ClientInfoFunc client_info_f; - static constexpr bool is_dynamic_cli_info_f = U1; - - mutable std::mutex data_mtx; - using DataGuard = std::lock_guard<decltype(data_mtx)>; - - // stable mapping between client ids and client queues - std::map<C,ClientRecRef> client_map; - - c::IndIntruHeap<ClientRecRef, - ClientRec, - &ClientRec::reserv_heap_data, - ClientCompare<&RequestTag::reservation, - ReadyOption::ignore, - false>, - B> resv_heap; -#if USE_PROP_HEAP - c::IndIntruHeap<ClientRecRef, - ClientRec, - &ClientRec::prop_heap_data, - ClientCompare<&RequestTag::proportion, - ReadyOption::ignore, - true>, - B> prop_heap; -#endif - c::IndIntruHeap<ClientRecRef, - ClientRec, - &ClientRec::lim_heap_data, - ClientCompare<&RequestTag::limit, - ReadyOption::lowers, - false>, - B> limit_heap; - c::IndIntruHeap<ClientRecRef, - ClientRec, - &ClientRec::ready_heap_data, - ClientCompare<&RequestTag::proportion, - ReadyOption::raises, - true>, - B> ready_heap; - - // if all reservations are met and all other requestes are under - // limit, this will allow the request next in terms of - // proportion to still get issued - bool allow_limit_break; - double anticipation_timeout; - - std::atomic_bool finishing; - - // every request creates a tick - Counter tick = 0; - - // performance data collection - size_t reserv_sched_count = 0; - size_t prop_sched_count = 0; - size_t limit_break_sched_count = 0; - - Duration idle_age; - Duration erase_age; - Duration check_time; - std::deque<MarkPoint> clean_mark_points; - - // NB: All threads declared at end, so they're destructed first! - - std::unique_ptr<RunEvery> cleaning_job; - - - // COMMON constructor that others feed into; we can accept three - // different variations of durations - template<typename Rep, typename Per> - PriorityQueueBase(ClientInfoFunc _client_info_f, - std::chrono::duration<Rep,Per> _idle_age, - std::chrono::duration<Rep,Per> _erase_age, - std::chrono::duration<Rep,Per> _check_time, - bool _allow_limit_break, - double _anticipation_timeout) : - client_info_f(_client_info_f), - allow_limit_break(_allow_limit_break), - anticipation_timeout(_anticipation_timeout), - finishing(false), - idle_age(std::chrono::duration_cast<Duration>(_idle_age)), - erase_age(std::chrono::duration_cast<Duration>(_erase_age)), - check_time(std::chrono::duration_cast<Duration>(_check_time)) - { - assert(_erase_age >= _idle_age); - assert(_check_time < _idle_age); - cleaning_job = - std::unique_ptr<RunEvery>( - new RunEvery(check_time, - std::bind(&PriorityQueueBase::do_clean, this))); - } - - - ~PriorityQueueBase() { - finishing = true; - } - - - inline const ClientInfo* get_cli_info(ClientRec& client) const { - if (is_dynamic_cli_info_f) { - client.info = client_info_f(client.client); - } - return client.info; - } - - - // data_mtx must be held by caller - void do_add_request(RequestRef&& request, - const C& client_id, - const ReqParams& req_params, - const Time time, - const double cost = 0.0) { - ++tick; - - // this pointer will help us create a reference to a shared - // pointer, no matter which of two codepaths we take - ClientRec* temp_client; - - auto client_it = client_map.find(client_id); - if (client_map.end() != client_it) { - temp_client = &(*client_it->second); // address of obj of shared_ptr - } else { - const ClientInfo* info = client_info_f(client_id); - ClientRecRef client_rec = - std::make_shared<ClientRec>(client_id, info, tick); - resv_heap.push(client_rec); -#if USE_PROP_HEAP - prop_heap.push(client_rec); -#endif - limit_heap.push(client_rec); - ready_heap.push(client_rec); - client_map[client_id] = client_rec; - temp_client = &(*client_rec); // address of obj of shared_ptr - } - - // for convenience, we'll create a reference to the shared pointer - ClientRec& client = *temp_client; - - if (client.idle) { - // We need to do an adjustment so that idle clients compete - // fairly on proportional tags since those tags may have - // drifted from real-time. Either use the lowest existing - // proportion tag -- O(1) -- or the client with the lowest - // previous proportion tag -- O(n) where n = # clients. - // - // So we don't have to maintain a propotional queue that - // keeps the minimum on proportional tag alone (we're - // instead using a ready queue), we'll have to check each - // client. - // - // The alternative would be to maintain a proportional queue - // (define USE_PROP_TAG) and do an O(1) operation here. - - // Was unable to confirm whether equality testing on - // std::numeric_limits<double>::max() is guaranteed, so - // we'll use a compile-time calculated trigger that is one - // third the max, which should be much larger than any - // expected organic value. - constexpr double lowest_prop_tag_trigger = - std::numeric_limits<double>::max() / 3.0; - - double lowest_prop_tag = std::numeric_limits<double>::max(); - for (auto const &c : client_map) { - // don't use ourselves (or anything else that might be - // listed as idle) since we're now in the map - if (!c.second->idle) { - double p; - // use either lowest proportion tag or previous proportion tag - if (c.second->has_request()) { - p = c.second->next_request().tag.proportion + - c.second->prop_delta; - } else { - p = c.second->get_req_tag().proportion + c.second->prop_delta; - } - - if (p < lowest_prop_tag) { - lowest_prop_tag = p; - } - } - } - - // if this conditional does not fire, it - if (lowest_prop_tag < lowest_prop_tag_trigger) { - client.prop_delta = lowest_prop_tag - time; - } - client.idle = false; - } // if this client was idle - -#ifndef DO_NOT_DELAY_TAG_CALC - RequestTag tag(0, 0, 0, time); - - if (!client.has_request()) { - const ClientInfo* client_info = get_cli_info(client); - assert(client_info); - tag = RequestTag(client.get_req_tag(), - *client_info, - req_params, - time, - cost, - anticipation_timeout); - - // copy tag to previous tag for client - client.update_req_tag(tag, tick); - } -#else - const ClientInfo* client_info = get_cli_info(client); - assert(client_info); - RequestTag tag(client.get_req_tag(), - *client_info, - req_params, - time, - cost, - anticipation_timeout); - - // copy tag to previous tag for client - client.update_req_tag(tag, tick); -#endif - - client.add_request(tag, client.client, std::move(request)); - if (1 == client.requests.size()) { - // NB: can the following 4 calls to adjust be changed - // promote? Can adding a request ever demote a client in the - // heaps? - resv_heap.adjust(client); - limit_heap.adjust(client); - ready_heap.adjust(client); -#if USE_PROP_HEAP - prop_heap.adjust(client); -#endif - } - - client.cur_rho = req_params.rho; - client.cur_delta = req_params.delta; - - resv_heap.adjust(client); - limit_heap.adjust(client); - ready_heap.adjust(client); -#if USE_PROP_HEAP - prop_heap.adjust(client); -#endif - } // add_request - - - // data_mtx should be held when called; top of heap should have - // a ready request - template<typename C1, IndIntruHeapData ClientRec::*C2, typename C3> - void pop_process_request(IndIntruHeap<C1, ClientRec, C2, C3, B>& heap, - std::function<void(const C& client, - RequestRef& request)> process) { - // gain access to data - ClientRec& top = heap.top(); - - RequestRef request = std::move(top.next_request().request); -#ifndef DO_NOT_DELAY_TAG_CALC - RequestTag tag = top.next_request().tag; -#endif - - // pop request and adjust heaps - top.pop_request(); - -#ifndef DO_NOT_DELAY_TAG_CALC - if (top.has_request()) { - ClientReq& next_first = top.next_request(); - const ClientInfo* client_info = get_cli_info(top); - assert(client_info); - next_first.tag = RequestTag(tag, *client_info, - top.cur_delta, top.cur_rho, - next_first.tag.arrival, - 0.0, anticipation_timeout); - - // copy tag to previous tag for client - top.update_req_tag(next_first.tag, tick); - } -#endif - - resv_heap.demote(top); - limit_heap.adjust(top); -#if USE_PROP_HEAP - prop_heap.demote(top); -#endif - ready_heap.demote(top); - - // process - process(top.client, request); - } // pop_process_request - - - // data_mtx should be held when called - void reduce_reservation_tags(ClientRec& client) { - for (auto& r : client.requests) { - r.tag.reservation -= client.info->reservation_inv; - -#ifndef DO_NOT_DELAY_TAG_CALC - // reduce only for front tag. because next tags' value are invalid - break; -#endif - } - // don't forget to update previous tag - client.prev_tag.reservation -= client.info->reservation_inv; - resv_heap.promote(client); - } - - - // data_mtx should be held when called - void reduce_reservation_tags(const C& client_id) { - auto client_it = client_map.find(client_id); - - // means the client was cleaned from map; should never happen - // as long as cleaning times are long enough - assert(client_map.end() != client_it); - reduce_reservation_tags(*client_it->second); - } - - - // data_mtx should be held when called - NextReq do_next_request(Time now) { - // if reservation queue is empty, all are empty (i.e., no - // active clients) - if(resv_heap.empty()) { - return NextReq::none(); - } - - // try constraint (reservation) based scheduling - - auto& reserv = resv_heap.top(); - if (reserv.has_request() && - reserv.next_request().tag.reservation <= now) { - return NextReq(HeapId::reservation); - } - - // no existing reservations before now, so try weight-based - // scheduling - - // all items that are within limit are eligible based on - // priority - auto limits = &limit_heap.top(); - while (limits->has_request() && - !limits->next_request().tag.ready && - limits->next_request().tag.limit <= now) { - limits->next_request().tag.ready = true; - ready_heap.promote(*limits); - limit_heap.demote(*limits); - - limits = &limit_heap.top(); - } - - auto& readys = ready_heap.top(); - if (readys.has_request() && - readys.next_request().tag.ready && - readys.next_request().tag.proportion < max_tag) { - return NextReq(HeapId::ready); - } - - // if nothing is schedulable by reservation or - // proportion/weight, and if we allow limit break, try to - // schedule something with the lowest proportion tag or - // alternatively lowest reservation tag. - if (allow_limit_break) { - if (readys.has_request() && - readys.next_request().tag.proportion < max_tag) { - return NextReq(HeapId::ready); - } else if (reserv.has_request() && - reserv.next_request().tag.reservation < max_tag) { - return NextReq(HeapId::reservation); - } - } - - // nothing scheduled; make sure we re-run when next - // reservation item or next limited item comes up - - Time next_call = TimeMax; - if (resv_heap.top().has_request()) { - next_call = - min_not_0_time(next_call, - resv_heap.top().next_request().tag.reservation); - } - if (limit_heap.top().has_request()) { - const auto& next = limit_heap.top().next_request(); - assert(!next.tag.ready || max_tag == next.tag.proportion); - next_call = min_not_0_time(next_call, next.tag.limit); - } - if (next_call < TimeMax) { - return NextReq(next_call); - } else { - return NextReq::none(); - } - } // do_next_request - - - // if possible is not zero and less than current then return it; - // otherwise return current; the idea is we're trying to find - // the minimal time but ignoring zero - static inline const Time& min_not_0_time(const Time& current, - const Time& possible) { - return TimeZero == possible ? current : std::min(current, possible); - } - - - /* - * This is being called regularly by RunEvery. Every time it's - * called it notes the time and delta counter (mark point) in a - * deque. It also looks at the deque to find the most recent - * mark point that is older than clean_age. It then walks the - * map and delete all server entries that were last used before - * that mark point. - */ - void do_clean() { - TimePoint now = std::chrono::steady_clock::now(); - DataGuard g(data_mtx); - clean_mark_points.emplace_back(MarkPoint(now, tick)); - - // first erase the super-old client records - - Counter erase_point = 0; - auto point = clean_mark_points.front(); - while (point.first <= now - erase_age) { - erase_point = point.second; - clean_mark_points.pop_front(); - point = clean_mark_points.front(); - } - - Counter idle_point = 0; - for (auto i : clean_mark_points) { - if (i.first <= now - idle_age) { - idle_point = i.second; - } else { - break; - } - } - - if (erase_point > 0 || idle_point > 0) { - for (auto i = client_map.begin(); i != client_map.end(); /* empty */) { - auto i2 = i++; - if (erase_point && i2->second->last_tick <= erase_point) { - delete_from_heaps(i2->second); - client_map.erase(i2); - } else if (idle_point && i2->second->last_tick <= idle_point) { - i2->second->idle = true; - } - } // for - } // if - } // do_clean - - - // data_mtx must be held by caller - template<IndIntruHeapData ClientRec::*C1,typename C2> - void delete_from_heap(ClientRecRef& client, - c::IndIntruHeap<ClientRecRef,ClientRec,C1,C2,B>& heap) { - auto i = heap.rfind(client); - heap.remove(i); - } - - - // data_mtx must be held by caller - void delete_from_heaps(ClientRecRef& client) { - delete_from_heap(client, resv_heap); -#if USE_PROP_HEAP - delete_from_heap(client, prop_heap); -#endif - delete_from_heap(client, limit_heap); - delete_from_heap(client, ready_heap); - } - }; // class PriorityQueueBase - - - template<typename C, typename R, bool U1=false, uint B=2> - class PullPriorityQueue : public PriorityQueueBase<C,R,U1,B> { - using super = PriorityQueueBase<C,R,U1,B>; - - public: - - // When a request is pulled, this is the return type. - struct PullReq { - struct Retn { - C client; - typename super::RequestRef request; - PhaseType phase; - }; - - typename super::NextReqType type; - boost::variant<Retn,Time> data; - - bool is_none() const { return type == super::NextReqType::none; } - - bool is_retn() const { return type == super::NextReqType::returning; } - Retn& get_retn() { - return boost::get<Retn>(data); - } - - bool is_future() const { return type == super::NextReqType::future; } - Time getTime() const { return boost::get<Time>(data); } - }; - - -#ifdef PROFILE - ProfileTimer<std::chrono::nanoseconds> pull_request_timer; - ProfileTimer<std::chrono::nanoseconds> add_request_timer; -#endif - - template<typename Rep, typename Per> - PullPriorityQueue(typename super::ClientInfoFunc _client_info_f, - std::chrono::duration<Rep,Per> _idle_age, - std::chrono::duration<Rep,Per> _erase_age, - std::chrono::duration<Rep,Per> _check_time, - bool _allow_limit_break = false, - double _anticipation_timeout = 0.0) : - super(_client_info_f, - _idle_age, _erase_age, _check_time, - _allow_limit_break, _anticipation_timeout) - { - // empty - } - - - // pull convenience constructor - PullPriorityQueue(typename super::ClientInfoFunc _client_info_f, - bool _allow_limit_break = false, - double _anticipation_timeout = 0.0) : - PullPriorityQueue(_client_info_f, - std::chrono::minutes(10), - std::chrono::minutes(15), - std::chrono::minutes(6), - _allow_limit_break, - _anticipation_timeout) - { - // empty - } - - - inline void add_request(R&& request, - const C& client_id, - const ReqParams& req_params, - double addl_cost = 0.0) { - add_request(typename super::RequestRef(new R(std::move(request))), - client_id, - req_params, - get_time(), - addl_cost); - } - - - inline void add_request(R&& request, - const C& client_id, - double addl_cost = 0.0) { - static const ReqParams null_req_params; - add_request(typename super::RequestRef(new R(std::move(request))), - client_id, - null_req_params, - get_time(), - addl_cost); - } - - - - inline void add_request_time(R&& request, - const C& client_id, - const ReqParams& req_params, - const Time time, - double addl_cost = 0.0) { - add_request(typename super::RequestRef(new R(std::move(request))), - client_id, - req_params, - time, - addl_cost); - } - - - inline void add_request(typename super::RequestRef&& request, - const C& client_id, - const ReqParams& req_params, - double addl_cost = 0.0) { - add_request(request, req_params, client_id, get_time(), addl_cost); - } - - - inline void add_request(typename super::RequestRef&& request, - const C& client_id, - double addl_cost = 0.0) { - static const ReqParams null_req_params; - add_request(request, null_req_params, client_id, get_time(), addl_cost); - } - - - // this does the work; the versions above provide alternate interfaces - void add_request(typename super::RequestRef&& request, - const C& client_id, - const ReqParams& req_params, - const Time time, - double addl_cost = 0.0) { - typename super::DataGuard g(this->data_mtx); -#ifdef PROFILE - add_request_timer.start(); -#endif - super::do_add_request(std::move(request), - client_id, - req_params, - time, - addl_cost); - // no call to schedule_request for pull version -#ifdef PROFILE - add_request_timer.stop(); -#endif - } - - - inline PullReq pull_request() { - return pull_request(get_time()); - } - - - PullReq pull_request(Time now) { - PullReq result; - typename super::DataGuard g(this->data_mtx); -#ifdef PROFILE - pull_request_timer.start(); -#endif - - typename super::NextReq next = super::do_next_request(now); - result.type = next.type; - switch(next.type) { - case super::NextReqType::none: - return result; - case super::NextReqType::future: - result.data = next.when_ready; - return result; - case super::NextReqType::returning: - // to avoid nesting, break out and let code below handle this case - break; - default: - assert(false); - } - - // we'll only get here if we're returning an entry - - auto process_f = - [&] (PullReq& pull_result, PhaseType phase) -> - std::function<void(const C&, - typename super::RequestRef&)> { - return [&pull_result, phase](const C& client, - typename super::RequestRef& request) { - pull_result.data = - typename PullReq::Retn{client, std::move(request), phase}; - }; - }; - - switch(next.heap_id) { - case super::HeapId::reservation: - super::pop_process_request(this->resv_heap, - process_f(result, PhaseType::reservation)); - ++this->reserv_sched_count; - break; - case super::HeapId::ready: - super::pop_process_request(this->ready_heap, - process_f(result, PhaseType::priority)); - { // need to use retn temporarily - auto& retn = boost::get<typename PullReq::Retn>(result.data); - super::reduce_reservation_tags(retn.client); - } - ++this->prop_sched_count; - break; - default: - assert(false); - } - -#ifdef PROFILE - pull_request_timer.stop(); -#endif - return result; - } // pull_request - - - protected: - - - // data_mtx should be held when called; unfortunately this - // function has to be repeated in both push & pull - // specializations - typename super::NextReq next_request() { - return next_request(get_time()); - } - }; // class PullPriorityQueue - - - // PUSH version - template<typename C, typename R, bool U1=false, uint B=2> - class PushPriorityQueue : public PriorityQueueBase<C,R,U1,B> { - - protected: - - using super = PriorityQueueBase<C,R,U1,B>; - - public: - - // a function to see whether the server can handle another request - using CanHandleRequestFunc = std::function<bool(void)>; - - // a function to submit a request to the server; the second - // parameter is a callback when it's completed - using HandleRequestFunc = - std::function<void(const C&,typename super::RequestRef,PhaseType)>; - - protected: - - CanHandleRequestFunc can_handle_f; - HandleRequestFunc handle_f; - // for handling timed scheduling - std::mutex sched_ahead_mtx; - std::condition_variable sched_ahead_cv; - Time sched_ahead_when = TimeZero; - -#ifdef PROFILE - public: - ProfileTimer<std::chrono::nanoseconds> add_request_timer; - ProfileTimer<std::chrono::nanoseconds> request_complete_timer; - protected: -#endif - - // NB: threads declared last, so constructed last and destructed first - - std::thread sched_ahead_thd; - - public: - - // push full constructor - template<typename Rep, typename Per> - PushPriorityQueue(typename super::ClientInfoFunc _client_info_f, - CanHandleRequestFunc _can_handle_f, - HandleRequestFunc _handle_f, - std::chrono::duration<Rep,Per> _idle_age, - std::chrono::duration<Rep,Per> _erase_age, - std::chrono::duration<Rep,Per> _check_time, - bool _allow_limit_break = false, - double anticipation_timeout = 0.0) : - super(_client_info_f, - _idle_age, _erase_age, _check_time, - _allow_limit_break, anticipation_timeout) - { - can_handle_f = _can_handle_f; - handle_f = _handle_f; - sched_ahead_thd = std::thread(&PushPriorityQueue::run_sched_ahead, this); - } - - - // push convenience constructor - PushPriorityQueue(typename super::ClientInfoFunc _client_info_f, - CanHandleRequestFunc _can_handle_f, - HandleRequestFunc _handle_f, - bool _allow_limit_break = false, - double _anticipation_timeout = 0.0) : - PushPriorityQueue(_client_info_f, - _can_handle_f, - _handle_f, - std::chrono::minutes(10), - std::chrono::minutes(15), - std::chrono::minutes(6), - _allow_limit_break, - _anticipation_timeout) - { - // empty - } - - - ~PushPriorityQueue() { - this->finishing = true; - sched_ahead_cv.notify_one(); - sched_ahead_thd.join(); - } - - public: - - inline void add_request(R&& request, - const C& client_id, - const ReqParams& req_params, - double addl_cost = 0.0) { - add_request(typename super::RequestRef(new R(std::move(request))), - client_id, - req_params, - get_time(), - addl_cost); - } - - - inline void add_request(typename super::RequestRef&& request, - const C& client_id, - const ReqParams& req_params, - double addl_cost = 0.0) { - add_request(request, req_params, client_id, get_time(), addl_cost); - } - - - inline void add_request_time(const R& request, - const C& client_id, - const ReqParams& req_params, - const Time time, - double addl_cost = 0.0) { - add_request(typename super::RequestRef(new R(request)), - client_id, - req_params, - time, - addl_cost); - } - - - void add_request(typename super::RequestRef&& request, - const C& client_id, - const ReqParams& req_params, - const Time time, - double addl_cost = 0.0) { - typename super::DataGuard g(this->data_mtx); -#ifdef PROFILE - add_request_timer.start(); -#endif - super::do_add_request(std::move(request), - client_id, - req_params, - time, - addl_cost); - schedule_request(); -#ifdef PROFILE - add_request_timer.stop(); -#endif - } - - - void request_completed() { - typename super::DataGuard g(this->data_mtx); -#ifdef PROFILE - request_complete_timer.start(); -#endif - schedule_request(); -#ifdef PROFILE - request_complete_timer.stop(); -#endif - } - - protected: - - // data_mtx should be held when called; furthermore, the heap - // should not be empty and the top element of the heap should - // not be already handled - // - // NOTE: the use of "super::ClientRec" in either the template - // construct or as a parameter to submit_top_request generated - // a compiler error in g++ 4.8.4, when ClientRec was - // "protected" rather than "public". By g++ 6.3.1 this was not - // an issue. But for backwards compatibility - // PriorityQueueBase::ClientRec is public. - template<typename C1, - IndIntruHeapData super::ClientRec::*C2, - typename C3, - uint B4> - C submit_top_request(IndIntruHeap<C1,typename super::ClientRec,C2,C3,B4>& heap, - PhaseType phase) { - C client_result; - super::pop_process_request(heap, - [this, phase, &client_result] - (const C& client, - typename super::RequestRef& request) { - client_result = client; - handle_f(client, std::move(request), phase); - }); - return client_result; - } - - - // data_mtx should be held when called - void submit_request(typename super::HeapId heap_id) { - C client; - switch(heap_id) { - case super::HeapId::reservation: - // don't need to note client - (void) submit_top_request(this->resv_heap, PhaseType::reservation); - // unlike the other two cases, we do not reduce reservation - // tags here - ++this->reserv_sched_count; - break; - case super::HeapId::ready: - client = submit_top_request(this->ready_heap, PhaseType::priority); - super::reduce_reservation_tags(client); - ++this->prop_sched_count; - break; - default: - assert(false); - } - } // submit_request - - - // data_mtx should be held when called; unfortunately this - // function has to be repeated in both push & pull - // specializations - typename super::NextReq next_request() { - return next_request(get_time()); - } - - - // data_mtx should be held when called; overrides member - // function in base class to add check for whether a request can - // be pushed to the server - typename super::NextReq next_request(Time now) { - if (!can_handle_f()) { - typename super::NextReq result; - result.type = super::NextReqType::none; - return result; - } else { - return super::do_next_request(now); - } - } // next_request - - - // data_mtx should be held when called - void schedule_request() { - typename super::NextReq next_req = next_request(); - switch (next_req.type) { - case super::NextReqType::none: - return; - case super::NextReqType::future: - sched_at(next_req.when_ready); - break; - case super::NextReqType::returning: - submit_request(next_req.heap_id); - break; - default: - assert(false); - } - } - - - // this is the thread that handles running schedule_request at - // future times when nothing can be scheduled immediately - void run_sched_ahead() { - std::unique_lock<std::mutex> l(sched_ahead_mtx); - - while (!this->finishing) { - if (TimeZero == sched_ahead_when) { - sched_ahead_cv.wait(l); - } else { - Time now; - while (!this->finishing && (now = get_time()) < sched_ahead_when) { - long microseconds_l = long(1 + 1000000 * (sched_ahead_when - now)); - auto microseconds = std::chrono::microseconds(microseconds_l); - sched_ahead_cv.wait_for(l, microseconds); - } - sched_ahead_when = TimeZero; - if (this->finishing) return; - - l.unlock(); - if (!this->finishing) { - typename super::DataGuard g(this->data_mtx); - schedule_request(); - } - l.lock(); - } - } - } - - - void sched_at(Time when) { - std::lock_guard<std::mutex> l(sched_ahead_mtx); - if (this->finishing) return; - if (TimeZero == sched_ahead_when || when < sched_ahead_when) { - sched_ahead_when = when; - sched_ahead_cv.notify_one(); - } - } - }; // class PushPriorityQueue - - } // namespace dmclock -} // namespace crimson diff --git a/src/dmclock/src/dmclock_util.cc b/src/dmclock/src/dmclock_util.cc deleted file mode 100644 index e8046cdce24..00000000000 --- a/src/dmclock/src/dmclock_util.cc +++ /dev/null @@ -1,34 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2017 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <signal.h> - -#include <iomanip> -#include <sstream> - -#include "dmclock_util.h" - - -std::string crimson::dmclock::format_time(const Time& time, uint modulo) { - long subtract = long(time / modulo) * modulo; - std::stringstream ss; - ss << std::fixed << std::setprecision(4) << (time - subtract); - return ss.str(); -} - - -void crimson::dmclock::debugger() { - raise(SIGCONT); -} diff --git a/src/dmclock/src/dmclock_util.h b/src/dmclock/src/dmclock_util.h deleted file mode 100644 index d3fd3f197e6..00000000000 --- a/src/dmclock/src/dmclock_util.h +++ /dev/null @@ -1,60 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2017 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <unistd.h> -#include <assert.h> -#include <sys/time.h> - -#include <limits> -#include <cmath> -#include <chrono> - - -namespace crimson { - namespace dmclock { - // we're using double to represent time, but we could change it by - // changing the following declarations (and by making sure a min - // function existed) - using Time = double; - static const Time TimeZero = 0.0; - static const Time TimeMax = std::numeric_limits<Time>::max(); - static const double NaN = nan(""); - - - inline Time get_time() { -#if defined(__linux__) - struct timespec now; - auto result = clock_gettime(CLOCK_REALTIME, &now); - (void) result; // reference result in case assert is compiled out - assert(0 == result); - return now.tv_sec + (now.tv_nsec / 1.0e9); -#else - struct timeval now; - auto result = gettimeofday(&now, NULL); - (void) result; // reference result in case assert is compiled out - assert(0 == result); - return now.tv_sec + (now.tv_usec / 1.0e6); -#endif - } - - std::string format_time(const Time& time, uint modulo = 1000); - - void debugger(); - - } // namespace dmclock -} // namespace crimson diff --git a/src/dmclock/support/src/debug.h b/src/dmclock/support/src/debug.h deleted file mode 100644 index d8e6713fdcf..00000000000 --- a/src/dmclock/support/src/debug.h +++ /dev/null @@ -1,24 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <signal.h> - - -inline void debugger() { - raise(SIGCONT); -} diff --git a/src/dmclock/support/src/heap.h b/src/dmclock/support/src/heap.h deleted file mode 100644 index 6a1f9963ab6..00000000000 --- a/src/dmclock/support/src/heap.h +++ /dev/null @@ -1,247 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <vector> -#include <ostream> - -#include "assert.h" - - -namespace crimson { - - /* - * T : type of data held in the heap. - * - * C : class that implements operator() with two arguments and - * returns a boolean when the first argument is greater than (higher - * in priority than) the second. - */ - template<typename T, typename C> - class Heap { - - public: - - class iterator { - - friend Heap<T,C>; - - Heap<T,C>& heap; - int index; - - iterator(Heap<T,C>& _heap, int _index) : - heap(_heap), - index(_index) - { - // empty - } - - public: - - iterator(iterator&& other) : - heap(other.heap), - index(other.index) - { - // empty - } - - iterator& operator++() { - ++index; - return *this; - } - - bool operator==(const iterator& other) const { - return index == other.index; - } - - bool operator!=(const iterator& other) const { - return !(*this == other); - } - - T& operator*() { - return heap.data[index]; - } - - // the item this iterator refers to - void increase() { - heap.siftUp(index); - } - }; // class iterator - - friend iterator; - - protected: - - std::vector<T> data; - int count; - C comparator; - - // parent(0) should be a negative value, which it is due to - // truncating towards negative infinity - static inline int parent(int i) { return (i - 1) / 2; } - - static inline int lhs(int i) { return 2*i + 1; } - - static inline int rhs(int i) { return 2*i + 2; } - - void siftUp(int i) { - assert(i < count); - - while (i > 0) { - int pi = parent(i); - if (!comparator(data[i], data[pi])) { - break; - } - - std::swap(data[i], data[pi]); - i = pi; - } - } - - void siftDown(int i) { - while (i < count) { - int li = lhs(i); - int ri = rhs(i); - - if (li < count) { - if (comparator(data[li], data[i])) { - if (ri < count && comparator(data[ri], data[li])) { - std::swap(data[i], data[ri]); - i = ri; - } else { - std::swap(data[i], data[li]); - i = li; - } - } else if (ri < count && comparator(data[ri], data[i])) { - std::swap(data[i], data[ri]); - i = ri; - } else { - break; - } - } else { - break; - } - } - } - - - public: - - Heap() : - count(0) - { - // empty - } - - Heap(const Heap<T,C>& other) { - data.resize(other.data.size()); - for (int i = 0; i < other.count; ++i) { - data[i] = other.data[i]; - } - count = other.count; - } - - const Heap<T,C>& operator=(const Heap<T,C>& other) { - data.resize(other.data.size()); - for (int i = 0; i < other.count; ++i) { - data[i] = other.data[i]; - } - count = other.count; - return *this; - } - - bool empty() const { return 0 == count; } - - T& top() { return data[0]; } - - void push(T item) { - int i = count++; - data.push_back(item); - siftUp(i); - } - - void pop() { - data[0] = data[--count]; - data.resize(count); - siftDown(0); - } - - void updateTop() { - siftDown(0); - } - - void clear() { - count = 0; - data.resize(0); - } - - iterator begin() { - return iterator(*this, 0); - } - - iterator end() { - return iterator(*this, count); - } - - std::ostream& displaySorted(std::ostream& out, - std::function<bool(const T&)> filter, - bool insert_line_breaks = true) const { - Heap<T,C> temp = *this; - - bool first = true; - out << "[ "; - - while(!temp.empty()) { - const T& top = temp.top(); - if (filter(top)) { - if (!first) { - out << ", "; - } - if (insert_line_breaks) { - out << std::endl << " "; - } - out << temp.top(); - first = false; - } - temp.pop(); - } - - out << " ]"; - if (insert_line_breaks) { - out << std::endl; - } - return out; - } - - template<typename T1, typename T2> - friend std::ostream& operator<<(std::ostream&, const Heap<T1,T2>&); - }; // class Heap - - - template<typename T1, typename T2> - std::ostream& operator<<(std::ostream& out, const Heap<T1,T2>& h) { - out << "[ "; - if (h.count) { - out << h.data[0]; - } - for (int i = 1; i < h.count; i++) { - out << ", " << h.data[i]; - } - out << " ]"; - return out; - } -} // namespace diff --git a/src/dmclock/support/src/indirect_intrusive_heap.h b/src/dmclock/support/src/indirect_intrusive_heap.h deleted file mode 100644 index 5e2af90fac1..00000000000 --- a/src/dmclock/support/src/indirect_intrusive_heap.h +++ /dev/null @@ -1,556 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <memory> -#include <vector> -#include <string> -#include <iostream> -#include <functional> -#include <algorithm> - -#include "assert.h" - - -namespace crimson { - using IndIntruHeapData = size_t; - - /* T is the ultimate data that's being stored in the heap, although - * through indirection. - * - * I is the indirect type that will actually be stored in the heap - * and that must allow dereferencing (via operator*) to yield a - * T&. - * - * C is a functor when given two T&'s will return true if the first - * must precede the second. - * - * heap_info is a data member pointer as to where the heap data in T - * is stored. - * - * K is the branching factor of the heap, default is 2 (binary heap). - */ - template<typename I, - typename T, - IndIntruHeapData T::*heap_info, - typename C, - uint K = 2> - class IndIntruHeap { - - // shorthand - using HeapIndex = IndIntruHeapData; - - static_assert( - std::is_same<T,typename std::pointer_traits<I>::element_type>::value, - "class I must resolve to class T by indirection (pointer dereference)"); - - static_assert( - std::is_same<bool, - typename std::result_of<C(const T&,const T&)>::type>::value, - "class C must define operator() to take two const T& and return a bool"); - - static_assert(K >= 2, "K (degree of branching) must be at least 2"); - - class Iterator { - friend IndIntruHeap<I, T, heap_info, C, K>; - - IndIntruHeap<I, T, heap_info, C, K>& heap; - HeapIndex index; - - Iterator(IndIntruHeap<I, T, heap_info, C, K>& _heap, HeapIndex _index) : - heap(_heap), - index(_index) - { - // empty - } - - public: - - Iterator(Iterator&& other) : - heap(other.heap), - index(other.index) - { - // empty - } - - Iterator(const Iterator& other) : - heap(other.heap), - index(other.index) - { - // empty - } - - Iterator& operator=(Iterator&& other) { - std::swap(heap, other.heap); - std::swap(index, other.index); - return *this; - } - - Iterator& operator=(const Iterator& other) { - heap = other.heap; - index = other.index; - } - - Iterator& operator++() { - if (index <= heap.count) { - ++index; - } - return *this; - } - - bool operator==(const Iterator& other) const { - return &heap == &other.heap && index == other.index; - } - - bool operator!=(const Iterator& other) const { - return !(*this == other); - } - - T& operator*() { - return *heap.data[index]; - } - - T* operator->() { - return &(*heap.data[index]); - } - -#if 0 - // the item this iterator refers to - void increase() { - heap.sift_up(index); - } -#endif - }; // class Iterator - - - class ConstIterator { - friend IndIntruHeap<I, T, heap_info, C, K>; - - const IndIntruHeap<I, T, heap_info, C, K>& heap; - HeapIndex index; - - ConstIterator(const IndIntruHeap<I, T, heap_info, C, K>& _heap, - HeapIndex _index) : - heap(_heap), - index(_index) - { - // empty - } - - public: - - ConstIterator(ConstIterator&& other) : - heap(other.heap), - index(other.index) - { - // empty - } - - ConstIterator(const ConstIterator& other) : - heap(other.heap), - index(other.index) - { - // empty - } - - ConstIterator& operator=(ConstIterator&& other) { - std::swap(heap, other.heap); - std::swap(index, other.index); - return *this; - } - - ConstIterator& operator=(const ConstIterator& other) { - heap = other.heap; - index = other.index; - } - - ConstIterator& operator++() { - if (index <= heap.count) { - ++index; - } - return *this; - } - - bool operator==(const ConstIterator& other) const { - return &heap == &other.heap && index == other.index; - } - - bool operator!=(const ConstIterator& other) const { - return !(*this == other); - } - - const T& operator*() { - return *heap.data[index]; - } - - const T* operator->() { - return &(*heap.data[index]); - } - }; // class ConstIterator - - - protected: - - std::vector<I> data; - HeapIndex count; - C comparator; - - public: - - IndIntruHeap() : - count(0) - { - // empty - } - - IndIntruHeap(const IndIntruHeap<I,T,heap_info,C,K>& other) : - count(other.count) - { - for (HeapIndex i = 0; i < other.count; ++i) { - data.push_back(other.data[i]); - } - } - - bool empty() const { return 0 == count; } - - size_t size() const { return (size_t) count; } - - T& top() { return *data[0]; } - - const T& top() const { return *data[0]; } - - I& top_ind() { return data[0]; } - - const I& top_ind() const { return data[0]; } - - void push(I&& item) { - HeapIndex i = count++; - intru_data_of(item) = i; - data.emplace_back(std::move(item)); - sift_up(i); - } - - void push(const I& item) { - I copy(item); - push(std::move(copy)); - } - - void pop() { - remove(HeapIndex(0)); - } - - void remove(Iterator& i) { - remove(i.index); - i = end(); - } - - Iterator find(const I& ind_item) { - for (HeapIndex i = 0; i < count; ++i) { - if (data[i] == ind_item) { - return Iterator(*this, i); - } - } - return end(); - } - - // when passing in value we do a comparison via operator== - Iterator find(const T& item) { - for (HeapIndex i = 0; i < count; ++i) { - if (*data[i] == item) { - return Iterator(*this, i); - } - } - return end(); - } - - // reverse find -- start looking from bottom of heap - Iterator rfind(const I& ind_item) { - // HeapIndex is unsigned, so we can't allow to go negative; so - // we'll keep it one more than actual index - for (HeapIndex i = count; i > 0; --i) { - if (data[i-1] == ind_item) { - return Iterator(*this, i-1); - } - } - return end(); - } - - // reverse find -- start looking from bottom of heap - Iterator rfind(const T& item) { - // HeapIndex is unsigned, so we can't allow to go negative; so - // we'll keep it one more than actual index - for (HeapIndex i = count; i > 0; --i) { - if (*data[i-1] == item) { - return Iterator(*this, i-1); - } - } - return end(); - } - - ConstIterator find(const I& ind_item) const { - for (HeapIndex i = 0; i < count; ++i) { - if (data[i] == ind_item) { - return ConstIterator(*this, i); - } - } - return cend(); - } - - // when passing in value we do a comparison via operator== - ConstIterator find(const T& item) const { - for (HeapIndex i = 0; i < count; ++i) { - if (*data[i] == item) { - return ConstIterator(*this, i); - } - } - return cend(); - } - - // reverse find -- start looking from bottom of heap - ConstIterator rfind(const I& ind_item) const { - // HeapIndex is unsigned, so we can't allow to go negative; so - // we'll keep it one more than actual index - for (HeapIndex i = count; i > 0; --i) { - if (data[i-1] == ind_item) { - return ConstIterator(*this, i-1); - } - } - return cend(); - } - - // reverse find -- start looking from bottom of heap - ConstIterator rfind(const T& item) const { - // HeapIndex is unsigned, so we can't allow to go negative; so - // we'll keep it one more than actual index - for (HeapIndex i = count; i > 0; --i) { - if (*data[i-1] == item) { - return ConstIterator(*this, i-1); - } - } - return cend(); - } - - void promote(T& item) { - sift_up(item.*heap_info); - } - - void demote(T& item) { - sift_down(item.*heap_info); - } - - void adjust(T& item) { - sift(item.*heap_info); - } - - Iterator begin() { - return Iterator(*this, 0); - } - - Iterator end() { - return Iterator(*this, count); - } - - ConstIterator cbegin() const { - return ConstIterator(*this, 0); - } - - ConstIterator cend() const { - return ConstIterator(*this, count); - } - - friend std::ostream& operator<<(std::ostream& out, const IndIntruHeap& h) { - auto i = h.data.cbegin(); - if (i != h.data.cend()) { - out << **i; - ++i; - while (i != h.data.cend()) { - out << ", " << **i; - } - } - return out; - } - - // can only be called if I is copyable; copies heap into a vector - // and sorts it before displaying it - std::ostream& - display_sorted(std::ostream& out, - std::function<bool(const T&)> filter = all_filter) const { - static_assert(std::is_copy_constructible<I>::value, - "cannot call display_sorted when class I is not copy" - " constructible"); - auto compare = [this] (const I first, const I second) -> bool { - return this->comparator(*first, *second); - }; - std::vector<I> copy(data); - std::sort(copy.begin(), copy.end(), compare); - - bool first = true; - for (auto c = copy.begin(); c != copy.end(); ++c) { - if (filter(**c)) { - if (!first) { - out << ", "; - } else { - first = false; - } - out << **c; - } - } - - return out; - } - - - protected: - - static IndIntruHeapData& intru_data_of(I& item) { - return (*item).*heap_info; - } - - void remove(HeapIndex i) { - std::swap(data[i], data[--count]); - intru_data_of(data[i]) = i; - data.pop_back(); - - // the following needs to be sift (and not sift_down) as it can - // go up or down the heap; imagine the heap vector contains 0, - // 10, 100, 20, 30, 200, 300, 40; then 200 is removed, and 40 - // would have to be sifted upwards - // sift(i); - sift(i); - } - - // default value of filter parameter to display_sorted - static bool all_filter(const T& data) { return true; } - - // when i is negative? - static inline HeapIndex parent(HeapIndex i) { - assert(0 != i); - return (i - 1) / K; - } - - // index of left child when K==2, index of left-most child when K>2 - static inline HeapIndex lhs(HeapIndex i) { return K*i + 1; } - - // index of right child when K==2, index of right-most child when K>2 - static inline HeapIndex rhs(HeapIndex i) { return K*i + K; } - - void sift_up(HeapIndex i) { - while (i > 0) { - HeapIndex pi = parent(i); - if (!comparator(*data[i], *data[pi])) { - break; - } - - std::swap(data[i], data[pi]); - intru_data_of(data[i]) = i; - intru_data_of(data[pi]) = pi; - i = pi; - } - } // sift_up - - // use this sift_down definition when K>2; it's more general and - // uses a loop; EnableBool insures template uses a template - // parameter - template<bool EnableBool=true> - typename std::enable_if<(K>2)&&EnableBool,void>::type sift_down(HeapIndex i) { - if (i >= count) return; - while (true) { - HeapIndex li = lhs(i); - - if (li < count) { - HeapIndex ri = std::min(rhs(i), count - 1); - - // find the index of min. child - HeapIndex min_i = li; - for (HeapIndex k = li + 1; k <= ri; ++k) { - if (comparator(*data[k], *data[min_i])) { - min_i = k; - } - } - - if (comparator(*data[min_i], *data[i])) { - std::swap(data[i], data[min_i]); - intru_data_of(data[i]) = i; - intru_data_of(data[min_i]) = min_i; - i = min_i; - } else { - // no child is smaller - break; - } - } else { - // no children - break; - } - } - } // sift_down - - // use this sift_down definition when K==2; EnableBool insures - // template uses a template parameter - template<bool EnableBool=true> - typename std::enable_if<K==2&&EnableBool,void>::type sift_down(HeapIndex i) { - if (i >= count) return; - while (true) { - const HeapIndex li = lhs(i); - const HeapIndex ri = 1 + li; - - if (li < count) { - if (comparator(*data[li], *data[i])) { - if (ri < count && comparator(*data[ri], *data[li])) { - std::swap(data[i], data[ri]); - intru_data_of(data[i]) = i; - intru_data_of(data[ri]) = ri; - i = ri; - } else { - std::swap(data[i], data[li]); - intru_data_of(data[i]) = i; - intru_data_of(data[li]) = li; - i = li; - } - } else if (ri < count && comparator(*data[ri], *data[i])) { - std::swap(data[i], data[ri]); - intru_data_of(data[i]) = i; - intru_data_of(data[ri]) = ri; - i = ri; - } else { - // no child is smaller - break; - } - } else { - // no children - break; - } - } // while - } // sift_down - - void sift(HeapIndex i) { - if (i == 0) { - // if we're at top, can only go down - sift_down(i); - } else { - HeapIndex pi = parent(i); - if (comparator(*data[i], *data[pi])) { - // if we can go up, we will - sift_up(i); - } else { - // otherwise we'll try to go down - sift_down(i); - } - } - } // sift - }; // class IndIntruHeap - -} // namespace crimson diff --git a/src/dmclock/support/src/intrusive_heap.h b/src/dmclock/support/src/intrusive_heap.h deleted file mode 100644 index 21d3ea9a0f5..00000000000 --- a/src/dmclock/support/src/intrusive_heap.h +++ /dev/null @@ -1,221 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <vector> -#include <string> -#include <iostream> -#include <functional> - -#include "assert.h" - - -namespace crimson { - using IntruHeapData = size_t; - - // T = type of data in heap; I = functor that returns a non-const - // reference to IntruHeapData; C = functor that compares two const - // refs and return true if the first precedes the second - template<typename T, typename I, typename C> - class IntruHeap { - - static_assert( - std::is_same<IntruHeapData&,typename std::result_of<I(T&)>::type>::value, - "class I must define operator() to take T& and return a IntruHeapData&."); - - static_assert( - std::is_same<bool,typename std::result_of<C(const T&,const T&)>::type>::value, - "class C must define operator() to take two const T& and return a bool."); - - - protected: - using index_t = IntruHeapData; - - std::vector<T> data; - index_t count; - I intru_data_of; - C comparator; - - public: - - IntruHeap() : - count(0) - { - // empty - } - - IntruHeap(const IntruHeap<T,I,C>& other) : - count(other.count) - { - for (uint i = 0; i < other.count; ++i) { - data.push_back(other.data[i]); - } - } - - bool empty() const { return 0 == count; } - - T& top() { return data[0]; } - - void push(T&& item) { - index_t i = count++; - intru_data_of(item) = i; - data.emplace_back(item); - sift_up(i); - } - - void push(const T& item) { - T copy(item); - push(std::move(copy)); - } - - void pop() { - std::swap(data[0], data[--count]); - intru_data_of(data[0]) = 0; - data.pop_back(); - sift_down(0); - } - - void adjust_up(T& item) { - sift_up(intru_data_of(item)); - } - - void adjust_down(T& item) { - sift_down(intru_data_of(item)); - } - - void adjust(T& item) { - sift(intru_data_of(item)); - } - - friend std::ostream& operator<<(std::ostream& out, const IntruHeap& h) { - for (uint i = 0; i < h.count; ++i) { - out << h.data[i] << ", "; - } - return out; - } - - std::ostream& - display_sorted(std::ostream& out, - bool insert_line_breaks = true, - std::function<bool(const T&)> filter = all_filter) const { - IntruHeap<T,I,C> copy = *this; - - bool first = true; - out << "[ "; - - while(!copy.empty()) { - const T& top = copy.top(); - if (filter(top)) { - if (!first) { - out << ", "; - } - if (insert_line_breaks) { - out << std::endl << " "; - } - out << copy.top(); - first = false; - } - copy.pop(); - } - - out << " ]"; - if (insert_line_breaks) { - out << std::endl; - } - - return out; - } - - - protected: - - // default value of filter parameter to display_sorted - static bool all_filter(const T& data) { return true; } - - // when i is negative? - static inline index_t parent(index_t i) { - assert(0 != i); - return (i - 1) / 2; - } - - static inline index_t lhs(index_t i) { return 2*i + 1; } - - static inline index_t rhs(index_t i) { return 2*i + 2; } - - void sift_up(index_t i) { - while (i > 0) { - index_t pi = parent(i); - if (!comparator(data[i], data[pi])) { - break; - } - - std::swap(data[i], data[pi]); - intru_data_of(data[i]) = i; - intru_data_of(data[pi]) = pi; - i = pi; - } - } // sift_up - - void sift_down(index_t i) { - while (i < count) { - index_t li = lhs(i); - index_t ri = rhs(i); - - if (li < count) { - if (comparator(data[li], data[i])) { - if (ri < count && comparator(data[ri], data[li])) { - std::swap(data[i], data[ri]); - intru_data_of(data[i]) = i; - intru_data_of(data[ri]) = ri; - i = ri; - } else { - std::swap(data[i], data[li]); - intru_data_of(data[i]) = i; - intru_data_of(data[li]) = li; - i = li; - } - } else if (ri < count && comparator(data[ri], data[i])) { - std::swap(data[i], data[ri]); - intru_data_of(data[i]) = i; - intru_data_of(data[ri]) = ri; - i = ri; - } else { - break; - } - } else { - break; - } - } - } // sift_down - - void sift(index_t i) { - if (i == 0) { - // if we're at top, can only go down - sift_down(i); - } else { - index_t pi = parent(i); - if (comparator(data[i], data[pi])) { - // if we can go up, we will - sift_up(i); - } else { - // otherwise we'll try to go down - sift_down(i); - } - } - } // sift - }; // class IntruHeap -} // namespace crimson diff --git a/src/dmclock/support/src/profile.h b/src/dmclock/support/src/profile.h deleted file mode 100644 index 8b357dbfceb..00000000000 --- a/src/dmclock/support/src/profile.h +++ /dev/null @@ -1,121 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - - -#include <cmath> -#include <chrono> - - -namespace crimson { - template<typename T> - class ProfileBase { - - protected: - - using clock = std::chrono::steady_clock; - - uint count = 0; - typename T::rep sum = 0; - typename T::rep sum_squares = 0; - typename T::rep low = 0; - typename T::rep high = 0; - - public: - - uint get_count() const { return count; } - typename T::rep get_sum() const { return sum; } - typename T::rep get_low() const { return low; } - typename T::rep get_high() const { return high; } - double get_mean() const { - if (0 == count) return nan(""); - return sum / double(count); } - double get_std_dev() const { - if (0 == count) return nan(""); - double variance = - (count * sum_squares - sum * sum) / double(count * count); - return sqrt(variance); - } - }; // class ProfileBase - - - // forward declaration for friend - template<typename T> - class ProfileCombiner; - - - template<typename T> - class ProfileTimer : public ProfileBase<T> { - friend ProfileCombiner<T>; - - using super = ProfileBase<T>; - - bool is_timing = false; - typename super::clock::time_point start_time; - - public: - - ProfileTimer() { - } - - void start() { - assert(!is_timing); - start_time = super::clock::now(); - is_timing = true; - } - - void stop() { - assert(is_timing); - T duration = std::chrono::duration_cast<T>(super::clock::now() - start_time); - typename T::rep duration_count = duration.count(); - this->sum += duration_count; - this->sum_squares += duration_count * duration_count; - if (0 == this->count) { - this->low = duration_count; - this->high = duration_count; - } else { - if (duration_count < this->low) this->low = duration_count; - else if (duration_count > this->high) this->high = duration_count; - } - ++this->count; - is_timing = false; - } - }; // class ProfileTimer - - - template<typename T> - class ProfileCombiner : public ProfileBase<T> { - - using super = ProfileBase<T>; - - public: - - ProfileCombiner() {} - - void combine(const ProfileTimer<T>& timer) { - if (0 == this->count) { - this->low = timer.low; - this->high = timer.high; - } else { - if (timer.low < this->low) this->low = timer.low; - else if (timer.high > this->high) this->high = timer.high; - } - this->count += timer.count; - this->sum += timer.sum; - this->sum_squares += timer.sum_squares; - } - }; // class ProfileCombiner -} // namespace crimson diff --git a/src/dmclock/support/src/run_every.cc b/src/dmclock/support/src/run_every.cc deleted file mode 100644 index d964261f359..00000000000 --- a/src/dmclock/support/src/run_every.cc +++ /dev/null @@ -1,88 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2017 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include "run_every.h" - - -// can define ADD_MOVE_SEMANTICS, although not fully debugged and tested - - -namespace chrono = std::chrono; - - -#ifdef ADD_MOVE_SEMANTICS -crimson::RunEvery::RunEvery() -{ - // empty -} - - -crimson::RunEvery& crimson::RunEvery::operator=(crimson::RunEvery&& other) -{ - // finish run every thread - { - Guard g(mtx); - finishing = true; - cv.notify_one(); - } - if (thd.joinable()) { - thd.join(); - } - - // transfer info over from previous thread - finishing.store(other.finishing); - wait_period = other.wait_period; - body = other.body; - - // finish other thread - other.finishing.store(true); - other.cv.notify_one(); - - // start this thread - thd = std::thread(&RunEvery::run, this); - - return *this; -} -#endif - - -crimson::RunEvery::~RunEvery() { - join(); -} - - -void crimson::RunEvery::join() { - { - Guard l(mtx); - if (finishing) return; - finishing = true; - cv.notify_all(); - } - thd.join(); -} - - -void crimson::RunEvery::run() { - Lock l(mtx); - while(!finishing) { - TimePoint until = chrono::steady_clock::now() + wait_period; - while (!finishing && chrono::steady_clock::now() < until) { - cv.wait_until(l, until); - } - if (!finishing) { - body(); - } - } -} diff --git a/src/dmclock/support/src/run_every.h b/src/dmclock/support/src/run_every.h deleted file mode 100644 index f321bebd38d..00000000000 --- a/src/dmclock/support/src/run_every.h +++ /dev/null @@ -1,79 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2017 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#pragma once - -#include <chrono> -#include <mutex> -#include <condition_variable> -#include <thread> -#include <functional> - - -namespace crimson { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - - // runs a given simple function object waiting wait_period - // milliseconds between; the destructor stops the other thread - // immediately - class RunEvery { - using Lock = std::unique_lock<std::mutex>; - using Guard = std::lock_guard<std::mutex>; - using TimePoint = std::chrono::steady_clock::time_point; - - bool finishing = false; - std::chrono::milliseconds wait_period; - std::function<void()> body; - std::mutex mtx; - std::condition_variable cv; - - // put threads last so all other variables are initialized first - - std::thread thd; - - public: - -#ifdef ADD_MOVE_SEMANTICS - RunEvery(); -#endif - - template<typename D> - RunEvery(D _wait_period, - std::function<void()> _body) : - wait_period(duration_cast<milliseconds>(_wait_period)), - body(_body) - { - thd = std::thread(&RunEvery::run, this); - } - - RunEvery(const RunEvery& other) = delete; - RunEvery& operator=(const RunEvery& other) = delete; - RunEvery(RunEvery&& other) = delete; -#ifdef ADD_MOVE_SEMANTICS - RunEvery& operator=(RunEvery&& other); -#else - RunEvery& operator=(RunEvery&& other) = delete; -#endif - - ~RunEvery(); - - void join(); - - protected: - - void run(); - }; -} diff --git a/src/dmclock/support/test/CMakeLists.txt b/src/dmclock/support/test/CMakeLists.txt deleted file mode 100644 index 1e1ea25cc14..00000000000 --- a/src/dmclock/support/test/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -include_directories(../src) - -set(local_flags "-Wall -pthread") - -# dmclock does not use intrusive heap (but it does use indirect -# intrusive heap), so we won't use this code -if(false) - set(srcs - test_intrusive_heap.cc) - add_executable(test_intru_heap test_intrusive_heap.cc) - set_source_files_properties(${srcs} - PROPERTIES - COMPILE_FLAGS "${local_flags}") -endif(false) - -set(test_srcs test_indirect_intrusive_heap.cc) - -set_source_files_properties(${test_srcs} - PROPERTIES - COMPILE_FLAGS "${local_flags}" - ) - -add_executable(dmclock-data-struct-tests ${test_srcs}) - -target_link_libraries(dmclock-data-struct-tests - LINK_PRIVATE gtest gtest_main pthread) diff --git a/src/dmclock/support/test/test_ind_intru_heap.cc b/src/dmclock/support/test/test_ind_intru_heap.cc deleted file mode 100644 index ca91cf7a967..00000000000 --- a/src/dmclock/support/test/test_ind_intru_heap.cc +++ /dev/null @@ -1,89 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <memory> -#include <string> -#include <iostream> - -#include "indirect_intrusive_heap.h" - - -class TestCompare; - - -class Test1 { - friend TestCompare; - - int data; - -public: - - crimson::IndIntruHeapData heap_data; - - Test1(int _data) : data(_data) {} - - friend std::ostream& operator<<(std::ostream& out, const Test1& d) { - out << d.data << " (" << d.heap_data << ")"; - return out; - } - - int& the_data() { return data; } -}; - - -struct TestCompare { - bool operator()(const Test1& d1, const Test1& d2) { - return d1.data < d2.data; - } -}; - - -int main(int argc, char** argv) { - Test1 d1(2); - Test1 d2(3); - Test1 d3(1); - Test1 d4(-5); - - crimson::IndIntruHeap<std::shared_ptr<Test1>, Test1, &Test1::heap_data, TestCompare> my_heap; - - const std::shared_ptr<Test1> d99 = std::make_shared<Test1>(99); - - my_heap.push(std::make_shared<Test1>(2)); - my_heap.push(d99); - my_heap.push(std::make_shared<Test1>(1)); - my_heap.push(std::make_shared<Test1>(-5)); - my_heap.push(std::make_shared<Test1>(12)); - my_heap.push(std::make_shared<Test1>(-12)); - my_heap.push(std::make_shared<Test1>(-7)); - - std::cout << my_heap << std::endl; - - auto& t = my_heap.top(); - t.the_data() = 17; - my_heap.adjust_down(t); - - std::cout << my_heap << std::endl; - - my_heap.display_sorted(std::cout); - - while (!my_heap.empty()) { - auto& top = my_heap.top(); - std::cout << top << std::endl; - my_heap.pop(); - std::cout << my_heap << std::endl; - } - - return 0; -} diff --git a/src/dmclock/support/test/test_indirect_intrusive_heap.cc b/src/dmclock/support/test/test_indirect_intrusive_heap.cc deleted file mode 100644 index 71573d722c0..00000000000 --- a/src/dmclock/support/test/test_indirect_intrusive_heap.cc +++ /dev/null @@ -1,938 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <iostream> -#include <memory> -#include <set> - -#include "gtest/gtest.h" - -#include "indirect_intrusive_heap.h" - - -struct Elem { - int data; - - crimson::IndIntruHeapData heap_data; - crimson::IndIntruHeapData heap_data_alt; - - Elem(int _data) : data(_data) { } - - bool operator==(const Elem& other) { - return data == other.data; - } - - friend std::ostream& operator<<(std::ostream& out, const Elem& d) { - out << d.data; - return out; - } -}; - - -// sorted low to high -struct ElemCompare { - bool operator()(const Elem& d1, const Elem& d2) const { - return d1.data < d2.data; - } -}; - - -// first all evens precede all odds, then they're sorted high to low -struct ElemCompareAlt { - bool operator()(const Elem& d1, const Elem& d2) { - if (0 == d1.data % 2) { - if (0 == d2.data % 2) { - return d1.data > d2.data; - } else { - return true; - } - } else if (0 == d2.data % 2) { - return false; - } else { - return d1.data > d2.data; - } - } -}; - - -class HeapFixture1: public ::testing::Test { - -public: - - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare> heap; - - std::shared_ptr<Elem> data1, data2, data3, data4, data5, data6, data7; - - void SetUp() { - data1 = std::make_shared<Elem>(2); - data2 = std::make_shared<Elem>(99); - data3 = std::make_shared<Elem>(1); - data4 = std::make_shared<Elem>(-5); - data5 = std::make_shared<Elem>(12); - data6 = std::make_shared<Elem>(-12); - data7 = std::make_shared<Elem>(-7); - - heap.push(data1); - heap.push(data2); - heap.push(data3); - heap.push(data4); - heap.push(data5); - heap.push(data6); - heap.push(data7); - } - - void TearDown() { - // nothing to do - } -}; // class HeapFixture1 - -TEST(IndIntruHeap, shared_ptr) { - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare> heap; - - EXPECT_TRUE(heap.empty()); - - heap.push(std::make_shared<Elem>(2)); - - EXPECT_FALSE(heap.empty()); - - heap.push(std::make_shared<Elem>(99)); - heap.push(std::make_shared<Elem>(1)); - heap.push(std::make_shared<Elem>(-5)); - heap.push(std::make_shared<Elem>(12)); - heap.push(std::make_shared<Elem>(-12)); - heap.push(std::make_shared<Elem>(-7)); - - // std::cout << heap << std::endl; - - EXPECT_FALSE(heap.empty()); - - EXPECT_EQ(-12, heap.top().data); - heap.pop(); - EXPECT_EQ(-7, heap.top().data); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - heap.pop(); - EXPECT_EQ(1, heap.top().data); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - - EXPECT_FALSE(heap.empty()); - heap.pop(); - EXPECT_TRUE(heap.empty()); -} - - -TEST(IndIntruHeap, unique_ptr) { - crimson::IndIntruHeap<std::unique_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare> heap; - - EXPECT_TRUE(heap.empty()); - - heap.push(std::unique_ptr<Elem>(new Elem(2))); - - EXPECT_FALSE(heap.empty()); - - heap.push(std::unique_ptr<Elem>(new Elem(99))); - heap.push(std::unique_ptr<Elem>(new Elem(1))); - heap.push(std::unique_ptr<Elem>(new Elem(-5))); - heap.push(std::unique_ptr<Elem>(new Elem(12))); - heap.push(std::unique_ptr<Elem>(new Elem(-12))); - heap.push(std::unique_ptr<Elem>(new Elem(-7))); - - EXPECT_FALSE(heap.empty()); - - EXPECT_EQ(-12, heap.top().data); - heap.pop(); - EXPECT_EQ(-7, heap.top().data); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - heap.pop(); - EXPECT_EQ(1, heap.top().data); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - - EXPECT_FALSE(heap.empty()); - heap.pop(); - EXPECT_TRUE(heap.empty()); -} - - -TEST(IndIntruHeap, regular_ptr) { - crimson::IndIntruHeap<Elem*, Elem, &Elem::heap_data, ElemCompare> heap; - - EXPECT_TRUE(heap.empty()); - - heap.push(new Elem(2)); - - EXPECT_FALSE(heap.empty()); - - heap.push(new Elem(99)); - heap.push(new Elem(1)); - heap.push(new Elem(-5)); - heap.push(new Elem(12)); - heap.push(new Elem(-12)); - heap.push(new Elem(-7)); - - EXPECT_FALSE(heap.empty()); - - EXPECT_EQ(-12, heap.top().data); - delete &heap.top(); - heap.pop(); - EXPECT_EQ(-7, heap.top().data); - delete &heap.top(); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - delete &heap.top(); - heap.pop(); - EXPECT_EQ(1, heap.top().data); - delete &heap.top(); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - delete &heap.top(); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - delete &heap.top(); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - - delete &heap.top(); - - EXPECT_FALSE(heap.empty()); - heap.pop(); - EXPECT_TRUE(heap.empty()); -} - - -TEST(IndIntruHeap, K_3) { - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 3> heap; - - EXPECT_TRUE(heap.empty()); - - heap.push(std::make_shared<Elem>(2)); - - EXPECT_FALSE(heap.empty()); - - heap.push(std::make_shared<Elem>(99)); - heap.push(std::make_shared<Elem>(1)); - heap.push(std::make_shared<Elem>(-5)); - heap.push(std::make_shared<Elem>(12)); - heap.push(std::make_shared<Elem>(-12)); - heap.push(std::make_shared<Elem>(-7)); - - // std::cout << heap << std::endl; - - EXPECT_FALSE(heap.empty()); - - EXPECT_EQ(-12, heap.top().data); - heap.pop(); - EXPECT_EQ(-7, heap.top().data); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - heap.pop(); - EXPECT_EQ(1, heap.top().data); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - - EXPECT_FALSE(heap.empty()); - heap.pop(); - EXPECT_TRUE(heap.empty()); -} - - -TEST(IndIntruHeap, K_4) { - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 4> heap; - - EXPECT_TRUE(heap.empty()); - - heap.push(std::make_shared<Elem>(2)); - - EXPECT_FALSE(heap.empty()); - - heap.push(std::make_shared<Elem>(99)); - heap.push(std::make_shared<Elem>(1)); - heap.push(std::make_shared<Elem>(-5)); - heap.push(std::make_shared<Elem>(12)); - heap.push(std::make_shared<Elem>(-12)); - heap.push(std::make_shared<Elem>(-7)); - - // std::cout << heap << std::endl; - - EXPECT_FALSE(heap.empty()); - - EXPECT_EQ(-12, heap.top().data); - heap.pop(); - EXPECT_EQ(-7, heap.top().data); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - heap.pop(); - EXPECT_EQ(1, heap.top().data); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - - EXPECT_FALSE(heap.empty()); - heap.pop(); - EXPECT_TRUE(heap.empty()); -} - - -TEST(IndIntruHeap, K_10) { - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 10> heap; - - EXPECT_TRUE(heap.empty()); - - heap.push(std::make_shared<Elem>(2)); - - EXPECT_FALSE(heap.empty()); - - heap.push(std::make_shared<Elem>(99)); - heap.push(std::make_shared<Elem>(1)); - heap.push(std::make_shared<Elem>(-5)); - heap.push(std::make_shared<Elem>(12)); - heap.push(std::make_shared<Elem>(-12)); - heap.push(std::make_shared<Elem>(-7)); - - // std::cout << heap << std::endl; - - EXPECT_FALSE(heap.empty()); - - EXPECT_EQ(-12, heap.top().data); - heap.pop(); - EXPECT_EQ(-7, heap.top().data); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - heap.pop(); - EXPECT_EQ(1, heap.top().data); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - - EXPECT_FALSE(heap.empty()); - heap.pop(); - EXPECT_TRUE(heap.empty()); -} - - -TEST(IndIntruHeap, multi_K) { - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 2> heap2; - - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 3> heap3; - - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 4> heap4; - - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 10> heap10; - - // 250 should give us at least 4 levels on all heaps - constexpr size_t count = 250; - - std::srand(std::time(0)); // use current time as seed for random generator - - // insert same set of random values into the four heaps - for (size_t i = 0; i < count; ++i) { - int value = std::rand() % 201 - 100; // -100...+100 - auto data = std::make_shared<Elem>(value); - heap2.push(data); - heap3.push(data); - heap4.push(data); - heap10.push(data); - } - - auto bound = std::numeric_limits<decltype(Elem::data)>::min(); - - for (size_t i = 0; i < count; ++i) { - auto current = heap2.top().data; - - EXPECT_GE(current, bound) << - "we should never go down, only increase or remain the same"; - EXPECT_EQ(current, heap3.top().data) << - "heap1's data and heap3's data should match"; - EXPECT_EQ(current, heap4.top().data) << - "heap1's data and heap4's data should match"; - EXPECT_EQ(current, heap10.top().data) << - "heap1's data and heap10's data should match"; - - heap2.pop(); - heap3.pop(); - heap4.pop(); - heap10.pop(); - - bound = current; - } - - EXPECT_TRUE(heap2.empty()) << "should be empty after all elements popped"; - EXPECT_TRUE(heap3.empty()) << "should be empty after all elements popped"; - EXPECT_TRUE(heap4.empty()) << "should be empty after all elements popped"; - EXPECT_TRUE(heap10.empty()) << "should be empty after all elements popped"; -} - - -TEST(IndIntruHeap, demote) { - crimson::IndIntruHeap<std::unique_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare> heap; - - heap.push(std::unique_ptr<Elem>(new Elem(2))); - heap.push(std::unique_ptr<Elem>(new Elem(99))); - heap.push(std::unique_ptr<Elem>(new Elem(1))); - heap.push(std::unique_ptr<Elem>(new Elem(-5))); - heap.push(std::unique_ptr<Elem>(new Elem(12))); - heap.push(std::unique_ptr<Elem>(new Elem(-12))); - heap.push(std::unique_ptr<Elem>(new Elem(-7))); - - heap.top().data = 24; - - heap.demote(heap.top()); - - EXPECT_EQ(-7, heap.top().data); - - heap.pop(); - heap.pop(); - heap.pop(); - heap.pop(); - heap.pop(); - - EXPECT_EQ(24, heap.top().data); -} - - -TEST(IndIntruHeap, demote_not) { - crimson::IndIntruHeap<std::unique_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare> heap; - - heap.push(std::unique_ptr<Elem>(new Elem(2))); - heap.push(std::unique_ptr<Elem>(new Elem(99))); - heap.push(std::unique_ptr<Elem>(new Elem(1))); - heap.push(std::unique_ptr<Elem>(new Elem(-5))); - heap.push(std::unique_ptr<Elem>(new Elem(12))); - heap.push(std::unique_ptr<Elem>(new Elem(-12))); - heap.push(std::unique_ptr<Elem>(new Elem(-7))); - - heap.top().data = -99; - - heap.demote(heap.top()); - - EXPECT_EQ(-99, heap.top().data); - - heap.pop(); - - EXPECT_EQ(-7, heap.top().data); -} - - -TEST(IndIntruHeap, promote_and_demote) { - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare> heap; - - auto data1 = std::make_shared<Elem>(1); - - heap.push(std::make_shared<Elem>(2)); - heap.push(std::make_shared<Elem>(99)); - heap.push(data1); - heap.push(std::make_shared<Elem>(-5)); - heap.push(std::make_shared<Elem>(12)); - heap.push(std::make_shared<Elem>(-12)); - heap.push(std::make_shared<Elem>(-7)); - - EXPECT_EQ(-12, heap.top().data); - - data1->data = -99; - heap.promote(*data1); - - EXPECT_EQ(-99, heap.top().data); - - data1->data = 999; - heap.demote(*data1); - - EXPECT_EQ(-12, heap.top().data); - - data1->data = 9; - heap.promote(*data1); - - heap.pop(); // remove -12 - heap.pop(); // remove -7 - heap.pop(); // remove -5 - heap.pop(); // remove 2 - - EXPECT_EQ(9, heap.top().data); -} - - -TEST(IndIntruHeap, adjust) { - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare> heap; - - auto data1 = std::make_shared<Elem>(1); - - heap.push(std::make_shared<Elem>(2)); - heap.push(std::make_shared<Elem>(99)); - heap.push(data1); - heap.push(std::make_shared<Elem>(-5)); - heap.push(std::make_shared<Elem>(12)); - heap.push(std::make_shared<Elem>(-12)); - heap.push(std::make_shared<Elem>(-7)); - - // heap.display_sorted(std::cout); - - EXPECT_EQ(-12, heap.top().data); - - data1->data = 999; - heap.adjust(*data1); - - EXPECT_EQ(-12, heap.top().data); - - data1->data = -99; - heap.adjust(*data1); - - EXPECT_EQ(-99, heap.top().data); - - data1->data = 9; - heap.adjust(*data1); - - EXPECT_EQ(-12, heap.top().data); - - heap.pop(); // remove -12 - heap.pop(); // remove -7 - heap.pop(); // remove -5 - heap.pop(); // remove 2 - - EXPECT_EQ(9, heap.top().data); -} - - -TEST(IndIntruHeap, remove_careful) { - // here we test whether a common mistake in implementing remove is - // done; if after we remove an item and move the last element of the - // heap to the position of the removed element, we need to sift it - // rather than sift_down it. - - crimson::IndIntruHeap<std::shared_ptr<Elem>, - Elem, - &Elem::heap_data, - ElemCompare, - 2> heap; - - heap.push(std::make_shared<Elem>(0)); - heap.push(std::make_shared<Elem>(10)); - heap.push(std::make_shared<Elem>(100)); - heap.push(std::make_shared<Elem>(20)); - heap.push(std::make_shared<Elem>(30)); - heap.push(std::make_shared<Elem>(200)); - heap.push(std::make_shared<Elem>(300)); - heap.push(std::make_shared<Elem>(40)); - - auto k = heap.find(Elem(200)); - EXPECT_NE(heap.end(), k) << - "we should have found an element with the value 200, which we'll remove"; - heap.remove(k); - - auto i = heap.cbegin(); - EXPECT_EQ(0, i->data); - ++i; - EXPECT_EQ(10, i->data); - ++i; - EXPECT_EQ(40, i->data) << - "this needs to be 40 or there's a mistake in implementation"; - ++i; - EXPECT_EQ(20, i->data); - ++i; - EXPECT_EQ(30, i->data); - ++i; - EXPECT_EQ(100, i->data) << - "this needs to be 100 or there's a mistake in implementation"; -} - - -TEST_F(HeapFixture1, shared_data) { - - crimson::IndIntruHeap<std::shared_ptr<Elem>,Elem,&Elem::heap_data_alt,ElemCompareAlt> heap2; - - heap2.push(data1); - heap2.push(data2); - heap2.push(data3); - heap2.push(data4); - heap2.push(data5); - heap2.push(data6); - heap2.push(data7); - - data3->data = 32; - heap.adjust(*data3); - heap2.adjust(*data3); - - EXPECT_EQ(-12, heap.top().data); - heap.pop(); - EXPECT_EQ(-7, heap.top().data); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - heap.pop(); - EXPECT_EQ(32, heap.top().data); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - - EXPECT_EQ(32, heap2.top().data); - heap2.pop(); - EXPECT_EQ(12, heap2.top().data); - heap2.pop(); - EXPECT_EQ(2, heap2.top().data); - heap2.pop(); - EXPECT_EQ(-12, heap2.top().data); - heap2.pop(); - EXPECT_EQ(99, heap2.top().data); - heap2.pop(); - EXPECT_EQ(-5, heap2.top().data); - heap2.pop(); - EXPECT_EQ(-7, heap2.top().data); -} - - -TEST_F(HeapFixture1, iterator_basics) { - { - uint count = 0; - for(auto i = heap.begin(); i != heap.end(); ++i) { - ++count; - } - - EXPECT_EQ(7u, count) << "count should be 7"; - } - - auto i1 = heap.begin(); - - EXPECT_EQ(-12, i1->data) << - "first member with * operator must be smallest"; - - EXPECT_EQ(-12, (*i1).data) << - "first member with -> operator must be smallest"; - - Elem& e1 = *i1; - EXPECT_EQ(-12, e1.data) << - "first member with -> operator must be smallest"; - - { - std::set<int> values; - values.insert(2); - values.insert(99); - values.insert(1); - values.insert(-5); - values.insert(12); - values.insert(-12); - values.insert(-7); - - for(auto i = heap.begin(); i != heap.end(); ++i) { - auto v = *i; - EXPECT_NE(values.end(), values.find(v.data)) << - "value in heap must be part of original set"; - values.erase(v.data); - } - EXPECT_EQ(0u, values.size()) << "all values must have been seen"; - } -} - - -TEST_F(HeapFixture1, const_iterator_basics) { - const auto& cheap = heap; - - { - uint count = 0; - for(auto i = cheap.cbegin(); i != cheap.cend(); ++i) { - ++count; - } - - EXPECT_EQ(7u, count) << "count should be 7"; - } - - auto i1 = heap.cbegin(); - - EXPECT_EQ(-12, i1->data) << - "first member with * operator must be smallest"; - - EXPECT_EQ(-12, (*i1).data) << - "first member with -> operator must be smallest"; - - const Elem& e1 = *i1; - EXPECT_EQ(-12, e1.data) << - "first member with -> operator must be smallest"; - - { - std::set<int> values; - values.insert(2); - values.insert(99); - values.insert(1); - values.insert(-5); - values.insert(12); - values.insert(-12); - values.insert(-7); - - for(auto i = heap.cbegin(); i != heap.cend(); ++i) { - auto v = *i; - EXPECT_NE(values.end(), values.find(v.data)) << - "value in heap must be part of original set"; - values.erase(v.data); - } - EXPECT_EQ(0u, values.size()) << "all values must have been seen"; - } -} - - -TEST_F(HeapFixture1, iterator_find_rfind) { - { - auto it1 = heap.find(data7); - EXPECT_NE(heap.end(), it1) << - "find by indirection for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "find by indirection for included element should result in right value"; - - auto fake_data = std::make_shared<Elem>(-7); - auto it2 = heap.find(fake_data); - EXPECT_EQ(heap.end(), it2) << - "find by indirection for not included element should fail"; - } - - { - auto it1 = heap.find(Elem(-7)); - EXPECT_NE(heap.end(), it1) << - "find by value for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "find by value for included element should result in right value"; - - auto it2 = heap.find(Elem(7)); - EXPECT_EQ(heap.end(), it2) << - "find by value for not included element should fail"; - } - - { - auto it1 = heap.rfind(data7); - EXPECT_NE(heap.end(), it1) << - "reverse find by indirecton for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "reverse find by indirection for included element should result " - "in right value"; - - auto fake_data = std::make_shared<Elem>(-7); - auto it2 = heap.rfind(fake_data); - EXPECT_EQ(heap.end(), it2) << - "reverse find by indirection for not included element should fail"; - } - - { - auto it1 = heap.rfind(Elem(-7)); - EXPECT_NE(heap.end(), it1) << - "reverse find by value for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "reverse find by value for included element should result " - "in right value"; - - auto it2 = heap.rfind(Elem(7)); - EXPECT_EQ(heap.end(), it2) << - "reverse find by value for not included element should fail"; - } -} - - -TEST_F(HeapFixture1, const_iterator_find_rfind) { - const auto& c_heap = heap; - - { - auto it1 = c_heap.find(data7); - EXPECT_NE(c_heap.cend(), it1) << - "find by indirection for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "find by indirection for included element should result in right value"; - - auto fake_data = std::make_shared<Elem>(-7); - auto it2 = c_heap.find(fake_data); - EXPECT_EQ(c_heap.cend(), it2) << - "find by indirection for not included element should fail"; - } - - { - auto it1 = c_heap.find(Elem(-7)); - EXPECT_NE(c_heap.cend(), it1) << - "find by value for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "find by value for included element should result in right value"; - - auto it2 = c_heap.find(Elem(7)); - EXPECT_EQ(c_heap.cend(), it2) << - "find by value for not included element should fail"; - } - - { - auto it1 = c_heap.rfind(data7); - EXPECT_NE(c_heap.cend(), it1) << - "reverse find by indirecton for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "reverse find by indirection for included element should result " - "in right value"; - - auto fake_data = std::make_shared<Elem>(-7); - auto it2 = c_heap.rfind(fake_data); - EXPECT_EQ(c_heap.cend(), it2) << - "reverse find by indirection for not included element should fail"; - } - - { - auto it1 = c_heap.rfind(Elem(-7)); - EXPECT_NE(c_heap.cend(), it1) << - "reverse find by value for included element should succeed"; - EXPECT_EQ(-7, it1->data) << - "reverse find by value for included element should result " - "in right value"; - - auto it2 = c_heap.rfind(Elem(7)); - EXPECT_EQ(c_heap.cend(), it2) << - "reverse find by value for not included element should fail"; - } -} - - -TEST_F(HeapFixture1, iterator_remove) { - auto it1 = heap.find(data7); - EXPECT_NE(heap.end(), it1) << "find for included element should succeed"; - - heap.remove(it1); - - auto it2 = heap.find(data7); - EXPECT_EQ(heap.end(), it2) << "find for removed element should fail"; - - for (auto it3 = heap.begin(); it3 != heap.end(); ++it3) { - EXPECT_NE(-7, it3->data) << - "iterating through heap should not find removed value"; - } - - // move through heap without -7 - EXPECT_EQ(-12, heap.top().data); - heap.pop(); - EXPECT_EQ(-5, heap.top().data); - heap.pop(); - EXPECT_EQ(1, heap.top().data); - heap.pop(); - EXPECT_EQ(2, heap.top().data); - heap.pop(); - EXPECT_EQ(12, heap.top().data); - heap.pop(); - EXPECT_EQ(99, heap.top().data); - heap.pop(); -} - - -TEST_F(HeapFixture1, four_tops) { - Elem& top1 = heap.top(); - EXPECT_EQ(-12, top1.data); - - const Elem& top2 = heap.top(); - EXPECT_EQ(-12, top2.data); - - std::shared_ptr<Elem> top3 = heap.top_ind(); - EXPECT_EQ(-12, top3->data); - - const std::shared_ptr<Elem> top4 = heap.top_ind(); - EXPECT_EQ(-12, top4->data); - - const auto& c_heap = heap; - - const Elem& top5 = c_heap.top(); - EXPECT_EQ(-12, top5.data); - - const std::shared_ptr<Elem> top6 = c_heap.top_ind(); - EXPECT_EQ(-12, top6->data); -} - - -TEST_F(HeapFixture1, display_sorted) { - std::stringstream ss; - - heap.display_sorted(ss); - - std::string s = ss.str(); - - EXPECT_GT(s.length(), 0u); - - auto negseven = s.find("-7"); - EXPECT_NE(negseven, std::string::npos); - - auto ninetynine = s.find("99"); - EXPECT_NE(ninetynine, std::string::npos); - - // index of -7 should be less than index of 99 - EXPECT_LT(negseven, ninetynine); - -#if 0 - std::cout << s << std::endl; -#endif -} diff --git a/src/dmclock/support/test/test_intrusive_heap.cc b/src/dmclock/support/test/test_intrusive_heap.cc deleted file mode 100644 index 13711775216..00000000000 --- a/src/dmclock/support/test/test_intrusive_heap.cc +++ /dev/null @@ -1,93 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <string> -#include <iostream> - -#include "intrusive_heap.h" - - -struct TestCompare; -struct TestIntruData; - - -class Test1 { - friend TestCompare; - friend TestIntruData; - - int data; - crimson::IntruHeapData heap_data; - -public: - Test1(int _data) : data(_data) {} - - friend std::ostream& operator<<(std::ostream& out, const Test1& d) { - out << d.data << " (" << d.heap_data << ")"; - return out; - } - - int& the_data() { return data; } -}; - - -struct TestCompare { - bool operator()(const Test1& d1, const Test1& d2) { - return d1.data < d2.data; - } -}; - - -struct TestIntruData { - crimson::IntruHeapData& operator()(Test1& d) { - return d.heap_data; - } -}; - - -int main(int argc, char** argv) { - Test1 d1(2); - Test1 d2(3); - Test1 d3(1); - Test1 d4(-5); - - crimson::IntruHeap<Test1, TestIntruData, TestCompare> my_heap; - - my_heap.push(d1); - my_heap.push(d2); - my_heap.push(d3); - my_heap.push(d4); - my_heap.push(Test1(-9)); - my_heap.push(Test1(99)); - my_heap.push(Test1(0)); - - std::cout << my_heap << std::endl; - - auto& t = my_heap.top(); - t.the_data() = 17; - my_heap.adjust_down(t); - - std::cout << my_heap << std::endl; - - my_heap.display_sorted(std::cout); - - while (!my_heap.empty()) { - auto& top = my_heap.top(); - std::cout << top << std::endl; - my_heap.pop(); - std::cout << my_heap << std::endl; - } - - return 0; -} diff --git a/src/dmclock/test/CMakeLists.txt b/src/dmclock/test/CMakeLists.txt deleted file mode 100644 index e2de4de3935..00000000000 --- a/src/dmclock/test/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -INCLUDE (CheckIncludeFiles) -CHECK_INCLUDE_FILES("sys/prctl.h" HAVE_SYS_PRCTL_H) -CONFIGURE_FILE(dmtest-config.h.in dmtest-config.h) - -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(../src) -include_directories(../support/src) -include_directories(../sim/src) - -set(support_srcs ../sim/src/test_dmclock.cc) -set(test_srcs - test_test_client.cc - test_dmclock_server.cc - test_dmclock_client.cc - ) - -set_source_files_properties(${core_srcs} ${test_srcs} - PROPERTIES - COMPILE_FLAGS "${local_flags}" - ) - -add_executable(dmclock-tests ${test_srcs} ${support_srcs}) -target_include_directories(dmclock-tests PRIVATE SYSTEM "${GTEST_INCLUDE_DIRS}") - -if (TARGET gtest AND TARGET gtest_main) - add_dependencies(dmclock-tests gtest gtest_main) - target_link_libraries(dmclock-tests - LINK_PRIVATE $<TARGET_FILE:dmclock> - pthread - $<TARGET_FILE:gtest> - $<TARGET_FILE:gtest_main>) -else() - target_link_libraries(dmclock-tests - LINK_PRIVATE $<TARGET_FILE:dmclock> pthread ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) -endif() - -add_dependencies(dmclock-tests dmclock) diff --git a/src/dmclock/test/dmcPrCtl.h b/src/dmclock/test/dmcPrCtl.h deleted file mode 100644 index 65e6c4d4e99..00000000000 --- a/src/dmclock/test/dmcPrCtl.h +++ /dev/null @@ -1,57 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Ceph - scalable distributed file system - * - * Copyright (C) 2017 Red Hat Inc. - * - * This is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software - * Foundation. See file COPYING. - * - */ - -// essentially the same as ceph's PrCtl.h, copied into the dmclock library - -#include <dmtest-config.h> -#ifdef HAVE_SYS_PRCTL_H -#include <iostream> -#include <sys/prctl.h> -#include <errno.h> - -struct PrCtl { - int saved_state = -1; - int set_dumpable(int new_state) { - int r = prctl(PR_SET_DUMPABLE, new_state); - if (r) { - r = -errno; - std::cerr << "warning: unable to " << (new_state ? "set" : "unset") - << " dumpable flag: " << strerror(r) - << std::endl; - } - return r; - } - PrCtl(int new_state = 0) { - int r = prctl(PR_GET_DUMPABLE); - if (r == -1) { - r = errno; - std::cerr << "warning: unable to get dumpable flag: " << strerror(r) - << std::endl; - } else if (r != new_state) { - if (!set_dumpable(new_state)) { - saved_state = r; - } - } - } - ~PrCtl() { - if (saved_state < 0) { - return; - } - set_dumpable(saved_state); - } -}; -#else -struct PrCtl {}; -#endif diff --git a/src/dmclock/test/dmtest-config.h.in b/src/dmclock/test/dmtest-config.h.in deleted file mode 100644 index ecd2044a89a..00000000000 --- a/src/dmclock/test/dmtest-config.h.in +++ /dev/null @@ -1,2 +0,0 @@ -/* Define to 1 if you have the <sys/prctl.h> header file. */ -#cmakedefine HAVE_SYS_PRCTL_H 1 diff --git a/src/dmclock/test/test_dmclock_client.cc b/src/dmclock/test/test_dmclock_client.cc deleted file mode 100644 index 8be3eaaf657..00000000000 --- a/src/dmclock/test/test_dmclock_client.cc +++ /dev/null @@ -1,307 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <chrono> -#include <mutex> -#include <functional> -#include <iostream> - - -#include "dmclock_client.h" -#include "dmclock_util.h" -#include "gtest/gtest.h" - - -namespace dmc = crimson::dmclock; - - -namespace crimson { - namespace dmclock { - - /* - * Allows us to test the code provided with the mutex provided locked. - */ - static void test_locked(std::mutex& mtx, std::function<void()> code) { - std::lock_guard<std::mutex> l(mtx); - code(); - } - - - TEST(dmclock_client, server_erase) { - using ServerId = int; - // using ClientId = int; - - ServerId server = 101; - // ClientId client = 3; - - // dmc::PhaseType resp_params = dmc::PhaseType::reservation; - - dmc::ServiceTracker<ServerId> st(std::chrono::seconds(2), - std::chrono::seconds(3)); - - auto lock_st = [&](std::function<void()> code) { - test_locked(st.data_mtx, code); - }; - - /* The timeline should be as follows: - * - * 0 seconds : request created - * - * 1 seconds : map is size 1 - * - * 2 seconds : clean notes first mark; +2 is base for further calcs - * - * 4 seconds : clean does nothing except makes another mark - * - * 5 seconds : when we're secheduled to erase (+2 + 3) - * - * 5 seconds : since the clean job hasn't run yet, map still size 1 - * - * 6 seconds : clean erases server - * - * 7 seconds : verified server is gone (map size 0) - */ - - lock_st([&] () { - EXPECT_EQ(0u, st.server_map.size()) << - "server map initially has size 0"; - }); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - - // call for side effects - (void) st.get_req_params(server); - - lock_st([&] () { - EXPECT_EQ(1u, st.server_map.size()) << - "server map has size 1 after first request"; - }); - - std::this_thread::sleep_for(std::chrono::seconds(4)); - - lock_st([&] () { - EXPECT_EQ(1u, st.server_map.size()) << - "server map has size 1 just before erase"; - }); - - std::this_thread::sleep_for(std::chrono::seconds(2)); - - lock_st([&] () { - EXPECT_EQ(0u, st.server_map.size()) << - "server map has size 0 just after erase"; - }); - } // TEST - - - TEST(dmclock_client, delta_rho_values) { - using ServerId = int; - // using ClientId = int; - - ServerId server1 = 101; - ServerId server2 = 7; - // ClientId client = 3; - - // RespParams<ServerId> resp_params(server, dmc::PhaseType::reservation); - - dmc::ServiceTracker<ServerId> st(std::chrono::seconds(2), - std::chrono::seconds(3)); - auto rp1 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp1.delta) << - "delta should be 1 with no intervening responses by" << - "other servers"; - EXPECT_EQ(1u, rp1.rho) << - "rho should be 1 with no intervening reservation responses by" << - "other servers"; - - auto rp2 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp2.delta) << - "delta should be 1 with no intervening responses by" << - "other servers"; - EXPECT_EQ(1u, rp2.rho) << - "rho should be 1 with no intervening reservation responses by" << - "other servers"; - - // RESPONSE - st.track_resp(server1, dmc::PhaseType::priority); - - auto rp3 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp3.delta) << - "delta should be 1 with no intervening responses by" << - "other servers"; - EXPECT_EQ(1u, rp3.rho) << - "rho should be 1 with no intervening reservation responses by" << - "other servers"; - - // RESPONSE - st.track_resp(server2, dmc::PhaseType::priority); - - auto rp4 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp4.delta) << - "delta should be 2 with one intervening priority response by " << - "another server"; - EXPECT_EQ(1u, rp4.rho) << - "rho should be 1 with one intervening priority responses by " << - "another server"; - - auto rp5 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp5.delta) << - "delta should be 1 with no intervening responses by" << - "other servers"; - EXPECT_EQ(1u, rp5.rho) << - "rho should be 1 with no intervening reservation responses by" << - "other servers"; - - // RESPONSE - st.track_resp(server2, dmc::PhaseType::reservation); - - auto rp6 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp6.delta) << - "delta should be 2 with one intervening reservation response by " << - "another server"; - EXPECT_EQ(1u, rp6.rho) << - "rho should be 2 with one intervening reservation responses by " << - "another server"; - - st.track_resp(server2, dmc::PhaseType::reservation); - st.track_resp(server1, dmc::PhaseType::priority); - st.track_resp(server2, dmc::PhaseType::priority); - st.track_resp(server2, dmc::PhaseType::reservation); - st.track_resp(server1, dmc::PhaseType::reservation); - st.track_resp(server1, dmc::PhaseType::priority); - st.track_resp(server2, dmc::PhaseType::priority); - - auto rp7 = st.get_req_params(server1); - - EXPECT_EQ(5u, rp7.delta) << - "delta should be 5 with four intervening responses by " << - "another server"; - EXPECT_EQ(1u, rp7.rho) << - "rho should be 1 with two intervening reservation responses by " << - "another server"; - - auto rp7b = st.get_req_params(server2); - - EXPECT_EQ(9u, rp7b.delta) << - "delta should be 9 with three intervening responses by " << - "another server"; - EXPECT_EQ(4u, rp7b.rho) << - "rho should be 4 with one intervening reservation responses by " << - "another server"; - - auto rp8 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp8.delta) << - "delta should be 1 with no intervening responses by " << - "another server"; - EXPECT_EQ(1u, rp8.rho) << - "rho should be 1 with no intervening reservation responses by " << - "another server"; - - auto rp8b = st.get_req_params(server2); - EXPECT_EQ(1u, rp8b.delta) << - "delta should be 1 with no intervening responses by " << - "another server"; - EXPECT_EQ(1u, rp8b.rho) << - "rho should be 1 with no intervening reservation responses by " << - "another server"; - } // TEST - - - // NB: the BorrowingTracker has not been fully tested and the - // expected values below have not yet been compared with the - // theoretically correct values. - TEST(dmclock_client, orig_tracker_delta_rho_values) { - using ServerId = int; - - ServerId server1 = 101; - ServerId server2 = 7; - - dmc::ServiceTracker<ServerId,OrigTracker> - st(std::chrono::seconds(2), std::chrono::seconds(3)); - - auto rp1 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp1.delta); - EXPECT_EQ(1u, rp1.rho); - - auto rp2 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp2.delta); - EXPECT_EQ(1u, rp2.rho); - - st.track_resp(server1, dmc::PhaseType::priority); - - auto rp3 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp3.delta); - EXPECT_EQ(1u, rp3.rho); - - st.track_resp(server2, dmc::PhaseType::priority); - - auto rp4 = st.get_req_params(server1); - - EXPECT_EQ(2u, rp4.delta); - EXPECT_EQ(1u, rp4.rho); - - auto rp5 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp5.delta); - EXPECT_EQ(1u, rp5.rho); - - st.track_resp(server2, dmc::PhaseType::reservation); - - auto rp6 = st.get_req_params(server1); - - EXPECT_EQ(2u, rp6.delta); - EXPECT_EQ(2u, rp6.rho); - - // auto rp6_b = st.get_req_params(server2); - - st.track_resp(server2, dmc::PhaseType::reservation); - st.track_resp(server1, dmc::PhaseType::priority); - st.track_resp(server2, dmc::PhaseType::priority); - st.track_resp(server2, dmc::PhaseType::reservation); - st.track_resp(server1, dmc::PhaseType::reservation); - st.track_resp(server1, dmc::PhaseType::priority); - st.track_resp(server2, dmc::PhaseType::priority); - - auto rp7 = st.get_req_params(server1); - - EXPECT_EQ(5u, rp7.delta); - EXPECT_EQ(3u, rp7.rho); - - auto rp7b = st.get_req_params(server2); - - EXPECT_EQ(4u, rp7b.delta); - EXPECT_EQ(2u, rp7b.rho); - - auto rp8 = st.get_req_params(server1); - - EXPECT_EQ(1u, rp8.delta); - EXPECT_EQ(1u, rp8.rho); - - auto rp8b = st.get_req_params(server2); - EXPECT_EQ(1u, rp8b.delta); - EXPECT_EQ(1u, rp8b.rho); - } // TEST - - } // namespace dmclock -} // namespace crimson diff --git a/src/dmclock/test/test_dmclock_server.cc b/src/dmclock/test/test_dmclock_server.cc deleted file mode 100644 index 3cc497235a2..00000000000 --- a/src/dmclock/test/test_dmclock_server.cc +++ /dev/null @@ -1,1026 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <memory> -#include <chrono> -#include <iostream> -#include <list> -#include <vector> - - -#include "dmclock_server.h" -#include "dmclock_util.h" -#include "gtest/gtest.h" - -// process control to prevent core dumps during gtest death tests -#include "dmcPrCtl.h" - - -namespace dmc = crimson::dmclock; - - -// we need a request object; an empty one will do -struct Request { -}; - - -namespace crimson { - namespace dmclock { - - /* - * Allows us to test the code provided with the mutex provided locked. - */ - static void test_locked(std::mutex& mtx, std::function<void()> code) { - std::unique_lock<std::mutex> l(mtx); - code(); - } - - - TEST(dmclock_server, bad_tag_deathtest) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 17; - ClientId client2 = 18; - - double reservation = 0.0; - double weight = 0.0; - - dmc::ClientInfo ci1(reservation, weight, 0.0); - dmc::ClientInfo ci2(reservation, weight, 1.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - if (client1 == c) return &ci1; - else if (client2 == c) return &ci2; - else { - ADD_FAILURE() << "got request from neither of two clients"; - return nullptr; - } - }; - - QueueRef pq(new Queue(client_info_f, false)); - ReqParams req_params(1,1); - - // Disable coredumps - PrCtl unset_dumpable; - - EXPECT_DEATH_IF_SUPPORTED(pq->add_request(Request{}, client1, req_params), - "Assertion.*reservation.*max_tag.*" - "proportion.*max_tag") << - "we should fail if a client tries to generate a reservation tag " - "where reservation and proportion are both 0"; - - - EXPECT_DEATH_IF_SUPPORTED(pq->add_request(Request{}, client2, req_params), - "Assertion.*reservation.*max_tag.*" - "proportion.*max_tag") << - "we should fail if a client tries to generate a reservation tag " - "where reservation and proportion are both 0"; - } - - - TEST(dmclock_server, client_idle_erase) { - using ClientId = int; - using Queue = dmc::PushPriorityQueue<ClientId,Request>; - int client = 17; - double reservation = 100.0; - - dmc::ClientInfo ci(reservation, 1.0, 0.0); - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &ci; - }; - auto server_ready_f = [] () -> bool { return true; }; - auto submit_req_f = [] (const ClientId& c, - std::unique_ptr<Request> req, - dmc::PhaseType phase) { - // empty; do nothing - }; - - Queue pq(client_info_f, - server_ready_f, - submit_req_f, - std::chrono::seconds(3), - std::chrono::seconds(5), - std::chrono::seconds(2), - false); - - auto lock_pq = [&](std::function<void()> code) { - test_locked(pq.data_mtx, code); - }; - - - /* The timeline should be as follows: - * - * 0 seconds : request created - * - * 1 seconds : map is size 1, idle is false - * - * 2 seconds : clean notes first mark; +2 is base for further calcs - * - * 4 seconds : clean does nothing except makes another mark - * - * 5 seconds : when we're secheduled to idle (+2 + 3) - * - * 6 seconds : clean idles client - * - * 7 seconds : when we're secheduled to erase (+2 + 5) - * - * 7 seconds : verified client is idle - * - * 8 seconds : clean erases client info - * - * 9 seconds : verified client is erased - */ - - lock_pq([&] () { - EXPECT_EQ(0u, pq.client_map.size()) << - "client map initially has size 0"; - }); - - Request req; - dmc::ReqParams req_params(1, 1); - pq.add_request_time(req, client, req_params, dmc::get_time()); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - - lock_pq([&] () { - EXPECT_EQ(1u, pq.client_map.size()) << - "client map has 1 after 1 client"; - EXPECT_FALSE(pq.client_map.at(client)->idle) << - "initially client map entry shows not idle."; - }); - - std::this_thread::sleep_for(std::chrono::seconds(6)); - - lock_pq([&] () { - EXPECT_TRUE(pq.client_map.at(client)->idle) << - "after idle age client map entry shows idle."; - }); - - std::this_thread::sleep_for(std::chrono::seconds(2)); - - lock_pq([&] () { - EXPECT_EQ(0u, pq.client_map.size()) << - "client map loses its entry after erase age"; - }); - } // TEST - - -#if 0 - TEST(dmclock_server, reservation_timing) { - using ClientId = int; - // NB? PUSH OR PULL - using Queue = std::unique_ptr<dmc::PriorityQueue<ClientId,Request>>; - using std::chrono::steady_clock; - - int client = 17; - - std::vector<dmc::Time> times; - std::mutex times_mtx; - using Guard = std::lock_guard<decltype(times_mtx)>; - - // reservation every second - dmc::ClientInfo ci(1.0, 0.0, 0.0); - Queue pq; - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &ci; - }; - auto server_ready_f = [] () -> bool { return true; }; - auto submit_req_f = [&] (const ClientId& c, - std::unique_ptr<Request> req, - dmc::PhaseType phase) { - { - Guard g(times_mtx); - times.emplace_back(dmc::get_time()); - } - std::thread complete([&](){ pq->request_completed(); }); - complete.detach(); - }; - - // NB? PUSH OR PULL - pq = Queue(new dmc::PriorityQueue<ClientId,Request>(client_info_f, - server_ready_f, - submit_req_f, - false)); - - Request req; - ReqParams<ClientId> req_params(client, 1, 1); - - for (int i = 0; i < 5; ++i) { - pq->add_request_time(req, req_params, dmc::get_time()); - } - - { - Guard g(times_mtx); - std::this_thread::sleep_for(std::chrono::milliseconds(5500)); - EXPECT_EQ(5, times.size()) << - "after 5.5 seconds, we should have 5 requests times at 1 second apart"; - } - } // TEST -#endif - - - TEST(dmclock_server, remove_by_req_filter) { - struct MyReq { - int id; - - MyReq(int _id) : - id(_id) - { - // empty - } - }; // MyReq - - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,MyReq>; - using MyReqRef = typename Queue::RequestRef; - - ClientId client1 = 17; - ClientId client2 = 98; - - dmc::ClientInfo info1(0.0, 1.0, 0.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info1; - }; - - Queue pq(client_info_f, true); - - EXPECT_EQ(0u, pq.client_count()); - EXPECT_EQ(0u, pq.request_count()); - - ReqParams req_params(1,1); - - pq.add_request(MyReq(1), client1, req_params); - pq.add_request(MyReq(11), client1, req_params); - pq.add_request(MyReq(2), client2, req_params); - pq.add_request(MyReq(0), client2, req_params); - pq.add_request(MyReq(13), client2, req_params); - pq.add_request(MyReq(2), client2, req_params); - pq.add_request(MyReq(13), client2, req_params); - pq.add_request(MyReq(98), client2, req_params); - pq.add_request(MyReq(44), client1, req_params); - - EXPECT_EQ(2u, pq.client_count()); - EXPECT_EQ(9u, pq.request_count()); - - pq.remove_by_req_filter([](MyReqRef&& r) -> bool {return 1 == r->id % 2;}); - - EXPECT_EQ(5u, pq.request_count()); - - std::list<MyReq> capture; - pq.remove_by_req_filter( - [&capture] (MyReqRef&& r) -> bool { - if (0 == r->id % 2) { - capture.push_front(*r); - return true; - } else { - return false; - } - }, - true); - - EXPECT_EQ(0u, pq.request_count()); - EXPECT_EQ(5u, capture.size()); - int total = 0; - for (auto i : capture) { - total += i.id; - } - EXPECT_EQ(146, total) << " sum of captured items should be 146"; - } // TEST - - - TEST(dmclock_server, remove_by_req_filter_ordering_forwards_visit) { - struct MyReq { - int id; - - MyReq(int _id) : - id(_id) - { - // empty - } - }; // MyReq - - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,MyReq>; - using MyReqRef = typename Queue::RequestRef; - - ClientId client1 = 17; - - dmc::ClientInfo info1(0.0, 1.0, 0.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info1; - }; - - Queue pq(client_info_f, true); - - EXPECT_EQ(0u, pq.client_count()); - EXPECT_EQ(0u, pq.request_count()); - - ReqParams req_params(1,1); - - pq.add_request(MyReq(1), client1, req_params); - pq.add_request(MyReq(2), client1, req_params); - pq.add_request(MyReq(3), client1, req_params); - pq.add_request(MyReq(4), client1, req_params); - pq.add_request(MyReq(5), client1, req_params); - pq.add_request(MyReq(6), client1, req_params); - - EXPECT_EQ(1u, pq.client_count()); - EXPECT_EQ(6u, pq.request_count()); - - // remove odd ids in forward order and append to end - - std::vector<MyReq> capture; - pq.remove_by_req_filter( - [&capture] (MyReqRef&& r) -> bool { - if (1 == r->id % 2) { - capture.push_back(*r); - return true; - } else { - return false; - } - }, - false); - - EXPECT_EQ(3u, pq.request_count()); - EXPECT_EQ(3u, capture.size()); - EXPECT_EQ(1, capture[0].id) << "items should come out in forward order"; - EXPECT_EQ(3, capture[1].id) << "items should come out in forward order"; - EXPECT_EQ(5, capture[2].id) << "items should come out in forward order"; - - // remove even ids in reverse order but insert at front so comes - // out forwards - - std::vector<MyReq> capture2; - pq.remove_by_req_filter( - [&capture2] (MyReqRef&& r) -> bool { - if (0 == r->id % 2) { - capture2.insert(capture2.begin(), *r); - return true; - } else { - return false; - } - }, - false); - - EXPECT_EQ(0u, pq.request_count()); - EXPECT_EQ(3u, capture2.size()); - EXPECT_EQ(6, capture2[0].id) << "items should come out in reverse order"; - EXPECT_EQ(4, capture2[1].id) << "items should come out in reverse order"; - EXPECT_EQ(2, capture2[2].id) << "items should come out in reverse order"; - } // TEST - - - TEST(dmclock_server, remove_by_req_filter_ordering_backwards_visit) { - struct MyReq { - int id; - - MyReq(int _id) : - id(_id) - { - // empty - } - }; // MyReq - - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,MyReq>; - using MyReqRef = typename Queue::RequestRef; - - ClientId client1 = 17; - - dmc::ClientInfo info1(0.0, 1.0, 0.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info1; - }; - - Queue pq(client_info_f, true); - - EXPECT_EQ(0u, pq.client_count()); - EXPECT_EQ(0u, pq.request_count()); - - ReqParams req_params(1,1); - - pq.add_request(MyReq(1), client1, req_params); - pq.add_request(MyReq(2), client1, req_params); - pq.add_request(MyReq(3), client1, req_params); - pq.add_request(MyReq(4), client1, req_params); - pq.add_request(MyReq(5), client1, req_params); - pq.add_request(MyReq(6), client1, req_params); - - EXPECT_EQ(1u, pq.client_count()); - EXPECT_EQ(6u, pq.request_count()); - - // now remove odd ids in forward order - - std::vector<MyReq> capture; - pq.remove_by_req_filter( - [&capture] (MyReqRef&& r) -> bool { - if (1 == r->id % 2) { - capture.insert(capture.begin(), *r); - return true; - } else { - return false; - } - }, - true); - - EXPECT_EQ(3u, pq.request_count()); - EXPECT_EQ(3u, capture.size()); - EXPECT_EQ(1, capture[0].id) << "items should come out in forward order"; - EXPECT_EQ(3, capture[1].id) << "items should come out in forward order"; - EXPECT_EQ(5, capture[2].id) << "items should come out in forward order"; - - // now remove even ids in reverse order - - std::vector<MyReq> capture2; - pq.remove_by_req_filter( - [&capture2] (MyReqRef&& r) -> bool { - if (0 == r->id % 2) { - capture2.push_back(*r); - return true; - } else { - return false; - } - }, - true); - - EXPECT_EQ(0u, pq.request_count()); - EXPECT_EQ(3u, capture2.size()); - EXPECT_EQ(6, capture2[0].id) << "items should come out in reverse order"; - EXPECT_EQ(4, capture2[1].id) << "items should come out in reverse order"; - EXPECT_EQ(2, capture2[2].id) << "items should come out in reverse order"; - } // TEST - - - TEST(dmclock_server, remove_by_client) { - struct MyReq { - int id; - - MyReq(int _id) : - id(_id) - { - // empty - } - }; // MyReq - - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,MyReq>; - using MyReqRef = typename Queue::RequestRef; - - ClientId client1 = 17; - ClientId client2 = 98; - - dmc::ClientInfo info1(0.0, 1.0, 0.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info1; - }; - - Queue pq(client_info_f, true); - - EXPECT_EQ(0u, pq.client_count()); - EXPECT_EQ(0u, pq.request_count()); - - ReqParams req_params(1,1); - - pq.add_request(MyReq(1), client1, req_params); - pq.add_request(MyReq(11), client1, req_params); - pq.add_request(MyReq(2), client2, req_params); - pq.add_request(MyReq(0), client2, req_params); - pq.add_request(MyReq(13), client2, req_params); - pq.add_request(MyReq(2), client2, req_params); - pq.add_request(MyReq(13), client2, req_params); - pq.add_request(MyReq(98), client2, req_params); - pq.add_request(MyReq(44), client1, req_params); - - EXPECT_EQ(2u, pq.client_count()); - EXPECT_EQ(9u, pq.request_count()); - - std::list<MyReq> removed; - - pq.remove_by_client(client1, - true, - [&removed] (MyReqRef&& r) { - removed.push_front(*r); - }); - - EXPECT_EQ(3u, removed.size()); - EXPECT_EQ(1, removed.front().id); - removed.pop_front(); - EXPECT_EQ(11, removed.front().id); - removed.pop_front(); - EXPECT_EQ(44, removed.front().id); - removed.pop_front(); - - EXPECT_EQ(6u, pq.request_count()); - - Queue::PullReq pr = pq.pull_request(); - EXPECT_TRUE(pr.is_retn()); - EXPECT_EQ(2, pr.get_retn().request->id); - - pr = pq.pull_request(); - EXPECT_TRUE(pr.is_retn()); - EXPECT_EQ(0, pr.get_retn().request->id); - - pq.remove_by_client(client2); - EXPECT_EQ(0u, pq.request_count()) << - "after second client removed, none left"; - } // TEST - - - TEST(dmclock_server_pull, pull_weight) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 17; - ClientId client2 = 98; - - dmc::ClientInfo info1(0.0, 1.0, 0.0); - dmc::ClientInfo info2(0.0, 2.0, 0.0); - - QueueRef pq; - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - if (client1 == c) return &info1; - else if (client2 == c) return &info2; - else { - ADD_FAILURE() << "client info looked up for non-existant client"; - return nullptr; - } - }; - - pq = QueueRef(new Queue(client_info_f, false)); - - ReqParams req_params(1,1); - - auto now = dmc::get_time(); - - for (int i = 0; i < 5; ++i) { - pq->add_request(Request{}, client1, req_params); - pq->add_request(Request{}, client2, req_params); - now += 0.0001; - } - - int c1_count = 0; - int c2_count = 0; - for (int i = 0; i < 6; ++i) { - Queue::PullReq pr = pq->pull_request(); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - - if (client1 == retn.client) ++c1_count; - else if (client2 == retn.client) ++c2_count; - else ADD_FAILURE() << "got request from neither of two clients"; - - EXPECT_EQ(PhaseType::priority, retn.phase); - } - - EXPECT_EQ(2, c1_count) << - "one-third of request should have come from first client"; - EXPECT_EQ(4, c2_count) << - "two-thirds of request should have come from second client"; - } - - - TEST(dmclock_server_pull, pull_reservation) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 52; - ClientId client2 = 8; - - dmc::ClientInfo info1(2.0, 0.0, 0.0); - dmc::ClientInfo info2(1.0, 0.0, 0.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - if (client1 == c) return &info1; - else if (client2 == c) return &info2; - else { - ADD_FAILURE() << "client info looked up for non-existant client"; - return nullptr; - } - }; - - QueueRef pq(new Queue(client_info_f, false)); - - ReqParams req_params(1,1); - - // make sure all times are well before now - auto old_time = dmc::get_time() - 100.0; - - for (int i = 0; i < 5; ++i) { - pq->add_request_time(Request{}, client1, req_params, old_time); - pq->add_request_time(Request{}, client2, req_params, old_time); - old_time += 0.001; - } - - int c1_count = 0; - int c2_count = 0; - - for (int i = 0; i < 6; ++i) { - Queue::PullReq pr = pq->pull_request(); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - - if (client1 == retn.client) ++c1_count; - else if (client2 == retn.client) ++c2_count; - else ADD_FAILURE() << "got request from neither of two clients"; - - EXPECT_EQ(PhaseType::reservation, retn.phase); - } - - EXPECT_EQ(4, c1_count) << - "two-thirds of request should have come from first client"; - EXPECT_EQ(2, c2_count) << - "one-third of request should have come from second client"; - } // dmclock_server_pull.pull_reservation - - - TEST(dmclock_server_pull, update_client_info) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request,false>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 17; - ClientId client2 = 98; - - dmc::ClientInfo info1(0.0, 100.0, 0.0); - dmc::ClientInfo info2(0.0, 200.0, 0.0); - - QueueRef pq; - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - if (client1 == c) return &info1; - else if (client2 == c) return &info2; - else { - ADD_FAILURE() << "client info looked up for non-existant client"; - return nullptr; - } - }; - - pq = QueueRef(new Queue(client_info_f, false)); - - ReqParams req_params(1,1); - - auto now = dmc::get_time(); - - for (int i = 0; i < 5; ++i) { - pq->add_request(Request{}, client1, req_params); - pq->add_request(Request{}, client2, req_params); - now += 0.0001; - } - - int c1_count = 0; - int c2_count = 0; - for (int i = 0; i < 10; ++i) { - Queue::PullReq pr = pq->pull_request(); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - - if (i > 5) continue; - if (client1 == retn.client) ++c1_count; - else if (client2 == retn.client) ++c2_count; - else ADD_FAILURE() << "got request from neither of two clients"; - - EXPECT_EQ(PhaseType::priority, retn.phase); - } - - EXPECT_EQ(2, c1_count) << - "before: one-third of request should have come from first client"; - EXPECT_EQ(4, c2_count) << - "before: two-thirds of request should have come from second client"; - - std::chrono::seconds dura(1); - std::this_thread::sleep_for(dura); - - info1 = dmc::ClientInfo(0.0, 200.0, 0.0); - pq->update_client_info(17); - - now = dmc::get_time(); - - for (int i = 0; i < 5; ++i) { - pq->add_request(Request{}, client1, req_params); - pq->add_request(Request{}, client2, req_params); - now += 0.0001; - } - - c1_count = 0; - c2_count = 0; - for (int i = 0; i < 6; ++i) { - Queue::PullReq pr = pq->pull_request(); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - - if (client1 == retn.client) ++c1_count; - else if (client2 == retn.client) ++c2_count; - else ADD_FAILURE() << "got request from neither of two clients"; - - EXPECT_EQ(PhaseType::priority, retn.phase); - } - - EXPECT_EQ(3, c1_count) << - "after: one-third of request should have come from first client"; - EXPECT_EQ(3, c2_count) << - "after: two-thirds of request should have come from second client"; - } - - - TEST(dmclock_server_pull, dynamic_cli_info_f) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request,true>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 17; - ClientId client2 = 98; - - std::vector<dmc::ClientInfo> info1; - std::vector<dmc::ClientInfo> info2; - - info1.push_back(dmc::ClientInfo(0.0, 100.0, 0.0)); - info1.push_back(dmc::ClientInfo(0.0, 150.0, 0.0)); - - info2.push_back(dmc::ClientInfo(0.0, 200.0, 0.0)); - info2.push_back(dmc::ClientInfo(0.0, 50.0, 0.0)); - - uint cli_info_group = 0; - - QueueRef pq; - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - if (client1 == c) return &info1[cli_info_group]; - else if (client2 == c) return &info2[cli_info_group]; - else { - ADD_FAILURE() << "client info looked up for non-existant client"; - return nullptr; - } - }; - - pq = QueueRef(new Queue(client_info_f, false)); - - ReqParams req_params(1,1); - - auto now = dmc::get_time(); - - for (int i = 0; i < 5; ++i) { - pq->add_request(Request{}, client1, req_params); - pq->add_request(Request{}, client2, req_params); - now += 0.0001; - } - - int c1_count = 0; - int c2_count = 0; - for (int i = 0; i < 10; ++i) { - Queue::PullReq pr = pq->pull_request(); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - - if (i > 5) continue; - if (client1 == retn.client) ++c1_count; - else if (client2 == retn.client) ++c2_count; - else ADD_FAILURE() << "got request from neither of two clients"; - - EXPECT_EQ(PhaseType::priority, retn.phase); - } - - EXPECT_EQ(2, c1_count) << - "before: one-third of request should have come from first client"; - EXPECT_EQ(4, c2_count) << - "before: two-thirds of request should have come from second client"; - - std::chrono::seconds dura(1); - std::this_thread::sleep_for(dura); - - cli_info_group = 1; - - now = dmc::get_time(); - - for (int i = 0; i < 6; ++i) { - pq->add_request(Request{}, client1, req_params); - pq->add_request(Request{}, client2, req_params); - now += 0.0001; - } - - c1_count = 0; - c2_count = 0; - for (int i = 0; i < 8; ++i) { - Queue::PullReq pr = pq->pull_request(); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - - if (client1 == retn.client) ++c1_count; - else if (client2 == retn.client) ++c2_count; - else ADD_FAILURE() << "got request from neither of two clients"; - - EXPECT_EQ(PhaseType::priority, retn.phase); - } - - EXPECT_EQ(6, c1_count) << - "after: one-third of request should have come from first client"; - EXPECT_EQ(2, c2_count) << - "after: two-thirds of request should have come from second client"; - } - - - // This test shows what happens when a request can be ready (under - // limit) but not schedulable since proportion tag is 0. We expect - // to get some future and none responses. - TEST(dmclock_server_pull, ready_and_under_limit) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 52; - ClientId client2 = 8; - - dmc::ClientInfo info1(1.0, 0.0, 0.0); - dmc::ClientInfo info2(1.0, 0.0, 0.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - if (client1 == c) return &info1; - else if (client2 == c) return &info2; - else { - ADD_FAILURE() << "client info looked up for non-existant client"; - return nullptr; - } - }; - - QueueRef pq(new Queue(client_info_f, false)); - - ReqParams req_params(1,1); - - // make sure all times are well before now - auto start_time = dmc::get_time() - 100.0; - - // add six requests; for same client reservations spaced one apart - for (int i = 0; i < 3; ++i) { - pq->add_request_time(Request{}, client1, req_params, start_time); - pq->add_request_time(Request{}, client2, req_params, start_time); - } - - Queue::PullReq pr = pq->pull_request(start_time + 0.5); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - pr = pq->pull_request(start_time + 0.5); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - pr = pq->pull_request(start_time + 0.5); - EXPECT_EQ(Queue::NextReqType::future, pr.type) << - "too soon for next reservation"; - - pr = pq->pull_request(start_time + 1.5); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - pr = pq->pull_request(start_time + 1.5); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - pr = pq->pull_request(start_time + 1.5); - EXPECT_EQ(Queue::NextReqType::future, pr.type) << - "too soon for next reservation"; - - pr = pq->pull_request(start_time + 2.5); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - pr = pq->pull_request(start_time + 2.5); - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - pr = pq->pull_request(start_time + 2.5); - EXPECT_EQ(Queue::NextReqType::none, pr.type) << "no more requests left"; - } - - - TEST(dmclock_server_pull, pull_none) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - dmc::ClientInfo info(1.0, 1.0, 1.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info; - }; - - QueueRef pq(new Queue(client_info_f, false)); - - // Request req; - ReqParams req_params(1,1); - - auto now = dmc::get_time(); - - Queue::PullReq pr = pq->pull_request(now + 100); - - EXPECT_EQ(Queue::NextReqType::none, pr.type); - } - - - TEST(dmclock_server_pull, pull_future) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 52; - // ClientId client2 = 8; - - dmc::ClientInfo info(1.0, 0.0, 1.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info; - }; - - QueueRef pq(new Queue(client_info_f, false)); - - ReqParams req_params(1,1); - - // make sure all times are well before now - auto now = dmc::get_time(); - - pq->add_request_time(Request{}, client1, req_params, now + 100); - Queue::PullReq pr = pq->pull_request(now); - - EXPECT_EQ(Queue::NextReqType::future, pr.type); - - Time when = boost::get<Time>(pr.data); - EXPECT_EQ(now + 100, when); - } - - - TEST(dmclock_server_pull, pull_future_limit_break_weight) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 52; - // ClientId client2 = 8; - - dmc::ClientInfo info(0.0, 1.0, 1.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info; - }; - - QueueRef pq(new Queue(client_info_f, true)); - - ReqParams req_params(1,1); - - // make sure all times are well before now - auto now = dmc::get_time(); - - pq->add_request_time(Request{}, client1, req_params, now + 100); - Queue::PullReq pr = pq->pull_request(now); - - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - EXPECT_EQ(client1, retn.client); - } - - - TEST(dmclock_server_pull, pull_future_limit_break_reservation) { - using ClientId = int; - using Queue = dmc::PullPriorityQueue<ClientId,Request>; - using QueueRef = std::unique_ptr<Queue>; - - ClientId client1 = 52; - // ClientId client2 = 8; - - dmc::ClientInfo info(1.0, 0.0, 1.0); - - auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* { - return &info; - }; - - QueueRef pq(new Queue(client_info_f, true)); - - ReqParams req_params(1,1); - - // make sure all times are well before now - auto now = dmc::get_time(); - - pq->add_request_time(Request{}, client1, req_params, now + 100); - Queue::PullReq pr = pq->pull_request(now); - - EXPECT_EQ(Queue::NextReqType::returning, pr.type); - - auto& retn = boost::get<Queue::PullReq::Retn>(pr.data); - EXPECT_EQ(client1, retn.client); - } - } // namespace dmclock -} // namespace crimson diff --git a/src/dmclock/test/test_test_client.cc b/src/dmclock/test/test_test_client.cc deleted file mode 100644 index a4aff6e3e63..00000000000 --- a/src/dmclock/test/test_test_client.cc +++ /dev/null @@ -1,136 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -/* - * Copyright (C) 2016 Red Hat Inc. - * - * Author: J. Eric Ivancich <ivancich@redhat.com> - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License version - * 2.1, as published by the Free Software Foundation. See file - * COPYING. - */ - - -#include <atomic> -#include <thread> -#include <chrono> -#include <iostream> - -#include "gtest/gtest.h" - -#include "sim_recs.h" -#include "sim_client.h" - -#include "test_dmclock.h" - - -using namespace std::placeholders; - -namespace dmc = crimson::dmclock; -namespace test = crimson::test_dmc; -namespace sim = crimson::qos_simulation; - -using TimePoint = std::chrono::time_point<std::chrono::system_clock>; - -static TimePoint now() { return std::chrono::system_clock::now(); } - - -TEST(test_client, full_bore_timing) { - std::atomic_ulong count(0); - - ServerId server_id = 3; - - sim::TestResponse resp(0); - dmc::PhaseType resp_params = dmc::PhaseType::priority; - test::DmcClient* client; - - auto start = now(); - client = - new test::DmcClient(ClientId(0), - [&] (const ServerId& server, - const sim::TestRequest& req, - const ClientId& client_id, - const dmc::ReqParams& req_params) { - ++count; - client->receive_response(resp, client_id, resp_params); - }, - [&] (const uint64_t seed) -> ServerId& { - return server_id; - }, - test::dmc_client_accumulate_f, - 1000, // ops to run - 100, // iops goal - 5); // outstanding ops allowed - client->wait_until_done(); - auto end = now(); - EXPECT_EQ(1000u, count) << "didn't get right number of ops"; - - int milliseconds = (end - start) / std::chrono::milliseconds(1); - EXPECT_LT(10000, milliseconds) << "timing too fast to be correct"; - EXPECT_GT(12000, milliseconds) << "timing suspiciously slow"; - - delete client; -} - - -TEST(test_client, paused_timing) { - std::atomic_ulong count(0); - std::atomic_ulong unresponded_count(0); - std::atomic_bool auto_respond(false); - - ClientId my_client_id = 0; - ServerId server_id = 3; - - sim::TestResponse resp(0); - dmc::PhaseType resp_params = dmc::PhaseType::priority; - test::DmcClient* client; - - auto start = now(); - client = - new test::DmcClient(my_client_id, - [&] (const ServerId& server, - const sim::TestRequest& req, - const ClientId& client_id, - const dmc::ReqParams& req_params) { - ++count; - if (auto_respond.load()) { - client->receive_response(resp, client_id, resp_params); - } else { - ++unresponded_count; - } - }, - [&] (const uint64_t seed) -> ServerId& { - return server_id; - }, - test::dmc_client_accumulate_f, - - 1000, // ops to run - 100, // iops goal - 50); // outstanding ops allowed - std::thread t([&]() { - std::this_thread::sleep_for(std::chrono::seconds(5)); - EXPECT_EQ(50u, unresponded_count.load()) << - "should have 50 unresponded calls"; - auto_respond = true; - // respond to those 50 calls - for(int i = 0; i < 50; ++i) { - client->receive_response(resp, my_client_id, resp_params); - --unresponded_count; - } - }); - - client->wait_until_done(); - auto end = now(); - int milliseconds = (end - start) / std::chrono::milliseconds(1); - - // the 50 outstanding ops allowed means the first half-second of - // requests get responded to during the 5 second pause. So we have - // to adjust our expectations by a half-second. - EXPECT_LT(15000 - 500, milliseconds) << "timing too fast to be correct"; - EXPECT_GT(17000 - 500, milliseconds) << "timing suspiciously slow"; - t.join(); - - delete client; -} |