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
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|