Fix race.

This commit is contained in:
Nikolai Kochetov 2022-11-21 18:26:31 +00:00
parent be1a8054c7
commit c305afd77a

View File

@ -39,39 +39,58 @@ struct MergeTreeSource::AsyncReadingState
struct Control struct Control
{ {
/// setResult and setException are the only methods
/// which can be called from background thread.
/// Invariant:
/// * background thread changes status InProgress -> IsFinished
/// * (status == InProgress) => (MergeTreeBaseSelectProcessor is alive)
void setResult(ChunkAndProgress chunk_)
{
chassert(stage == Stage::InProgress);
chunk = std::move(chunk_);
finish();
}
void setException(std::exception_ptr exception_)
{
chassert(stage == Stage::InProgress);
exception = exception_;
finish();
}
private:
EventFD event; EventFD event;
std::atomic<Stage> stage = Stage::NotStarted; std::atomic<Stage> stage = Stage::NotStarted;
ChunkAndProgress chunk;
std::exception_ptr exception;
void finish() void finish()
{ {
stage = Stage::IsFinished; stage = Stage::IsFinished;
event.write(); event.write();
} }
ChunkAndProgress getResult()
{
chassert(stage == Stage::IsFinished);
event.read();
stage = Stage::NotStarted;
if (exception)
std::rethrow_exception(exception);
return std::move(chunk);
}
friend struct AsyncReadingState;
}; };
/// setResult and setException are the only methods
/// which can be called from background thread.
/// Invariant:
/// * background thread changes status InProgress -> IsFinished
/// * (status == InProgress) => (MergeTreeBaseSelectProcessor is alive)
void setResult(ChunkAndProgress chunk_)
{
assert(control->stage == Stage::InProgress);
chunk = std::move(chunk_);
control->finish();
}
void setException(std::exception_ptr exception_)
{
assert(control->stage == Stage::InProgress);
exception = exception_;
control->finish();
}
std::shared_ptr<Control> start() std::shared_ptr<Control> start()
{ {
assert(control->stage == Stage::NotStarted); chassert(control->stage == Stage::NotStarted);
control->stage = Stage::InProgress; control->stage = Stage::InProgress;
return control; return control;
} }
@ -83,14 +102,7 @@ struct MergeTreeSource::AsyncReadingState
ChunkAndProgress getResult() ChunkAndProgress getResult()
{ {
assert(control->stage == Stage::IsFinished); return control->getResult();
control->event.read();
control->stage = Stage::NotStarted;
if (exception)
std::rethrow_exception(exception);
return std::move(chunk);
} }
Stage getStage() const { return control->stage; } Stage getStage() const { return control->stage; }
@ -123,8 +135,6 @@ struct MergeTreeSource::AsyncReadingState
private: private:
ThreadPoolCallbackRunner<void> callback_runner; ThreadPoolCallbackRunner<void> callback_runner;
ChunkAndProgress chunk;
std::exception_ptr exception;
std::shared_ptr<Control> control; std::shared_ptr<Control> control;
}; };
@ -168,8 +178,7 @@ std::optional<Chunk> MergeTreeSource::tryGenerate()
if (async_reading_state->getStage() == AsyncReadingState::Stage::IsFinished) if (async_reading_state->getStage() == AsyncReadingState::Stage::IsFinished)
return reportProgress(async_reading_state->getResult()); return reportProgress(async_reading_state->getResult());
assert(async_reading_state->getStage() == AsyncReadingState::Stage::NotStarted); chassert(async_reading_state->getStage() == AsyncReadingState::Stage::NotStarted);
async_reading_state->start();
/// It is important to store control into job. /// It is important to store control into job.
/// Otherwise, race between job and ~MergeTreeBaseSelectProcessor is possible. /// Otherwise, race between job and ~MergeTreeBaseSelectProcessor is possible.
@ -179,11 +188,11 @@ std::optional<Chunk> MergeTreeSource::tryGenerate()
try try
{ {
async_reading_state->setResult(algorithm->read()); holder->setResult(algorithm->read());
} }
catch (...) catch (...)
{ {
async_reading_state->setException(std::current_exception()); holder->setException(std::current_exception());
} }
}; };