int main() { WorkerPool workers; for (int i = 0; i != 100; ++i) { workers.schedule([=]{ usleep(10); }, 0); } for (int i = 0; i != 50; ++i) { workers.schedule([=]{ usleep(10); }, 1); } for (int i = 0; i != 40; ++i) { workers.schedule([=]{ usleep(10); }, 2); } for (int i = 0; i != 10; ++i) { workers.schedule([=]{ usleep(10); }, 3); } sleep(1); quit = true; }
MORDOR_UNITTEST(SSLStream, forceDuplex) { WorkerPool pool; std::pair<Stream::ptr, Stream::ptr> pipes = pipeStream(); SSLStream::ptr sslserver(new SSLStream(pipes.first, false)); SSLStream::ptr sslclient(new SSLStream(pipes.second, true)); Stream::ptr server = sslserver, client = sslclient; int sequence = 0; pool.schedule(boost::bind(&accept, sslserver)); sslclient->connect(); pool.dispatch(); pool.schedule(boost::bind(&readWorld, client, boost::ref(sequence))); pool.dispatch(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 2); // Read is pending client->write("hello"); client->flush(false); pool.dispatch(); server->write("world"); server->flush(false); pool.dispatch(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); }
// Similar to above, but after the scheduler has stopped, yielding // to it again should implicitly restart it MORDOR_UNITTEST(Scheduler, hijackMultipleDispatch) { Fiber::ptr doNothingFiber(new Fiber(&doNothing)); WorkerPool pool; MORDOR_TEST_ASSERT_EQUAL(Scheduler::getThis(), &pool); pool.schedule(doNothingFiber); MORDOR_TEST_ASSERT_EQUAL(doNothingFiber->state(), Fiber::INIT); pool.dispatch(); MORDOR_TEST_ASSERT_EQUAL(doNothingFiber->state(), Fiber::TERM); doNothingFiber->reset(); pool.schedule(doNothingFiber); MORDOR_TEST_ASSERT_EQUAL(doNothingFiber->state(), Fiber::INIT); pool.dispatch(); MORDOR_TEST_ASSERT_EQUAL(doNothingFiber->state(), Fiber::TERM); }
MORDOR_UNITTEST(SSLStream, duplexStress) { WorkerPool pool; // Force more fiber context switches by having a smaller buffer std::pair<Stream::ptr, Stream::ptr> pipes = pipeStream(1024); SSLStream::ptr sslserver(new SSLStream(pipes.first, false)); SSLStream::ptr sslclient(new SSLStream(pipes.second, true)); pool.schedule(boost::bind(&accept, sslserver)); sslclient->connect(); pool.dispatch(); // Transfer 1 MB long long toTransfer = 1024 * 1024; std::vector<boost::function<void ()> > dgs; bool complete1 = false, complete2 = false, complete3 = false, complete4 = false; dgs.push_back(boost::bind(&writeLotsaData, sslserver, toTransfer, boost::ref(complete1))); dgs.push_back(boost::bind(&readLotsaData, sslserver, toTransfer, boost::ref(complete2))); dgs.push_back(boost::bind(&writeLotsaData, sslclient, toTransfer, boost::ref(complete3))); dgs.push_back(boost::bind(&readLotsaData, sslclient, toTransfer, boost::ref(complete4))); parallel_do(dgs); MORDOR_ASSERT(complete1); MORDOR_ASSERT(complete2); MORDOR_ASSERT(complete3); MORDOR_ASSERT(complete4); }
MORDOR_UNITTEST(SSLStream, basic) { WorkerPool pool; std::pair<Stream::ptr, Stream::ptr> pipes = pipeStream(); SSLStream::ptr sslserver(new SSLStream(pipes.first, false)); SSLStream::ptr sslclient(new SSLStream(pipes.second, true)); pool.schedule(boost::bind(&accept, sslserver)); sslclient->connect(); pool.dispatch(); Stream::ptr server = sslserver, client = sslclient; char buf[6]; buf[5] = '\0'; client->write("hello"); client->flush(false); MORDOR_TEST_ASSERT_EQUAL(server->read(buf, 5), 5u); MORDOR_TEST_ASSERT_EQUAL((const char *)buf, "hello"); server->write("world"); server->flush(false); MORDOR_TEST_ASSERT_EQUAL(client->read(buf, 5), 5u); MORDOR_TEST_ASSERT_EQUAL((const char *)buf, "world"); }
// Just calling stop should still clear all pending work MORDOR_UNITTEST(Scheduler, hijackStopOnScheduled) { Fiber::ptr doNothingFiber(new Fiber(&doNothing)); WorkerPool pool; MORDOR_TEST_ASSERT_EQUAL(Scheduler::getThis(), &pool); pool.schedule(doNothingFiber); MORDOR_TEST_ASSERT_EQUAL(doNothingFiber->state(), Fiber::INIT); pool.stop(); MORDOR_TEST_ASSERT_EQUAL(doNothingFiber->state(), Fiber::TERM); }
MORDOR_UNITTEST(PipeStream, closeOnBlockingReader) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(); WorkerPool pool; int sequence = 1; pool.schedule(Fiber::ptr(new Fiber(boost::bind(&closeOnBlockingReader, pipe.first, boost::ref(sequence))))); Buffer output; MORDOR_TEST_ASSERT_EQUAL(pipe.second->read(output, 10), 0u); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); }
MORDOR_UNITTEST(PipeStream, cancelOnBlockingWriter) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(5); WorkerPool pool; int sequence = 1; pool.schedule(Fiber::ptr(new Fiber(boost::bind(&cancelOnBlockingWriter, pipe.first, boost::ref(sequence))))); Scheduler::yield(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 3); pipe.first->cancelWrite(); pool.dispatch(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 5); }
MORDOR_UNITTEST(PipeStream, cancelOnBlockingReader) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(); WorkerPool pool; int sequence = 1; pool.schedule(Fiber::ptr(new Fiber(boost::bind(&cancelOnBlockingReader, pipe.first, boost::ref(sequence))))); Buffer output; MORDOR_TEST_ASSERT_EXCEPTION(pipe.first->read(output, 10), OperationAbortedException); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); MORDOR_TEST_ASSERT_EXCEPTION(pipe.first->read(output, 10), OperationAbortedException); }
MORDOR_UNITTEST(PipeStream, closeOnBlockingWriter) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(5); WorkerPool pool; int sequence = 1; pool.schedule(Fiber::ptr(new Fiber(boost::bind(&closeOnBlockingWriter, pipe.first, boost::ref(sequence))))); MORDOR_TEST_ASSERT_EQUAL(pipe.second->write("hello"), 5u); MORDOR_TEST_ASSERT_EQUAL(++sequence, 2); MORDOR_TEST_ASSERT_EXCEPTION(pipe.second->write("world"), BrokenPipeException); MORDOR_TEST_ASSERT_EQUAL(++sequence, 5); }
MORDOR_UNITTEST(PipeStream, blockingRead) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(5); WorkerPool pool; int sequence = 1; pool.schedule(Fiber::ptr(new Fiber(boost::bind(&blockingRead, pipe.second, boost::ref(sequence))))); Buffer output; MORDOR_TEST_ASSERT_EQUAL(pipe.first->read(output, 10), 5u); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); MORDOR_TEST_ASSERT(output == "hello"); }
MORDOR_UNITTEST(PipeStream, destructOnBlockingReader) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(); WorkerPool pool; int sequence = 1; Fiber::ptr f = Fiber::ptr(new Fiber(boost::bind(&destructOnBlockingReader, boost::weak_ptr<Stream>(pipe.first), boost::ref(sequence)))); f->call(); pipe.first.reset(); pool.schedule(f); Buffer output; MORDOR_TEST_ASSERT_EXCEPTION(pipe.second->read(output, 10), BrokenPipeException); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); }
MORDOR_UNITTEST(PipeStream, destructOnBlockingWriter) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(5); WorkerPool pool; int sequence = 1; Fiber::ptr f = Fiber::ptr(new Fiber(boost::bind(&destructOnBlockingWriter, boost::weak_ptr<Stream>(pipe.first), boost::ref(sequence)))); f->call(); pipe.first.reset(); pool.schedule(f); MORDOR_TEST_ASSERT_EQUAL(pipe.second->write("hello"), 5u); MORDOR_TEST_ASSERT_EQUAL(++sequence, 2); MORDOR_TEST_ASSERT_EXCEPTION(pipe.second->write("world"), BrokenPipeException); MORDOR_TEST_ASSERT_EQUAL(++sequence, 5); }
MORDOR_UNITTEST(PipeStream, basicInFibers) { std::pair<Stream::ptr, Stream::ptr> pipe = pipeStream(); // pool must destruct before pipe, because when pool destructs it // waits for the other fiber to complete, which has a weak ref // to pipe.second; if pipe.second is gone, it will throw an // exception that we don't want WorkerPool pool; int sequence = 1; pool.schedule(Fiber::ptr(new Fiber(boost::bind(&basicInFibers, pipe.first, boost::ref(sequence))))); Buffer read; MORDOR_TEST_ASSERT_EQUAL(pipe.second->read(read, 10), 1u); MORDOR_TEST_ASSERT(read == "a"); ++sequence; MORDOR_TEST_ASSERT_EQUAL(sequence, 2); MORDOR_TEST_ASSERT_EQUAL(pipe.second->read(read, 10), 0u); }
// When hijacking the calling thread, you can stop() from anywhere within // it MORDOR_UNITTEST(Scheduler, stopScheduledHijack) { WorkerPool pool; pool.schedule(boost::bind(&Scheduler::stop, &pool)); pool.dispatch(); }
MORDOR_UNITTEST(Scheduler, tolerantException) { WorkerPool pool; pool.schedule(throwException); MORDOR_TEST_ASSERT_ANY_EXCEPTION(pool.stop()); }