foundationdb/fdbrpc/dsltest.actor.cpp

1510 lines
36 KiB
C++

/*
* dsltest.actor.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include <algorithm>
#include "flow/FastRef.h"
#undef ERROR
#include "fdbrpc/simulator.h"
#include "fdbrpc/ActorFuzz.h"
#include "flow/DeterministicRandom.h"
#include "flow/ThreadHelper.actor.h"
#include "flow/actorcompiler.h" // This must be the last #include.
void* allocateLargePages(int total);
bool testFuzzActor(Future<int> (*actor)(FutureStream<int> const&, PromiseStream<int> const&, Future<Void> const&),
const char* desc,
std::vector<int> const& expectedOutput) {
// Run the test 5 times with different "timing"
int i, outCount;
bool ok = true;
for (int trial = 0; trial < 5; trial++) {
PromiseStream<int> in, out;
Promise<Void> err;
int before = deterministicRandom()->randomInt(0, 4);
int errorBefore = before + deterministicRandom()->randomInt(0, 4);
// printf("\t\tTrial #%d: %d, %d\n", trial, before, errorBefore);
if (errorBefore <= before)
err.sendError(operation_failed());
for (i = 0; i < before; i++) {
in.send((i + 1) * 1000);
}
Future<int> ret = (*actor)(in.getFuture(), out, err.getFuture());
while (i < 1000000 && !ret.isReady()) {
i++;
if (errorBefore == i)
err.sendError(operation_failed());
in.send(i * 1000);
}
if (ret.isReady()) {
if (ret.isError())
out.send(ret.getError().code());
else
out.send(ret.get());
} else {
printf("\tERROR: %s did not return after consuming %d input values\n", desc, i);
if (trial)
printf("\t\tResult was inconsistent between runs! (Trial %d)\n", trial);
ok = false;
// return false;
}
outCount = -1;
while (out.getFuture().isReady()) {
int o = out.getFuture().pop();
outCount++;
if (outCount < expectedOutput.size() && expectedOutput[outCount] != o) {
printf("\tERROR: %s output #%d incorrect: %d != expected %d\n",
desc,
outCount,
o,
expectedOutput[outCount]);
if (trial)
printf("\t\tResult was inconsistent between runs!\n");
ok = false;
// return false;
}
}
if (outCount + 1 != expectedOutput.size()) {
printf(
"\tERROR: %s output length incorrect: %d != expected %zu\n", desc, outCount + 1, expectedOutput.size());
if (trial)
printf("\t\tResult was inconsistent between runs!\n");
ok = false;
// return false;
}
// We might have put in values that weren't actually consumed...
while (in.getFuture().isReady()) {
in.getFuture().pop();
i--;
}
}
// printf("\t%s: OK, %d input values -> %d output values\n", desc, i, outCount);
return ok;
}
#if 0
void memoryTest2() {
const int Size = 2000 << 20;
const int Reads = 4 << 20;
const int MaxThreads = 4;
char* block = new char[Size];
memset(block, 0, Size);
char** random = new char*[ Reads * MaxThreads ];
random[0] = block;
for(int i=1; i<Reads*MaxThreads; ) {
char *s = &block[ deterministicRandom()->randomInt(0, Size) ];
random[i++] = s;
/*for(int j=0; j<10 && i<Reads*MaxThreads; j++,i++) {
random[i] = s + deterministicRandom()->randomInt(0, 4096);
if (random[i] >= block+Size) random[i] -= Size;
}*/
}
for(int threads=1; threads<=MaxThreads; threads++) {
double tstart = timer();
std::vector<ThreadFuture<Void>> done;
for(int t=0; t<threads; t++) {
char** r = random + Reads*t;
done.push_back(
inThread<Void>( [r,Reads] () -> Void {
for(int i=0; i<Reads; i++)
if ( *r[i] )
std::cout << "Does not happen" << std::endl;
return Void();
}));
}
waitForAll(done).getBlocking();
double duration = timer() - tstart;
std::cout << format("%d threads: %f sec, %0.2fM/sec", threads, duration, Reads*threads/1e6/duration) << std::endl;
}
}
#endif
enum { MaxTraversalsPerThread = 64 };
void showNumaStatus();
void* numaAllocate(size_t size);
#if 0
void memoryTest() {
//memoryTest2();
//return;
showNumaStatus();
const int N = 128<<20; // 128 = 1GB
const int N2 = 8<<20;
std::cout << "Preparing memory test with " << N / 1e6 * sizeof(void*) << " MB" << std::endl;
void **x;
if (0) {
std::cout << " NUMA large pages" << std::endl;
x = (void**)numaAllocate(size_t(N)*sizeof(void*));
} else if (1) {
std::cout << " Normal pages" << std::endl;
x = new void*[ N ];
printf(" at %p\n", x);
} else {
std::cout << " Large pages" << std::endl;
x = (void**)allocate(N*sizeof(void*), true);
}
memset(x, 0, ((int64_t)N) * sizeof(void*));
showNumaStatus();
if (1) {
std::cout <<" Random permutation" << std::endl;
// Random cyclic permutation
for(int i=0; i<N; i++)
x[i] = &x[i];
// Sattolo's algorithm
for(int n = N-1; n >= 1; n--) {
int k = deterministicRandom()->randomInt(0, n); //random.IRandomX(0, n-1);
std::swap( x[k], x[n] );
}
} else {
std::cout <<" Sequential permutation" << std::endl;
// Sequential
for(int i=0; i<N-1; i++)
x[i] = &x[i+1];
x[N-1] = &x[0];
}
void **p = x;
for(int i=0; i<N; i++) {
p = (void**)*p;
if (p == x) {
std::cout << "Cycle " << i << std::endl;
if (i != N-1) terminate();
}
}
const int MT = 16;
for(int TraversalsPerThread = 1; TraversalsPerThread <= MaxTraversalsPerThread; TraversalsPerThread *= 2)
{
const int PseudoThreads = MT * TraversalsPerThread;
void **starts[MT*MaxTraversalsPerThread];
for(int t=0; t<PseudoThreads; t++) {
starts[t] = &x[ N/PseudoThreads * t ];
//starts[t] = &x[ deterministicRandom()->randomInt(0,N) ];
}
for(int T=1; T<=MT; T+=T) {
double start = timer();
std::vector< Future<double> > done;
for(int t=0; t<T; t++) {
void*** start = starts + t*TraversalsPerThread;
done.push_back(
inThread<double>( [start,N2,TraversalsPerThread] () -> double {
void **p[MaxTraversalsPerThread];
for(int j=0; j<TraversalsPerThread; j++)
p[j] = start[j];
for(int i=0; i<N2; i++)
for(int j=0; j<TraversalsPerThread; j++) {
p[j] = (void**)*p[j];
if (TraversalsPerThread > 1)
_mm_prefetch( (const char*)p[j], _MM_HINT_T0 );
}
for(int j=0; j<TraversalsPerThread; j++)
if (p[j] == p[(j+1)%TraversalsPerThread])
std::cout << "N";
return timer();
}));
}
double firstEnd = 1e30;
for(int t=0; t<T; t++) {
done[t].getBlocking();
firstEnd = std::min(firstEnd, done[t].get());
}
double end = timer();
printf(" %2dx%2d traversals: %5.3fs, %6.1f M/sec, %4.1f%%\n", T, (int)TraversalsPerThread, end-start,
N2 / 1e6 * (T*TraversalsPerThread) / (end-start),
(firstEnd-start)/(end-start)*100.0);
}
}
//delete[] x; // TODO: Free large pages
}
#endif
ACTOR template <int N, class X>
[[flow_allow_discard]] Future<X> addN(Future<X> in) {
X i = wait(in);
return i + N;
}
ACTOR template <class A, class B>
[[flow_allow_discard]] Future<Void> switchTest(FutureStream<A> as, Future<B> oneb) {
loop choose {
when(A a = waitNext(as)) { std::cout << "A " << a << std::endl; }
when(B b = wait(oneb)) {
std::cout << "B " << b << std::endl;
break;
}
}
loop {
std::cout << "Done!" << std::endl;
return Void();
}
}
class TestBuffer : public ReferenceCounted<TestBuffer> {
public:
static TestBuffer* create(int length) {
#if defined(__INTEL_COMPILER)
return new TestBuffer(length);
#else
auto b = (TestBuffer*)new int[(length + 7) / 4];
new (b) TestBuffer(length);
return b;
#endif
}
#if !defined(__INTEL_COMPILER)
void operator delete(void* buf) {
std::cout << "Freeing buffer" << std::endl;
delete[](int*) buf;
}
#endif
int size() const { return length; }
uint8_t* begin() { return data; }
uint8_t* end() { return data + length; }
const uint8_t* begin() const { return data; }
const uint8_t* end() const { return data + length; }
private:
TestBuffer(int length) noexcept : length(length) {}
int length;
uint8_t data[1];
};
int fastKeyCount = 0;
class FastKey : public FastAllocated<FastKey>, public ReferenceCounted<FastKey> {
public:
FastKey() : length(0) {}
FastKey(char* b, int length) : length(length) {
ASSERT(length <= sizeof(data));
memcpy(data, b, length);
}
~FastKey() { fastKeyCount++; }
int size() const { return length; }
uint8_t* begin() { return data; }
uint8_t* end() { return data + length; }
const uint8_t* begin() const { return data; }
const uint8_t* end() const { return data + length; }
private:
int length;
uint8_t data[252];
};
struct TestB : FastAllocated<TestB> {
char x[65];
};
void fastAllocTest() {
double t;
std::vector<void*> d;
for (int i = 0; i < 1000000; i++) {
d.push_back(FastAllocator<64>::allocate());
int r = deterministicRandom()->randomInt(0, 1000000);
if (r < d.size()) {
FastAllocator<64>::release(d[r]);
d[r] = d.back();
d.pop_back();
}
}
std::sort(d.begin(), d.end());
if (std::unique(d.begin(), d.end()) != d.end())
std::cout << "Pointer returned twice!?" << std::endl;
for (int i = 0; i < 2; i++) {
void* p = FastAllocator<64>::allocate();
void* q = FastAllocator<64>::allocate();
std::cout << (intptr_t)p << " " << (intptr_t)q << std::endl;
FastAllocator<64>::release(p);
FastAllocator<64>::release(q);
}
t = timer();
for (int i = 0; i < 1000000; i++)
(void)FastAllocator<64>::allocate();
t = timer() - t;
std::cout << "Allocations: " << (1 / t) << "M/sec" << std::endl;
t = timer();
for (int i = 0; i < 1000000; i++)
FastAllocator<64>::release(FastAllocator<64>::allocate());
t = timer() - t;
std::cout << "Allocate/Release pairs: " << (1 / t) << "M/sec" << std::endl;
t = timer();
void* pp[100];
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 100; j++)
pp[j] = FastAllocator<64>::allocate();
for (int j = 0; j < 100; j++)
FastAllocator<64>::release(pp[j]);
}
t = timer() - t;
std::cout << "Allocate/Release interleaved(100): " << (1 / t) << "M/sec" << std::endl;
t = timer();
for (int i = 0; i < 1000000; i++)
delete new TestB;
t = timer() - t;
std::cout << "Allocate/Release TestB pairs: " << (1 / t) << "M/sec" << std::endl;
#if FLOW_THREAD_SAFE
t = timer();
std::vector<Future<bool>> results;
for (int i = 0; i < 4; i++)
results.push_back(inThread<bool>([]() -> bool {
TestB* pp[100];
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 100; j++)
pp[j] = new TestB;
for (int j = 0; j < 100; j++)
delete pp[j];
}
return true;
}));
waitForAll(results).getBlocking();
t = timer() - t;
std::cout << "Threaded Allocate/Release TestB interleaved (100): " << results.size() << " x " << (1 / t) << "M/sec"
<< std::endl;
#endif
volatile int32_t v = 0;
t = timer();
for (int i = 0; i < 10000000; i++)
interlockedIncrement(&v);
t = timer() - t;
std::cout << "interlocked increment: " << 10.0 / t << "M/sec " << v << std::endl;
v = 5;
t = timer();
for (int i = 0; i < 10000000; i++) {
interlockedCompareExchange(&v, 5, 5);
}
t = timer() - t;
std::cout << "1 state machine: " << 10.0 / t << "M/sec " << v << std::endl;
v = 0;
t = timer();
for (int i = 0; i < 10000000; i++)
v++;
t = timer() - t;
std::cout << "volatile increment: " << 10.0 / t << "M/sec " << v << std::endl;
{
Reference<TestBuffer> b(TestBuffer::create(1000));
memcpy(b->begin(), "Hello, world!", 14);
t = timer();
for (int i = 0; i < 10000000; i++) {
Reference<TestBuffer> r = std::move(b);
b = std::move(r);
}
t = timer() - t;
std::cout << "move Reference<Buffer>: " << 10.0 / t << "M/sec " << std::endl;
t = timer();
for (int i = 0; i < 10000000; i++) {
Reference<TestBuffer> r = b;
}
t = timer() - t;
std::cout << "copy (1) Reference<Buffer>: " << 10.0 / t << "M/sec " << std::endl;
Reference<TestBuffer> c = b;
t = timer();
for (int i = 0; i < 10000000; i++) {
Reference<TestBuffer> r = b;
}
t = timer() - t;
std::cout << "copy (2) Reference<Buffer>: " << 10.0 / t << "M/sec " << std::endl;
std::cout << (const char*)b->begin() << std::endl;
}
t = timer();
for (int i = 0; i < 10000000; i++) {
delete new FastKey;
}
t = timer() - t;
std::cout << "delete new FastKey: " << 10.0 / t << "M/sec " << fastKeyCount << std::endl;
t = timer();
for (int i = 0; i < 10000000; i++) {
Reference<FastKey> r(new FastKey);
}
t = timer() - t;
std::cout << "new Reference<FastKey>: " << 10.0 / t << "M/sec " << fastKeyCount << std::endl;
}
template <class PromiseT>
Future<Void> threadSafetySender(std::vector<PromiseT>& v, Event& start, Event& ready, int iterations) {
for (int i = 0; i < iterations; i++) {
start.block();
if (v.size() == 0)
return Void();
for (int i = 0; i < v.size(); i++)
v[i].send(Void());
ready.set();
}
return Void();
}
ACTOR [[flow_allow_discard]] void threadSafetyWaiter(Future<Void> f, int32_t* count) {
wait(f);
interlockedIncrement(count);
}
ACTOR [[flow_allow_discard]] void threadSafetyWaiter(FutureStream<Void> f, int n, int32_t* count) {
while (n--) {
waitNext(f);
interlockedIncrement(count);
}
}
#if 0
void threadSafetyTest() {
double t = timer();
int N = 10000, V = 100;
std::vector<Promise<Void>> v;
Event start, ready;
Future<Void> sender = inThread<Void>( [&] { return threadSafetySender( v, start, ready, N ); } );
for(int i=0; i<N; i++) {
v.clear();
for (int j = 0; j < V; j++)
v.push_back(Promise<Void>());
std::vector<Future<Void>> f( v.size() );
for(int i=0; i<v.size(); i++)
f[i] = v[i].getFuture();
std::random_shuffle( f.begin(), f.end() );
start.set();
int32_t count = 0;
for(int i=0; i<f.size(); i++)
threadSafetyWaiter( f[i], &count );
ready.block();
if (count != V)
std::cout << "Thread safety error: " << count << std::endl;
}
t = timer()-t;
std::cout << "Thread safety test (2t): " << (V*N/1e6/t) << "M/sec" << std::endl;
}
void threadSafetyTest2() {
double t = timer();
int N = 1000, V = 100;
// std::vector<PromiseStream<Void>> streams( 100 );
std::vector<PromiseStream<Void>> streams;
for (int i = 0; i < 100; i++)
streams.push_back(PromiseStream<Void>());
std::vector<PromiseStream<Void>> v;
Event start, ready;
Future<Void> sender = inThread<Void>( [&] { return threadSafetySender( v, start, ready, N ); } );
for(int i=0; i<N; i++) {
std::vector<int> counts( streams.size() );
v.clear();
for(int k=0; k<V; k++) {
int i = deterministicRandom()->randomInt(0, (int)streams.size());
counts[i]++;
v.push_back( streams[i] );
}
start.set();
int32_t count = 0;
for(int i=0; i<streams.size(); i++)
threadSafetyWaiter( streams[i].getFuture(), counts[i], &count );
ready.block();
if (count != V)
std::cout << "Thread safety error: " << count << std::endl;
}
t = timer()-t;
std::cout << "Thread safety test 2 (2t): " << (V*N/1e6/t) << "M/sec" << std::endl;
}
volatile int32_t cancelled = 0, returned = 0;
ACTOR [[flow_allow_discard]] Future<Void> returnCancelRacer( Future<Void> f ) {
try {
wait(f);
} catch ( Error& ) {
interlockedIncrement( &cancelled );
throw;
}
interlockedIncrement( &returned );
return Void();
}
void returnCancelRaceTest() {
int N = 100, M = 100;
for(int i=0; i<N; i++) {
std::vector< Promise<Void> > promises;
std::vector< Future<Void> > futures;
for(int i=0; i < M; i++) {
promises.push_back( Promise<Void>() );
futures.push_back( returnCancelRacer( promises.back().getFuture() ) );
}
std::random_shuffle( futures.begin(), futures.end() );
// FIXME: Doesn't work as written with auto-reset
// events. Probably not particularly racy as written. Test may
// FAIL or PASS at whim.
Event ev1, ev2;
ThreadFuture<Void> b = inThread<Void>( [&] ()->Void {
ev1.block();
for(int i=0; i<promises.size(); i++)
futures[i] = Future<Void>();
return Void();
} );
ThreadFuture<Void> a = inThread<Void>([&]()->Void {
ev2.block();
for(int i=0; i<promises.size(); i++) {
promises[i].send(Void());
for( volatile int32_t dummy = 0; dummy < 10; dummy ++ );
}
return Void();
} );
ev1.set(); ev2.set();
a.getBlocking();
b.getBlocking();
}
bool ok = cancelled && returned && cancelled+returned == N*M;
printf("ReturnCancelRaceTest: %s\n", ok ? "PASS" : "FAIL");
printf(" %d cancelled, %d returned\n", cancelled, returned);
}
#endif
ACTOR [[flow_allow_discard]] Future<int> chooseTest(Future<int> a, Future<int> b) {
choose {
when(int A = wait(a)) { return A; }
when(int B = wait(b)) { return B; }
}
}
void showArena(ArenaBlock* a, ArenaBlock* parent) {
printf("ArenaBlock %p (<-%p): %d bytes, %d refs\n", a, parent, a->size(), a->debugGetReferenceCount());
if (!a->isTiny()) {
int o = a->nextBlockOffset;
while (o) {
ArenaBlockRef* r = (ArenaBlockRef*)((char*)a->getData() + o);
// If alignedBuffer is valid then print its pointer and size, else recurse
if (r->aligned4kBufferSize != 0) {
printf("AlignedBuffer %p (<-%p) %u bytes\n", r->aligned4kBuffer, a, r->aligned4kBufferSize);
} else {
showArena(r->next, a);
}
o = r->nextBlockOffset;
}
}
}
void arenaTest() {
BinaryWriter wr(AssumeVersion(g_network->protocolVersion()));
{
Arena arena;
VectorRef<StringRef> test;
test.push_back(arena, StringRef(arena, LiteralStringRef("Hello")));
test.push_back(arena, StringRef(arena, LiteralStringRef(", ")));
test.push_back(arena, StringRef(arena, LiteralStringRef("World!")));
for (auto i = test.begin(); i != test.end(); ++i)
for (auto j = i->begin(); j != i->end(); ++j)
std::cout << *j;
std::cout << std::endl;
wr << test;
}
{
Arena arena2;
VectorRef<StringRef> test2;
BinaryReader reader(wr.getData(), wr.getLength(), AssumeVersion(g_network->protocolVersion()));
reader >> test2 >> arena2;
for (auto i = test2.begin(); i != test2.end(); ++i)
for (auto j = i->begin(); j != i->end(); ++j)
std::cout << *j;
std::cout << std::endl;
}
double t = timer();
for (int i = 0; i < 100; i++) {
Arena ar;
for (int i = 0; i < 10000000; i++)
new (ar) char[10];
}
printf("100 x 10M x 10B allocated+freed from Arenas: %f sec\n", timer() - t);
// printf("100M x 8bytes allocations: %d bytes used\n", 0);//ar.getSize());
// showArena( ar.impl.getPtr(), 0 );
};
ACTOR [[flow_allow_discard]] void testStream(FutureStream<int> xs) {
loop {
int x = waitNext(xs);
std::cout << x << std::endl;
}
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest1(bool b) {
printf("1");
if (b)
throw future_version();
return Void();
}
ACTOR [[flow_allow_discard]] void actorTest2(bool b) {
printf("2");
if (b)
throw future_version();
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest3(bool b) {
try {
if (b)
throw future_version();
} catch (Error&) {
printf("3");
return Void();
}
printf("\nactorTest3 failed\n");
return Void();
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest4(bool b) {
state double tstart = now();
try {
if (b)
throw operation_failed();
} catch (...) {
wait(delay(1));
}
if (now() < tstart + 1)
printf("actorTest4 failed");
else
printf("4");
return Void();
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest5() {
state bool caught = false;
loop {
loop {
state bool inloop = false;
if (caught) {
printf("5");
return true;
}
try {
loop {
if (inloop) {
printf("\nactorTest5 failed\n");
return false;
}
inloop = true;
if (1)
throw operation_failed();
}
} catch (Error&) {
caught = true;
}
}
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest6() {
state bool caught = false;
loop {
if (caught) {
printf("6");
return true;
}
try {
if (1)
throw operation_failed();
} catch (Error&) {
caught = true;
}
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest7() {
try {
loop {
loop {
if (1)
throw operation_failed();
if (1) {
printf("actorTest7 failed (1)\n");
return false;
}
if (0)
break;
}
if (1) {
printf("actorTest7 failed (2)\n");
return false;
}
}
} catch (Error&) {
printf("7");
return true;
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest8() {
state bool caught = false;
state Future<bool> set = true;
loop {
state bool inloop = false;
if (caught) {
printf("8");
return true;
}
try {
loop {
if (inloop) {
printf("\nactorTest8 failed\n");
return false;
}
bool b = wait(set);
inloop = true;
if (1)
throw operation_failed();
}
} catch (Error&) {
caught = true;
}
}
}
ACTOR [[flow_allow_discard]] Future<bool> actorTest9A(Future<Void> setAfterCalling) {
state int count = 0;
loop {
if (count == 4) {
printf("9");
return true;
}
if (count && count != 4) {
printf("\nactorTest9 failed\n");
return false;
}
loop {
loop {
wait(setAfterCalling);
loop {
loop {
count++;
break;
}
wait(Future<Void>(Void()));
count++;
break;
}
count++;
break;
}
count++;
break;
}
// loopDepth < 0 ???
}
}
Future<bool> actorTest9() {
Promise<Void> p;
Future<bool> f = actorTest9A(p.getFuture());
p.send(Void());
return f;
}
ACTOR [[flow_allow_discard]] Future<Void> actorTest10A(FutureStream<int> inputStream, Future<Void> go) {
state int i;
for (i = 0; i < 5; i++) {
wait(go);
int input = waitNext(inputStream);
}
return Void();
}
void actorTest10() {
PromiseStream<int> ins;
Promise<Void> go;
for (int x = 0; x < 2; x++)
ins.send(x);
Future<Void> a = actorTest10A(ins.getFuture(), go.getFuture());
go.send(Void());
for (int x = 0; x < 3; x++)
ins.send(x);
if (!a.isReady())
printf("\nactorTest10 failed\n");
else
printf("10");
}
ACTOR [[flow_allow_discard]] Future<Void> cancellable() {
wait(Never());
return Void();
}
ACTOR [[flow_allow_discard]] Future<Void> simple() {
return Void();
}
ACTOR [[flow_allow_discard]] Future<Void> simpleWait() {
wait(Future<Void>(Void()));
return Void();
}
ACTOR [[flow_allow_discard]] Future<int> simpleRet(Future<int> x) {
int i = wait(x);
return i;
}
template <int i>
Future<int> chain(Future<int> const& x);
ACTOR template <int i>
[[flow_allow_discard]] Future<int> achain(Future<int> x) {
int k = wait(chain<i>(x));
return k + 1;
}
template <int i>
Future<int> chain(Future<int> const& x) {
return achain<i - 1>(x);
}
template <>
Future<int> chain<0>(Future<int> const& x) {
return x;
}
ACTOR [[flow_allow_discard]] Future<int> chain2(Future<int> x, int i);
ACTOR [[flow_allow_discard]] Future<int> chain2(Future<int> x, int i) {
if (i > 1) {
int k = wait(chain2(x, i - 1));
return k + 1;
} else {
int k = wait(x);
return k + i;
}
}
ACTOR [[flow_allow_discard]] Future<Void> cancellable2() {
try {
wait(Never());
return Void();
} catch (Error& e) {
throw;
}
}
ACTOR [[flow_allow_discard]] Future<int> introLoadValueFromDisk(Future<std::string> filename) {
std::string file = wait(filename);
if (file == "/dev/threes")
return 3;
else
ASSERT(false);
return 0; // does not happen
}
ACTOR [[flow_allow_discard]] Future<int> introAdd(Future<int> a, Future<int> b) {
state int x = wait(a);
int y = wait(b);
return x + y; // x would be undefined here if it was not "state"
}
ACTOR [[flow_allow_discard]] Future<int> introFirst(Future<int> a, Future<int> b) {
choose {
when(int x = wait(a)) { return x; }
when(int x = wait(b)) { return x; }
}
}
struct AddReply {
int sum;
AddReply() {}
AddReply(int x) : sum(x) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, sum);
}
};
struct AddRequest {
int a, b;
Promise<AddReply> reply; // Self-addressed envelope
AddRequest() {}
AddRequest(int a, int b) : a(a), b(b) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, a, b, reply);
}
};
ACTOR [[flow_allow_discard]] void introAddServer(PromiseStream<AddRequest> add) {
loop choose {
when(AddRequest req = waitNext(add.getFuture())) {
printf("%d + %d = %d\n", req.a, req.b, req.a + req.b);
req.reply.send(req.a + req.b);
}
}
}
void introPromiseFuture() {
Promise<int> myPromise;
Future<int> myFuture = myPromise.getFuture();
myPromise.send(12345);
ASSERT(myFuture.isReady() && myFuture.get() == 12345);
}
void introActor() {
Future<int> f = introLoadValueFromDisk(std::string("/dev/threes"));
ASSERT(f.get() == 3);
Promise<int> a, b;
Future<int> sum = introAdd(a.getFuture(), b.getFuture());
b.send(3);
ASSERT(!sum.isReady());
a.send(2);
ASSERT(sum.get() == 5);
Promise<int> c, d;
Future<int> first = introFirst(c.getFuture(), d.getFuture());
ASSERT(!first.isReady());
// d.send(100);
d.sendError(operation_failed());
ASSERT(first.isError() && first.getError().code() == error_code_operation_failed);
// ASSERT( first.getBlocking() == 100 );
PromiseStream<AddRequest> addInterface;
introAddServer(addInterface);
Future<AddReply> reply = addInterface.getReply(AddRequest(5, 2));
ASSERT(reply.get().sum == 7);
printf("OK\n");
}
template <int N>
void chainTest() {
auto startt = timer();
for (int i = 0; i < 100000; i++) {
Promise<int> p;
Future<int> f = chain<N>(p.getFuture());
p.send(i);
ASSERT(f.get() == i + N);
}
auto endt = timer();
printf("chain<%d>: %0.3f M/sec\n", N, 0.1 / (endt - startt));
startt = timer();
for (int i = 0; i < 100000; i++) {
Promise<int> p;
Future<int> f = chain2(p.getFuture(), N);
p.send(i);
ASSERT(f.get() == i + N);
}
endt = timer();
printf("chain2<%d>: %0.3f M/sec\n", N, 0.1 / (endt - startt));
}
ACTOR [[flow_allow_discard]] void cycle(FutureStream<Void> in, PromiseStream<Void> out, int* ptotal) {
loop {
waitNext(in);
(*ptotal)++;
out.send(Void());
}
}
ACTOR [[flow_allow_discard]] Future<Void> cycleTime(int nodes, int times) {
state std::vector<PromiseStream<Void>> n(nodes);
state int total = 0;
// 1->2, 2->3, ..., n-1->0
for (int i = 1; i < nodes; i++)
cycle(n[i].getFuture(), n[(i + 1) % nodes], &total);
state double startT = timer();
n[1].send(Void());
loop {
waitNext(n[0].getFuture());
if (!--times)
break;
n[1].send(Void());
}
printf("Ring test: %d nodes, %d total ops, %.3f seconds\n", nodes, total, timer() - startT);
return Void();
}
void sleeptest() {
#ifdef __linux__
int times[] = { 0, 100, 500, 1000, 5000, 100000, 500000, 1000000 };
for (int j = 0; j < 8; j++) {
double b = timer();
int n = std::min(100, 4000000 / (1 + times[j]));
for (int i = 0; i < n; i++) {
timespec ts;
ts.tv_sec = times[j] / 1000000;
ts.tv_nsec = (times[j] % 1000000) * 1000;
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, nullptr);
// nanosleep(&ts, nullptr);
}
double t = timer() - b;
printf("Sleep test (%dus x %d): %0.1f\n", times[j], n, double(t) / n * 1e6);
}
#endif
}
void asyncMapTest() {
Future<Void> c;
{
AsyncMap<int, int> m1;
m1.set(10, 1);
ASSERT(m1.get(10) == 1);
ASSERT(m1.get(20) == 0);
Future<Void> a = m1.onChange(10);
Future<Void> b = m1.onChange(20);
c = m1.onChange(30);
ASSERT(!a.isReady() && !b.isReady());
m1.set(10, 0);
ASSERT(a.isReady() && !a.isError() && !b.isReady() && m1.get(10) == 0);
m1.set(20, 5);
ASSERT(b.isReady() && !b.isError() && m1.get(20) == 5);
a = m1.onChange(10);
b = m1.onChange(20);
m1.triggerRange(15, 25);
ASSERT(!a.isReady() && b.isReady() && !b.isError() && m1.get(20) == 5);
}
ASSERT(c.isReady() && c.isError() && c.getError().code() == error_code_broken_promise);
printf("AsyncMap: OK\n");
double startt;
AsyncMap<int, int> m2;
startt = timer();
for (int i = 0; i < 1000000; i++) {
m2.set(5, 0);
m2.set(5, 1);
}
printf(" set(not present/present): %0.1fM/sec\n", 2.0 / (timer() - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
m2.set(5, 1);
m2.set(5, 2);
}
printf(" set(present/present): %0.1fM/sec\n", 2.0 / (timer() - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
m2.set(5, 1);
}
printf(" set(no change): %0.1fM/sec\n", 1.0 / (timer() - startt));
m2.set(5, 5);
startt = timer();
for (int i = 0; i < 1000000; i++)
m2.onChange(5);
printf(" onChange(present, cancelled): %0.1fM/sec\n", 1.0 / (timer() - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
m2.onChange(10);
printf(" onChange(not present, cancelled): %0.1fM/sec\n", 1.0 / (timer() - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
auto f = m2.onChange(10);
m2.set(10, 1);
m2.set(10, 0);
}
printf(" onChange(not present, set): %0.1fM/sec\n", 1.0 / (timer() - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
auto f = m2.onChange(5);
m2.set(5, i + 1);
}
printf(" onChange(present, set): %0.1fM/sec\n", 1.0 / (timer() - startt));
}
extern void net2_test();
void dsltest() {
double startt, endt;
setThreadLocalDeterministicRandomSeed(40);
asyncMapTest();
net2_test();
// sleeptest();
Future<Void> ctf = cycleTime(1000, 1000);
ctf.get();
introPromiseFuture();
introActor();
// return;
printf("Actor control flow tests: ");
actorTest1(true);
actorTest2(true);
actorTest3(true);
// if (g_network == &g_simulator)
// g_simulator.run( actorTest4(true) );
actorTest5();
actorTest6();
actorTest7();
actorTest8();
actorTest9();
actorTest10();
printf("\n");
printf("Running actor fuzz tests:\n");
// Only include this test outside of Windows because of MSVC compiler bug
#ifndef WIN32
auto afResults = actorFuzzTests();
#else
std::pair<int, int> afResults(0, 0);
#endif
printf("Actor fuzz tests: %d/%d passed\n", afResults.first, afResults.second);
startt = timer();
for (int i = 0; i < 1000000; i++)
deterministicRandom()->random01();
endt = timer();
printf("Random01: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
Promise<Void>();
endt = timer();
printf("Promises: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
Promise<Void>().send(Void());
endt = timer();
printf("Promises (with send): %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
Promise<Void> p;
Future<Void> f = p.getFuture();
p.send(Void());
f.get();
}
endt = timer();
printf("Promise/Future/send roundtrip: %0.2f M/sec\n", 1.0 / (endt - startt));
Promise<Void> p;
startt = timer();
for (int i = 0; i < 1000000; i++)
p.getFuture();
endt = timer();
printf("Futures: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
PromiseStream<Void>();
endt = timer();
printf("PromiseStreams: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
PromiseStream<Void>().send(Void());
endt = timer();
printf("PromiseStreams (with send): %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
PromiseStream<Void> p;
FutureStream<Void> f = p.getFuture();
p.send(Void());
f.pop();
}
endt = timer();
printf("PromiseStream/FutureStream/send/popBlocking roundtrip: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
{
PromiseStream<int> ps;
for (int i = 0; i < 1000000; i++) {
ps.send(i);
}
}
endt = timer();
printf("PromiseStream queued send: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
cancellable();
endt = timer();
printf("Cancellations: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
cancellable2();
endt = timer();
printf("Cancellations with catch: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
simple();
endt = timer();
printf("Actor creation: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++)
simpleWait();
endt = timer();
printf("With trivial wait: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
Promise<int> p;
Future<int> f = simpleRet(p.getFuture());
p.send(i);
ASSERT(f.get() == i);
}
endt = timer();
printf("Bounce int through actor: %0.2f M/sec\n", 1.0 / (endt - startt));
startt = timer();
for (int i = 0; i < 1000000; i++) {
Promise<int> p;
Future<int> f = simpleRet(p.getFuture());
Future<int> g = simpleRet(p.getFuture());
p.send(i);
ASSERT(f.get() == i);
ASSERT(g.get() == i);
}
endt = timer();
printf("Bounce int through two actors in parallel: %0.2f M/sec\n", 1.0 / (endt - startt));
/*chainTest<1>();
chainTest<4>();
chainTest<16>();
chainTest<64>();
startt = timer();
for(int i=0; i<1000000; i++)
try {
throw success();
} catch (Error&) {
}
endt = timer();
printf("C++ exception: %0.2f M/sec\n", 1.0/(endt-startt));*/
arenaTest();
{
Promise<int> a, b;
Future<int> c = chooseTest(a.getFuture(), b.getFuture());
a.send(1);
b.send(2);
std::cout << "c=" << c.get() << std::endl;
}
{
Promise<double> i;
Future<double> d = addN<20>(i.getFuture());
i.send(1.1);
std::cout << d.get() << std::endl;
}
{
Promise<double> i;
i.sendError(operation_failed());
Future<double> d = addN<20>(i.getFuture());
if (d.isError() && d.getError().code() == error_code_operation_failed)
std::cout << "Error transmitted OK" << std::endl;
else
std::cout << "Error not transmitted!" << std::endl;
}
/*{
int na = Actor::allActors.size();
PromiseStream<int> t;
testStream(t.getFuture());
if (Actor::allActors.size() != na+1)
std::cout << "Actor not created!" << std::endl;
t = PromiseStream<int>();
if (Actor::allActors.size() != na)
std::cout << "Actor not cleaned up!" << std::endl;
}*/
PromiseStream<int> as;
Promise<double> bs;
as.send(4);
Future<Void> sT = switchTest(as.getFuture(), bs.getFuture());
as.send(5);
// sT = move(Future<Void>());
as.send(6);
bs.send(10.1);
as.send(7);
fastAllocTest();
#if FLOW_THREAD_SAFE
returnCancelRaceTest();
threadSafetyTest();
threadSafetyTest2();
#else
printf("Thread safety disabled.\n");
#endif
}
/*ACTOR Future<Void> pingServer( FutureStream<Promise<bool>> requests, int rate ) {
state int count = 0;
loop {
Promise<bool> req = waitNext( requests );
req.send( (++count)%rate != 0 );
}
}
ACTOR Future<int> ping( PromiseStream<Promise<bool>> server ) {
state int count = 0;
loop {
bool result = wait( server.getReply<bool>() );
count++;
if (!result)
break;
}
return count;
}
void pingtest() {
double start = timer();
PromiseStream<Promise<bool>> serverInterface;
Future<Void> pS = pingServer( serverInterface.getFuture(), 5000000 );
Future<int> count = ping( serverInterface );
double end = timer();
std::cout << count.get() << " pings completed in " << (end-start) << " sec" << std::endl;
}*/
void copyTest() {
double start, elapsed;
Arena arena;
StringRef s(new (arena) uint8_t[10 << 20], 10 << 20);
{
start = timer();
for (int i = 0; i < 100; i++) {
StringRef k = s;
(void)k;
}
elapsed = timer() - start;
printf("StringRef->StringRef: %fs/GB\n", elapsed);
}
{
start = timer();
for (int i = 0; i < 100; i++)
Standalone<StringRef> a = s;
elapsed = timer() - start;
printf("StringRef->Standalone: %fs/GB\n", elapsed);
}
{
Standalone<StringRef> sa = s;
start = timer();
for (int i = 0; i < 100; i++)
Standalone<StringRef> a = sa;
elapsed = timer() - start;
printf("Standalone->Standalone: %fs/GB\n", elapsed);
}
{
Standalone<StringRef> sa = s, sb;
start = timer();
for (int i = 0; i < 50; i++) {
sb = std::move(sa);
sa = std::move(sb);
}
elapsed = timer() - start;
printf("move(Standalone)->Standalone: %fs/GB\n", elapsed);
}
}
/*ACTOR void badTest( FutureStream<int> is ) {
state PromiseStream<int> js;
loop choose {
when( int j = waitNext( js.getFuture() ) ) {
std::cout << "J" << j << std::endl;
}
when( int i = waitNext( is ) ) {
std::cout << "I" << i << std::endl;
js.send( i );
std::cout << "-I" << i << std::endl;
}
}
}
void dsltest() {
PromiseStream<int> is;
badTest( is.getFuture() );
is.send(1);
is.send(2);
is.send(3);
throw not_implemented();
}
void pingtest() {}*/