| 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,17 +200,18 @@ pub fn Custom( |
| 200 | /// cause an existing key or value pointer to become invalidated will | 200 | /// cause an existing key or value pointer to become invalidated will |
| 201 | /// instead trigger an assertion. | 201 | /// instead trigger an assertion. |
| 202 | /// | 202 | /// |
| 203 | /// An additional call to `lockPointers` in such state also triggers an | 203 | /// `lockPointers` may be called multiple times. This allows multiple |
| 204 | /// assertion. | 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 | pub fn lockPointers(self: *Self) void { | 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 | pub fn unlockPointers(self: *Self) void { | 213 | pub fn unlockPointers(self: *Self) void { |
| 213 | self.pointer_stability.unlock(); | 214 | self.pointer_stability.unlockShared(); |
| 214 | } | 215 | } |
| 215 | 216 | ||
| 216 | /// Clears the map but retains the backing allocation for future use. | 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,17 +74,18 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type |
| 74 | /// cause an existing value pointer to become invalidated will | 74 | /// cause an existing value pointer to become invalidated will |
| 75 | /// instead trigger an assertion. | 75 | /// instead trigger an assertion. |
| 76 | /// | 76 | /// |
| 77 | /// An additional call to `lockPointers` in such state also triggers an | 77 | /// `lockPointers` may be called multiple times. This allows multiple |
| 78 | /// assertion. | 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 | pub fn lockPointers(self: *Self) void { | 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 | pub fn unlockPointers(self: *Self) void { | 87 | pub fn unlockPointers(self: *Self) void { |
| 87 | self.pointer_stability.unlock(); | 88 | self.pointer_stability.unlockShared(); |
| 88 | } | 89 | } |
| 89 | 90 | ||
| 90 | /// ArrayList takes ownership of the passed in slice. The slice must have been | 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,17 +699,18 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { |
| 698 | /// cause an existing value pointer to become invalidated will | 699 | /// cause an existing value pointer to become invalidated will |
| 699 | /// instead trigger an assertion. | 700 | /// instead trigger an assertion. |
| 700 | /// | 701 | /// |
| 701 | /// An additional call to `lockPointers` in such state also triggers an | 702 | /// `lockPointers` may be called multiple times. This allows multiple |
| 702 | /// assertion. | 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 | pub fn lockPointers(self: *Self) void { | 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 | pub fn unlockPointers(self: *Self) void { | 712 | pub fn unlockPointers(self: *Self) void { |
| 711 | self.pointer_stability.unlock(); | 713 | self.pointer_stability.unlockShared(); |
| 712 | } | 714 | } |
| 713 | 715 | ||
| 714 | /// Convert this list into an analogous memory-managed one. | 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,20 +1851,46 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize |
| 1851 | pub const SafetyLock = struct { | 1851 | pub const SafetyLock = struct { |
| 1852 | state: State = if (runtime_safety) .unlocked else .unknown, | 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 | pub fn lock(l: *SafetyLock) void { | 1868 | pub fn lock(l: *SafetyLock) void { |
| 1857 | if (!runtime_safety) return; | 1869 | if (!runtime_safety) return; |
| 1858 | assert(l.state == .unlocked); | 1870 | assert(l.state == .unlocked); |
| 1859 | l.state = .locked; | 1871 | l.state = .exclusive; |
| 1860 | } | 1872 | } |
| 1861 | 1873 | ||
| 1862 | pub fn unlock(l: *SafetyLock) void { | 1874 | pub fn unlock(l: *SafetyLock) void { |
| 1863 | if (!runtime_safety) return; | 1875 | if (!runtime_safety) return; |
| 1864 | assert(l.state == .locked); | 1876 | assert(l.state == .exclusive); |
| 1865 | l.state = .unlocked; | 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 | pub fn assertUnlocked(l: SafetyLock) void { | 1894 | pub fn assertUnlocked(l: SafetyLock) void { |
| 1869 | if (!runtime_safety) return; | 1895 | if (!runtime_safety) return; |
| 1870 | assert(l.state == .unlocked); | 1896 | assert(l.state == .unlocked); |
| ... | @@ -1872,7 +1898,12 @@ pub const SafetyLock = struct { | ... | @@ -1872,7 +1898,12 @@ pub const SafetyLock = struct { |
| 1872 | 1898 | ||
| 1873 | pub fn assertLocked(l: SafetyLock) void { | 1899 | pub fn assertLocked(l: SafetyLock) void { |
| 1874 | if (!runtime_safety) return; | 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,6 +1914,12 @@ test SafetyLock { |
| 1883 | safety_lock.assertLocked(); | 1914 | safety_lock.assertLocked(); |
| 1884 | safety_lock.unlock(); | 1915 | safety_lock.unlock(); |
| 1885 | safety_lock.assertUnlocked(); | 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 | /// Detect whether the program is being executed in the Valgrind virtual machine. | 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,15 +191,16 @@ pub fn HashMap( |
| 191 | /// cause an existing key or value pointer to become invalidated will | 191 | /// cause an existing key or value pointer to become invalidated will |
| 192 | /// instead trigger an assertion. | 192 | /// instead trigger an assertion. |
| 193 | /// | 193 | /// |
| 194 | /// An additional call to `lockPointers` in such state also triggers an | 194 | /// `lockPointers` may be called multiple times. This allows multiple |
| 195 | /// assertion. | 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 | pub fn lockPointers(self: *Self) void { | 199 | pub fn lockPointers(self: *Self) void { |
| 199 | self.unmanaged.lockPointers(); | 200 | self.unmanaged.lockPointers(); |
| 200 | } | 201 | } |
| 201 | 202 | ||
| 202 | /// Undoes a call to `lockPointers`. | 203 | /// Undoes one call to `lockPointers`. |
| 203 | pub fn unlockPointers(self: *Self) void { | 204 | pub fn unlockPointers(self: *Self) void { |
| 204 | self.unmanaged.unlockPointers(); | 205 | self.unmanaged.unlockPointers(); |
| 205 | } | 206 | } |
| ... | @@ -709,17 +710,18 @@ fn Custom( | ... | @@ -709,17 +710,18 @@ fn Custom( |
| 709 | /// cause an existing key or value pointer to become invalidated will | 710 | /// cause an existing key or value pointer to become invalidated will |
| 710 | /// instead trigger an assertion. | 711 | /// instead trigger an assertion. |
| 711 | /// | 712 | /// |
| 712 | /// An additional call to `lockPointers` in such state also triggers an | 713 | /// `lockPointers` may be called multiple times. This allows multiple |
| 713 | /// assertion. | 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 | pub fn lockPointers(self: *Self) void { | 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 | pub fn unlockPointers(self: *Self) void { | 723 | pub fn unlockPointers(self: *Self) void { |
| 722 | self.pointer_stability.unlock(); | 724 | self.pointer_stability.unlockShared(); |
| 723 | } | 725 | } |
| 724 | 726 | ||
| 725 | fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool { | 727 | fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool { |