From c660c05323237bf118e6844c71703bf96be70d32 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 14 May 2019 18:12:20 +0200 Subject: [PATCH] In ImageInput, make one global update thread instead of one per image. Makes for fewer threads (obviously), and also probably makes it easier to give it an OpenGL context later. --- nageru/image_input.cpp | 77 ++++++++++++++++++++++++++---------------- nageru/image_input.h | 7 ++-- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/nageru/image_input.cpp b/nageru/image_input.cpp index 0abefe3..12789dc 100644 --- a/nageru/image_input.cpp +++ b/nageru/image_input.cpp @@ -79,9 +79,11 @@ shared_ptr ImageInput::load_image(const string &filenam } all_images[pathname] = load_image_raw(pathname); - timespec first_modified = all_images[pathname]->last_modified; - update_threads[pathname] = - thread(bind(update_thread_func, filename, pathname, first_modified)); + + if (!update_thread_started) { + update_thread = thread(update_thread_func); + update_thread_started = true; + } return all_images[pathname]; } @@ -198,44 +200,58 @@ shared_ptr ImageInput::load_image_raw(const string &pat return image; } -// Fire up a thread to update the image every second. +// Fire up a thread to update all images every second. // We could do inotify, but this is good enough for now. -void ImageInput::update_thread_func(const std::string &filename, const std::string &pathname, const timespec &first_modified) +void ImageInput::update_thread_func() { - char thread_name[16]; - snprintf(thread_name, sizeof(thread_name), "Update_%s", filename.c_str()); - pthread_setname_np(pthread_self(), thread_name); + pthread_setname_np(pthread_self(), "Update_Images"); - timespec last_modified = first_modified; struct stat buf; for ( ;; ) { { unique_lock lock(threads_should_quit_mu); threads_should_quit_modified.wait_for(lock, chrono::seconds(1), []() { return threads_should_quit; }); } - if (threads_should_quit) { return; } - if (stat(pathname.c_str(), &buf) != 0) { - fprintf(stderr, "%s: Couldn't check for new version, leaving the old in place.\n", pathname.c_str()); - continue; - } - if (buf.st_mtim.tv_sec == last_modified.tv_sec && - buf.st_mtim.tv_nsec == last_modified.tv_nsec) { - // Not changed. - continue; + // Go through all loaded images and see if they need to be updated. + // We do one pass first through the array with no I/O, to avoid + // blocking the renderer. + vector> images_to_check; + { + unique_lock lock(all_images_lock); + for (const auto &pathname_and_image : all_images) { + const string pathname = pathname_and_image.first; + const timespec last_modified = pathname_and_image.second->last_modified; + images_to_check.emplace_back(pathname, last_modified); + } } - shared_ptr image = load_image_raw(pathname); - if (image == nullptr) { - fprintf(stderr, "Couldn't load image, leaving the old in place.\n"); - continue; + + for (const auto &pathname_and_timespec : images_to_check) { + const string pathname = pathname_and_timespec.first; + const timespec last_modified = pathname_and_timespec.second; + + if (stat(pathname.c_str(), &buf) != 0) { + fprintf(stderr, "%s: Couldn't check for new version, leaving the old in place.\n", pathname.c_str()); + continue; + } + if (buf.st_mtim.tv_sec == last_modified.tv_sec && + buf.st_mtim.tv_nsec == last_modified.tv_nsec) { + // Not changed. + continue; + } + + shared_ptr image = load_image_raw(pathname); + if (image == nullptr) { + fprintf(stderr, "Couldn't load image, leaving the old in place.\n"); + continue; + } + + unique_lock lock(all_images_lock); + all_images[pathname] = image; } - fprintf(stderr, "Loaded new version of %s from disk.\n", pathname.c_str()); - lock_guard lock(all_images_lock); - all_images[pathname] = image; - last_modified = image->last_modified; } } @@ -246,14 +262,17 @@ void ImageInput::shutdown_updaters() threads_should_quit = true; threads_should_quit_modified.notify_all(); } - for (auto &it : update_threads) { - it.second.join(); + + lock_guard lock(all_images_lock); + if (update_thread_started) { + update_thread.join(); } } mutex ImageInput::all_images_lock; map> ImageInput::all_images; -map ImageInput::update_threads; +bool ImageInput::update_thread_started = false; +thread ImageInput::update_thread; mutex ImageInput::threads_should_quit_mu; bool ImageInput::threads_should_quit = false; condition_variable ImageInput::threads_should_quit_modified; diff --git a/nageru/image_input.h b/nageru/image_input.h index 6a75df4..ac3c519 100644 --- a/nageru/image_input.h +++ b/nageru/image_input.h @@ -36,10 +36,11 @@ private: static std::shared_ptr load_image(const std::string &filename, const std::string &pathname); static std::shared_ptr load_image_raw(const std::string &pathname); - static void update_thread_func(const std::string &filename, const std::string &pathname, const timespec &first_modified); + static void update_thread_func(); static std::mutex all_images_lock; - static std::map> all_images; - static std::map update_threads; + static std::map> all_images; // Under all_images_lock. + static bool update_thread_started; // Under all_images_lock. + static std::thread update_thread; // Under all_images_lock. static std::mutex threads_should_quit_mu; static bool threads_should_quit; // Under threads_should_quit_mu. -- 2.39.2