diff --git a/spec/3_1-header-fields.md b/spec/3_1-header-fields.md index 0f2c96715..3a09d50a0 100644 --- a/spec/3_1-header-fields.md +++ b/spec/3_1-header-fields.md @@ -306,8 +306,12 @@ spaces. A positive, 32-bit, power of 2 value, e. g. `1`. ##### `worstAlignment` -The most bytes could theoretically be wasted on the value [`alignment`](#alignment). A non-negative -32-bit value, e. g. `0`. +The most bytes could theoretically be wasted on the value [`alignment`](#alignment). The value +alignment waste might vary, if the [`chunkSize`](#chunksize) is not a multiple of +[`alignment`](#alignment), and/or due to amount of memory before value in the [stored entry +structure](3-memory-layout.md#stored-entry-structure) might vary. + +A non-negative 32-bit value, e. g. `0`. > ## The reference Java implementation > diff --git a/spec/6-queries.md b/spec/6-queries.md new file mode 100644 index 000000000..b4e909fad --- /dev/null +++ b/spec/6-queries.md @@ -0,0 +1,465 @@ +# 6. Chronicle Map Data Store Query Operations + +Some definitions, used on this page: + +##### Entries number field + +The *entries number field* of some tier is the 2nd field of the [segment header structure]( +3-memory-layout.md#segment-header-structure) (if the tier is the first in the segment it belongs +to), or the 6th field of the [tier counters area](3-memory-layout.md#segment-tier-counters-area) +(if the tier is not the first in the segment it belongs to). + +##### Smallest free chunk index field + +The *smallest free chunk index field* of some tier is the 3rd field of the [segment header +structure](3-memory-layout.md#segment-header-structure) (if the tier is the first in the segment it +belongs to), or the 3rd field of the [tier counters area]( +3-memory-layout.md#segment-tier-counters-area) (if the tier is not the first in the segment it +belongs to). + +##### Next tier field + +The *next tier field* of some tier is the 4th field of the [segment header structure]( +3-memory-layout.md#segment-header-structure) (if the tier is the first in the segment it belongs +to), or the 1st field of the [counters area](3-memory-layout.md#segment-tier-counters-area) (if the +tier belongs to one of [extra tier bulks](3-memory-layout.md#extra-tier-bulks), i. e. not the first +in the segment it belongs to). + +## Key lookup + + 1. [Compute hash code](4-hashing-algorithms.md) of the key and determine the segment index and the + part of the key's hash code to be stored in hashLookup using [`hashSplitting`]( + 3_1-header-fields.md#hashsplitting) algorithm. + + 2. Ensure the lock (the 1st field of the [segment header structure]( + 3-memory-layout.md#segment-header-structure) of the segment with the index, determined on the + previous step (below this segment is simply called the *current* segment), is acquired at least on + the read level (using one of [read locking operations]( + 3_2-lock-structure.md#try-acquire-read-lock). + + This step (and the previous step) might be done already, if the key lookup is performed as a part + of some multi-key query. Also, if multiple keys in the multi-key query go to the same segment, the + segment lock is acquired only once at the highest level, required by sub-operations with those + keys. + + 3. Compute the starting slot index in [hash lookups](3-memory-layout.md#hash-lookup), that is the + reminder of integral division of the [hash lookup key](3-memory-layout.md#hash-lookup-key) + (computed on the 1st step of this operation) by the [`tierHashLookupCapacity`]( + 3_1-header-fields.md#tierhashlookupcapacity). + + 4. Read the value in the slot with the index, computed on the previous step, of the current tier + (initially the [first tier](3-memory-layout.md#main-segments-area) of the current segment). + + This read operation is followed by a read barrier, as formally described in [memory ordering]( + 1-design-goals.md#memory-ordering) section of the Chronicle Map's platform assumptions. I. e. + writes performed before writing the slot value which was read are visible after the read. + + > In the reference Java implementation, this is a volatile read of `int` or `long` value. + + > The memory ordering is needed because [entry insertion](#entry-insertion) is performed with the + > segment lock held on the update level, then concurrent readers may exist who should read correct + > entry data after it is inserted by updating the slot value. + + 5. If the slot is non-empty, check if the hash lookup key stored in the slot is equal to the hash + lookup key, computed on the 3rd step. If they are equal, compare the queried key (of this key + lookup operation) with the key, stored in the [entry](3-memory-layout.md#stored-entry-structure), + starting from the chunk with the index, stored in the value of the current slot. If the keys are + equal, the lookup operation is successful (the value could be read from the located [entry + structure](3-memory-layout.md#stored-entry-structure)). + + After reading the value stored for the queried key (if needed) the segment lock is released, + however this could be deferred in the case of multi-key query. + + If the slot is empty, continue with the 7th step. + + If any of two checks is failed during this step, continue with the next step. + + 6. Compute the next hash lookup slot, by incrementing the current slot index and wrapping around + [`tierHashLookupCapacity`](3_1-header-fields.md#tierhashlookupcapacity) (i. e. instead of + `tierHashLookupCapacity` the slot index becomes 0). Then continue with the 4th step. + + 7. If the current tier is the last tier in the chain for the current segment, i. e. the value of + the [next tier field](#next-tier-field) of the current tier is 0, the lookup operation is failed. + Otherwise, continue with the next linked tier (as the new current tier) and go to the 3rd step. + +> The reference Java implementation: [`SegmentStages`]( +> ../src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java), [`KeySearch`]( +> ../src/main/java/net/openhft/chronicle/hash/impl/stage/query/KeySearch.java), [`HashQuery`]( +> ../src/main/java/net/openhft/chronicle/hash/impl/stage/query/HashQuery.java) classes. + + +## Entry insertion + + 1. Perform a [key lookup](#key-lookup) for the key that is going to be inserted, ensuring that the + segment is locked at least on the *update* level on the 2nd step of lookup operation. If the + segment for the key is already locked on the read level (which couldn't be upgraded to a higher + level), the lock is released, then acquired again on the update level, and then key lookup is + performed. + + Insertion is possible only if the lookup operation is *failed*. + + 2. Compute the number of bytes would be spanned by the [stored entry structure]( + 3-memory-layout.md#stored-entry-structure) with the key and the value that are going to be + inserted, assuming the [worst possible value alignment](3_1-header-fields.md#worstalignment). + + 3. Compute the number of chunks that covers the entry size computed on the previous step. I. e. the + smallest multiple of [`chunkSize`](3_1-header-fields.md#chunksize) that is equal or greater than + the computed entry size. + + 4. Set the *current tier* to the first tier in the segment. + + 5. If the value in the [entries number field](#entries-number-field) of the current tier is equal + to or greater than the [`maxEntriesPerHashLookup`](3_1-header-fields.md#maxentriesperhashlookup), + go to the 14th step. + + 6. Try to find a continuous block of bits set to 0, the number of bits is computed on the 3rd step, + in the [free list](3-memory-layout.md#free-list) of the current tier. + + Start scanning free list from the index, stored in the [smallest free chunk index + field](#smallest-free-chunk-index-field) of the current tier. + + If a block of bits set to 0 of the required length is found, continue with the next step, otherwise + go to the 14th step. + + 7. Set the bits, found on the previous step, to 1. + + 8. Increment the value in the [entries number field](#entries-number-field) of the current tier. + + 9. If the index of the first bit in the block, found on the previous step, equals to the value, + stored in the [smallest free chunk index field](#smallest-free-chunk-index-field), then update the + stored value to the index of the bit, following after the found block (or the + [`actualChunksPerSegmentTier`](3_1-header-fields.md#actualchunkspersegmenttier) value, if the found + block ends at the end of the free list). + + 10. Write the entry to be inserted in the [entry space](3-memory-layout.md#entry-space) of the + current tier, starting from the chunk at index equal to the index of the first bit in the block, + that was found and set to 1 on the previous steps. The procedure of writing an entry is effectively + defined by [stored entry structure](3-memory-layout.md#stored-entry-structure). + + > In the reference Java implementation, writing the checksum is delayed until the segment lock + > is released or downgraded to the read level. This is done because sometimes (especially when + > the map is replicated) the entry is updated several times during one operation, delaying checksum + > write allows to avoid unnecessary multiple checksum computations (relatively expensive). + + 11. If the actual value alignment, required by the stored entry and actual addresses at which it + was stored is smaller than the worst possible alignment, used to compute expected size of entry + structure on the 2nd step of this operation, and smaller number of chunks is required to cover the + actual stored entry structure, [free extra chunks](#free-chunks). + + 12. [Locate an empty slot](#locate-empty-slot) in the [hash lookup](3-memory-layout.md#hash-lookup) + of the current tier. + + Instead of re-computing empty slot positions, empty slot indexes could be cached when performed key + lookup (in the 1st step of entry insertion operation), and used in the next step right away. + + 13. Write a new value to the slot, located on the previous step. The value is combined from + [hash lookup key](3-memory-layout.md#hash-lookup-key) (computed on the 1st step of this operation) + in the [`tierHashLookupKeyBits`](3_1-header-fields.md#tierhashlookupkeybits) lowest bits and index + of the first bit found on the 6th step of this operation in the [`tierHashLookupValueBits`]( + 3_1-header-fields.md#tierhashlookupvaluebits) higher bits. + + This write operation is preceded by a write barrier, as formally described in [memory ordering]( + 1-design-goals.md#memory-ordering) section of the Chronicle Map's platform assumptions. I. e. + writes performed before the slot value write are visible after this value is read during [key + lookup](#key-lookup) operation on the 4th step. + + > In the reference Java implementation, this is a volatile write of `int` or `long` value. + + After the 13th step the insertion operation is successful. The segment lock is released, however + this could be deferred in the case of multi-key query, or the lock could be downgraded to the read + level. + + 14. If the current tier is the last tier in the chain for the segment, i. e. the value of the [next + tier field](#next-tier-field) of the current tier is 0, go to the 15th step. Otherwise, continue + with the next linked tier (as the new current tier) and go to the 5th step. + + 15. Try to [allocate a new tier](5-initialization.md#tier-allocation). If the allocation operation + fails, the entry insertion operation also fails. If the allocation is successful, [link the newly + allocated tier](#link-new-tier-to-segment-chain) to the segment chain. + + Set the current tier to the newly allocated tier and continue with the 5th step. + +> The reference Java implementation: [`MapAbsent.putEntry()`]( +> ../src/main/java/net/openhft/chronicle/map/impl/stage/query/MapAbsent.java) method. + + +## Value update + +Steps 4-5 are *in-place* value update. Steps from 6 to the end of the operation are called +*relocation* (relocating value update). + + 1. Perform a [key lookup](#key-lookup) for the key that value is going to be updated for, ensuring + that the segment is locked at least on the *update* level on the 2nd step of lookup operation. If + the segment for the key is already locked on the read level (which couldn't be upgraded to a higher + level), the lock is released, then acquired again on the update level, and then key lookup is + performed. + + > Value update always requires the segment lock to be held on the write level at some point of the + > operation, therefore if the segment is not yet locked on the update level, acquiring the lock + > on the write level right away allows not to perform a lock upgrade operation later. On the other + > hand, if there are frequent concurrent readers, reducing the time when the lock is held on an + > exclusive level improves concurrency. This is an implementation choice. The reference Java + > implementation takes the second way, i. e. acquires lock on the update level first, and upgrades + > to the write level for the least possible time. + + Value update is possible only if the lookup operation is *successful*. + + 2. Compute the number of bytes of the [entry structure](3-memory-layout.md#stored-entry-structure) + at the current addresses, but with the updated value. (Alignment could change, because if the value + size is different, number of bytes taken by value size encoding (the 3rd field of entry structure) + could also change.) + + 3. Compute the new number of chunks that covers the new entry structure. + + If the new number of chunks required for the entry is less than the former, [free the extra + chunks](#free-chunks). If the new number of chunks is greater than the former, and the block + of bits of length equal to the number of additional chunks required, and starting at the index of + chunk immediately following the last chunk taken by the entry currently, is all set to 0, set these + bits to 1. If the block is not empty (some bits are set to 1), go to the 6th step. Otherwise, + continue with the next step. + + 4. If the segment is locked on the update level, [upgrade to the write + level](#time-limited-update-to-write-lock-upgrade). + + > In-place value update (the following step) is performed on fully exclusive locking level, because + > concurrent readers could see inconsistent value state, while the update is in progress. If the + > value could be updated atomically on machine level: e. g. the value is just 4 or 8 bytes long + > and could be updated by a single ordinary (atomic on x86) memory transfer, implementations + > may omit acquiring the segment lock on the write level on this step. + + > Similar effect could be achieved with hardware memory transactions (`XBEGIN` and `XCOMMIT` + > instructions) when updating larger values, though this topic is not explored in this + > specification. + + 5. Write the new value into the old [entry structure](3-memory-layout.md#stored-entry-structure), + updating value size if needed. Update the entry checksum (the 6th field of entry structure). + + > In the reference Java implementation, updating the checksum is delayed until the segment lock + > is released or downgraded to the read level. This is done because sometimes (especially when + > the map is replicated) the entry is updated several times during one operation, delaying checksum + > write allows to avoid unnecessary multiple checksum computations (relatively expensive). + + After the 5th step the update operation is successful. The segment lock is released, however this + could be deferred in the case of multi-key query, or the lock could be downgraded to a lower level. + + 6. Compute the number of bytes would be spanned by a new [entry structure]( + 3-memory-layout.md#stored-entry-structure) with the queried key and the value that is going to + replace the previous value, assuming the [worst possible value alignment]( + 3_1-header-fields.md#worstalignment). + + 7. Compute the number of chunks that covers the entry size computed on the previous step. I. e. the + smallest multiple of [`chunkSize`](3_1-header-fields.md#chunksize) that is equal or greater than + the computed entry size. + + 8. If the value in the [entries number field](#entries-number-field) of the current tier is equal + to or greater than the [`maxEntriesPerHashLookup`](3_1-header-fields.md#maxentriesperhashlookup), + go to the 19th step. + + 9. Try to find a continuous block of bits set to 0, the number of bits is computed on the 7th step, + in the [free list](3-memory-layout.md#free-list) of the current tier. + + Start scanning free list from the index, stored in the [smallest free chunk index + field](#smallest-free-chunk-index-field) of the current tier. + + If a block of bits set to 0 of the required length is found, continue with the next step, otherwise + go to the 19th step. + + 10. Set the bits, found on the previous step, to 1. + + 11. Increment the value in the [entries number field](#entries-number-field) of the current tier. + + 12. If the index of the first bit in the block, found on the previous step, equals to the value, + stored in the [smallest free chunk index field](#smallest-free-chunk-index-field), then update the + stored value to the index of the bit, following after the found block (or the + [`actualChunksPerSegmentTier`](3_1-header-fields.md#actualchunkspersegmenttier) value, if the found + block ends at the end of the free list). + + 13. If the current tier is not changed since the 3rd step of this operation, [free the + chunks](#free-chunks), taken by the previous entry for the queried key. + + > The previous entry if freed only after the new is allocated in order to prohibit overlapping + > of these blocks of chunks. This is needed, because in the reference Java implementation, the + > following step is essentially a `sun.misc.Unsafe.copyMemory()` call, that is not guaranteed + > to work correctly for overlapping memory regions. + + 14. Copy the part of the already existing entry (located on the first step of this operation) + before the 3rd field, i. e. stored key size and the key itself, to the entry space of the current + tier, starting from the chunk at index equal to the index of the first bit in the block, that was + found and set to 1 on the previous steps. Write the rest of the entry (the new value size, the + new value and optionally checksum) after the copied part. The procedure of writing an entry is + effectively defined by [stored entry structure](3-memory-layout.md#stored-entry-structure). + + > In the reference Java implementation, writing the checksum is delayed until the segment lock + > is released or downgraded to the read level. This is done because sometimes (especially when + > the map is replicated) the entry is updated several times during one operation, delaying checksum + > write allows to avoid unnecessary multiple checksum computations (relatively expensive). + + 15. If the actual value alignment, required by the stored entry and actual addresses at which it + was stored is smaller than the worst possible alignment, used to compute expected size of entry + structure on the 6th step of this operation, and smaller number of chunks is required to cover the + actual stored entry structure, [free extra chunks](#free-chunks). + + 16. If the current tier is not changed since the 3rd step of this operation, write a new value to + the slot, located during key lookup on the 1st step of this operation. + + If the current tier is changed since the 3rd step of this operation, [locate an empty + slot](#locate-empty-slot) in the [hash lookup](3-memory-layout.md#hash-lookup) of the current tier + and write a new value to this slot. + + The new value is combined from [hash lookup key](3-memory-layout.md#hash-lookup-key) (computed on + the 1st step of this operation, and remaining unchanged in the slot value) in the + [`tierHashLookupKeyBits`](3_1-header-fields.md#tierhashlookupkeybits) lowest bits and index of the + first bit found on the 9th step of this operation in the [`tierHashLookupValueBits`]( + 3_1-header-fields.md#tierhashlookupvaluebits) higher bits. + + This write operation is preceded by a write barrier, as formally described in [memory ordering]( + 1-design-goals.md#memory-ordering) section of the Chronicle Map's platform assumptions. I. e. + writes performed before the slot value write are visible after this value is read during [key + lookup](#key-lookup) operation on the 4th step. + + > In the reference Java implementation, this is a volatile write of `int` or `long` value. + + 17. Ensure the segment lock is acquired on the write level. To this step, the segment lock is + already acquired at least on the update level; [upgrade to the write level]( + 3_2-lock-structure.md#time-limited-update-to-write-lock-upgrade) if needed. + + > While relocating value update is in progress, and if a block of free chunks for the updated entry + > is found in the same tier, where the entry was originally located, concurrent segment readers are + > safe, because the slot state is updated atomically (on the previous step) and with memory + > barriers. Acquiring the segment lock on the fully exclusive write level after the relocation is + > already done, is effectively waiting until all concurrent readers release the segment lock. This + > is needed, because if some concurrent readers hold the lock (and observe the previous value + > mapped for the queried key, in the previous location) after value update operation exits and the + > current thread releases the segment lock, another updating thread might come in and overwrite + > bytes in the previous entry location in the entry state, compromising long-staying readers. That + > is why we need to ensure, that the readers which come before this value update operation started + > (or in the process of the value update) exit before this value update operation exits. + > + > Example: [`TrickyContextCasesTest.testPutShouldBeWriteLocked()`]( + > ../src/test/java/net/openhft/chronicle/map/TrickyContextCasesTest.java) method. + > + > Possibility to optimize this is explored in [HCOLL-425]( + > https://higherfrequencytrading.atlassian.net/browse/HCOLL-425). + > + > If a block of free chunks for the updated entry is found in a different tier from the one where + > the entry was originally located, the slot should be removed in the previous tier hash lookup + > (see the next step), that is not safe for concurrent readers, that is why the lock should also be + > upgraded to the write level. + + 18. If the current tier is changed since the 3rd step of this operation, [shift-remove the + slot](#shift-remove-hash-lookup-slot), located on the 1st step of this operation. + + 19. If the current tier is not changed since the 3rd step of this operation, [free the + chunks](#free-chunks), taken by the previous entry for the queried key. + + 20. If this step is performed for the first time, set the current tier to the first tier of this + segment. + + 21. If the current tier is the last tier in the chain for the segment, i. e. the value of the [next + tier field](#next-tier-field) of the current tier is 0, go to the 22th step. Otherwise, set the + current tier to the next linked tier. If the current tier equals to the then-current tier on the + 3rd step of this operation, repeat this step. Otherwise, go to the 8th step. + + 22. Try to [allocate a new tier](5-initialization.md#tier-allocation). If the allocation operation + fails, the entry insertion operation also fails. If the allocation is successful, [link the newly + allocated tier](#link-new-tier-to-segment-chain) to the segment chain. + + Set the current tier to the newly allocated tier and continue with the 8th step. + +> The reference Java implementation: mostly [`MapEntryStages.innerDefaultReplaceValue()`]( +> ../src/main/java/net/openhft/chronicle/map/impl/stage/entry/MapEntryStages.java) method. + + +## Entry removal + + 1. Perform a [key lookup](#key-lookup) for the key that is going to be removed, ensuring that the + segment is locked on the *write* level on the 2nd step of lookup operation. If the segment for the + key is already locked on the read level (which couldn't be upgraded to the write level), the lock + is released, then acquired again on the write level, and then key lookup is performed. + + Entry removal is possible only if the lookup operation is *successful*. + + 2. [Free the chunks](#free-chunks), spanned by the entry. + + 3. [Shift-remove the slot in the hash lookup](#shift-remove-hash-lookup-slot), located on the first + step of this operation. + + > The reference Java implementation: [`HashQuery.doRemove()`]( + > ../src/main/java/net/openhft/chronicle/hash/impl/stage/query/HashQuery.java) method. + + +Supplementary operations: + +#### Locate empty slot + +Locate an empty slot in the [hash lookup](3-memory-layout.md#hash-lookup) of some tier, while +querying for some key: + + 1. Compute the starting slot index, that is the reminder of integral division of the [hash lookup + key](3-memory-layout.md#hash-lookup-key) by the [`tierHashLookupCapacity`]( + 3_1-header-fields.md#tierhashlookupcapacity). + 2. Read the value in the current slot. + 3. If the slot is empty this sub-operation is finished. Otherwise, continue with the next step. + 4. Compute the next hash lookup slot, by incrementing the current slot index and wrapping around + [`tierHashLookupCapacity`](3_1-header-fields.md#tierhashlookupcapacity) (i. e. instead of + `tierHashLookupCapacity` the slot index becomes 0). Then continue with the 2nd step. + +> In the reference Java implementation, this sub-operation and the steps 3-6 of [key +> lookup](#key-lookup) operation are implemented by a single `initKeySearch()` method in +> [`KeySearch`](../src/main/java/net/openhft/chronicle/hash/impl/stage/query/KeySearch.java). The +> above sub-operation is weaker than the corresponding part of key lookup: doesn't require volatile +> read of slots values and checking slot contents, only if slot is empty or full. + +#### Free chunks + +Set to 0 a block of bits with indexes equal to indexes of some block of chunks in the entry space. +If the index of the first cleared bit is less than the value, stored in the [smallest free chunk +index field](#smallest-free-chunk-index-field) of the current tier, set the value of this field to +the index of the first freed extra chunk. + +> The reference Java implementation: `free()` and `freeExtra()` methods in [`SegmentStages`]( +> ../src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java) class. + +#### Shift-remove hash lookup slot + +Removal of some slot in the hash lookup of some tier (an open addressing hash table with linear +probing). + + 1. Set the *remove slot* and the *shift slot* to the slot that is going to be removed. + 2. Compute the next shift slot, by incrementing the current shift slot index and wrapping around + [`tierHashLookupCapacity`](3_1-header-fields.md#tierhashlookupcapacity) (i. e. instead of + `tierHashLookupCapacity` the slot index becomes 0). + 3. Read the value in the shift slot. + 4. If the shift slot value is 0 (i. e. the slot is empty), go to the 6th step. Otherwise, extract + the hash lookup key of the shift slot (at the lowest [`tierHashLookupKeyBits`]( + 3_1-header-fields.md#tierhashlookupkeybits) bits of the slot value), and determine, if the remove + slot is on the shortest slot chain between the starting search slot for the extracted hash lookup + key and the shift slot. In other words, check if the value in the shift slot could be inserted into + the remove slot (and the hash table is still correct), given that the hash table is not full. If + so, write the shift slot value to the remove slot, set the remove slot to the current shift slot. + 5. Go to the 2nd step. + 6. Write 0 to the remove slot, i. e. clear the slot. + +> The reference Java implementation: [`CompactOffHeapLinearHashTable.remove()`]( +> ../src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java) + +#### Link new tier to segment chain + +Link a newly-[allocated](5-initialization.md#tier-allocation) tier to the end of the tier chain of +some segment. + + 1. Write the [index](3-memory-layout.md#tier-index) of the newly allocated tier to the + [next tier field](#next-tier-field) of the last tier in the chain. + 2. Write the index of the segment to the 4th field of the [counters area]( + 3-memory-layout.md#segment-tier-counters-area) of the newly allocated tier. + 3. Write the length of the tier chain in the segment (before this operation) to the 5th field of + the counters area of the newly allocated tier. + 4. Write 0 to the 1st field, and the index of the current tier to the 2nd field of the counters + area of the newly allocated tier. + +> The reference Java implementation: [`SegmentStages.nextTier()`]( +> ../src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java) method. + + + + diff --git a/spec/README.md b/spec/README.md index 7c72d00ca..28943cada 100644 --- a/spec/README.md +++ b/spec/README.md @@ -9,6 +9,7 @@ This is currently work-in-progress. 2. [Lock structure and locking operations](3_2-lock-structure.md) 4. [Key hashing and checksum algorithms](4-hashing-algorithms.md) 5. [Initialization operations](5-initialization.md) + 6. [Query operations](6-queries.md) ## Rational behind this Specification diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java index 2cc7bf877..dae7e2353 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java @@ -684,6 +684,8 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { if (fromPos + newChunks < hh.h().actualChunksPerSegmentTier && freeList.isRangeClear(fromPos + oldChunks, fromPos + newChunks)) { freeList.setRange(fromPos + oldChunks, fromPos + newChunks); + // checking and updating lowestPossiblyFreeChunk is omitted because adds computational + // complexity for seemingly very small gain return true; } else { return false; diff --git a/src/test/java/net/openhft/chronicle/map/TrickyContextCasesTest.java b/src/test/java/net/openhft/chronicle/map/TrickyContextCasesTest.java index d86f7b026..b6e054822 100644 --- a/src/test/java/net/openhft/chronicle/map/TrickyContextCasesTest.java +++ b/src/test/java/net/openhft/chronicle/map/TrickyContextCasesTest.java @@ -16,14 +16,11 @@ package net.openhft.chronicle.map; -import net.openhft.chronicle.algo.hashing.LongHashFunction; import net.openhft.chronicle.core.values.IntValue; import net.openhft.chronicle.values.Values; -import org.junit.Assert; import org.junit.Test; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.Arrays; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors;