Преглед изворни кода

Removing `Lock` and converting build_data/ to SrwLock

Kenny Mecham пре 2 недеља
родитељ
комит
24ebfa4608

+ 10 - 6
Sunrise/src/state/build_data/abilities/ability_bucket_catalog.cpp

@@ -1,11 +1,15 @@
 #include "ability_bucket_catalog.h"
 
+#include <mutex>
+#include <shared_mutex>
+
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::abilities {
 namespace {
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 
 /** @return True when both rows name the same subclass selection. */
@@ -18,7 +22,7 @@ Table<Definition, kDefinitionCapacity> g_definitions;
 
 /** Clears every generated ability bucket row under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
 }
 
@@ -42,7 +46,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
     if (!valid(definitions)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     return g_definitions.replace(definitions);
 }
 
@@ -52,7 +56,7 @@ bool find(std::uint16_t socketEntryListIndex,
           Definition& definition) noexcept {
     definition = {};
     const Definition wanted{socketEntryListIndex, selection};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     for (const Definition& row : g_definitions.rows()) {
         if (same_key(row, wanted)) {
             definition = row;
@@ -64,13 +68,13 @@ bool find(std::uint16_t socketEntryListIndex,
 
 /** Copies every row in publication order. */
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 /** @return Number of generated ability bucket rows, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 

+ 9 - 7
Sunrise/src/state/build_data/collectibles/collectible_catalog.cpp

@@ -1,20 +1,22 @@
 #include "collectible_catalog.h"
 
 #include <array>
+#include <shared_mutex>
 
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::collectibles {
 namespace {
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 
 } // namespace
 
 /** Clears the table while no reader can observe a partial replacement. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
 }
 
@@ -66,7 +68,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
     if (!valid(definitions)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     const std::span<Definition> storage = g_definitions.reset(definitions.size());
     if (storage.size() != definitions.size()) {
         return false;
@@ -81,7 +83,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
 bool find(std::uint16_t collectibleIndex, Definition& definition) noexcept {
     definition = {};
     definition.itemDefinitionIndex = kUnavailableItemDefinitionIndex;
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     const bool found = static_cast<std::size_t>(collectibleIndex) < rows.size();
     if (found) {
@@ -95,7 +97,7 @@ bool grants_item(std::uint16_t itemDefinitionIndex) noexcept {
     if (itemDefinitionIndex == kUnavailableItemDefinitionIndex) {
         return false;
     }
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     for (const Definition& definition : g_definitions.rows()) {
         if (definition.itemDefinitionIndex == itemDefinitionIndex) {
             return true;
@@ -106,13 +108,13 @@ bool grants_item(std::uint16_t itemDefinitionIndex) noexcept {
 
 /** Copies the dense rows without exposing catalog storage. */
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 /** @return Number of published rows, read under the catalog lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 

+ 8 - 4
Sunrise/src/state/build_data/constants/investment_constant_catalog.cpp

@@ -1,19 +1,23 @@
 #include "investment_constant_catalog.h"
 
+#include <mutex>
+#include <shared_mutex>
+
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::constants {
 namespace {
 
 // One row, not a table, so it holds the value directly under the shared Lock.
-Lock g_lock;
+core::threading::SrwLock g_lock;
 InvestmentConstants g_constants{};
 
 } // namespace
 
 /** Clears the published investment constants under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_constants = {};
 }
 
@@ -22,14 +26,14 @@ bool replace(const InvestmentConstants& value) noexcept {
     if (!valid(value)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_constants = value;
     return true;
 }
 
 /** @param value Receives the published constants. @return True when a row is published. */
 bool find(InvestmentConstants& value) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     value = g_constants;
     return value.extracted;
 }

+ 8 - 6
Sunrise/src/state/build_data/hash_names/hash_name_catalog.cpp

@@ -1,13 +1,15 @@
 #include "hash_name_catalog.h"
 
 #include <algorithm>
+#include <shared_mutex>
 
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::hash_names {
 namespace {
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Name, kNameCapacity> g_names;
 
 /** @return True when the name is a valid identifier and fits its storage. */
@@ -30,7 +32,7 @@ Table<Name, kNameCapacity> g_names;
 
 /** Clears every resolved bubble name under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_names.clear();
 }
 
@@ -53,14 +55,14 @@ bool replace(std::span<const Name> names) noexcept {
     if (!valid(names)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     return g_names.replace(names);
 }
 
 /** Finds one bubble name by its hash. */
 bool find(std::uint32_t hash, Name& name) noexcept {
     name = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Name> rows = g_names.rows();
     const auto found =
         std::lower_bound(rows.begin(), rows.end(), hash, [](const Name& row, std::uint32_t key) {
@@ -75,13 +77,13 @@ bool find(std::uint32_t hash, Name& name) noexcept {
 
 /** Copies every row in ascending hash order. */
 bool snapshot(std::span<Name> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_names.snapshot(output, count);
 }
 
 /** @return Number of resolved names, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_names.count();
 }
 

+ 9 - 6
Sunrise/src/state/build_data/inventory/buckets/inventory_bucket_catalog.cpp

@@ -4,8 +4,11 @@
 #include <array>
 #include <bitset>
 #include <limits>
+#include <mutex>
+#include <shared_mutex>
 
 #include "../../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::inventory::buckets {
 namespace {
@@ -13,7 +16,7 @@ namespace {
 /** An all-one row marks a bucket id with no published descriptor. */
 constexpr std::uint16_t kEmptyLookupRow = (std::numeric_limits<std::uint16_t>::max)();
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Descriptor, kDescriptorCapacity> g_descriptors;
 // Bucket id to descriptor row, rebuilt with the table under the same exclusive hold.
 std::array<std::uint16_t, kDescriptorCapacity> g_lookup{};
@@ -64,7 +67,7 @@ constexpr std::size_t kEquipmentSlotCount = 19;
 
 /** Clears every generated inventory-bucket descriptor under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_descriptors.clear();
     std::fill(g_lookup.begin(), g_lookup.end(), kEmptyLookupRow);
 }
@@ -103,7 +106,7 @@ bool replace(std::span<const Descriptor> descriptors) noexcept {
         return false;
     }
 
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     std::fill(g_lookup.begin(), g_lookup.end(), kEmptyLookupRow);
     if (!g_descriptors.replace(descriptors)) {
         return false;
@@ -121,7 +124,7 @@ bool find(std::uint8_t bucketId, Descriptor& descriptor) noexcept {
         return false;
     }
 
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Descriptor> rows = g_descriptors.rows();
     const std::uint16_t row = g_lookup[bucketId];
     const bool found = row != kEmptyLookupRow && row < rows.size();
@@ -133,13 +136,13 @@ bool find(std::uint8_t bucketId, Descriptor& descriptor) noexcept {
 
 /** Copies descriptors in publication order, without exposing the catalog storage. */
 bool snapshot(std::span<Descriptor> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_descriptors.snapshot(output, count);
 }
 
 /** @return Number of inventory-bucket descriptors, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_descriptors.count();
 }
 

+ 8 - 6
Sunrise/src/state/build_data/items/details/item_detail_catalog.cpp

@@ -4,11 +4,13 @@
 #include <array>
 #include <bitset>
 #include <limits>
+#include <shared_mutex>
 #include <span>
 #include <vector>
 
 #include "../../table.h"
 #include "../item_catalog.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::items::details {
 namespace {
@@ -19,7 +21,7 @@ constexpr std::size_t kNativeDefinitionIndexCapacity =
 /** An all-one row marks a native index with no published configured detail. */
 constexpr std::uint16_t kEmptyLookupRow = (std::numeric_limits<std::uint16_t>::max)();
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 std::vector<Definition> g_definitions;
 std::size_t g_definitionCount{};
 // Native definition index to detail row, rebuilt with the table under the same exclusive hold.
@@ -83,7 +85,7 @@ static_assert(kDefinitionCapacity < kEmptyLookupRow);
 
 /** Clears every generated configured item detail under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
     g_definitions.shrink_to_fit();
     g_definitionCount = 0;
@@ -114,7 +116,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
 
     std::vector<Definition> staged(definitions.begin(), definitions.end());
 
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     std::fill(g_lookup.begin(), g_lookup.end(), kEmptyLookupRow);
     for (std::size_t index = 0; index < definitions.size(); ++index) {
         g_lookup[definitions[index].definitionIndex] = static_cast<std::uint16_t>(index);
@@ -127,7 +129,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
 /** Finds one configured item detail by native definition index. */
 bool find(std::uint16_t definitionIndex, Definition& definition) noexcept {
     definition = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows{g_definitions.data(), g_definitionCount};
     const std::uint16_t row = g_lookup[definitionIndex];
     const bool found = row != kEmptyLookupRow && row < rows.size();
@@ -139,7 +141,7 @@ bool find(std::uint16_t definitionIndex, Definition& definition) noexcept {
 
 /** Copies details in publication order, without exposing the catalog storage. */
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     count = 0;
     if (output.size() < g_definitionCount) {
         return false;
@@ -153,7 +155,7 @@ bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
 
 /** @return Number of configured item details, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitionCount;
 }
 

+ 11 - 8
Sunrise/src/state/build_data/items/item_catalog.cpp

@@ -3,8 +3,11 @@
 #include <algorithm>
 #include <array>
 #include <limits>
+#include <mutex>
+#include <shared_mutex>
 
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::items {
 namespace {
@@ -20,7 +23,7 @@ constexpr std::uint64_t kHashPrime = 1099511628211ULL;
 /** Four definition-hash bytes precede the bucket byte in the lookup key. */
 constexpr std::size_t kDefinitionHashByteCount = sizeof(std::uint32_t);
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 // Open-addressed probes into the dense rows, rebuilt with them under the same exclusive hold.
 std::array<std::uint16_t, kLookupCapacity> g_lookup{};
@@ -87,7 +90,7 @@ void insert_hash_lookup(const Definition& definition) noexcept {
 
 /** Clears every generated item mapping under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
     std::fill(g_lookup.begin(), g_lookup.end(), kEmptyLookupRow);
     std::fill(g_hashLookup.begin(), g_hashLookup.end(), kEmptyLookupRow);
@@ -114,7 +117,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
     if (!valid(definitions)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     std::fill(g_lookup.begin(), g_lookup.end(), kEmptyLookupRow);
     std::fill(g_hashLookup.begin(), g_hashLookup.end(), kEmptyLookupRow);
     // valid() proved each index appears once, so every row lands in its own slot.
@@ -139,7 +142,7 @@ bool find_hash(std::uint32_t definitionHash, Definition& definition) noexcept {
     const std::size_t start = start_hash_slot(definitionHash);
     std::uint16_t match = kEmptyLookupRow;
     bool ambiguous = false;
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     for (std::size_t probe = 0; probe < g_hashLookup.size(); ++probe) {
         const std::uint16_t row = g_hashLookup[(start + probe) & (g_hashLookup.size() - 1)];
@@ -167,7 +170,7 @@ bool find(std::uint32_t definitionHash, std::uint8_t bucketId, Definition& defin
     const std::size_t start = start_slot(key);
     std::uint16_t match = kEmptyLookupRow;
     bool ambiguous = false;
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     for (std::size_t probe = 0; probe < g_lookup.size(); ++probe) {
         const std::uint16_t row = g_lookup[(start + probe) & (g_lookup.size() - 1)];
@@ -189,7 +192,7 @@ bool find(std::uint32_t definitionHash, std::uint8_t bucketId, Definition& defin
 /** Finds one dense installed-build row by its native definition index. */
 bool find_index(std::uint16_t definitionIndex, Definition& definition) noexcept {
     definition = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     const bool found = static_cast<std::size_t>(definitionIndex) < rows.size();
     if (found) {
@@ -200,13 +203,13 @@ bool find_index(std::uint16_t definitionIndex, Definition& definition) noexcept
 
 /** Copies the dense rows in native-index order, without exposing the catalog storage. */
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 /** @return Number of installed-build item mappings, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 

+ 10 - 8
Sunrise/src/state/build_data/items/socket_plugs/socket_plug_catalog.cpp

@@ -2,13 +2,15 @@
 
 #include <algorithm>
 #include <bitset>
+#include <shared_mutex>
 
 #include "../../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::items::socket_plugs {
 namespace {
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Rule, kRuleCapacity> g_rules;
 Table<Pool, kPoolCapacity> g_pools;
 Table<Member, kMemberCapacity> g_members;
@@ -24,7 +26,7 @@ std::bitset<details::kDefinitionCapacity> g_membership;
 
 /** Clears the complete socket-plug catalog under one exclusive hold. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_rules.clear();
     g_pools.clear();
     g_members.clear();
@@ -78,7 +80,7 @@ bool replace(std::span<const Rule> rules,
     for (const Member member : members) {
         membership.set(member);
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     if (!g_rules.replace(rules) || !g_pools.replace(pools) || !g_members.replace(members)) {
         return false;
     }
@@ -93,7 +95,7 @@ bool allowed(std::uint16_t itemDefinitionIndex,
     if (lane >= kLaneCapacity) {
         return false;
     }
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const auto rules = g_rules.rows();
     const auto pools = g_pools.rows();
     const auto members = g_members.rows();
@@ -120,7 +122,7 @@ bool visit_pool(std::uint16_t itemDefinitionIndex,
     if (lane >= kLaneCapacity || visitor == nullptr) {
         return false;
     }
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const auto rules = g_rules.rows();
     const auto pools = g_pools.rows();
     const auto members = g_members.rows();
@@ -145,7 +147,7 @@ bool visit_pool(std::uint16_t itemDefinitionIndex,
 
 /** Answers whether one definition occurs in any installed ordinary-socket plug pool. */
 bool contains(Member plugDefinitionIndex) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return plugDefinitionIndex < g_membership.size() && g_membership.test(plugDefinitionIndex);
 }
 
@@ -159,14 +161,14 @@ bool snapshot(std::span<Rule> rules,
     ruleCount = 0;
     poolCount = 0;
     memberCount = 0;
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_rules.snapshot(rules, ruleCount) && g_pools.snapshot(pools, poolCount)
            && g_members.snapshot(members, memberCount);
 }
 
 /** Reports the published rule count under the catalog lock. */
 std::size_t rule_count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_rules.count();
 }
 

+ 9 - 6
Sunrise/src/state/build_data/material_requirements/material_requirement_catalog.cpp

@@ -1,19 +1,22 @@
 #include "material_requirement_catalog.h"
 
 #include <array>
+#include <mutex>
+#include <shared_mutex>
 
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::material_requirements {
 namespace {
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 
 } // namespace
 
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
 }
 
@@ -68,7 +71,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
     if (!valid(definitions)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     const std::span<Definition> storage = g_definitions.reset(definitions.size());
     if (storage.size() != definitions.size()) {
         return false;
@@ -81,7 +84,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
 
 bool find(std::uint16_t requirementSetIndex, Definition& definition) noexcept {
     definition = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     const bool found = static_cast<std::size_t>(requirementSetIndex) < rows.size();
     if (found) {
@@ -91,12 +94,12 @@ bool find(std::uint16_t requirementSetIndex, Definition& definition) noexcept {
 }
 
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 

+ 9 - 6
Sunrise/src/state/build_data/progressions/progression_catalog.cpp

@@ -1,18 +1,21 @@
 #include "progression_catalog.h"
 
+#include <shared_mutex>
+
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::progressions {
 namespace {
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 
 } // namespace
 
 /** Clears every generated progression definition under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
 }
 
@@ -34,14 +37,14 @@ bool replace(std::span<const Definition> definitions) noexcept {
     if (!valid(definitions)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     return g_definitions.replace(definitions);
 }
 
 /** Lists the definition index each slot of one scope's record array carries. */
 bool slots(Scope scope, std::span<std::uint16_t> output, std::size_t& count) noexcept {
     count = 0;
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     bool complete = !rows.empty();
     for (const Definition& row : rows) {
@@ -65,13 +68,13 @@ bool slots(Scope scope, std::span<std::uint16_t> output, std::size_t& count) noe
 
 /** Copies every row in native definition order. */
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 /** @return Number of generated progression definitions, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 

+ 13 - 9
Sunrise/src/state/build_data/scenarios/scenario_catalog.cpp

@@ -1,12 +1,16 @@
 #include "scenario_catalog.h"
 
+#include <mutex>
+#include <shared_mutex>
+
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::scenarios {
 namespace {
 
 // One lock covers both tables: a reader must never see new layouts against old roster groups.
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 Table<RosterGroup, kRosterGroupCapacity> g_groups;
 
@@ -83,7 +87,7 @@ Table<RosterGroup, kRosterGroupCapacity> g_groups;
 
 /** Clears every extracted destination layout and roster group under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
     g_groups.clear();
 }
@@ -121,7 +125,7 @@ bool replace(std::span<const Definition> definitions,
     if (!valid(definitions, groups)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     // Both run, with no short-circuit, so the pair cannot be left half replaced. valid() already
     // checked each against its size, which is the only reason either can refuse.
     const bool storedDefinitions = g_definitions.replace(definitions);
@@ -132,7 +136,7 @@ bool replace(std::span<const Definition> definitions,
 /** Copies one roster group by table index. */
 bool group(std::size_t index, RosterGroup& group) noexcept {
     group = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const RosterGroup> rows = g_groups.rows();
     const bool present = index < rows.size();
     if (present) {
@@ -143,13 +147,13 @@ bool group(std::size_t index, RosterGroup& group) noexcept {
 
 /** @return Published roster group count. */
 std::size_t group_count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_groups.count();
 }
 
 /** Copies every roster group in extraction order. */
 bool snapshot_groups(std::span<RosterGroup> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_groups.snapshot(output, count);
 }
 
@@ -159,7 +163,7 @@ bool find(std::string_view name, Definition& definition) noexcept {
     if (name.empty() || name.size() > kNameCapacity) {
         return false;
     }
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     for (const Definition& row : g_definitions.rows()) {
         if (name_of(row) == name) {
             definition = row;
@@ -171,13 +175,13 @@ bool find(std::string_view name, Definition& definition) noexcept {
 
 /** Copies every row in extraction order. */
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 /** @return The number of extracted destination layouts, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 

+ 9 - 5
Sunrise/src/state/build_data/socket_entry_buckets/socket_entry_bucket_catalog.cpp

@@ -1,18 +1,22 @@
 #include "socket_entry_bucket_catalog.h"
 
+#include <mutex>
+#include <shared_mutex>
+
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::socket_entry_buckets {
 namespace {
 
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 
 } // namespace
 
 /** Clears every resolved entry-bucket row under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
 }
 
@@ -36,14 +40,14 @@ bool replace(std::span<const Definition> definitions) noexcept {
     if (!valid(definitions)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     return g_definitions.replace(definitions);
 }
 
 /** Finds one socket-entry list's resolved per-entry ability-bucket destinations. */
 bool find(std::uint16_t socketEntryListIndex, Definition& definition) noexcept {
     definition = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     for (const Definition& row : g_definitions.rows()) {
         if (row.socketEntryListIndex == socketEntryListIndex) {
             definition = row;
@@ -55,7 +59,7 @@ bool find(std::uint16_t socketEntryListIndex, Definition& definition) noexcept {
 
 /** @return Number of resolved entry-bucket rows, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 

+ 12 - 9
Sunrise/src/state/build_data/socket_entry_lists/socket_entry_list_catalog.cpp

@@ -1,14 +1,17 @@
 #include "socket_entry_list_catalog.h"
 
 #include <array>
+#include <mutex>
+#include <shared_mutex>
 
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::socket_entry_lists {
 namespace {
 
 // One lock covers both tables: an entry table is only meaningful against its own list rows.
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Definition, kDefinitionCapacity> g_definitions;
 Table<EntryTable, kEntryTableCapacity> g_entryTables;
 
@@ -28,7 +31,7 @@ Table<EntryTable, kEntryTableCapacity> g_entryTables;
 
 /** Clears every generated socket-entry-list mapping under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_definitions.clear();
     g_entryTables.clear();
 }
@@ -56,7 +59,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
         return false;
     }
 
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     // valid() proved every index in the input range appears once, so each row lands once.
     const std::span<Definition> storage = g_definitions.reset(definitions.size());
     if (storage.size() != definitions.size()) {
@@ -71,7 +74,7 @@ bool replace(std::span<const Definition> definitions) noexcept {
 /** Finds one socket-entry-list mapping by native definition index. */
 bool find(std::uint16_t definitionIndex, Definition& definition) noexcept {
     definition = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     const bool found = definitionIndex < rows.size();
     if (found) {
@@ -82,13 +85,13 @@ bool find(std::uint16_t definitionIndex, Definition& definition) noexcept {
 
 /** Copies dense native-index rows without handing out mutable catalog storage. */
 bool snapshot(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 /** @return The number of complete socket-entry-list mappings, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 
@@ -115,14 +118,14 @@ bool replace_entry_tables(std::span<const EntryTable> tables) noexcept {
     if (!valid_entry_tables(tables)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     return g_entryTables.replace(tables);
 }
 
 /** Finds one list's per-entry selection inputs. */
 bool find_entry_table(std::uint16_t definitionIndex, EntryTable& table) noexcept {
     table = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     for (const EntryTable& row : g_entryTables.rows()) {
         if (row.definitionIndex == definitionIndex) {
             table = row;
@@ -134,7 +137,7 @@ bool find_entry_table(std::uint16_t definitionIndex, EntryTable& table) noexcept
 
 /** Copies every kept entry table. */
 bool snapshot_entry_tables(std::span<EntryTable> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_entryTables.snapshot(output, count);
 }
 

+ 15 - 13
Sunrise/src/state/build_data/spawn_sets/spawn_set_catalog.cpp

@@ -3,9 +3,11 @@
 #include <algorithm>
 #include <cmath>
 #include <limits>
+#include <shared_mutex>
 #include <string_view>
 
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::spawn_sets {
 namespace {
@@ -15,7 +17,7 @@ constexpr float kPositionBound = 1.0e9F;
 
 // One lock covers all three tables. A stem names a hash range and a point names a stem row, so
 // replacing one alone would leave a reader resolving past the end.
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<Stem, kStemCapacity> g_stems;
 Table<NameHash, kNameHashCapacity> g_nameHashes;
 Table<Point, kPointCapacity> g_points;
@@ -98,7 +100,7 @@ Table<Point, kPointCapacity> g_points;
 
 /** Clears every extracted stem, name-hash and point row under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_stems.clear();
     g_nameHashes.clear();
     g_points.clear();
@@ -146,7 +148,7 @@ bool replace(std::span<const Stem> stems,
     if (!valid(stems, nameHashes) || !valid_points(points, stems)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     // All three run with no short-circuit, so the set is never left half replaced.
     const bool storedStems = g_stems.replace(stems);
     const bool storedHashes = g_nameHashes.replace(nameHashes);
@@ -161,7 +163,7 @@ bool nearest_point(std::string_view stem,
                    float& distance) noexcept {
     point = {};
     distance = 0.0F;
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     std::size_t stemIndex = 0;
     if (!stem_index_locked(stem, stemIndex)) {
         return false;
@@ -188,20 +190,20 @@ bool nearest_point(std::string_view stem,
 
 /** Copies the whole point bank. */
 bool snapshot_points(std::span<Point> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_points.snapshot(output, count);
 }
 
 /** @return The point row count, read under the lock. */
 std::size_t point_count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_points.count();
 }
 
 /** Finds one stem by its normalized name. */
 bool find(std::string_view name, Stem& stem) noexcept {
     stem = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Stem> rows = g_stems.rows();
     const auto found =
         std::lower_bound(rows.begin(), rows.end(), name, [](const Stem& row, auto key) {
@@ -217,7 +219,7 @@ bool find(std::string_view name, Stem& stem) noexcept {
 /** Finds one spawn-name hash inside one stem. */
 bool find_hash(std::string_view stem, std::uint32_t value, NameHash& nameHash) noexcept {
     nameHash = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Stem> stemRows = g_stems.rows();
     const auto foundStem =
         std::lower_bound(stemRows.begin(), stemRows.end(), stem, [](const Stem& row, auto key) {
@@ -241,7 +243,7 @@ bool find_hash(std::string_view stem, std::uint32_t value, NameHash& nameHash) n
 /** Copies the name-hash rows one stem owns. */
 bool stem_hashes(const Stem& stem, std::span<NameHash> output, std::size_t& count) noexcept {
     count = 0;
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const NameHash> bank = g_nameHashes.rows();
     const std::size_t offset = stem.nameHashOffset;
     const std::size_t rows = stem.nameHashCount;
@@ -257,25 +259,25 @@ bool stem_hashes(const Stem& stem, std::span<NameHash> output, std::size_t& coun
 
 /** Copies every stem row in ascending name order. */
 bool snapshot(std::span<Stem> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_stems.snapshot(output, count);
 }
 
 /** Copies the whole flat name-hash bank. */
 bool snapshot_hashes(std::span<NameHash> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_nameHashes.snapshot(output, count);
 }
 
 /** @return The stem row count, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_stems.count();
 }
 
 /** @return The name-hash row count, read under the lock. */
 std::size_t hash_count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_nameHashes.count();
 }
 

+ 0 - 48
Sunrise/src/state/build_data/table.h

@@ -9,54 +9,6 @@
 
 namespace sunrise::state::build_data {
 
-/**
- * Reader/writer lock guarding one domain's published rows.
- * The lock sits apart from the storage so a domain with several arrays holds one lock across all
- * of them. A reader then never sees one array replaced and another not.
- */
-class Lock final {
-public:
-    /** Holds the lock for reading until it leaves scope. */
-    class Shared final {
-    public:
-        explicit Shared(const Lock& owner) noexcept : owner_(owner) {
-            AcquireSRWLockShared(&owner_.lock_);
-        }
-        ~Shared() {
-            ReleaseSRWLockShared(&owner_.lock_);
-        }
-        Shared(const Shared&) = delete;
-        Shared(Shared&&) = delete;
-        Shared& operator=(const Shared&) = delete;
-        Shared& operator=(Shared&&) = delete;
-
-    private:
-        const Lock& owner_;
-    };
-
-    /** Holds the lock for writing until it leaves scope. */
-    class Exclusive final {
-    public:
-        explicit Exclusive(Lock& owner) noexcept : owner_(owner) {
-            AcquireSRWLockExclusive(&owner_.lock_);
-        }
-        ~Exclusive() {
-            ReleaseSRWLockExclusive(&owner_.lock_);
-        }
-        Exclusive(const Exclusive&) = delete;
-        Exclusive(Exclusive&&) = delete;
-        Exclusive& operator=(const Exclusive&) = delete;
-        Exclusive& operator=(Exclusive&&) = delete;
-
-    private:
-        Lock& owner_;
-    };
-
-private:
-    // The acquire calls take a non-const pointer, and a shared hold does not modify the rows.
-    mutable SRWLOCK lock_{SRWLOCK_INIT};
-};
-
 /**
  * Fixed row storage for one published table.
  * The caller must already hold the domain's Lock: exclusive to write, shared to read. Storage is

+ 18 - 16
Sunrise/src/state/build_data/vendors/vendor_catalog.cpp

@@ -1,15 +1,17 @@
 #include "vendor_catalog.h"
 
 #include <algorithm>
+#include <shared_mutex>
 
 #include "../table.h"
+#include "core/threading/srw_lock.h"
 
 namespace sunrise::state::build_data::vendors {
 namespace {
 
 // One lock covers all four tables. A definition names its rows by range, so a reader must
 // never see one table replaced and another not.
-Lock g_lock;
+core::threading::SrwLock g_lock;
 Table<IndexEntry, kIndexCapacity> g_index;
 Table<Definition, kDefinitionCapacity> g_definitions;
 Table<SaleRow, kSaleRowCapacity> g_saleRows;
@@ -147,7 +149,7 @@ template <typename Row>
 
 /** Clears the index, every held definition, and both row banks under the catalog lock. */
 void clear() noexcept {
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     g_index.clear();
     g_definitions.clear();
     g_saleRows.clear();
@@ -195,7 +197,7 @@ bool replace(std::span<const IndexEntry> index,
     if (!valid(index, definitions, saleRows, installedRows)) {
         return false;
     }
-    const Lock::Exclusive guard(g_lock);
+    const std::lock_guard guard(g_lock);
     // All four run with no short-circuit, so the set cannot be left half replaced. Capacity is
     // the only reason one can refuse, and valid() already checked it.
     const bool storedIndex = g_index.replace(index);
@@ -208,7 +210,7 @@ bool replace(std::span<const IndexEntry> index,
 /** Finds one index row by the vendor definition hash. */
 bool find_hash(std::uint32_t definitionHash, IndexEntry& entry) noexcept {
     entry = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const IndexEntry> rows = g_index.rows();
     const auto found =
         std::find_if(rows.begin(), rows.end(), [definitionHash](const IndexEntry& row) {
@@ -229,7 +231,7 @@ bool find_hash(std::uint32_t definitionHash, IndexEntry& entry) noexcept {
 /** Reads one index row by its position. */
 bool find_index(std::uint16_t index, IndexEntry& entry) noexcept {
     entry = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const IndexEntry> rows = g_index.rows();
     const bool present = index < rows.size();
     if (present) {
@@ -241,7 +243,7 @@ bool find_index(std::uint16_t index, IndexEntry& entry) noexcept {
 /** Finds one held definition by the vendor definition hash. */
 bool find(std::uint32_t definitionHash, Definition& definition) noexcept {
     definition = {};
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     const std::span<const Definition> rows = g_definitions.rows();
     const auto found =
         std::find_if(rows.begin(), rows.end(), [definitionHash](const Definition& row) {
@@ -258,7 +260,7 @@ bool find(std::uint32_t definitionHash, Definition& definition) noexcept {
 bool sale_rows(const Definition& definition,
                std::span<SaleRow> output,
                std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return copy_range(
         g_saleRows.rows(), definition.saleRowOffset, definition.saleCount, output, count);
 }
@@ -267,7 +269,7 @@ bool sale_rows(const Definition& definition,
 bool installed_rows(const Definition& definition,
                     std::span<InstalledRow> output,
                     std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return copy_range(g_installedRows.rows(),
                       definition.installedRowOffset,
                       definition.installedCount,
@@ -277,49 +279,49 @@ bool installed_rows(const Definition& definition,
 
 /** Copies every index row in ascending index order. */
 bool snapshot_index(std::span<IndexEntry> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_index.snapshot(output, count);
 }
 
 /** Copies every held definition in ascending index order. */
 bool snapshot_definitions(std::span<Definition> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.snapshot(output, count);
 }
 
 /** Copies the whole flat sale bank. */
 bool snapshot_sale_rows(std::span<SaleRow> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_saleRows.snapshot(output, count);
 }
 
 /** Copies the whole flat installed bank. */
 bool snapshot_installed_rows(std::span<InstalledRow> output, std::size_t& count) noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_installedRows.snapshot(output, count);
 }
 
 /** @return The index row count, read under the lock. */
 std::size_t count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_index.count();
 }
 
 /** @return The held definition count, read under the lock. */
 std::size_t definition_count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_definitions.count();
 }
 
 /** @return The flat sale bank row count, read under the lock. */
 std::size_t sale_row_count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_saleRows.count();
 }
 
 /** @return The flat installed bank row count, read under the lock. */
 std::size_t installed_row_count() noexcept {
-    const Lock::Shared guard(g_lock);
+    const std::shared_lock guard(g_lock);
     return g_installedRows.count();
 }