@@ -49,6 +49,10 @@ final class SfManifest implements QuietCloseable {
4949 private final int fd ;
5050 private final FilesFacade filesFacade ;
5151 private final String path ;
52+ // Preallocated record scratch for update(): the trim path calls update()
53+ // once per batch and must not malloc/free per call. Guarded by this
54+ // object's monitor (update() and close() are both synchronized).
55+ private final long writeScratch ;
5256 private long activeBase ;
5357 private boolean closed ;
5458 private long generation ;
@@ -62,6 +66,7 @@ private SfManifest(FilesFacade filesFacade, String path, int fd,
6266 this .generation = generation ;
6367 this .headBase = headBase ;
6468 this .activeBase = activeBase ;
69+ this .writeScratch = Unsafe .malloc (RECORD_SIZE , MemoryTag .NATIVE_DEFAULT );
6570 }
6671
6772 static SfManifest create (FilesFacade filesFacade , String dir , long headBase , long activeBase ) {
@@ -71,11 +76,12 @@ static SfManifest create(FilesFacade filesFacade, String dir, long headBase, lon
7176 throw new MmapSegmentException ("exclusive create failed for SF manifest " + path );
7277 }
7378 boolean success = false ;
79+ SfManifest manifest = null ;
7480 try {
7581 if (!filesFacade .allocate (fd , FILE_SIZE )) {
7682 throw new MmapSegmentException ("could not allocate SF manifest " + path );
7783 }
78- SfManifest manifest = new SfManifest (filesFacade , path , fd , 0 , -1 , -1 );
84+ manifest = new SfManifest (filesFacade , path , fd , 0 , -1 , -1 );
7985 manifest .update (headBase , activeBase );
8086 if (filesFacade .fsyncDir (dir ) != 0 ) {
8187 throw new MmapSegmentException ("could not sync SF manifest directory " + dir );
@@ -84,7 +90,13 @@ static SfManifest create(FilesFacade filesFacade, String dir, long headBase, lon
8490 return manifest ;
8591 } finally {
8692 if (!success ) {
87- filesFacade .close (fd );
93+ if (manifest != null ) {
94+ // close() frees the constructor-owned scratch buffer as
95+ // well as the fd; closing only the raw fd would leak it.
96+ manifest .close ();
97+ } else {
98+ filesFacade .close (fd );
99+ }
88100 filesFacade .remove (path );
89101 }
90102 }
@@ -151,9 +163,13 @@ long activeBase() {
151163 }
152164
153165 @ Override
154- public void close () {
166+ public synchronized void close () {
167+ // Synchronized against update() so the scratch buffer can never be
168+ // freed under a concurrent writer; update() checks `closed` inside
169+ // the same monitor.
155170 if (!closed ) {
156171 closed = true ;
172+ Unsafe .free (writeScratch , RECORD_SIZE , MemoryTag .NATIVE_DEFAULT );
157173 filesFacade .close (fd );
158174 }
159175 }
@@ -202,29 +218,24 @@ synchronized void update(long newHeadBase, long newActiveBase) {
202218 return ;
203219 }
204220 long nextGeneration = generation + 1 ;
205- long buffer = Unsafe .malloc (RECORD_SIZE , MemoryTag .NATIVE_DEFAULT );
206- try {
207- Unsafe .getUnsafe ().setMemory (buffer , RECORD_SIZE , (byte ) 0 );
208- Unsafe .getUnsafe ().putInt (buffer , MAGIC );
209- Unsafe .getUnsafe ().putInt (buffer + 4 , VERSION );
210- Unsafe .getUnsafe ().putLong (buffer + 8 , nextGeneration );
211- Unsafe .getUnsafe ().putLong (buffer + 16 , newHeadBase );
212- Unsafe .getUnsafe ().putLong (buffer + 24 , newActiveBase );
213- int crc = Crc32c .update (Crc32c .INIT , buffer , CRC_OFFSET );
214- Unsafe .getUnsafe ().putInt (buffer + CRC_OFFSET , crc );
215- long offset = (nextGeneration & 1L ) * RECORD_SIZE ;
216- if (filesFacade .write (fd , buffer , RECORD_SIZE , offset ) != RECORD_SIZE ) {
217- throw new MmapSegmentException ("short write updating SF manifest " + path );
218- }
219- if (filesFacade .fsync (fd ) != 0 ) {
220- throw new MmapSegmentException ("could not sync SF manifest " + path );
221- }
222- generation = nextGeneration ;
223- headBase = newHeadBase ;
224- activeBase = newActiveBase ;
225- } finally {
226- Unsafe .free (buffer , RECORD_SIZE , MemoryTag .NATIVE_DEFAULT );
221+ Unsafe .getUnsafe ().setMemory (writeScratch , RECORD_SIZE , (byte ) 0 );
222+ Unsafe .getUnsafe ().putInt (writeScratch , MAGIC );
223+ Unsafe .getUnsafe ().putInt (writeScratch + 4 , VERSION );
224+ Unsafe .getUnsafe ().putLong (writeScratch + 8 , nextGeneration );
225+ Unsafe .getUnsafe ().putLong (writeScratch + 16 , newHeadBase );
226+ Unsafe .getUnsafe ().putLong (writeScratch + 24 , newActiveBase );
227+ int crc = Crc32c .update (Crc32c .INIT , writeScratch , CRC_OFFSET );
228+ Unsafe .getUnsafe ().putInt (writeScratch + CRC_OFFSET , crc );
229+ long offset = (nextGeneration & 1L ) * RECORD_SIZE ;
230+ if (filesFacade .write (fd , writeScratch , RECORD_SIZE , offset ) != RECORD_SIZE ) {
231+ throw new MmapSegmentException ("short write updating SF manifest " + path );
232+ }
233+ if (filesFacade .fsync (fd ) != 0 ) {
234+ throw new MmapSegmentException ("could not sync SF manifest " + path );
227235 }
236+ generation = nextGeneration ;
237+ headBase = newHeadBase ;
238+ activeBase = newActiveBase ;
228239 }
229240
230241 /**
0 commit comments