Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'attic/test/test_functions.hpp')
-rw-r--r--attic/test/test_functions.hpp926
1 files changed, 926 insertions, 0 deletions
diff --git a/attic/test/test_functions.hpp b/attic/test/test_functions.hpp
new file mode 100644
index 00000000..7606f6a8
--- /dev/null
+++ b/attic/test/test_functions.hpp
@@ -0,0 +1,926 @@
+/* Unit tests for TripleGit
+(C) 2013 Niall Douglas http://www.nedprod.com/
+Created: Feb 2013
+*/
+
+#ifndef BOOST_AFIO_TEST_FUNCTIONS_HPP
+#define BOOST_AFIO_TEST_FUNCTIONS_HPP
+
+// Uses a ton more memory and is many orders of magnitude slower, but no longer optional.
+#define DEBUG_TORTURE_TEST 1
+
+// Reduces CPU cores used to execute, which can be useful for certain kinds of race condition.
+//#define MAXIMUM_TEST_CPUS 1
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "boost/afio/afio.hpp"
+
+#ifdef __MINGW32__
+#include <stdlib.h> // To pull in __MINGW64_VERSION_MAJOR
+#ifndef __MINGW64_VERSION_MAJOR
+// Mingw32 doesn't define putenv() needed by Boost.Test
+extern "C" int putenv(char*);
+#endif
+// Mingw doesn't define tzset() either
+extern "C" void tzset(void);
+#endif
+
+#include <utility>
+#include <sstream>
+#include <iostream>
+#include <algorithm>
+#include <deque>
+#include <set>
+#include <unordered_set>
+#include <random>
+#include <fstream>
+#include "../detail/SpookyV2.h"
+#include "Aligned_Allocator.hpp"
+#include "boost/afio/v2/detail/valgrind/valgrind.h"
+#include <time.h>
+
+#ifdef BOOST_AFIO_INCLUDE_SPOOKY_IMPL
+#include "../detail/SpookyV2.cpp"
+#endif
+
+#if defined(__has_feature)
+# if __has_feature(thread_sanitizer)
+# define RUNNING_ON_SANITIZER 1
+# endif
+#endif
+#ifndef RUNNING_ON_SANITIZER
+# define RUNNING_ON_SANITIZER 0
+#endif
+
+//if we're building the tests all together don't define the test main
+#ifndef BOOST_AFIO_TEST_ALL
+# define BOOST_TEST_MAIN //must be defined before unit_test.hpp is included
+#endif
+#ifndef BOOST_TEST_MODULE
+#define BOOST_TEST_MODULE Boost.AFIO
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4535) // calling _set_se_translator() requires /EHa
+#endif
+
+#ifndef BOOST_AFIO_USE_BOOST_UNIT_TEST
+# define BOOST_AFIO_USE_BOOST_UNIT_TEST 0
+#endif
+#if BOOST_AFIO_USE_BOOST_UNIT_TEST
+# include "boost/test/unit_test.hpp"
+# include "boost/test/unit_test_monitor.hpp"
+
+//define a simple macro to check any exception using Boost.Test
+#define BOOST_AFIO_CHECK_THROWS(expr)\
+try{\
+ expr;\
+ BOOST_FAIL("Exception was not thrown");\
+}catch(...){/*BOOST_CHECK(true);*/}
+#define BOOST_AFIO_CHECK_NO_THROW(expr)\
+try{\
+ expr;\
+ /*BOOST_CHECK(true);*/ \
+}catch(...){BOOST_FAIL("Exception was thrown");}
+
+#else
+# include "../include/boost/afio/bindlib/include/boost/test/unit_test.hpp"
+# define BOOST_AFIO_CHECK_THROWS(expr) BOOST_CHECK_THROWS(expr)
+# define BOOST_AFIO_CHECK_NO_THROW(expr) BOOST_CHECK_NO_THROW(expr)
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+// Force maximum CPUs available to threads in this process, if available on this platform
+#ifdef MAXIMUM_TEST_CPUS
+static inline void set_maximum_cpus(size_t no=MAXIMUM_TEST_CPUS)
+{
+#ifdef WIN32
+ DWORD_PTR mask=0;
+ for(size_t n=0; n<no; n++)
+ mask|=(size_t)1<<n;
+ if(!SetProcessAffinityMask(GetCurrentProcess(), mask))
+ {
+ std::cerr << "ERROR: Failed to set process affinity mask due to reason " << GetLastError() << std::endl;
+ abort();
+ }
+#endif
+#ifdef __linux__
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+ for(size_t n=0; n<no; n++)
+ CPU_SET(n, &mask);
+ sched_setaffinity(getpid(), sizeof(mask), &mask);
+#endif
+}
+#else
+static inline void set_maximum_cpus(size_t no=0)
+{
+}
+#endif
+
+// Boost.Test uses alarm() to timeout tests, which is nearly useless. Hence do our own.
+static inline void watchdog_thread(size_t timeout, std::shared_ptr<std::pair<atomic<bool>, condition_variable>> cv)
+{
+ detail::set_threadname("watchdog_thread");
+ if(getenv("BOOST_AFIO_TEST_DISABLE_WATCHDOG_TIMER"))
+ return;
+ bool docountdown=timeout>10;
+ if(docountdown) timeout-=10;
+ chrono::duration<size_t, ratio<1, 1>> d(timeout);
+ mutex m;
+ unique_lock<mutex> lock(m);
+ if(!cv->second.wait_for(lock, d, [cv]{return !!cv->first;}))
+ {
+ if(docountdown)
+ {
+ std::cerr << "Test about to time out ...";
+ d=chrono::duration<size_t, ratio<1, 1>>(1);
+ for(size_t n=0; n<10; n++)
+ {
+ if(cv->second.wait_for(lock, d, [cv]{return !!cv->first;}))
+ return;
+ std::cerr << " " << 10-n;
+ }
+ std::cerr << " ... ";
+ }
+ BOOST_CHECK_MESSAGE(false, "Test timed out");
+ std::cerr << "Test timed out, aborting" << std::endl;
+ abort();
+ }
+}
+
+#define BOOST_AFIO_TRAP_EXCEPTIONS_IN_TEST(callable) \
+ try { callable; } \
+ catch(const BOOST_AFIO_V2_NAMESPACE::system_error &e) { std::cerr << "ERROR: unit test exits via system_error code " << e.code().value() << " (" << e.what() << ")" << std::endl; throw; } \
+ catch(const std::exception &e) { std::cerr << "ERROR: unit test exits via exception (" << e.what() << ")" << std::endl; throw; }
+#if BOOST_AFIO_USE_BOOST_UNIT_TEST
+template<class T> inline void wrap_test_method(T &t)
+{
+ BOOST_AFIO_TRAP_EXCEPTIONS_IN_TEST(t.test_method())
+ catch(const boost::execution_aborted &) { throw; }
+ catch(...) { std::cerr << "ERROR: unit test exits via unknown exception" << std::endl; throw; }
+}
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_USE_BOOST_UNIT_TEST
+// Define a unit test description and timeout
+#ifdef BOOST_TEST_UNIT_TEST_SUITE_IMPL_HPP_071894GER
+#define BOOST_AFIO_AUTO_TEST_CASE_REGISTRAR(test_name) \
+BOOST_AUTO_TU_REGISTRAR( test_name )( \
+ boost::unit_test::make_test_case( \
+ &BOOST_AUTO_TC_INVOKER( test_name ), #test_name ), \
+ boost::unit_test::ut_detail::auto_tc_exp_fail< \
+ BOOST_AUTO_TC_UNIQUE_ID( test_name )>::instance()->value() ); \
+ \
+void test_name::test_method() \
+
+#else
+#define BOOST_AFIO_AUTO_TEST_CASE_REGISTRAR(test_name) \
+BOOST_AUTO_TU_REGISTRAR( test_name )( \
+ boost::unit_test::make_test_case( \
+ &BOOST_AUTO_TC_INVOKER( test_name ), \
+ #test_name, __FILE__, __LINE__ ), \
+ boost::unit_test::decorator::collector::instance() ); \
+ \
+void test_name::test_method() \
+
+#endif
+
+#define BOOST_AFIO_AUTO_TEST_CASE(test_name, desc, _timeout) \
+struct test_name : public BOOST_AUTO_TEST_CASE_FIXTURE { void test_method(); }; \
+ \
+static void BOOST_AUTO_TC_INVOKER( test_name )() \
+{ \
+ test_name t; \
+ size_t timeout=_timeout; \
+ if(RUNNING_ON_VALGRIND) { \
+ VALGRIND_PRINTF("BOOST.AFIO TEST INVOKER: Unit test running in valgrind so tripling timeout\n"); \
+ timeout*=3; \
+ } \
+ if(RUNNING_ON_SANITIZER) { \
+ BOOST_TEST_MESSAGE("BOOST.AFIO TEST INVOKER: Unit test running in thread sanitiser so tripling timeout"); \
+ timeout*=3; \
+ } \
+ /*boost::unit_test::unit_test_monitor_t::instance().p_timeout.set(timeout);*/ \
+ BOOST_TEST_MESSAGE(desc); \
+ std::cout << std::endl << desc << std::endl; \
+ try { BOOST_AFIO_V2_NAMESPACE::filesystem::remove_all("testdir"); } catch(...) { } \
+ BOOST_AFIO_V2_NAMESPACE::set_maximum_cpus(); \
+ auto cv=std::make_shared<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>>(); \
+ cv->first=false; \
+ BOOST_AFIO_V2_NAMESPACE::thread watchdog(BOOST_AFIO_V2_NAMESPACE::watchdog_thread, timeout, cv); \
+ boost::unit_test::unit_test_monitor_t::instance().execute([&]() -> int { BOOST_AFIO_V2_NAMESPACE::wrap_test_method(t); cv->first=true; cv->second.notify_all(); watchdog.join(); return 0; }); \
+} \
+ \
+struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {}; \
+ \
+BOOST_AFIO_AUTO_TEST_CASE_REGISTRAR ( test_name )
+
+#else
+
+#define BOOST_AFIO_AUTO_TEST_CASE(__test_name, __desc, __timeout) \
+static void __test_name ## _impl(); \
+CATCH_TEST_CASE(BOOST_CATCH_AUTO_TEST_CASE_NAME(__test_name), __desc) \
+{ \
+ size_t timeout=__timeout; \
+ if(RUNNING_ON_VALGRIND) { \
+ VALGRIND_PRINTF("BOOST.AFIO TEST INVOKER: Unit test running in valgrind so tripling timeout\n"); \
+ timeout*=3; \
+ } \
+ if(RUNNING_ON_SANITIZER) { \
+ BOOST_TEST_MESSAGE("BOOST.AFIO TEST INVOKER: Unit test running in thread sanitiser so tripling timeout"); \
+ timeout*=3; \
+ } \
+ BOOST_TEST_MESSAGE(__desc); \
+ std::cout << std::endl << __desc << std::endl; \
+ try { BOOST_AFIO_V2_NAMESPACE::filesystem::remove_all("testdir"); } catch(...) { } \
+ BOOST_AFIO_V2_NAMESPACE::set_maximum_cpus(); \
+ auto cv=std::make_shared<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>>(); \
+ cv->first=false; \
+ struct __deleter_t { \
+ std::shared_ptr<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>> cv; \
+ BOOST_AFIO_V2_NAMESPACE::thread watchdog; \
+ __deleter_t(std::shared_ptr<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>> _cv, \
+ BOOST_AFIO_V2_NAMESPACE::thread &&_watchdog) : cv(std::move(_cv)), watchdog(std::move(_watchdog)) { } \
+ ~__deleter_t() { cv->first=true; cv->second.notify_all(); watchdog.join(); } \
+ } __deleter(cv, BOOST_AFIO_V2_NAMESPACE::thread(BOOST_AFIO_V2_NAMESPACE::watchdog_thread, timeout, cv)); \
+ BOOST_AFIO_TRAP_EXCEPTIONS_IN_TEST(__test_name ## _impl()) \
+ catch(...) { std::cerr << "ERROR: unit test exits via unknown exception" << std::endl; throw; } \
+} \
+static void __test_name ## _impl() \
+
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+// From http://burtleburtle.net/bob/rand/smallprng.html
+typedef unsigned int u4;
+typedef struct ranctx { u4 a; u4 b; u4 c; u4 d; } ranctx;
+
+#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))
+static u4 ranval(ranctx *x) {
+ u4 e = x->a - rot(x->b, 27);
+ x->a = x->b ^ rot(x->c, 17);
+ x->b = x->c + x->d;
+ x->c = x->d + e;
+ x->d = e + x->a;
+ return x->d;
+}
+
+static void raninit(ranctx *x, u4 seed) {
+ u4 i;
+ x->a = 0xf1ea5eed, x->b = x->c = x->d = seed;
+ for (i = 0; i < 20; ++i) {
+ (void) ranval(x);
+ }
+}
+
+static void dofilter(atomic<size_t> *callcount, detail::OpType, future<> &) { ++*callcount; }
+static void checkwrite(detail::OpType, handle *h, const detail::io_req_impl<true> &req, off_t offset, size_t idx, size_t no, const error_code &, size_t transferred)
+{
+ size_t amount=0;
+ for(auto &i: req.buffers)
+ amount+=asio::buffer_size(i);
+ if(offset!=0)
+ BOOST_CHECK(offset==0);
+ if(transferred!=amount)
+ BOOST_CHECK(transferred==amount);
+}
+static int donothing(atomic<size_t> *callcount, int i) { ++*callcount; return i; }
+static void _1000_open_write_close_deletes(dispatcher_ptr dispatcher, size_t bytes)
+{
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ std::vector<char, detail::aligned_allocator<char, 4096>> towrite(bytes, 'N');
+ assert(!(((size_t) &towrite.front()) & 4095));
+
+ // Wait for six seconds to let filing system recover and prime SpeedStep
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<6);
+
+ // Install a file open filter
+ dispatcher->post_op_filter_clear();
+ atomic<size_t> filtercount(0);
+ std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_t>>> filters;
+ filters.push_back(std::make_pair<detail::OpType, std::function<dispatcher::filter_t>>(detail::OpType::file, std::bind(dofilter, &filtercount, std::placeholders::_1, std::placeholders::_2)));
+ dispatcher->post_op_filter(filters);
+
+ // Install a write filter
+ std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_readwrite_t>>> rwfilters;
+ rwfilters.push_back(std::make_pair(detail::OpType::Unknown, std::function<dispatcher::filter_readwrite_t>(checkwrite)));
+ dispatcher->post_readwrite_filter(rwfilters);
+
+ // Start opening 1000 files
+ begin=chrono::high_resolution_clock::now();
+ std::vector<path_req> manyfilereqs;
+ manyfilereqs.reserve(1000);
+ for(size_t n=0; n<1000; n++)
+ manyfilereqs.push_back(path_req::relative(mkdir, to_string(n), file_flags::create|file_flags::write));
+ auto manyopenfiles(dispatcher->file(manyfilereqs));
+
+ // Write to each of those 1000 files as they are opened
+ std::vector<io_req<const char>> manyfilewrites;
+ manyfilewrites.reserve(manyfilereqs.size());
+ auto openit=manyopenfiles.begin();
+ for(size_t n=0; n<manyfilereqs.size(); n++)
+ manyfilewrites.push_back(io_req<const char>(*openit++, &towrite.front(), towrite.size(), 0));
+ auto manywrittenfiles(dispatcher->write(manyfilewrites));
+
+ // Close each of those 1000 files once one byte has been written
+ auto manyclosedfiles(dispatcher->close(manywrittenfiles));
+
+ // Delete each of those 1000 files once they are closed
+ auto it(manyclosedfiles.begin());
+ for(auto &i: manyfilereqs)
+ i.precondition=dispatcher->depends(*it++, mkdir);
+ auto manydeletedfiles(dispatcher->rmfile(manyfilereqs));
+
+ // As a test of call() which involves significant template metaprogramming, have a do nothing callback
+ atomic<size_t> callcount(0);
+ typedef int (*callable_type)(atomic<size_t> *, int);
+ callable_type callable=donothing;
+ std::vector<std::function<int()>> callables;
+ callables.reserve(1000);
+ for(size_t n=0; n<1000; n++)
+ callables.push_back(std::bind(callable, &callcount, 78));
+ auto manycallbacks(dispatcher->call(manydeletedfiles, std::move(callables)));
+ auto dispatched=chrono::high_resolution_clock::now();
+ std::cout << "There are now " << std::dec << dispatcher->fd_count() << " handles open with a queue depth of " << dispatcher->wait_queue_depth() << std::endl;
+
+ // Wait for all files to open
+ when_all_p(manyopenfiles.begin(), manyopenfiles.end()).get();
+ auto openedsync=chrono::high_resolution_clock::now();
+ // Wait for all files to write
+ when_all_p(manywrittenfiles.begin(), manywrittenfiles.end()).get();
+ auto writtensync=chrono::high_resolution_clock::now();
+ // Wait for all files to close
+ when_all_p(manyclosedfiles.begin(), manyclosedfiles.end()).get();
+ auto closedsync=chrono::high_resolution_clock::now();
+ // Wait for all files to delete
+ when_all_p(manydeletedfiles.begin(), manydeletedfiles.end()).get();
+ auto deletedsync=chrono::high_resolution_clock::now();
+ // Wait for all callbacks
+ when_all_p(manycallbacks.begin(), manycallbacks.end()).get();
+
+ auto end=deletedsync;
+ auto rmdir(dispatcher->rmdir(path_req("testdir")));
+
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to do all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(dispatched-begin);
+ std::cout << " It took " << diff.count() << " secs to dispatch all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(end-dispatched);
+ std::cout << " It took " << diff.count() << " secs to finish all operations" << std::endl << std::endl;
+
+ diff=chrono::duration_cast<secs_type>(openedsync-begin);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file opens per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(writtensync-openedsync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file writes per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(closedsync-writtensync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file closes per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(deletedsync-closedsync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file deletions per sec" << std::endl;
+
+ // Fetch any outstanding error
+ when_all_p(rmdir).wait();
+ BOOST_CHECK((callcount==1000U));
+ BOOST_CHECK((filtercount==1000U));
+}
+
+#ifdef DEBUG_TORTURE_TEST
+ struct Op
+ {
+ bool write;
+ std::vector<char *> data;
+ io_req<char> req;
+ };
+#else
+ struct Op
+ {
+ Hash256 hash; // Only used for reading
+ bool write;
+ ranctx seed;
+ io_req<char> req;
+ };
+#endif
+
+#ifdef DEBUG_TORTURE_TEST
+static u4 mkfill() { static char ret='0'; if(ret+1>'z') ret='0'; return ret++; }
+#endif
+
+static void evil_random_io(dispatcher_ptr dispatcher, size_t no, size_t bytes, size_t alignment=0)
+{
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+
+ detail::aligned_allocator<char, 4096> aligned_allocator;
+ std::vector<std::vector<char, detail::aligned_allocator<char, 4096>>> towrite(no);
+ std::vector<char *> towriteptrs(no);
+ std::vector<size_t> towritesizes(no);
+
+#ifdef DEBUG_TORTURE_TEST
+ std::vector<std::vector<Op>> todo(no);
+#else
+ static_assert(!(sizeof(PadSizeToMultipleOf<Op, 32>)&31), "Op's stored size must be a multiple of 32 bytes");
+ std::vector<std::vector<PadSizeToMultipleOf<Op, 32>, detail::aligned_allocator<Op, 32>>> todo(no);
+#endif
+
+ for(size_t n=0; n<no; n++)
+ {
+ towrite[n].reserve(bytes);
+ towrite[n].resize(bytes);
+ assert(!(((size_t) &towrite[n].front()) & 4095));
+ towriteptrs[n]=&towrite[n].front();
+ towritesizes[n]=bytes;
+ }
+ // We create no lots of random writes and reads representing about 100% of bytes
+ // We simulate what we _ought_ to see appear in storage during the test and
+ // SHA256 out the results
+ // We then replay the same with real storage to see if it matches
+ auto begin=chrono::high_resolution_clock::now();
+#pragma omp parallel for
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ {
+ ranctx gen;
+ raninit(&gen, 0x78adbcff^(u4) n);
+ for(off_t bytessofar=0; bytessofar<bytes;)
+ {
+ Op op;
+ u4 r=ranval(&gen), toissue=(r>>24) & 15;
+ size_t thisbytes=0, m;
+ if(!toissue) toissue=1;
+ op.write=bytessofar<bytes/4 ? true : !(r&(1<<31));
+ op.req.where=r & (bytes-1);
+ if(op.req.where>bytes-1024*1024) op.req.where=bytes-1024*1024;
+ if(alignment)
+ op.req.where&=~(alignment-1);
+#ifdef DEBUG_TORTURE_TEST
+ u4 fillvalue=mkfill();
+ fillvalue|=fillvalue<<8;
+ fillvalue|=fillvalue<<16;
+#else
+ ranctx writeseed=op.seed=gen;
+#endif
+ for(m=0; m<toissue; m++)
+ {
+ u4 s=ranval(&gen) & ((256*1024-1)&~63); // Must be a multiple of 64 bytes for SHA256
+ if(s<64) s=64;
+ if(alignment)
+ s=(s+4095)&~(alignment-1);
+ if(thisbytes+s>1024*1024) break;
+ char *buffertouse=(char *)((size_t)towriteptrs[n]+(size_t) op.req.where+thisbytes);
+#ifdef DEBUG_TORTURE_TEST
+ op.data.push_back(aligned_allocator.allocate(s));
+ char *buffer=op.data.back();
+#endif
+ if(op.write)
+ {
+ for(size_t x=0; x<s; x+=4)
+#ifndef DEBUG_TORTURE_TEST
+ *(u4 *)(buffer+thisbytes+x)=ranval(&writeseed);
+#else
+ *(u4 *)(buffer+x)=fillvalue;
+ memcpy((char *)((size_t)towriteptrs[n]+(size_t) op.req.where+thisbytes), buffer, s);
+ buffertouse=buffer;
+#endif
+ }
+#ifdef DEBUG_TORTURE_TEST
+ else
+ memcpy(buffer, (char *)((size_t)towriteptrs[n]+(size_t) op.req.where+thisbytes), s);
+#endif
+ thisbytes+=s;
+ op.req.buffers.push_back(asio::mutable_buffer(buffertouse, s));
+ }
+#ifndef DEBUG_TORTURE_TEST
+ if(!op.write)
+ {
+ op.hash=Hash256();
+ op.hash.AddSHA256To((const char *)(((size_t)towriteptrs[n]+(size_t) op.req.where)), thisbytes);
+ //__sha256_osol((const char *)(((size_t)towriteptrs[n]+(size_t) op.req.where)), thisbytes);
+#ifdef _DEBUG
+ std::cout << "<=SHA256 of " << thisbytes << " bytes at " << op.req.where << " is " << op.hash.asHexString() << std::endl;
+#endif
+ }
+#endif
+#ifdef _DEBUG
+ // Quickly make sure none of these exceed 10Mb
+ off_t end=op.req.where;
+ for(auto &b: op.req.buffers)
+ end+=asio::buffer_size(b);
+ assert(end<=bytes);
+#endif
+ todo[n].push_back(std::move(op));
+ bytessofar+=thisbytes;
+ }
+ }
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to simulate torture test in RAM" << std::endl;
+ begin=chrono::high_resolution_clock::now(); // start timer for hashes
+
+ // a vector to hold the hash values from SpookyHash
+ //SpookyHash returns 2 64bit integers for a 128 bit hash, so we store them as a pair
+ std::vector<std::pair<uint64, uint64>> memhashes(no);
+ // variables to seed and return the hashed values
+ uint64 hash1, hash2, seed;
+ seed = 1; //initialize the seed value. Completely arbitrary, but it needs to remain consistent
+
+ for(size_t i = 0; i < no; ++i)
+ {
+ // set up seeds and a variables to store hash values
+ hash1 = seed;
+ hash2 = seed;
+
+ //hash the data from towriteptrs
+ SpookyHash::Hash128(towriteptrs[i], towritesizes[i], &hash1, &hash2);
+
+ // store the hash values for this data in memhashes for later comparison
+ memhashes[i]= std::make_pair(hash1, hash2);
+
+ }
+
+ end=chrono::high_resolution_clock::now(); // end timer for hashes
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to hash the results" << std::endl;
+ for(size_t n=0; n<no; n++)
+ memset(towriteptrs[n], 0, towritesizes[n]);
+
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ // Wait for three seconds to let filing system recover and prime SpeedStep
+ //begin=chrono::high_resolution_clock::now();
+ //while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ // Open our test files
+ begin=chrono::high_resolution_clock::now();
+ std::vector<path_req> manyfilereqs;
+ manyfilereqs.reserve(no);
+ for(size_t n=0; n<no; n++)
+ manyfilereqs.push_back(path_req::relative(mkdir, to_string(n), file_flags::create|file_flags::read_write));
+ auto manyopenfiles(dispatcher->file(manyfilereqs));
+ std::vector<off_t> sizes(no, bytes);
+ auto manywrittenfiles(dispatcher->truncate(manyopenfiles, sizes));
+#if defined(_DEBUG) && 0
+ for(size_t n=0; n<manywrittenfiles.size(); n++)
+ std::cout << n << ": " << manywrittenfiles[n].id << " (" << when_all_p(manywrittenfiles[n]).get().front()->path() << ") " << std::endl;
+#endif
+ // Schedule a replay of our in-RAM simulation
+
+ spinlock<size_t> failureslock;
+ std::deque<std::pair<const Op *, size_t>> failures;
+ auto checkHash=[&failureslock, &failures](Op &op, char *base, size_t, future<> _h) -> std::pair<bool, handle_ptr> {
+ const char *data=(const char *)(((size_t) base+(size_t) op.req.where));
+ size_t idxoffset=0;
+
+ for(size_t m=0; m<op.req.buffers.size(); m++)
+ {
+ const char *buffer=op.data[m];
+ size_t idx;
+ for(idx=0; idx < asio::buffer_size(op.req.buffers[m]); idx++)
+ {
+ if(data[idx]!=buffer[idx])
+ {
+ {
+ lock_guard<decltype(failureslock)> h(failureslock);
+ failures.push_back(std::make_pair(&op, idxoffset+idx));
+ }
+#ifdef _DEBUG
+ std::string contents(data, 8), shouldbe(buffer, 8);
+ std::cout << "Contents of file at " << op.req.where << " +" << idx << " contains " << contents << " instead of " << shouldbe << std::endl;
+#endif
+ break;
+ }
+ }
+ if(idx!=asio::buffer_size(op.req.buffers[m])) break;
+ data+=asio::buffer_size(op.req.buffers[m]);
+ idxoffset+=asio::buffer_size(op.req.buffers[m]);
+ }
+ return std::make_pair(true, _h.get_handle());
+ };
+#pragma omp parallel for
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ {
+ for(Op &op: todo[n])
+ {
+ op.req.precondition=manywrittenfiles[n];
+ if(op.write)
+ {
+ manywrittenfiles[n]=dispatcher->write(op.req);
+ }
+ else
+ manywrittenfiles[n]=dispatcher->completion(dispatcher->read(op.req), std::pair<async_op_flags, std::function<dispatcher::completion_t>>(async_op_flags::none, std::bind(checkHash, std::ref(op), towriteptrs[n], std::placeholders::_1, std::placeholders::_2)));
+ }
+ // After replay, read the entire file into memory
+ manywrittenfiles[n]=dispatcher->read(io_req<char>(manywrittenfiles[n], towriteptrs[n], towritesizes[n], 0));
+ }
+
+ // Close each of those files
+ auto manyclosedfiles(dispatcher->close(manywrittenfiles));
+ auto dispatched=chrono::high_resolution_clock::now();
+ std::cout << "There are now " << std::dec << dispatcher->fd_count() << " handles open with a queue depth of " << dispatcher->wait_queue_depth() << std::endl;
+
+ // Wait for all files to open
+ when_all_p(manyopenfiles.begin(), manyopenfiles.end()).get();
+ auto openedsync=chrono::high_resolution_clock::now();
+ // Wait for all files to write
+ when_all_p(manywrittenfiles.begin(), manywrittenfiles.end()).get();
+ auto writtensync=chrono::high_resolution_clock::now();
+ // Wait for all files to close
+ when_all_p(manyclosedfiles.begin(), manyclosedfiles.end()).get();
+ auto closedsync=chrono::high_resolution_clock::now();
+ end=closedsync;
+
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to do all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(dispatched-begin);
+ std::cout << " It took " << diff.count() << " secs to dispatch all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(end-dispatched);
+ std::cout << " It took " << diff.count() << " secs to finish all operations" << std::endl << std::endl;
+ off_t readed=0, written=0;
+ size_t ops=0;
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ for(auto &i: manyclosedfiles)
+ {
+ readed+=when_all_p(i).get().front()->read_count();
+ written+=when_all_p(i).get().front()->write_count();
+ }
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ ops+=todo[n].size();
+ std::cout << "We read " << readed << " bytes and wrote " << written << " bytes during " << ops << " operations." << std::endl;
+ std::cout << " That makes " << (readed+written)/diff.count()/1024/1024 << " Mb/sec" << std::endl;
+
+ diff=chrono::duration_cast<secs_type>(openedsync-begin);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file opens per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(writtensync-openedsync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file reads and writes per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(closedsync-writtensync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file closes per sec" << std::endl;
+
+ BOOST_CHECK(failures.empty());
+ if(!failures.empty())
+ {
+ std::cerr << "The following hash failures occurred:" << std::endl;
+ while(!failures.empty())
+ {
+ std::pair<const Op *, size_t> &failedop=failures.front();
+ size_t _bytes=0;
+ for(auto &b: failedop.first->req.buffers)
+ _bytes+=asio::buffer_size(b);
+ std::cerr << " " << (failedop.first->write ? "Write to" : "Read from") << " " << to_string(failedop.first->req.where) << " at offset " << failedop.second << " into bytes " << _bytes << std::endl;
+ failures.pop_front();
+ }
+ }
+ BOOST_TEST_MESSAGE("Checking if the final files have exactly the right contents ... this may take a bit ...");
+ std::cout << "Checking if the final files have exactly the right contents ... this may take a bit ..." << std::endl;
+ {
+ // a vector for holding hash results from SpookyHash
+ //SpookyHash returns 2 64bit integers for a 128 bit hash, so we store them as a pair
+ std::vector<std::pair<uint64, uint64>> filehashes(no);
+
+ for(size_t i = 0; i < no; ++i)
+ {
+ // set up seeds and a variables to store hash values
+ hash1 = seed;
+ hash2 = seed;
+
+ // hash the data from towriteptrs
+ SpookyHash::Hash128(towriteptrs[i], towritesizes[i], &hash1, &hash2);
+
+ // store the hash values for this data in filehashes for a later comparison
+ filehashes[i]= std::make_pair(hash1, hash2);
+
+ }
+ for(size_t n=0; n<no; n++)
+ if(memhashes[n]!=filehashes[n]) // compare hash values from ram and actual IO
+ {
+ std::string failmsg("File "+to_string(n)+" contents were not what they were supposed to be!");
+ BOOST_TEST_MESSAGE(failmsg.c_str());
+ std::cerr << failmsg << std::endl;
+ }
+ }
+#ifdef DEBUG_TORTURE_TEST
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ {
+ for(Op &op: todo[n])
+ {
+ for(auto &i: op.data)
+ aligned_allocator.deallocate(i, 0);
+ }
+ }
+#endif
+ // Delete each of those files once they are closed
+ auto it(manyclosedfiles.begin());
+ for(auto &i: manyfilereqs)
+ i.precondition=dispatcher->depends(*it++, mkdir);
+ auto manydeletedfiles(dispatcher->rmfile(manyfilereqs));
+ // Wait for all files to delete
+ when_all_p(manydeletedfiles.begin(), manydeletedfiles.end()).get();
+ auto rmdir(dispatcher->rmdir(path_req("testdir")));
+ // Fetch any outstanding error
+ when_all_p(rmdir).get();
+}
+
+
+static std::ostream &operator<<(std::ostream &s, const chrono::system_clock::time_point &ts)
+{
+ char buf[64];
+ struct tm *t;
+ size_t len=sizeof(buf);
+ size_t ret;
+ time_t v=chrono::system_clock::to_time_t(ts);
+ chrono::system_clock::duration remainder(ts-chrono::system_clock::from_time_t(v));
+
+#ifdef _MSC_VER
+ _tzset();
+#else
+ tzset();
+#endif
+ if ((t=localtime(&v)) == NULL)
+ {
+ s << "<bad timespec>";
+ return s;
+ }
+
+ ret = strftime(buf, len, "%Y-%m-%d %H:%M:%S", t);
+ if (ret == 0)
+ {
+ s << "<bad timespec>";
+ return s;
+ }
+ //len -= ret - 1;
+
+ size_t end=strlen(buf);
+ sprintf(&buf[end], "%f", remainder.count()/((double) chrono::system_clock::period::den / chrono::system_clock::period::num));
+ memmove(&buf[end], &buf[end+1], strlen(&buf[end]));
+ s << buf;
+
+ return s;
+}
+
+static stat_t print_stat(handle_ptr h)
+{
+ using namespace boost::afio;
+ auto entry=h->lstat(metadata_flags::All);
+ std::cout << "Entry " << h->path(true) << " is a ";
+ switch(entry.st_type)
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ case filesystem::file_type::symlink_file:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory_file:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular_file:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#else
+ case filesystem::file_type::symlink:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#endif
+ }
+ std::cout << " and it has the following information:" << std::endl;
+ if(!h->path().empty())
+ {
+ std::cout << " Normalised path: " << normalise_path(h->path()) << std::endl;
+ std::cout << " Normalised path with volume GUID: " << normalise_path(h->path(), path_normalise::guid_volume) << std::endl;
+ std::cout << " Normalised path as GUID: ";
+ try
+ {
+ std::cout << normalise_path(h->path(), path_normalise::guid_all) << std::endl;
+ }
+ catch (...)
+ {
+ std::cout << "EXCEPTION THROWN!" << std::endl;
+ }
+ }
+ if(
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ filesystem::file_type::symlink_file
+#else
+ filesystem::file_type::symlink
+#endif
+ ==entry.st_type)
+ {
+ std::cout << " Target=" << h->target() << std::endl;
+ }
+#define PRINT_FIELD(field, ...) \
+ std::cout << " st_" #field ": "; if(!!(directory_entry::metadata_supported()&metadata_flags::field)) std::cout << __VA_ARGS__ entry.st_##field; else std::cout << "unknown"; std::cout << std::endl
+#ifndef WIN32
+ PRINT_FIELD(dev);
+#endif
+ PRINT_FIELD(ino);
+ PRINT_FIELD(type, (int));
+#ifndef WIN32
+ PRINT_FIELD(perms);
+#endif
+ PRINT_FIELD(nlink);
+#ifndef WIN32
+ PRINT_FIELD(uid);
+ PRINT_FIELD(gid);
+ PRINT_FIELD(rdev);
+#endif
+ PRINT_FIELD(atim);
+ PRINT_FIELD(mtim);
+ PRINT_FIELD(ctim);
+ PRINT_FIELD(size);
+ PRINT_FIELD(allocated);
+ PRINT_FIELD(blocks);
+ PRINT_FIELD(blksize);
+ PRINT_FIELD(flags);
+ PRINT_FIELD(gen);
+ PRINT_FIELD(birthtim);
+ PRINT_FIELD(sparse);
+ PRINT_FIELD(compressed);
+ PRINT_FIELD(reparse_point);
+#undef PRINT_FIELD
+ return entry;
+}
+
+static void print_stat(handle_ptr dirh, directory_entry direntry)
+{
+ using namespace boost::afio;
+ std::cout << "Entry " << direntry.name() << " is a ";
+ auto entry=direntry.fetch_lstat(dirh);
+ switch(entry.st_type)
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ case filesystem::file_type::symlink_file:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory_file:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular_file:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#else
+ case filesystem::file_type::symlink:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#endif
+ }
+ std::cout << " and it has the following information:" << std::endl;
+
+#define PRINT_FIELD(field, ...) \
+ std::cout << " st_" #field ": "; if(!!(direntry.metadata_ready()&metadata_flags::field)) std::cout << __VA_ARGS__ entry.st_##field; else std::cout << "unknown"; std::cout << std::endl
+#ifndef WIN32
+ PRINT_FIELD(dev);
+#endif
+ PRINT_FIELD(ino);
+ PRINT_FIELD(type, (int));
+#ifndef WIN32
+ PRINT_FIELD(perms);
+#endif
+ PRINT_FIELD(nlink);
+#ifndef WIN32
+ PRINT_FIELD(uid);
+ PRINT_FIELD(gid);
+ PRINT_FIELD(rdev);
+#endif
+ PRINT_FIELD(atim);
+ PRINT_FIELD(mtim);
+ PRINT_FIELD(ctim);
+ PRINT_FIELD(size);
+ PRINT_FIELD(allocated);
+ PRINT_FIELD(blocks);
+ PRINT_FIELD(blksize);
+ PRINT_FIELD(flags);
+ PRINT_FIELD(gen);
+ PRINT_FIELD(birthtim);
+ PRINT_FIELD(sparse);
+ PRINT_FIELD(compressed);
+ PRINT_FIELD(reparse_point);
+#undef PRINT_FIELD
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#endif