@@ -48,18 +48,20 @@ memory_segments: MemorySegmentList,
48
48
49
49
// RW lock used to control general mutations to either file_segments or memory_segments.
50
50
// This lock needs to be held for any read/write operations on either list.
51
+ // Once you hold this lock, you can be sure that no changes are happening to either list.
51
52
segments_lock : std.Thread.RwLock = .{},
52
53
53
- // Mutex used to control exclusivity during re-shuffling of segment lists during checkpoint/merges.
54
- // This lock by itself doesn't give access to either list, you need to hold the segments_lock as well.
54
+ // These locks give partial access to the respective segments list.
55
+ // 1) For memory_segments, new segment can be appended to the list without this lock.
56
+ // 2) For file_segments, no write operation can happen without this lock.
57
+ // These lock can be only acquired before segments_lock, never after, to avoid deadlock situatons.
58
+ // They are mostly useful to allowing read access to segments during merge/checkpoint, without blocking real-time update.
55
59
file_segments_lock : std.Thread.Mutex = .{},
60
+ memory_segments_lock : std.Thread.Mutex = .{},
56
61
57
62
// Mutex used to control linearity of updates.
58
63
update_lock : std.Thread.Mutex = .{},
59
64
60
- // Mutex used to control merging of in-memory segments.
61
- memory_merge_lock : std.Thread.Mutex = .{},
62
-
63
65
checkpoint_mutex : std.Thread.Mutex = .{},
64
66
checkpoint_condition : std.Thread.Condition = .{},
65
67
checkpoint_stop : bool = false ,
@@ -135,8 +137,8 @@ fn flattenMemorySegmentIds(self: *Self) void {
135
137
}
136
138
137
139
fn prepareMemorySegmentMerge (self : * Self ) ! ? MemorySegmentList.PreparedMerge {
138
- self .segments_lock . lockShared ();
139
- defer self .segments_lock . unlockShared ();
140
+ self .memory_segments_lock . lock ();
141
+ defer self .memory_segments_lock . unlock ();
140
142
141
143
var merge = try self .memory_segments .prepareMerge () orelse return null ;
142
144
defer merge .merger .deinit ();
@@ -148,6 +150,9 @@ fn prepareMemorySegmentMerge(self: *Self) !?MemorySegmentList.PreparedMerge {
148
150
}
149
151
150
152
fn finishMemorySegmentMerge (self : * Self , merge : MemorySegmentList.PreparedMerge ) bool {
153
+ self .memory_segments_lock .lock ();
154
+ defer self .memory_segments_lock .unlock ();
155
+
151
156
defer self .memory_segments .cleanupAfterMerge (merge , .{});
152
157
153
158
self .segments_lock .lock ();
@@ -166,9 +171,6 @@ fn finishMemorySegmentMerge(self: *Self, merge: MemorySegmentList.PreparedMerge)
166
171
167
172
// Perform partial compaction on the in-memory segments.
168
173
fn maybeMergeMemorySegments (self : * Self ) ! bool {
169
- self .memory_merge_lock .lock ();
170
- defer self .memory_merge_lock .unlock ();
171
-
172
174
const merge = try self .prepareMemorySegmentMerge () orelse return false ;
173
175
return self .finishMemorySegmentMerge (merge );
174
176
}
@@ -292,6 +294,10 @@ fn doCheckpoint(self: *Self) !bool {
292
294
293
295
try filefmt .writeIndexFile (self .data_dir , ids .items );
294
296
297
+ // we are about to remove segment from the memory_segments list
298
+ self .memory_segments_lock .lock ();
299
+ defer self .memory_segments_lock .unlock ();
300
+
295
301
self .segments_lock .lock ();
296
302
defer self .segments_lock .unlock ();
297
303
@@ -405,8 +411,8 @@ fn stopFileSegmentMergeThread(self: *Self) void {
405
411
}
406
412
407
413
fn prepareFileSegmentMerge (self : * Self ) ! ? FileSegmentList.PreparedMerge {
408
- self .segments_lock . lockShared ();
409
- defer self .segments_lock . unlockShared ();
414
+ self .file_segments_lock . lock ();
415
+ defer self .file_segments_lock . unlock ();
410
416
411
417
var merge = try self .file_segments .prepareMerge () orelse return null ;
412
418
defer merge .merger .deinit ();
0 commit comments