| author | |
| committer | |
| log | d7148f7d056a5891a2b4c8e22ce71ef09d60d356 |
| tree | 2ed6300eb2cf2cbd7677a5f1f54c9bfffa4b9c30 |
| parent | 85e44dbae2af3ce6d71210f6d91014ca09b9597a |
The existing `lock` and `unlock` methods continue to act exclusively.
They should be used when data may be mutated. New methods, `lockShared`
and `unlockShared`, may be used for shared locking in situations where
multiple independent users are reading but not mutating data.4 files changed, 74 insertions(+), 32 deletions(-)
lib/std/array_hash_map.zig+7-6| ... | ... | @@ -200,17 +200,18 @@ pub fn Custom( |
| 200 | 200 | /// cause an existing key or value pointer to become invalidated will |
| 201 | 201 | /// instead trigger an assertion. |
| 202 | 202 | /// |
| 203 | /// An additional call to `lockPointers` in such state also triggers an | |
| 204 | /// assertion. | |
| 203 | /// `lockPointers` may be called multiple times. This allows multiple | |
| 204 | /// independent users of the hash map to keep it locked simultaneously. | |
| 205 | 205 | /// |
| 206 | /// `unlockPointers` returns the hash map to the previous state. | |
| 206 | /// `unlockPointers` restores the hash map to its previous state when | |
| 207 | /// called the same number of times as `lockPointers`. | |
| 207 | 208 | pub fn lockPointers(self: *Self) void { |
| 208 | self.pointer_stability.lock(); | |
| 209 | self.pointer_stability.lockShared(); | |
| 209 | 210 | } |
| 210 | 211 | |
| 211 | /// Undoes a call to `lockPointers`. | |
| 212 | /// Undoes one call to `lockPointers`. | |
| 212 | 213 | pub fn unlockPointers(self: *Self) void { |
| 213 | self.pointer_stability.unlock(); | |
| 214 | self.pointer_stability.unlockShared(); | |
| 214 | 215 | } |
| 215 | 216 | |
| 216 | 217 | /// Clears the map but retains the backing allocation for future use. |
lib/std/array_list.zig+14-12| ... | ... | @@ -74,17 +74,18 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type |
| 74 | 74 | /// cause an existing value pointer to become invalidated will |
| 75 | 75 | /// instead trigger an assertion. |
| 76 | 76 | /// |
| 77 | /// An additional call to `lockPointers` in such state also triggers an | |
| 78 | /// assertion. | |
| 77 | /// `lockPointers` may be called multiple times. This allows multiple | |
| 78 | /// independent users of the list to keep it locked simultaneously. | |
| 79 | 79 | /// |
| 80 | /// `unlockPointers` returns the array list to the previous state. | |
| 80 | /// `unlockPointers` restores the list to its previous state when | |
| 81 | /// called the same number of times as `lockPointers`. | |
| 81 | 82 | pub fn lockPointers(self: *Self) void { |
| 82 | self.pointer_stability.lock(); | |
| 83 | self.pointer_stability.lockShared(); | |
| 83 | 84 | } |
| 84 | 85 | |
| 85 | /// Undoes a call to `lockPointers`. | |
| 86 | /// Undoes one call to `lockPointers`. | |
| 86 | 87 | pub fn unlockPointers(self: *Self) void { |
| 87 | self.pointer_stability.unlock(); | |
| 88 | self.pointer_stability.unlockShared(); | |
| 88 | 89 | } |
| 89 | 90 | |
| 90 | 91 | /// ArrayList takes ownership of the passed in slice. The slice must have been |
| ... | ... | @@ -698,17 +699,18 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 698 | 699 | /// cause an existing value pointer to become invalidated will |
| 699 | 700 | /// instead trigger an assertion. |
| 700 | 701 | /// |
| 701 | /// An additional call to `lockPointers` in such state also triggers an | |
| 702 | /// assertion. | |
| 702 | /// `lockPointers` may be called multiple times. This allows multiple | |
| 703 | /// independent users of the list to keep it locked simultaneously. | |
| 703 | 704 | /// |
| 704 | /// `unlockPointers` returns the unmanaged array list to the previous state. | |
| 705 | /// `unlockPointers` restores the list to its previous state when | |
| 706 | /// called the same number of times as `lockPointers`. | |
| 705 | 707 | pub fn lockPointers(self: *Self) void { |
| 706 | self.pointer_stability.lock(); | |
| 708 | self.pointer_stability.lockShared(); | |
| 707 | 709 | } |
| 708 | 710 | |
| 709 | /// Undoes a call to `lockPointers`. | |
| 711 | /// Undoes one call to `lockPointers`. | |
| 710 | 712 | pub fn unlockPointers(self: *Self) void { |
| 711 | self.pointer_stability.unlock(); | |
| 713 | self.pointer_stability.unlockShared(); | |
| 712 | 714 | } |
| 713 | 715 | |
| 714 | 716 | /// Convert this list into an analogous memory-managed one. |
lib/std/debug.zig+41-4| ... | ... | @@ -1851,20 +1851,46 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize |
| 1851 | 1851 | pub const SafetyLock = struct { |
| 1852 | 1852 | state: State = if (runtime_safety) .unlocked else .unknown, |
| 1853 | 1853 | |
| 1854 | pub const State = if (runtime_safety) enum { unlocked, locked } else enum { unknown }; | |
| 1854 | pub const State = if (runtime_safety) enum(usize) { | |
| 1855 | unlocked = 0, | |
| 1856 | exclusive = math.maxInt(usize), | |
| 1857 | _, // shared lock count | |
| 1858 | ||
| 1859 | fn isShared(state: State) bool { | |
| 1860 | return switch (state) { | |
| 1861 | _ => true, | |
| 1862 | else => false, | |
| 1863 | }; | |
| 1864 | } | |
| 1865 | } else enum { unknown }; | |
| 1855 | 1866 | |
| 1867 | /// Exclusive. Use when mutating data. | |
| 1856 | 1868 | pub fn lock(l: *SafetyLock) void { |
| 1857 | 1869 | if (!runtime_safety) return; |
| 1858 | 1870 | assert(l.state == .unlocked); |
| 1859 | l.state = .locked; | |
| 1871 | l.state = .exclusive; | |
| 1860 | 1872 | } |
| 1861 | 1873 | |
| 1862 | 1874 | pub fn unlock(l: *SafetyLock) void { |
| 1863 | 1875 | if (!runtime_safety) return; |
| 1864 | assert(l.state == .locked); | |
| 1876 | assert(l.state == .exclusive); | |
| 1865 | 1877 | l.state = .unlocked; |
| 1866 | 1878 | } |
| 1867 | 1879 | |
| 1880 | /// Use when consuming data in a read-only manner. | |
| 1881 | pub fn lockShared(l: *SafetyLock) void { | |
| 1882 | if (!runtime_safety) return; | |
| 1883 | assert(l.state != .exclusive); | |
| 1884 | l.state = @fromBackingInt(@backingInt(l.state) + 1); | |
| 1885 | assert(l.state.isShared()); // Catch overflow to `exclusive`. | |
| 1886 | } | |
| 1887 | ||
| 1888 | pub fn unlockShared(l: *SafetyLock) void { | |
| 1889 | if (!runtime_safety) return; | |
| 1890 | assert(l.state.isShared()); | |
| 1891 | l.state = @fromBackingInt(@backingInt(l.state) - 1); | |
| 1892 | } | |
| 1893 | ||
| 1868 | 1894 | pub fn assertUnlocked(l: SafetyLock) void { |
| 1869 | 1895 | if (!runtime_safety) return; |
| 1870 | 1896 | assert(l.state == .unlocked); |
| ... | ... | @@ -1872,7 +1898,12 @@ pub const SafetyLock = struct { |
| 1872 | 1898 | |
| 1873 | 1899 | pub fn assertLocked(l: SafetyLock) void { |
| 1874 | 1900 | if (!runtime_safety) return; |
| 1875 | assert(l.state == .locked); | |
| 1901 | assert(l.state == .exclusive); | |
| 1902 | } | |
| 1903 | ||
| 1904 | pub fn assertLockedShared(l: SafetyLock) void { | |
| 1905 | if (!runtime_safety) return; | |
| 1906 | assert(l.state.isShared()); | |
| 1876 | 1907 | } |
| 1877 | 1908 | }; |
| 1878 | 1909 | |
| ... | ... | @@ -1883,6 +1914,12 @@ test SafetyLock { |
| 1883 | 1914 | safety_lock.assertLocked(); |
| 1884 | 1915 | safety_lock.unlock(); |
| 1885 | 1916 | safety_lock.assertUnlocked(); |
| 1917 | safety_lock.lockShared(); | |
| 1918 | safety_lock.assertLockedShared(); | |
| 1919 | for (0..3) |_| safety_lock.lockShared(); | |
| 1920 | safety_lock.assertLockedShared(); | |
| 1921 | for (0..4) |_| safety_lock.unlockShared(); | |
| 1922 | safety_lock.assertUnlocked(); | |
| 1886 | 1923 | } |
| 1887 | 1924 | |
| 1888 | 1925 | /// Detect whether the program is being executed in the Valgrind virtual machine. |
lib/std/hash_map.zig+12-10| ... | ... | @@ -191,15 +191,16 @@ pub fn HashMap( |
| 191 | 191 | /// cause an existing key or value pointer to become invalidated will |
| 192 | 192 | /// instead trigger an assertion. |
| 193 | 193 | /// |
| 194 | /// An additional call to `lockPointers` in such state also triggers an | |
| 195 | /// assertion. | |
| 194 | /// `lockPointers` may be called multiple times. This allows multiple | |
| 195 | /// independent users of the hash map to keep it locked simultaneously. | |
| 196 | 196 | /// |
| 197 | /// `unlockPointers` returns the hash map to the previous state. | |
| 197 | /// `unlockPointers` restores the hash map to its previous state when | |
| 198 | /// called the same number of times as `lockPointers`. | |
| 198 | 199 | pub fn lockPointers(self: *Self) void { |
| 199 | 200 | self.unmanaged.lockPointers(); |
| 200 | 201 | } |
| 201 | 202 | |
| 202 | /// Undoes a call to `lockPointers`. | |
| 203 | /// Undoes one call to `lockPointers`. | |
| 203 | 204 | pub fn unlockPointers(self: *Self) void { |
| 204 | 205 | self.unmanaged.unlockPointers(); |
| 205 | 206 | } |
| ... | ... | @@ -709,17 +710,18 @@ fn Custom( |
| 709 | 710 | /// cause an existing key or value pointer to become invalidated will |
| 710 | 711 | /// instead trigger an assertion. |
| 711 | 712 | /// |
| 712 | /// An additional call to `lockPointers` in such state also triggers an | |
| 713 | /// assertion. | |
| 713 | /// `lockPointers` may be called multiple times. This allows multiple | |
| 714 | /// independent users of the hash map to keep it locked simultaneously. | |
| 714 | 715 | /// |
| 715 | /// `unlockPointers` returns the hash map to the previous state. | |
| 716 | /// `unlockPointers` restores the hash map to its previous state when | |
| 717 | /// called the same number of times as `lockPointers`. | |
| 716 | 718 | pub fn lockPointers(self: *Self) void { |
| 717 | self.pointer_stability.lock(); | |
| 719 | self.pointer_stability.lockShared(); | |
| 718 | 720 | } |
| 719 | 721 | |
| 720 | /// Undoes a call to `lockPointers`. | |
| 722 | /// Undoes one call to `lockPointers`. | |
| 721 | 723 | pub fn unlockPointers(self: *Self) void { |
| 722 | self.pointer_stability.unlock(); | |
| 724 | self.pointer_stability.unlockShared(); | |
| 723 | 725 | } |
| 724 | 726 | |
| 725 | 727 | fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool { |