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

196 lines
4.6 KiB
C
Raw Normal View History

2011-11-28 05:48:52 +00:00
#pragma once
2012-10-20 02:10:47 +00:00
#include <Poco/Mutex.h>
#include <Poco/Thread.h>
#include <Poco/Runnable.h>
2011-11-28 05:48:52 +00:00
2011-11-28 06:22:25 +00:00
#include <DB/DataStreams/IProfilingBlockInputStream.h>
2011-11-28 05:48:52 +00:00
namespace DB
{
2012-10-20 02:10:47 +00:00
/** thread-safe очередь из одного элемента,
* рассчитанная на одного producer-а и одного consumer-а.
2011-11-28 05:48:52 +00:00
*/
2012-10-20 02:10:47 +00:00
template <typename T>
class OneElementQueue
2011-11-28 05:48:52 +00:00
{
2012-10-20 02:10:47 +00:00
private:
T data;
Poco::FastMutex mutex_fill; /// Захвачен, когда данные есть.
Poco::FastMutex mutex_empty; /// Захвачен, когда данных нет.
2011-11-28 05:48:52 +00:00
public:
2012-10-20 02:10:47 +00:00
OneElementQueue()
2011-11-28 05:48:52 +00:00
{
2012-10-20 02:10:47 +00:00
mutex_empty.lock();
2011-11-28 05:48:52 +00:00
}
2012-10-20 02:10:47 +00:00
/// Вызывается единственным producer-ом.
void push(const T & x)
2011-11-28 05:48:52 +00:00
{
2012-10-20 02:10:47 +00:00
mutex_fill.lock();
data = x;
mutex_empty.unlock();
}
/// Вызывается единственным consumer-ом.
void pop(T & x)
{
mutex_empty.lock();
x = data;
mutex_fill.unlock();
}
/// Позволяет ждать элемента не дольше заданного таймаута. Вызывается единственным consumer-ом.
bool poll(UInt64 milliseconds)
{
if (mutex_empty.tryLock(milliseconds))
2011-11-28 06:22:25 +00:00
{
2012-10-20 02:10:47 +00:00
mutex_empty.unlock();
return true;
2011-11-28 06:22:25 +00:00
}
2011-11-28 05:48:52 +00:00
2012-10-20 02:10:47 +00:00
return false;
}
};
2011-11-28 05:48:52 +00:00
2012-10-20 02:10:47 +00:00
/** Выполняет другой BlockInputStream в отдельном потоке.
* Это служит для двух целей:
* 1. Позволяет сделать так, чтобы разные стадии конвеьера выполнения запроса работали параллельно.
* 2. Позволяет не ждать до того, как данные будут готовы, а периодически проверять их готовность без блокировки.
* Это нужно, например, чтобы можно было во время ожидания проверить, не пришёл ли по сети пакет с просьбой прервать выполнение запроса.
* Также это позволяет выполнить несколько запросов одновременно.
*/
class AsynchronousBlockInputStream : public IProfilingBlockInputStream
{
public:
AsynchronousBlockInputStream(BlockInputStreamPtr in_) : in(in_), started(false), runnable(*this)
{
children.push_back(in);
}
2011-11-28 05:48:52 +00:00
2012-10-20 02:10:47 +00:00
/** Ждать готовность данных не более заданного таймаута. Запустить получение данных, если нужно.
* Если функция вернула true - данные готовы и можно делать read().
*/
bool poll(UInt64 milliseconds)
{
startIfNeed();
return output_queue.poll(milliseconds);
2011-11-28 05:48:52 +00:00
}
2012-10-20 02:10:47 +00:00
2011-11-28 05:48:52 +00:00
String getName() const { return "AsynchronousBlockInputStream"; }
BlockInputStreamPtr clone() { return new AsynchronousBlockInputStream(in); }
2012-10-20 02:10:47 +00:00
2011-11-28 05:48:52 +00:00
2012-03-05 02:34:20 +00:00
~AsynchronousBlockInputStream()
{
if (started)
2012-10-20 02:10:47 +00:00
thread->join();
2012-03-05 02:34:20 +00:00
}
2011-11-28 05:48:52 +00:00
protected:
2012-10-20 02:10:47 +00:00
Block readImpl()
{
OutputData res;
2011-11-28 05:48:52 +00:00
2012-10-20 02:10:47 +00:00
startIfNeed();
2011-11-28 05:48:52 +00:00
2012-10-20 02:10:47 +00:00
/// Будем ждать, пока будет готов следующий блок или будет выкинуто исключение.
output_queue.pop(res);
if (res.exception)
res.exception->rethrow();
return res.block;
}
void startIfNeed()
2011-11-28 05:48:52 +00:00
{
2012-10-20 02:10:47 +00:00
if (!started)
2011-11-28 05:48:52 +00:00
{
2012-10-20 02:10:47 +00:00
thread = new Poco::Thread;
thread->start(runnable);
2011-11-28 05:48:52 +00:00
}
2012-10-20 02:10:47 +00:00
}
/// Вычисления, которые могут выполняться в отдельном потоке
class Thread : public Poco::Runnable
{
public:
Thread(AsynchronousBlockInputStream & parent_) : parent(parent_) {}
void run()
2011-11-28 05:48:52 +00:00
{
2012-10-20 02:10:47 +00:00
ExceptionPtr exception;
try
{
loop();
}
catch (const Exception & e)
{
exception = e.clone();
}
catch (const Poco::Exception & e)
{
exception = e.clone();
}
catch (const std::exception & e)
{
exception = new Exception(e.what(), ErrorCodes::STD_EXCEPTION);
}
catch (...)
{
exception = new Exception("Unknown exception", ErrorCodes::UNKNOWN_EXCEPTION);
}
if (exception)
{
parent.cancel();
/// Отдаём эксепшен в основной поток.
parent.output_queue.push(exception);
}
2011-11-28 05:48:52 +00:00
}
2012-10-20 02:10:47 +00:00
void loop()
2011-11-28 05:48:52 +00:00
{
2012-10-20 02:10:47 +00:00
while (Block res = parent.in->read())
parent.output_queue.push(res);
2011-11-28 05:48:52 +00:00
}
2012-10-20 02:10:47 +00:00
private:
AsynchronousBlockInputStream & parent;
};
BlockInputStreamPtr in;
bool started;
struct OutputData
{
Block block;
ExceptionPtr exception;
OutputData() {}
OutputData(Block & block_) : block(block_) {}
OutputData(ExceptionPtr & exception_) : exception(exception_) {}
};
OneElementQueue<OutputData> output_queue;
Thread runnable;
SharedPtr<Poco::Thread> thread;
2011-11-28 05:48:52 +00:00
};
}