diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2022-11-08 18:29:56 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2022-11-08 18:29:56 +0300 |
commit | aa9b976e9f9b8baff194f5bfadcf9e7694cf8d15 (patch) | |
tree | b30ce7abaa65be85c147222cb074571c056b59df /source/blender/blenlib | |
parent | 410b87ca781d6b6b061bab0440005ac1ab82688f (diff) | |
parent | bbb1d3e5e7eb4059a0324ae786e1e793852963a9 (diff) |
Merge branch 'master' into temp-gpencil-automasktemp-gpencil-automask
Diffstat (limited to 'source/blender/blenlib')
-rw-r--r-- | source/blender/blenlib/BLI_cache_mutex.hh | 106 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_string.h | 7 | ||||
-rw-r--r-- | source/blender/blenlib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenlib/intern/cache_mutex.cc | 25 | ||||
-rw-r--r-- | source/blender/blenlib/intern/path_util.c | 4 | ||||
-rw-r--r-- | source/blender/blenlib/intern/string.c | 15 | ||||
-rw-r--r-- | source/blender/blenlib/intern/uuid.cc | 27 | ||||
-rw-r--r-- | source/blender/blenlib/intern/winstuff.c | 14 |
8 files changed, 177 insertions, 23 deletions
diff --git a/source/blender/blenlib/BLI_cache_mutex.hh b/source/blender/blenlib/BLI_cache_mutex.hh new file mode 100644 index 00000000000..8e2a0d1b1a5 --- /dev/null +++ b/source/blender/blenlib/BLI_cache_mutex.hh @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * A #CacheMutex is used to protect a lazily computed cache from being computed more than once. + * Using #CacheMutex instead of a "raw mutex" to protect a cache has some benefits: + * - Avoid common pitfalls like forgetting to use task isolation or a double checked lock. + * - Cleaner and less redundant code because the same locking patterns don't have to be repeated + * everywhere. + * - One can benefit from potential future improvements to #CacheMutex of which there are a few + * mentioned below. + * + * The data protected by #CacheMutex is not part of #CacheMutex. Instead, the #CacheMutex and its + * protected data should generally be placed next to each other. + * + * Each #CacheMutex protects exactly one cache, so multiple cache mutexes have to be used when a + * class has multiple caches. That is contrary to a "custom" solution using `std::mutex` where one + * mutex could protect multiple caches at the cost of higher lock contention. + * + * To make sure the cache is up to date, call `CacheMutex::ensure` and pass in the function that + * computes the cache. + * + * To tell the #CacheMutex that the cache is invalidated and to be re-evaluated upon next access + * use `CacheMutex::tag_dirty`. + * + * This example shows how one could implement a lazily computed average vertex position in an + * imaginary `Mesh` data structure: + * + * \code{.cpp} + * class Mesh { + * private: + * mutable CacheMutex average_position_cache_mutex_; + * mutable float3 average_position_cache_; + * + * public: + * const float3 &average_position() const + * { + * average_position_cache_mutex_.ensure([&]() { + * average_position_cache_ = actually_compute_average_position(); + * }); + * return average_position_cache_; + * } + * + * void tag_positions_changed() + * { + * average_position_cache_mutex_.tag_dirty(); + * } + * }; + * \endcode + * + * Possible future improvements: + * - Avoid task isolation when we know that the cache computation does not use threading. + * - Try to use a smaller mutex. The mutex does not have to be fair for this use case. + * - Try to join the cache computation instead of blocking if another thread is computing the cache + * already. + */ + +#include <atomic> +#include <mutex> + +#include "BLI_function_ref.hh" + +namespace blender { + +class CacheMutex { + private: + std::mutex mutex_; + std::atomic<bool> cache_valid_ = false; + + public: + /** + * Make sure the cache exists and is up to date. This calls `compute_cache` once to update the + * cache (which is stored outside of this class) if it is dirty, otherwise it does nothing. + * + * This function is thread-safe under the assumption that the same parameters are passed from + * every thread. + */ + void ensure(FunctionRef<void()> compute_cache); + + /** + * Reset the cache. The next time #ensure is called, it will recompute that code. + */ + void tag_dirty() + { + cache_valid_.store(false); + } + + /** + * Return true if the cache currently does not exist or has been invalidated. + */ + bool is_dirty() const + { + return !this->is_cached(); + } + + /** + * Return true if the cache exists and is valid. + */ + bool is_cached() const + { + return cache_valid_.load(std::memory_order_relaxed); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 17abcf52ecc..fb02ea5fb17 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -206,6 +206,13 @@ char *BLI_sprintfN(const char *__restrict format, ...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1, 2); /** + * A wrapper around ::sprintf() which does not generate security warnings. + * + * \note Use BLI_snprintf for cases when the string size is known. + */ +int BLI_sprintf(char *__restrict str, const char *__restrict format, ...); + +/** * This roughly matches C and Python's string escaping with double quotes - `"`. * * Since every character may need escaping, diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 2ac77f000e9..693a4d98675 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -54,6 +54,7 @@ set(SRC intern/bitmap_draw_2d.c intern/boxpack_2d.c intern/buffer.c + intern/cache_mutex.cc intern/compute_context.cc intern/convexhull_2d.c intern/cpp_type.cc @@ -178,6 +179,7 @@ set(SRC BLI_bounds.hh BLI_boxpack_2d.h BLI_buffer.h + BLI_cache_mutex.hh BLI_color.hh BLI_color_mix.hh BLI_compiler_attrs.h diff --git a/source/blender/blenlib/intern/cache_mutex.cc b/source/blender/blenlib/intern/cache_mutex.cc new file mode 100644 index 00000000000..db474b1ef87 --- /dev/null +++ b/source/blender/blenlib/intern/cache_mutex.cc @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_cache_mutex.hh" +#include "BLI_task.hh" + +namespace blender { + +void CacheMutex::ensure(const FunctionRef<void()> compute_cache) +{ + if (cache_valid_.load(std::memory_order_acquire)) { + return; + } + std::scoped_lock lock{mutex_}; + /* Double checked lock. */ + if (cache_valid_.load(std::memory_order_relaxed)) { + return; + } + /* Use task isolation because a mutex is locked and the cache computation might use + * multi-threading. */ + threading::isolate_task(compute_cache); + + cache_valid_.store(true, std::memory_order_release); +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index d13f3fe5ced..2376bd82b69 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -123,7 +123,7 @@ int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort void BLI_path_sequence_encode( char *string, const char *head, const char *tail, ushort numlen, int pic) { - sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); + BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); } static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */ @@ -620,7 +620,7 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char } BLI_strncpy(extension, string + a, sizeof(extension)); - sprintf(string + a, "%s%s%s", sep, suffix, extension); + BLI_sprintf(string + a, "%s%s%s", sep, suffix, extension); return true; } diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 755d2dbd55d..3c3dcaf90f4 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -241,6 +241,17 @@ char *BLI_sprintfN(const char *__restrict format, ...) return n; } +int BLI_sprintf(char *__restrict str, const char *__restrict format, ...) +{ + va_list arg; + + va_start(arg, format); + const int result = vsprintf(str, format, arg); + va_end(arg); + + return result; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1114,7 +1125,7 @@ static size_t BLI_str_format_int_grouped_ex(char src[16], char dst[16], int num_ size_t BLI_str_format_int_grouped(char dst[16], int num) { char src[16]; - int num_len = sprintf(src, "%d", num); + const int num_len = BLI_snprintf(src, sizeof(src), "%d", num); return BLI_str_format_int_grouped_ex(src, dst, num_len); } @@ -1124,7 +1135,7 @@ size_t BLI_str_format_uint64_grouped(char dst[16], uint64_t num) /* NOTE: Buffer to hold maximum `uint64`, which is 1.8e+19. but * we also need space for commas and null-terminator. */ char src[27]; - int num_len = sprintf(src, "%" PRIu64 "", num); + const int num_len = BLI_snprintf(src, sizeof(src), "%" PRIu64 "", num); return BLI_str_format_int_grouped_ex(src, dst, num_len); } diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index 023dd1ec409..b845208f0da 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -5,6 +5,7 @@ */ #include "BLI_assert.h" +#include "BLI_string.h" #include "BLI_uuid.h" #include <cstdio> @@ -85,19 +86,19 @@ bool BLI_uuid_equal(const bUUID uuid1, const bUUID uuid2) void BLI_uuid_format(char *buffer, const bUUID uuid) { - std::sprintf(buffer, - "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - uuid.time_low, - uuid.time_mid, - uuid.time_hi_and_version, - uuid.clock_seq_hi_and_reserved, - uuid.clock_seq_low, - uuid.node[0], - uuid.node[1], - uuid.node[2], - uuid.node[3], - uuid.node[4], - uuid.node[5]); + BLI_sprintf(buffer, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, + uuid.time_mid, + uuid.time_hi_and_version, + uuid.clock_seq_hi_and_reserved, + uuid.clock_seq_low, + uuid.node[0], + uuid.node[1], + uuid.node[2], + uuid.node[3], + uuid.node[4], + uuid.node[5]); } bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index 7e2c5e8f1dd..3a574b60ae2 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -110,7 +110,7 @@ bool BLI_windows_register_blend_extension(const bool background) &hkey, &dwd); if (lresult == ERROR_SUCCESS) { - sprintf(buffer, "\"%s\" \"%%1\"", BlPath); + BLI_snprintf(buffer, sizeof(buffer), "\"%s\" \"%%1\"", BlPath); lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); RegCloseKey(hkey); } @@ -129,7 +129,7 @@ bool BLI_windows_register_blend_extension(const bool background) &hkey, &dwd); if (lresult == ERROR_SUCCESS) { - sprintf(buffer, "\"%s\", 1", BlPath); + BLI_snprintf(buffer, sizeof(buffer), "\"%s\", 1", BlPath); lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); RegCloseKey(hkey); } @@ -167,10 +167,12 @@ bool BLI_windows_register_blend_extension(const bool background) RegCloseKey(root); printf("success (%s)\n", usr_mode ? "user" : "system"); if (!background) { - sprintf(MBox, - "File extension registered for %s.", - usr_mode ? "the current user. To register for all users, run as an administrator" : - "all users"); + BLI_snprintf(MBox, + sizeof(MBox), + "File extension registered for %s.", + usr_mode ? + "the current user. To register for all users, run as an administrator" : + "all users"); MessageBox(0, MBox, "Blender", MB_OK | MB_ICONINFORMATION); } return true; |