ClickHouse/dbms/include/DB/DataStreams/UnionBlockInputStream.h

209 lines
5.3 KiB
C
Raw Normal View History

2012-01-10 22:11:51 +00:00
#pragma once
#include <Poco/Semaphore.h>
#include <statdaemons/threadpool.hpp>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
namespace DB
{
using Poco::SharedPtr;
/** Объединяет несколько источников в один.
* Блоки из разных источников перемежаются друг с другом произвольным образом.
* Можно указать количество потоков (max_threads),
* в которых будет выполняться получение данных из разных источников.
*/
class UnionBlockInputStream : public IProfilingBlockInputStream
{
public:
UnionBlockInputStream(BlockInputStreams inputs_, unsigned max_threads_ = 1)
: max_threads(std::min(inputs_.size(), static_cast<size_t>(max_threads_))),
pool(max_threads),
ready_any(0, inputs_.size())
{
children.insert(children.end(), inputs_.begin(), inputs_.end());
2012-06-24 23:17:06 +00:00
2012-01-10 22:11:51 +00:00
for (size_t i = 0; i < inputs_.size(); ++i)
2012-06-24 23:17:06 +00:00
{
threads_data.push_back(ThreadData());
threads_data.back().in = inputs_[i];
threads_data.back().i = i;
}
2012-01-10 22:11:51 +00:00
}
Block readImpl()
{
Block res;
2012-06-22 18:27:40 +00:00
while (1)
2012-01-10 22:11:51 +00:00
{
2012-06-22 18:27:40 +00:00
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
2012-01-10 22:11:51 +00:00
2012-06-24 23:17:06 +00:00
if (threads_data.empty())
2012-06-22 18:27:40 +00:00
return res;
2012-03-05 02:34:20 +00:00
2012-06-25 00:17:19 +00:00
ssize_t max_threads_to_start = static_cast<ssize_t>(pool.size()) - (pool.pending() + pool.active());
if (max_threads_to_start > 0)
2012-01-10 22:11:51 +00:00
{
2012-06-24 23:17:06 +00:00
/// Запустим вычисления для как можно большего количества источников, которые ещё ни разу не брались
2012-06-25 00:17:19 +00:00
std::cerr << "Starting initial threads" << std::endl;
2012-06-24 23:17:06 +00:00
2012-06-25 00:17:19 +00:00
ssize_t started_threads = 0;
ThreadsData::iterator it = threads_data.begin();
while (it != threads_data.end() && 0 == it->count)
2012-06-22 18:27:40 +00:00
{
2012-06-24 23:17:06 +00:00
std::cerr << "Scheduling initial " << it->i << std::endl;
++it->count;
2012-06-22 18:27:40 +00:00
++started_threads;
2012-06-25 00:17:19 +00:00
/// Переносим этот источник в конец списка
threads_data.push_back(*it);
threads_data.erase(it++);
pool.schedule(boost::bind(&UnionBlockInputStream::calculate, this, boost::ref(threads_data.back())));
2012-06-22 18:27:40 +00:00
2012-06-24 23:17:06 +00:00
if (started_threads == max_threads_to_start)
2012-06-22 18:27:40 +00:00
break;
}
2012-01-10 22:11:51 +00:00
}
}
2012-06-24 23:17:06 +00:00
std::cerr << "Waiting for one thread to finish" << std::endl;
2012-01-10 22:11:51 +00:00
ready_any.wait();
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
2012-03-05 02:34:20 +00:00
2012-06-24 23:17:06 +00:00
std::cerr << std::endl << "pool.pending: " << pool.pending() << ", pool.active: " << pool.active() << ", pool.size: " << pool.size() << std::endl;
if (threads_data.empty())
2012-01-10 22:11:51 +00:00
return res;
2012-06-24 23:17:06 +00:00
std::cerr << "Searching for first ready block" << std::endl;
2012-01-10 22:11:51 +00:00
/** Найдём и вернём готовый непустой блок, если такой есть.
* При чём, выберем блок из источника, из которого было получено меньше всего блоков.
*/
2012-06-24 23:17:06 +00:00
ThreadsData::iterator it = threads_data.begin();
while (it != threads_data.end())
2012-01-10 22:11:51 +00:00
{
2012-06-24 23:17:06 +00:00
if (it->exception)
it->exception->rethrow();
2012-01-10 22:11:51 +00:00
2012-06-24 23:17:06 +00:00
if (it->ready)
2012-01-10 22:11:51 +00:00
{
2012-06-24 23:17:06 +00:00
if (!it->block)
threads_data.erase(it++);
else
break;
2012-01-10 22:11:51 +00:00
}
2012-06-24 23:17:06 +00:00
else
++it;
2012-01-10 22:11:51 +00:00
}
2012-06-24 23:17:06 +00:00
if (it == threads_data.end())
2012-03-05 02:34:20 +00:00
{
2012-06-24 23:17:06 +00:00
std::cerr << "Continue" << std::endl;
2012-01-10 22:11:51 +00:00
continue;
2012-03-05 02:34:20 +00:00
}
2012-01-10 22:11:51 +00:00
2012-06-24 23:17:06 +00:00
std::cerr << "Found block " << it->i << std::endl;
2012-01-10 22:11:51 +00:00
2012-06-24 23:17:06 +00:00
res = it->block;
2012-01-10 22:11:51 +00:00
/// Запустим получение следующего блока
2012-06-24 23:17:06 +00:00
it->reset();
std::cerr << "Scheduling again " << it->i << std::endl;
++it->count;
pool.schedule(boost::bind(&UnionBlockInputStream::calculate, this, boost::ref(*it)));
2012-01-10 22:11:51 +00:00
return res;
}
}
}
String getName() const { return "UnionBlockInputStream"; }
BlockInputStreamPtr clone() { return new UnionBlockInputStream(children, max_threads); }
2012-06-22 18:16:47 +00:00
~UnionBlockInputStream()
2012-03-05 00:09:41 +00:00
{
2012-06-22 19:03:33 +00:00
pool.clear();
2012-03-05 00:09:41 +00:00
pool.wait();
}
2012-01-10 22:11:51 +00:00
private:
unsigned max_threads;
boost::threadpool::pool pool;
/// Данные отдельного источника
struct ThreadData
{
BlockInputStreamPtr in;
unsigned count; /// Сколько блоков было вычислено
bool ready; /// Блок уже вычислен
Block block;
ExceptionPtr exception;
2012-06-24 23:17:06 +00:00
size_t i; /// Порядковый номер источника.
2012-01-10 22:11:51 +00:00
void reset()
{
ready = false;
block = Block();
exception = NULL;
}
2012-06-24 23:17:06 +00:00
ThreadData() : count(0), ready(false), i(0) {}
2012-01-10 22:11:51 +00:00
};
2012-06-24 23:17:06 +00:00
/// Список упорядочен по количеству полученных из источника блоков.
typedef std::list<ThreadData> ThreadsData;
2012-01-10 22:11:51 +00:00
ThreadsData threads_data;
Poco::FastMutex mutex;
Poco::Semaphore ready_any;
/// Вычисления, которые выполняться в отдельном потоке
2012-06-24 23:17:06 +00:00
void calculate(ThreadData & data)
2012-01-10 22:11:51 +00:00
{
try
{
Block block = data.in->read();
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
data.ready = true;
data.block = block;
}
ready_any.set();
}
catch (const Exception & e)
{
data.exception = e.clone();
2012-01-10 22:11:51 +00:00
}
catch (const Poco::Exception & e)
{
data.exception = e.clone();
2012-01-10 22:11:51 +00:00
}
catch (const std::exception & e)
{
data.exception = new Exception(e.what(), ErrorCodes::STD_EXCEPTION);
}
catch (...)
{
data.exception = new Exception("Unknown exception", ErrorCodes::UNKNOWN_EXCEPTION);
}
}
};
}