change: change code style to Linux style except indent.
This commit is contained in:
+243
-209
@@ -23,11 +23,12 @@
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "render/rendermanager.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
RenderTask::RenderTask() :
|
||||
running_tickets_(0),
|
||||
native_progress_signalling_(true)
|
||||
RenderTask::RenderTask()
|
||||
: running_tickets_(0)
|
||||
, native_progress_signalling_(true)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -35,279 +36,312 @@ RenderTask::~RenderTask()
|
||||
{
|
||||
}
|
||||
|
||||
bool RenderTask::Render(ColorManager* manager,
|
||||
const TimeRangeList& video_range,
|
||||
const TimeRangeList &audio_range, const TimeRange &subtitle_range,
|
||||
RenderMode::Mode mode,
|
||||
FrameHashCache* cache, const QSize &force_size,
|
||||
const QMatrix4x4 &force_matrix, PixelFormat force_format,
|
||||
int force_channel_count, ColorProcessorPtr force_color_output)
|
||||
bool RenderTask::Render(ColorManager *manager, const TimeRangeList &video_range,
|
||||
const TimeRangeList &audio_range,
|
||||
const TimeRange &subtitle_range, RenderMode::Mode mode,
|
||||
FrameHashCache *cache, const QSize &force_size,
|
||||
const QMatrix4x4 &force_matrix,
|
||||
PixelFormat force_format, int force_channel_count,
|
||||
ColorProcessorPtr force_color_output)
|
||||
{
|
||||
QMetaObject::invokeMethod(RenderManager::instance(), "SetAggressiveGarbageCollection", Q_ARG(bool, true));
|
||||
QMetaObject::invokeMethod(RenderManager::instance(),
|
||||
"SetAggressiveGarbageCollection",
|
||||
Q_ARG(bool, true));
|
||||
|
||||
// Run watchers in another thread so they can accept signals even while this thread is blocked
|
||||
QThread watcher_thread;
|
||||
watcher_thread.start();
|
||||
// Run watchers in another thread so they can accept signals even while this thread is blocked
|
||||
QThread watcher_thread;
|
||||
watcher_thread.start();
|
||||
|
||||
double progress_counter = 0;
|
||||
double total_length = 0;
|
||||
double progress_counter = 0;
|
||||
double total_length = 0;
|
||||
|
||||
// Store real time before any rendering takes place
|
||||
// Queue audio jobs
|
||||
foreach (const TimeRange& range, audio_range) {
|
||||
// Don't count audio progress, since it's generally a lot faster than video and is weighted at
|
||||
// 50%, which makes the progress bar look weird to the uninitiated
|
||||
//total_length += r.length().toDouble();
|
||||
// Store real time before any rendering takes place
|
||||
// Queue audio jobs
|
||||
foreach (const TimeRange &range, audio_range) {
|
||||
// Don't count audio progress, since it's generally a lot faster than video and is weighted at
|
||||
// 50%, which makes the progress bar look weird to the uninitiated
|
||||
//total_length += r.length().toDouble();
|
||||
|
||||
RenderManager::RenderAudioParams rap(viewer_->GetConnectedSampleOutput(),
|
||||
range,
|
||||
audio_params_,
|
||||
RenderMode::kOnline);
|
||||
RenderManager::RenderAudioParams rap(
|
||||
viewer_->GetConnectedSampleOutput(), range, audio_params_,
|
||||
RenderMode::kOnline);
|
||||
|
||||
RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
watcher->setProperty("range", QVariant::fromValue(range));
|
||||
PrepareWatcher(watcher, &watcher_thread);
|
||||
IncrementRunningTickets();
|
||||
watcher->SetTicket(RenderManager::instance()->RenderAudio(rap));
|
||||
}
|
||||
RenderTicketWatcher *watcher = new RenderTicketWatcher();
|
||||
watcher->setProperty("range", QVariant::fromValue(range));
|
||||
PrepareWatcher(watcher, &watcher_thread);
|
||||
IncrementRunningTickets();
|
||||
watcher->SetTicket(RenderManager::instance()->RenderAudio(rap));
|
||||
}
|
||||
|
||||
// Look up hashes
|
||||
TimeRangeListFrameIterator iterator(video_range, video_params().frame_rate_as_time_base());
|
||||
total_number_of_frames_ = iterator.size();
|
||||
total_length += total_number_of_frames_;
|
||||
// Look up hashes
|
||||
TimeRangeListFrameIterator iterator(
|
||||
video_range, video_params().frame_rate_as_time_base());
|
||||
total_number_of_frames_ = iterator.size();
|
||||
total_length += total_number_of_frames_;
|
||||
|
||||
// Start a render of a limited amount, and then render one frame for each frame that gets
|
||||
// finished. This prevents rendered frames from stacking up in memory indefinitely while the
|
||||
// encoder is processing them. The amount is kind of arbitrary, but we use the thread count so
|
||||
// each of the system's threads are utilized as memory allows.
|
||||
const int maximum_rendered_frames = QThread::idealThreadCount();
|
||||
// Start a render of a limited amount, and then render one frame for each frame that gets
|
||||
// finished. This prevents rendered frames from stacking up in memory indefinitely while the
|
||||
// encoder is processing them. The amount is kind of arbitrary, but we use the thread count so
|
||||
// each of the system's threads are utilized as memory allows.
|
||||
const int maximum_rendered_frames = QThread::idealThreadCount();
|
||||
|
||||
rational next_frame;
|
||||
for (int i=0; i<maximum_rendered_frames && iterator.GetNext(&next_frame); i++) {
|
||||
StartTicket(&watcher_thread, manager, next_frame, mode, cache, force_size, force_matrix, force_format, force_channel_count, force_color_output);
|
||||
}
|
||||
rational next_frame;
|
||||
for (int i = 0;
|
||||
i < maximum_rendered_frames && iterator.GetNext(&next_frame); i++) {
|
||||
StartTicket(&watcher_thread, manager, next_frame, mode, cache,
|
||||
force_size, force_matrix, force_format, force_channel_count,
|
||||
force_color_output);
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
bool result = true;
|
||||
|
||||
// Subtitle loop, loops over all blocks in sequence on all tracks
|
||||
if (!subtitle_range.length().isNull()) {
|
||||
if (Sequence *sequence = dynamic_cast<Sequence*>(viewer_)) {
|
||||
TrackList *list = sequence->track_list(Track::kSubtitle);
|
||||
QVector<int> block_indexes(list->GetTrackCount(), 0);
|
||||
// Subtitle loop, loops over all blocks in sequence on all tracks
|
||||
if (!subtitle_range.length().isNull()) {
|
||||
if (Sequence *sequence = dynamic_cast<Sequence *>(viewer_)) {
|
||||
TrackList *list = sequence->track_list(Track::kSubtitle);
|
||||
QVector<int> block_indexes(list->GetTrackCount(), 0);
|
||||
|
||||
QVector<int> tracks_to_push;
|
||||
do {
|
||||
tracks_to_push.clear();
|
||||
QVector<int> tracks_to_push;
|
||||
do {
|
||||
tracks_to_push.clear();
|
||||
|
||||
for (int i=0; i<block_indexes.size(); i++) {
|
||||
Track *this_track = list->GetTrackAt(i);
|
||||
if (this_track->IsMuted()) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < block_indexes.size(); i++) {
|
||||
Track *this_track = list->GetTrackAt(i);
|
||||
if (this_track->IsMuted()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int &this_block_index = block_indexes[i];
|
||||
if (this_block_index >= this_track->Blocks().size()) {
|
||||
continue;
|
||||
}
|
||||
Block *this_block = this_track->Blocks().at(this_block_index);
|
||||
int &this_block_index = block_indexes[i];
|
||||
if (this_block_index >= this_track->Blocks().size()) {
|
||||
continue;
|
||||
}
|
||||
Block *this_block =
|
||||
this_track->Blocks().at(this_block_index);
|
||||
|
||||
Track *compare_track = tracks_to_push.isEmpty() ? nullptr : list->GetTrackAt(tracks_to_push.first());
|
||||
const int &compare_block_index = tracks_to_push.isEmpty() ? -1 : block_indexes.at(tracks_to_push.first());
|
||||
Block *compare_block = compare_track ? compare_track->Blocks().at(compare_block_index) : nullptr;
|
||||
if (!compare_track || compare_block->in() >= this_block->in()) {
|
||||
if (compare_track && compare_block->in() != this_block->in()) {
|
||||
tracks_to_push.clear();
|
||||
}
|
||||
tracks_to_push.append(i);
|
||||
}
|
||||
}
|
||||
Track *compare_track =
|
||||
tracks_to_push.isEmpty() ?
|
||||
nullptr :
|
||||
list->GetTrackAt(tracks_to_push.first());
|
||||
const int &compare_block_index =
|
||||
tracks_to_push.isEmpty() ?
|
||||
-1 :
|
||||
block_indexes.at(tracks_to_push.first());
|
||||
Block *compare_block =
|
||||
compare_track ?
|
||||
compare_track->Blocks().at(compare_block_index) :
|
||||
nullptr;
|
||||
if (!compare_track ||
|
||||
compare_block->in() >= this_block->in()) {
|
||||
if (compare_track &&
|
||||
compare_block->in() != this_block->in()) {
|
||||
tracks_to_push.clear();
|
||||
}
|
||||
tracks_to_push.append(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<tracks_to_push.size(); i++) {
|
||||
Track *this_track = list->GetTrackAt(tracks_to_push.at(i));
|
||||
Block *this_block = this_track->Blocks().at(block_indexes.at(tracks_to_push.at(i)));
|
||||
for (int i = 0; i < tracks_to_push.size(); i++) {
|
||||
Track *this_track = list->GetTrackAt(tracks_to_push.at(i));
|
||||
Block *this_block = this_track->Blocks().at(
|
||||
block_indexes.at(tracks_to_push.at(i)));
|
||||
|
||||
if (const SubtitleBlock *sub = dynamic_cast<const SubtitleBlock*>(this_block)) {
|
||||
if (sub->is_enabled()) {
|
||||
if (!EncodeSubtitle(sub)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const SubtitleBlock *sub =
|
||||
dynamic_cast<const SubtitleBlock *>(this_block)) {
|
||||
if (sub->is_enabled()) {
|
||||
if (!EncodeSubtitle(sub)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_indexes[tracks_to_push.at(i)]++;
|
||||
}
|
||||
} while (!tracks_to_push.isEmpty());
|
||||
}
|
||||
}
|
||||
block_indexes[tracks_to_push.at(i)]++;
|
||||
}
|
||||
} while (!tracks_to_push.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
finished_watcher_mutex_.lock();
|
||||
finished_watcher_mutex_.lock();
|
||||
|
||||
while (result && !IsCancelled()) {
|
||||
while (!finished_watchers_.empty() && !IsCancelled() && result) {
|
||||
RenderTicketWatcher* watcher = finished_watchers_.front();
|
||||
finished_watchers_.pop_front();
|
||||
while (result && !IsCancelled()) {
|
||||
while (!finished_watchers_.empty() && !IsCancelled() && result) {
|
||||
RenderTicketWatcher *watcher = finished_watchers_.front();
|
||||
finished_watchers_.pop_front();
|
||||
|
||||
finished_watcher_mutex_.unlock();
|
||||
finished_watcher_mutex_.unlock();
|
||||
|
||||
// Analyze watcher here
|
||||
RenderManager::TicketType ticket_type = watcher->GetTicket()->property("type").value<RenderManager::TicketType>();
|
||||
// Analyze watcher here
|
||||
RenderManager::TicketType ticket_type =
|
||||
watcher->GetTicket()
|
||||
->property("type")
|
||||
.value<RenderManager::TicketType>();
|
||||
|
||||
if (ticket_type == RenderManager::kTypeAudio) {
|
||||
if (ticket_type == RenderManager::kTypeAudio) {
|
||||
TimeRange range = watcher->property("range").value<TimeRange>();
|
||||
|
||||
TimeRange range = watcher->property("range").value<TimeRange>();
|
||||
if (!AudioDownloaded(range,
|
||||
watcher->Get().value<SampleBuffer>())) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (!AudioDownloaded(range, watcher->Get().value<SampleBuffer>())) {
|
||||
result = false;
|
||||
}
|
||||
// Don't count audio progress, since it's generally a lot faster than video and is weighted at
|
||||
// 50%, which makes the progress bar look weird to the uninitiated
|
||||
//progress_counter += range.length().toDouble();
|
||||
//emit ProgressChanged(progress_counter / total_length);
|
||||
|
||||
// Don't count audio progress, since it's generally a lot faster than video and is weighted at
|
||||
// 50%, which makes the progress bar look weird to the uninitiated
|
||||
//progress_counter += range.length().toDouble();
|
||||
//emit ProgressChanged(progress_counter / total_length);
|
||||
} else if (ticket_type == RenderManager::kTypeVideo &&
|
||||
TwoStepFrameRendering()) {
|
||||
if (!DownloadFrame(
|
||||
&watcher_thread, watcher->Get().value<FramePtr>(),
|
||||
watcher->property("time").value<rational>())) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
} else if (ticket_type == RenderManager::kTypeVideo && TwoStepFrameRendering()) {
|
||||
if (native_progress_signalling_) {
|
||||
progress_counter += 0.5;
|
||||
emit ProgressChanged(progress_counter / total_length);
|
||||
}
|
||||
|
||||
if (!DownloadFrame(&watcher_thread, watcher->Get().value<FramePtr>(), watcher->property("time").value<rational>())) {
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
// Assume single-step video or video download ticket
|
||||
if (!FrameDownloaded(
|
||||
watcher->Get().value<FramePtr>(),
|
||||
watcher->property("time").value<rational>())) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (native_progress_signalling_) {
|
||||
progress_counter += 0.5;
|
||||
emit ProgressChanged(progress_counter / total_length);
|
||||
}
|
||||
if (native_progress_signalling_) {
|
||||
double progress_to_add = 1.0;
|
||||
if (TwoStepFrameRendering()) {
|
||||
progress_to_add *= 0.5;
|
||||
}
|
||||
progress_counter += progress_to_add;
|
||||
|
||||
} else {
|
||||
emit ProgressChanged(progress_counter / total_length);
|
||||
}
|
||||
|
||||
// Assume single-step video or video download ticket
|
||||
if (!FrameDownloaded(watcher->Get().value<FramePtr>(), watcher->property("time").value<rational>())) {
|
||||
result = false;
|
||||
}
|
||||
if (iterator.GetNext(&next_frame)) {
|
||||
StartTicket(&watcher_thread, manager, next_frame, mode,
|
||||
cache, force_size, force_matrix, force_format,
|
||||
force_channel_count, force_color_output);
|
||||
}
|
||||
}
|
||||
|
||||
if (native_progress_signalling_) {
|
||||
double progress_to_add = 1.0;
|
||||
if (TwoStepFrameRendering()) {
|
||||
progress_to_add *= 0.5;
|
||||
}
|
||||
progress_counter += progress_to_add;
|
||||
delete watcher;
|
||||
running_watchers_.removeOne(watcher);
|
||||
|
||||
emit ProgressChanged(progress_counter / total_length);
|
||||
}
|
||||
finished_watcher_mutex_.lock();
|
||||
}
|
||||
|
||||
if (iterator.GetNext(&next_frame)) {
|
||||
StartTicket(&watcher_thread, manager, next_frame, mode, cache, force_size, force_matrix, force_format, force_channel_count, force_color_output);
|
||||
}
|
||||
if (IsCancelled() || !result) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// Run out of finished watchers. If we still have running tickets, wait for the next one to finish.
|
||||
if (running_tickets_ > 0) {
|
||||
finished_watcher_wait_cond_.wait(&finished_watcher_mutex_);
|
||||
} else {
|
||||
// No more running tickets or finished tickets, wem ust be
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete watcher;
|
||||
running_watchers_.removeOne(watcher);
|
||||
finished_watcher_mutex_.unlock();
|
||||
|
||||
finished_watcher_mutex_.lock();
|
||||
}
|
||||
if (IsCancelled() || !result) {
|
||||
// Cancel every watcher we created
|
||||
foreach (RenderTicketWatcher *watcher, running_watchers_) {
|
||||
watcher->Cancel();
|
||||
disconnect(watcher, &RenderTicketWatcher::Finished, this,
|
||||
&RenderTask::TicketDone);
|
||||
RenderManager::instance()->RemoveTicket(watcher->GetTicket());
|
||||
}
|
||||
|
||||
if (IsCancelled() || !result) {
|
||||
break;
|
||||
}
|
||||
foreach (RenderTicketWatcher *watcher, running_watchers_) {
|
||||
watcher->WaitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
// Run out of finished watchers. If we still have running tickets, wait for the next one to finish.
|
||||
if (running_tickets_ > 0) {
|
||||
finished_watcher_wait_cond_.wait(&finished_watcher_mutex_);
|
||||
} else {
|
||||
// No more running tickets or finished tickets, wem ust be
|
||||
break;
|
||||
}
|
||||
}
|
||||
watcher_thread.quit();
|
||||
watcher_thread.wait();
|
||||
|
||||
finished_watcher_mutex_.unlock();
|
||||
QMetaObject::invokeMethod(RenderManager::instance(),
|
||||
"SetAggressiveGarbageCollection",
|
||||
Q_ARG(bool, false));
|
||||
|
||||
if (IsCancelled() || !result) {
|
||||
// Cancel every watcher we created
|
||||
foreach (RenderTicketWatcher* watcher, running_watchers_) {
|
||||
watcher->Cancel();
|
||||
disconnect(watcher, &RenderTicketWatcher::Finished, this, &RenderTask::TicketDone);
|
||||
RenderManager::instance()->RemoveTicket(watcher->GetTicket());
|
||||
}
|
||||
|
||||
foreach (RenderTicketWatcher* watcher, running_watchers_) {
|
||||
watcher->WaitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
watcher_thread.quit();
|
||||
watcher_thread.wait();
|
||||
|
||||
QMetaObject::invokeMethod(RenderManager::instance(), "SetAggressiveGarbageCollection", Q_ARG(bool, false));
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RenderTask::DownloadFrame(QThread *thread, FramePtr frame, const rational &time)
|
||||
bool RenderTask::DownloadFrame(QThread *thread, FramePtr frame,
|
||||
const rational &time)
|
||||
{
|
||||
//RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
//PrepareWatcher(watcher, thread);
|
||||
//RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
//PrepareWatcher(watcher, thread);
|
||||
|
||||
//IncrementRunningTickets();
|
||||
//IncrementRunningTickets();
|
||||
|
||||
//watcher->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_->video_frame_cache(), frame, time));
|
||||
//watcher->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_->video_frame_cache(), frame, time));
|
||||
|
||||
// NOTE: Doesn't reflect the actual return result of SaveFrameToCache
|
||||
return true;
|
||||
// NOTE: Doesn't reflect the actual return result of SaveFrameToCache
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderTask::EncodeSubtitle(const SubtitleBlock *subtitle)
|
||||
{
|
||||
Q_UNUSED(subtitle)
|
||||
return true;
|
||||
Q_UNUSED(subtitle)
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderTask::PrepareWatcher(RenderTicketWatcher *watcher, QThread *thread)
|
||||
{
|
||||
watcher->moveToThread(thread);
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &RenderTask::TicketDone, Qt::DirectConnection);
|
||||
running_watchers_.append(watcher);
|
||||
watcher->moveToThread(thread);
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this,
|
||||
&RenderTask::TicketDone, Qt::DirectConnection);
|
||||
running_watchers_.append(watcher);
|
||||
}
|
||||
|
||||
void RenderTask::IncrementRunningTickets()
|
||||
{
|
||||
finished_watcher_mutex_.lock();
|
||||
running_tickets_++;
|
||||
finished_watcher_mutex_.unlock();
|
||||
finished_watcher_mutex_.lock();
|
||||
running_tickets_++;
|
||||
finished_watcher_mutex_.unlock();
|
||||
}
|
||||
|
||||
void RenderTask::StartTicket(QThread* watcher_thread, ColorManager* manager,
|
||||
const rational& time, RenderMode::Mode mode, FrameHashCache* cache,
|
||||
const QSize &force_size, const QMatrix4x4 &force_matrix,
|
||||
PixelFormat force_format, int force_channel_count,
|
||||
ColorProcessorPtr force_color_output)
|
||||
void RenderTask::StartTicket(QThread *watcher_thread, ColorManager *manager,
|
||||
const rational &time, RenderMode::Mode mode,
|
||||
FrameHashCache *cache, const QSize &force_size,
|
||||
const QMatrix4x4 &force_matrix,
|
||||
PixelFormat force_format, int force_channel_count,
|
||||
ColorProcessorPtr force_color_output)
|
||||
{
|
||||
RenderManager::RenderVideoParams rvp(viewer_->GetConnectedTextureOutput(), video_params_, audio_params_,
|
||||
time, manager, mode);
|
||||
RenderManager::RenderVideoParams rvp(viewer_->GetConnectedTextureOutput(),
|
||||
video_params_, audio_params_, time,
|
||||
manager, mode);
|
||||
|
||||
rvp.force_size = force_size;
|
||||
rvp.force_matrix = force_matrix;
|
||||
rvp.force_format = force_format;
|
||||
rvp.force_color_output = force_color_output;
|
||||
rvp.force_channel_count = force_channel_count;
|
||||
rvp.force_size = force_size;
|
||||
rvp.force_matrix = force_matrix;
|
||||
rvp.force_format = force_format;
|
||||
rvp.force_color_output = force_color_output;
|
||||
rvp.force_channel_count = force_channel_count;
|
||||
|
||||
if (cache) {
|
||||
rvp.AddCache(cache);
|
||||
}
|
||||
if (cache) {
|
||||
rvp.AddCache(cache);
|
||||
}
|
||||
|
||||
RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
watcher->setProperty("time", QVariant::fromValue(time));
|
||||
PrepareWatcher(watcher, watcher_thread);
|
||||
IncrementRunningTickets();
|
||||
watcher->SetTicket(RenderManager::instance()->RenderFrame(rvp));
|
||||
RenderTicketWatcher *watcher = new RenderTicketWatcher();
|
||||
watcher->setProperty("time", QVariant::fromValue(time));
|
||||
PrepareWatcher(watcher, watcher_thread);
|
||||
IncrementRunningTickets();
|
||||
watcher->SetTicket(RenderManager::instance()->RenderFrame(rvp));
|
||||
}
|
||||
|
||||
void RenderTask::TicketDone(RenderTicketWatcher* watcher)
|
||||
void RenderTask::TicketDone(RenderTicketWatcher *watcher)
|
||||
{
|
||||
finished_watcher_mutex_.lock();
|
||||
finished_watchers_.push_back(watcher);
|
||||
finished_watcher_wait_cond_.wakeAll();
|
||||
running_tickets_--;
|
||||
finished_watcher_mutex_.unlock();
|
||||
finished_watcher_mutex_.lock();
|
||||
finished_watchers_.push_back(watcher);
|
||||
finished_watcher_wait_cond_.wakeAll();
|
||||
running_tickets_--;
|
||||
finished_watcher_mutex_.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user