| ... | @@ -659,45 +659,40 @@ test "reset while retaining a buffer" { | ... | @@ -659,45 +659,40 @@ test "reset while retaining a buffer" { |
| 659 | try std.testing.expectEqual(2, arena_allocator.queryCapacity()); | 659 | try std.testing.expectEqual(2, arena_allocator.queryCapacity()); |
| 660 | } | 660 | } |
| 661 | | 661 | |
| 662 | test "fuzz" { | 662 | test "fuzz multi threaded" { |
| 663 | @disableInstrumentation(); | 663 | @disableInstrumentation(); |
| 664 | if (@import("builtin").single_threaded) return error.SkipZigTest; | 664 | if (@import("builtin").single_threaded) return error.SkipZigTest; |
| 665 | | 665 | |
| 666 | const gpa = std.heap.smp_allocator; | 666 | const gpa = std.heap.smp_allocator; |
| 667 | | 667 | |
| | 668 | var io_instance: std.Io.Threaded = .init(gpa, .{}); |
| | 669 | defer io_instance.deinit(); |
| | 670 | |
| 668 | var arena_state: ArenaAllocator.State = .init; | 671 | var arena_state: ArenaAllocator.State = .init; |
| 669 | // No need to deinit arena_state, all allocations are in `sample_buffer`! | 672 | // No need to deinit arena_state, all allocations are in `sample_buffer`! |
| 670 | | 673 | |
| 671 | const control_buffer = try gpa.alloc(u8, 64 << 10 << 10); | 674 | const buffer_size = FuzzContext.max_alloc_count * FuzzContext.max_alloc_size; |
| | 675 | |
| | 676 | const control_buffer = try gpa.alloc(u8, buffer_size); |
| 672 | defer gpa.free(control_buffer); | 677 | defer gpa.free(control_buffer); |
| 673 | var control_instance: std.heap.FixedBufferAllocator = .init(control_buffer); | 678 | var control_instance: std.heap.FixedBufferAllocator = .init(control_buffer); |
| 674 | | 679 | |
| 675 | const sample_buffer = try gpa.alloc(u8, 64 << 10 << 10); | 680 | const sample_buffer = try gpa.alloc(u8, buffer_size); |
| 676 | defer gpa.free(sample_buffer); | 681 | defer gpa.free(sample_buffer); |
| 677 | var sample_instance: FuzzAllocator = .init(sample_buffer); | 682 | var sample_instance: FuzzAllocator = .init(sample_buffer); |
| 678 | | 683 | |
| 679 | var allocs: FuzzContext.Allocs = try .initCapacity(gpa, FuzzContext.max_alloc_count); | | |
| 680 | defer allocs.deinit(gpa); | | |
| 681 | | | |
| 682 | try std.testing.fuzz(FuzzContext.Init{ | 684 | try std.testing.fuzz(FuzzContext.Init{ |
| 683 | .gpa = gpa, | 685 | .threaded_instance = &io_instance, |
| 684 | .allocs = &allocs, | | |
| 685 | .arena_state = &arena_state, | 686 | .arena_state = &arena_state, |
| 686 | .control_instance = &control_instance, | 687 | .control_instance = &control_instance, |
| 687 | .sample_instance = &sample_instance, | 688 | .sample_instance = &sample_instance, |
| 688 | }, fuzzArenaAllocator, .{}); | 689 | }, fuzzMultiThreaded, .{}); |
| 689 | } | 690 | } |
| 690 | | 691 | |
| 691 | fn fuzzArenaAllocator(fuzz_init: FuzzContext.Init, smith: *std.testing.Smith) anyerror!void { | 692 | fn fuzzMultiThreaded(fuzz_init: FuzzContext.Init, smith: *std.testing.Smith) anyerror!void { |
| 692 | @disableInstrumentation(); | 693 | @disableInstrumentation(); |
| 693 | const testing = std.testing; | 694 | const testing = std.testing; |
| 694 | | 695 | const io = fuzz_init.threaded_instance.io(); |
| 695 | // We use a 'fresh' `Threaded` instance every time to reset threadlocals to | | |
| 696 | // their default values. | | |
| 697 | | | |
| 698 | var io_instance: std.Io.Threaded = .init(fuzz_init.gpa, .{}); | | |
| 699 | defer io_instance.deinit(); | | |
| 700 | const io = io_instance.io(); | | |
| 701 | | 696 | |
| 702 | fuzz_init.sample_instance.prepareFailures(smith); | 697 | fuzz_init.sample_instance.prepareFailures(smith); |
| 703 | | 698 | |
| ... | @@ -708,59 +703,56 @@ fn fuzzArenaAllocator(fuzz_init: FuzzContext.Init, smith: *std.testing.Smith) an | ... | @@ -708,59 +703,56 @@ fn fuzzArenaAllocator(fuzz_init: FuzzContext.Init, smith: *std.testing.Smith) an |
| 708 | defer fuzz_init.arena_state.* = arena_instance.state; | 703 | defer fuzz_init.arena_state.* = arena_instance.state; |
| 709 | | 704 | |
| 710 | var ctx: FuzzContext = .init( | 705 | var ctx: FuzzContext = .init( |
| 711 | io, | | |
| 712 | control_allocator, | 706 | control_allocator, |
| 713 | arena_instance.allocator(), | 707 | arena_instance.allocator(), |
| 714 | fuzz_init.allocs, | | |
| 715 | ); | 708 | ); |
| 716 | defer ctx.deinit(); | 709 | defer ctx.deinit(); |
| 717 | | 710 | |
| 718 | ctx.rwl.lockUncancelable(io); | | |
| 719 | | | |
| 720 | var group: std.Io.Group = .init; | 711 | var group: std.Io.Group = .init; |
| 721 | defer group.cancel(io); | 712 | defer group.cancel(io); |
| 722 | | 713 | |
| | 714 | var n_allocs: usize = 0; |
| 723 | var n_actions: usize = 0; | 715 | var n_actions: usize = 0; |
| 724 | while (!smith.eosWeightedSimple(99, 1) and n_actions < FuzzContext.max_action_count) { | 716 | while (!smith.eosWeightedSimple(99, 1) and n_actions < FuzzContext.max_action_count) { |
| 725 | errdefer comptime unreachable; | 717 | errdefer comptime unreachable; |
| 726 | | 718 | |
| 727 | const ActionTag = @typeInfo(FuzzContext.Action).@"union".tag_type.?; | 719 | const weights: []const testing.Smith.Weight = if (n_allocs == FuzzContext.max_alloc_count) |
| 728 | const weights: []const testing.Smith.Weight = weights: { | 720 | &.{ |
| 729 | if (ctx.allocs.len == ctx.allocs.capacity) | 721 | .value(FuzzContext.Action, .resize, 1), |
| 730 | break :weights &.{ | 722 | .value(FuzzContext.Action, .remap, 1), |
| 731 | .value(ActionTag, .resize, 1), | 723 | .value(FuzzContext.Action, .free, 1), |
| 732 | .value(ActionTag, .remap, 1), | 724 | } |
| 733 | .value(ActionTag, .free, 1), | 725 | else |
| 734 | }; | 726 | &.{ |
| 735 | break :weights testing.Smith.baselineWeights(ActionTag) ++ | 727 | .value(FuzzContext.Action, .resize, 1), |
| 736 | .{testing.Smith.Weight.value(ActionTag, .alloc, 2)}; | 728 | .value(FuzzContext.Action, .remap, 1), |
| 737 | }; | 729 | .value(FuzzContext.Action, .free, 1), |
| 738 | const action: FuzzContext.Action = switch (smith.valueWeighted(ActionTag, weights)) { | 730 | .value(FuzzContext.Action, .alloc, 3), |
| 739 | .alloc => action: { | 731 | }; |
| 740 | const alloc_index = ctx.allocs.addOneBounded() catch continue; | 732 | switch (smith.valueWeighted(FuzzContext.Action, weights)) { |
| 741 | ctx.allocs.items(.len)[alloc_index] = .free; | 733 | .alloc => { |
| 742 | break :action .{ .alloc = .{ | 734 | const alloc_index = n_allocs; |
| 743 | .len = nextLen(smith), | 735 | n_allocs += 1; |
| 744 | .alignment = smith.valueRangeAtMost( | 736 | ctx.allocs[alloc_index].common.len = .free; |
| | 737 | group.concurrent(io, FuzzContext.doOneAlloc, .{ |
| | 738 | &ctx, nextLen(smith), |
| | 739 | smith.valueRangeAtMost( |
| 745 | Alignment, | 740 | Alignment, |
| 746 | .@"1", | 741 | .@"1", |
| 747 | .fromByteUnits(2 * std.heap.page_size_max), | 742 | .fromByteUnits(2 * std.heap.page_size_max), |
| 748 | ), | 743 | ), |
| 749 | .index = alloc_index, | 744 | @enumFromInt(alloc_index), |
| 750 | } }; | 745 | }) catch unreachable; |
| 751 | }, | 746 | }, |
| 752 | .resize => .{ .resize = .{ .new_len = nextLen(smith) } }, | 747 | .resize => group.concurrent(io, FuzzContext.doOneResize, .{ &ctx, nextLen(smith) }) catch unreachable, |
| 753 | .remap => .{ .remap = .{ .new_len = nextLen(smith) } }, | 748 | .remap => group.concurrent(io, FuzzContext.doOneRemap, .{ &ctx, nextLen(smith) }) catch unreachable, |
| 754 | .free => .free, | 749 | .free => group.concurrent(io, FuzzContext.doOneFree, .{&ctx}) catch unreachable, |
| 755 | }; | 750 | } |
| 756 | group.concurrent(io, FuzzContext.doOneAction, .{ &ctx, action }) catch break; | | |
| 757 | n_actions += 1; | 751 | n_actions += 1; |
| 758 | } | 752 | } |
| 759 | | 753 | |
| 760 | ctx.rwl.unlock(io); | | |
| 761 | | | |
| 762 | try group.await(io); | 754 | try group.await(io); |
| 763 | try ctx.check(); | 755 | try ctx.check(n_allocs); |
| 764 | | 756 | |
| 765 | // This also covers the `deinit` logic since `free_all` uses it internally. | 757 | // This also covers the `deinit` logic since `free_all` uses it internally. |
| 766 | | 758 | |
| ... | @@ -783,73 +775,71 @@ fn fuzzArenaAllocator(fuzz_init: FuzzContext.Init, smith: *std.testing.Smith) an | ... | @@ -783,73 +775,71 @@ fn fuzzArenaAllocator(fuzz_init: FuzzContext.Init, smith: *std.testing.Smith) an |
| 783 | } | 775 | } |
| 784 | | 776 | |
| 785 | fuzz_init.control_instance.reset(); | 777 | fuzz_init.control_instance.reset(); |
| 786 | fuzz_init.allocs.clearRetainingCapacity(); | | |
| 787 | } | | |
| 788 | fn nextLen(smith: *std.testing.Smith) usize { | | |
| 789 | @disableInstrumentation(); | | |
| 790 | return usizeRange(smith, 1, 16 << 10 << 10); | | |
| 791 | } | 778 | } |
| 792 | fn usizeRange(smith: *std.testing.Smith, at_least: usize, at_most: usize) usize { | 779 | fn nextLen(smith: *std.testing.Smith) @typeInfo(FuzzContext.Alloc.Len).@"enum".tag_type { |
| 793 | @disableInstrumentation(); | 780 | @disableInstrumentation(); |
| 794 | const Int = @Int(.unsigned, @min(64, @bitSizeOf(usize))); | 781 | const BackingInt = @typeInfo(FuzzContext.Alloc.Len).@"enum".tag_type; |
| 795 | return smith.valueRangeAtMost(Int, @intCast(at_least), @intCast(at_most)); | 782 | return smith.valueRangeAtMost(BackingInt, 1, FuzzContext.max_alloc_size); |
| 796 | } | 783 | } |
| 797 | | 784 | |
| 798 | const FuzzContext = struct { | 785 | const FuzzContext = struct { |
| 799 | io: std.Io, | | |
| 800 | rwl: std.Io.RwLock, | | |
| 801 | | | |
| 802 | control_allocator: Allocator, | 786 | control_allocator: Allocator, |
| 803 | sample_allocator: Allocator, | 787 | sample_allocator: Allocator, |
| 804 | | 788 | |
| 805 | allocs: *Allocs, | 789 | last_alloc_index: Alloc.Index, |
| | 790 | allocs: [max_alloc_count]Alloc, |
| 806 | | 791 | |
| 807 | const max_alloc_count = 4096; | 792 | const max_alloc_count = 64; |
| 808 | const max_action_count = 2 * max_alloc_count; | 793 | const max_action_count = 2 * max_alloc_count; |
| 809 | | 794 | |
| 810 | const Allocs = std.MultiArrayList(struct { | 795 | const max_alloc_size = 16 << 10; |
| | 796 | |
| | 797 | const Alloc = struct { |
| 811 | control_ptr: [*]u8, | 798 | control_ptr: [*]u8, |
| 812 | sample_ptr: [*]u8, | 799 | sample_ptr: [*]u8, |
| 813 | len: Len, | 800 | common: packed struct(usize) { |
| 814 | alignment: Alignment, | 801 | len: Len, |
| 815 | }); | 802 | alignment: Alignment, |
| | 803 | _: @Int(.unsigned, padding_bits) = 0, |
| | 804 | }, |
| | 805 | |
| | 806 | const Len = enum(@Int(.unsigned, len_bits)) { |
| | 807 | free = (1 << len_bits) - 1, |
| | 808 | _, |
| | 809 | }; |
| | 810 | const len_bits = @min(64, @bitSizeOf(usize)) - @bitSizeOf(Alignment); |
| | 811 | const padding_bits = @bitSizeOf(usize) - (len_bits + @bitSizeOf(Alignment)); |
| 816 | | 812 | |
| 817 | const Len = enum(usize) { | 813 | const Index = enum(usize) { |
| 818 | free = std.math.maxInt(usize), | 814 | none = std.math.maxInt(usize), |
| 819 | _, | 815 | _, |
| | 816 | }; |
| 820 | }; | 817 | }; |
| 821 | | 818 | |
| 822 | const Action = union(enum(u8)) { | 819 | const Action = enum { |
| 823 | alloc: struct { len: usize, alignment: Alignment, index: usize }, | 820 | alloc, |
| 824 | resize: struct { new_len: usize }, | 821 | resize, |
| 825 | remap: struct { new_len: usize }, | 822 | remap, |
| 826 | free, | 823 | free, |
| 827 | }; | 824 | }; |
| 828 | | 825 | |
| 829 | threadlocal var tls_next: u8 = 0; | | |
| 830 | threadlocal var tls_last_index: ?usize = null; | | |
| 831 | | | |
| 832 | const Init = struct { | 826 | const Init = struct { |
| 833 | gpa: Allocator, | 827 | threaded_instance: *std.Io.Threaded, |
| 834 | allocs: *FuzzContext.Allocs, | | |
| 835 | arena_state: *ArenaAllocator.State, | 828 | arena_state: *ArenaAllocator.State, |
| 836 | control_instance: *std.heap.FixedBufferAllocator, | 829 | control_instance: *std.heap.FixedBufferAllocator, |
| 837 | sample_instance: *FuzzAllocator, | 830 | sample_instance: *FuzzAllocator, |
| 838 | }; | 831 | }; |
| 839 | | 832 | |
| 840 | fn init( | 833 | fn init( |
| 841 | io: std.Io, | | |
| 842 | control_allocator: Allocator, | 834 | control_allocator: Allocator, |
| 843 | sample_allocator: Allocator, | 835 | sample_allocator: Allocator, |
| 844 | allocs: *Allocs, | | |
| 845 | ) FuzzContext { | 836 | ) FuzzContext { |
| 846 | @disableInstrumentation(); | 837 | @disableInstrumentation(); |
| 847 | return .{ | 838 | return .{ |
| 848 | .io = io, | | |
| 849 | .rwl = .init, | | |
| 850 | .control_allocator = control_allocator, | 839 | .control_allocator = control_allocator, |
| 851 | .sample_allocator = sample_allocator, | 840 | .sample_allocator = sample_allocator, |
| 852 | .allocs = allocs, | 841 | .last_alloc_index = .none, |
| | 842 | .allocs = undefined, |
| 853 | }; | 843 | }; |
| 854 | } | 844 | } |
| 855 | | 845 | |
| ... | @@ -858,35 +848,22 @@ const FuzzContext = struct { | ... | @@ -858,35 +848,22 @@ const FuzzContext = struct { |
| 858 | ctx.* = undefined; | 848 | ctx.* = undefined; |
| 859 | } | 849 | } |
| 860 | | 850 | |
| 861 | fn check(ctx: *const FuzzContext) !void { | 851 | fn check(ctx: *const FuzzContext, n_allocs: usize) !void { |
| 862 | @disableInstrumentation(); | 852 | @disableInstrumentation(); |
| 863 | for (0..ctx.allocs.len) |index| { | 853 | for (ctx.allocs[0..n_allocs]) |allocation| { |
| 864 | const len: usize = switch (ctx.allocs.items(.len)[index]) { | 854 | const len: usize = switch (allocation.common.len) { |
| 865 | .free => continue, | 855 | .free => continue, |
| 866 | _ => |len| @intFromEnum(len), | 856 | _ => |len| @intFromEnum(len), |
| 867 | }; | 857 | }; |
| 868 | const control = ctx.allocs.items(.control_ptr)[index][0..len]; | 858 | const control = allocation.control_ptr[0..len]; |
| 869 | const sample = ctx.allocs.items(.sample_ptr)[index][0..len]; | 859 | const sample = allocation.sample_ptr[0..len]; |
| 870 | try std.testing.expectEqualSlices(u8, control, sample); | 860 | try std.testing.expectEqualSlices(u8, control, sample); |
| 871 | } | 861 | } |
| 872 | } | 862 | } |
| 873 | | 863 | |
| 874 | fn doOneAction(ctx: *FuzzContext, action: Action) std.Io.Cancelable!void { | 864 | fn doOneAlloc(ctx: *FuzzContext, len: usize, alignment: Alignment, index: Alloc.Index) void { |
| 875 | @disableInstrumentation(); | | |
| 876 | ctx.rwl.lockSharedUncancelable(ctx.io); | | |
| 877 | defer ctx.rwl.unlockShared(ctx.io); | | |
| 878 | | | |
| 879 | switch (action) { | | |
| 880 | .alloc => |act| ctx.doOneAlloc(act.len, act.alignment, act.index), | | |
| 881 | .resize => |act| ctx.doOneResize(act.new_len), | | |
| 882 | .remap => |act| ctx.doOneRemap(act.new_len), | | |
| 883 | .free => ctx.doOneFree(), | | |
| 884 | } | | |
| 885 | } | | |
| 886 | | | |
| 887 | fn doOneAlloc(ctx: *FuzzContext, len: usize, alignment: Alignment, index: usize) void { | | |
| 888 | @disableInstrumentation(); | 865 | @disableInstrumentation(); |
| 889 | assert(ctx.allocs.items(.len)[index] == .free); | 866 | assert(ctx.allocs[@intFromEnum(index)].common.len == .free); |
| 890 | | 867 | |
| 891 | const control_ptr = ctx.control_allocator.rawAlloc(len, alignment, @returnAddress()) orelse | 868 | const control_ptr = ctx.control_allocator.rawAlloc(len, alignment, @returnAddress()) orelse |
| 892 | return; | 869 | return; |
| ... | @@ -895,38 +872,42 @@ const FuzzContext = struct { | ... | @@ -895,38 +872,42 @@ const FuzzContext = struct { |
| 895 | return; | 872 | return; |
| 896 | }; | 873 | }; |
| 897 | | 874 | |
| 898 | ctx.allocs.set(index, .{ | 875 | ctx.allocs[@intFromEnum(index)] = .{ |
| 899 | .control_ptr = control_ptr, | 876 | .control_ptr = control_ptr, |
| 900 | .sample_ptr = sample_ptr, | 877 | .sample_ptr = sample_ptr, |
| 901 | .len = @enumFromInt(len), | 878 | .common = .{ |
| 902 | .alignment = alignment, | 879 | .len = @enumFromInt(len), |
| 903 | }); | 880 | .alignment = alignment, |
| 904 | | 881 | }, |
| 905 | for (control_ptr[0..len], sample_ptr[0..len]) |*control, *sample| { | 882 | }; |
| 906 | control.* = tls_next; | 883 | |
| 907 | sample.* = tls_next; | 884 | for (control_ptr[0..len], sample_ptr[0..len], 0..) |*control, *sample, i| { |
| 908 | tls_next +%= 1; | 885 | control.* = @truncate(i); |
| | 886 | sample.* = @truncate(i); |
| 909 | } | 887 | } |
| 910 | | 888 | |
| 911 | tls_last_index = index; | 889 | @atomicStore(Alloc.Index, &ctx.last_alloc_index, index, .release); |
| 912 | } | 890 | } |
| 913 | fn doOneResize(ctx: *FuzzContext, new_len: usize) void { | 891 | fn doOneResize(ctx: *FuzzContext, new_len: usize) void { |
| 914 | @disableInstrumentation(); | 892 | @disableInstrumentation(); |
| 915 | const index = tls_last_index orelse return; | | |
| 916 | const len = ctx.allocs.items(.len)[index]; | | |
| 917 | assert(len != .free); | | |
| 918 | const memory = ctx.allocs.items(.sample_ptr)[index][0..@intFromEnum(len)]; | | |
| 919 | const alignment = ctx.allocs.items(.alignment)[index]; | | |
| 920 | | 893 | |
| 921 | assert(alignment.check(@intFromPtr(ctx.allocs.items(.control_ptr)[index]))); | 894 | const index = @atomicRmw(Alloc.Index, &ctx.last_alloc_index, .Xchg, .none, .acquire); |
| 922 | assert(alignment.check(@intFromPtr(ctx.allocs.items(.sample_ptr)[index]))); | 895 | if (index == .none) return; |
| | 896 | |
| | 897 | const allocation = &ctx.allocs[@intFromEnum(index)]; |
| | 898 | assert(allocation.common.len != .free); |
| | 899 | const memory = allocation.sample_ptr[0..@intFromEnum(allocation.common.len)]; |
| | 900 | const alignment = allocation.common.alignment; |
| | 901 | |
| | 902 | assert(alignment.check(@intFromPtr(allocation.control_ptr))); |
| | 903 | assert(alignment.check(@intFromPtr(allocation.sample_ptr))); |
| 923 | | 904 | |
| 924 | // Since `resize` is fallible, we have to ensure that `control_allocator` | 905 | // Since `resize` is fallible, we have to ensure that `control_allocator` |
| 925 | // is always successful by reserving the memory we need beforehand. | 906 | // is always successful by reserving the memory we need beforehand. |
| 926 | const new_control_ptr = ctx.control_allocator.rawAlloc(new_len, alignment, @returnAddress()) orelse | 907 | const new_control_ptr = ctx.control_allocator.rawAlloc(new_len, alignment, @returnAddress()) orelse |
| 927 | return; | 908 | return; |
| 928 | if (ctx.sample_allocator.rawResize(memory, alignment, new_len, @returnAddress())) { | 909 | if (ctx.sample_allocator.rawResize(memory, alignment, new_len, @returnAddress())) { |
| 929 | const old_control = ctx.allocs.items(.control_ptr)[index][0..memory.len]; | 910 | const old_control = allocation.control_ptr[0..memory.len]; |
| 930 | const overlap = @min(memory.len, new_len); | 911 | const overlap = @min(memory.len, new_len); |
| 931 | @memcpy(new_control_ptr[0..overlap], old_control[0..overlap]); | 912 | @memcpy(new_control_ptr[0..overlap], old_control[0..overlap]); |
| 932 | ctx.control_allocator.rawFree(old_control, alignment, @returnAddress()); | 913 | ctx.control_allocator.rawFree(old_control, alignment, @returnAddress()); |
| ... | @@ -935,23 +916,27 @@ const FuzzContext = struct { | ... | @@ -935,23 +916,27 @@ const FuzzContext = struct { |
| 935 | return; | 916 | return; |
| 936 | } | 917 | } |
| 937 | | 918 | |
| 938 | ctx.allocs.set(index, .{ | 919 | ctx.allocs[@intFromEnum(index)] = .{ |
| 939 | .control_ptr = new_control_ptr, | 920 | .control_ptr = new_control_ptr, |
| 940 | .sample_ptr = memory.ptr, | 921 | .sample_ptr = memory.ptr, |
| 941 | .len = @enumFromInt(new_len), | 922 | .common = .{ |
| 942 | .alignment = alignment, | 923 | .len = @enumFromInt(new_len), |
| 943 | }); | 924 | .alignment = alignment, |
| | 925 | }, |
| | 926 | }; |
| 944 | | 927 | |
| 945 | if (new_len > memory.len) { | 928 | if (new_len > memory.len) { |
| 946 | for ( | 929 | for ( |
| 947 | ctx.allocs.items(.control_ptr)[index][memory.len..new_len], | 930 | allocation.control_ptr[memory.len..new_len], |
| 948 | ctx.allocs.items(.sample_ptr)[index][memory.len..new_len], | 931 | allocation.sample_ptr[memory.len..new_len], |
| 949 | ) |*control, *sample| { | 932 | 0.., |
| 950 | control.* = tls_next; | 933 | ) |*control, *sample, i| { |
| 951 | sample.* = tls_next; | 934 | control.* = @truncate(i); |
| 952 | tls_next +%= 1; | 935 | sample.* = @truncate(i); |
| 953 | } | 936 | } |
| 954 | } | 937 | } |
| | 938 | |
| | 939 | @atomicStore(Alloc.Index, &ctx.last_alloc_index, index, .release); |
| 955 | } | 940 | } |
| 956 | fn doOneRemap(ctx: *FuzzContext, new_len: usize) void { | 941 | fn doOneRemap(ctx: *FuzzContext, new_len: usize) void { |
| 957 | @disableInstrumentation(); | 942 | @disableInstrumentation(); |
| ... | @@ -959,26 +944,29 @@ const FuzzContext = struct { | ... | @@ -959,26 +944,29 @@ const FuzzContext = struct { |
| 959 | } | 944 | } |
| 960 | fn doOneFree(ctx: *FuzzContext) void { | 945 | fn doOneFree(ctx: *FuzzContext) void { |
| 961 | @disableInstrumentation(); | 946 | @disableInstrumentation(); |
| 962 | const index = tls_last_index orelse return; | | |
| 963 | const len = ctx.allocs.items(.len)[index]; | | |
| 964 | assert(len != .free); | | |
| 965 | const memory = ctx.allocs.items(.sample_ptr)[index][0..@intFromEnum(len)]; | | |
| 966 | const alignment = ctx.allocs.items(.alignment)[index]; | | |
| 967 | | 947 | |
| 968 | assert(alignment.check(@intFromPtr(ctx.allocs.items(.control_ptr)[index]))); | 948 | const index = @atomicRmw(Alloc.Index, &ctx.last_alloc_index, .Xchg, .none, .acquire); |
| 969 | assert(alignment.check(@intFromPtr(ctx.allocs.items(.sample_ptr)[index]))); | 949 | if (index == .none) return; |
| 970 | | 950 | |
| 971 | ctx.control_allocator.rawFree(ctx.allocs.items(.control_ptr)[index][0..memory.len], alignment, @returnAddress()); | 951 | const allocation = &ctx.allocs[@intFromEnum(index)]; |
| 972 | ctx.sample_allocator.rawFree(ctx.allocs.items(.sample_ptr)[index][0..memory.len], alignment, @returnAddress()); | 952 | assert(allocation.common.len != .free); |
| | 953 | const len: usize = @intFromEnum(allocation.common.len); |
| | 954 | const alignment = allocation.common.alignment; |
| 973 | | 955 | |
| 974 | ctx.allocs.set(index, .{ | 956 | assert(alignment.check(@intFromPtr(allocation.control_ptr))); |
| | 957 | assert(alignment.check(@intFromPtr(allocation.sample_ptr))); |
| | 958 | |
| | 959 | ctx.control_allocator.rawFree(allocation.control_ptr[0..len], alignment, @returnAddress()); |
| | 960 | ctx.sample_allocator.rawFree(allocation.sample_ptr[0..len], alignment, @returnAddress()); |
| | 961 | |
| | 962 | ctx.allocs[@intFromEnum(index)] = .{ |
| 975 | .control_ptr = undefined, | 963 | .control_ptr = undefined, |
| 976 | .sample_ptr = undefined, | 964 | .sample_ptr = undefined, |
| 977 | .len = .free, | 965 | .common = .{ |
| 978 | .alignment = undefined, | 966 | .len = .free, |
| 979 | }); | 967 | .alignment = .@"1", |
| 980 | | 968 | }, |
| 981 | tls_last_index = null; | 969 | }; |
| 982 | } | 970 | } |
| 983 | }; | 971 | }; |
| 984 | | 972 | |