Skip to content

Commit ab9b562

Browse files
committed
- Remove MinBlockUnits/MinBlockSize usage (redundant)
- Better document and add checks to allocate_aligned
1 parent bb105ba commit ab9b562

File tree

3 files changed

+123
-91
lines changed

3 files changed

+123
-91
lines changed

include/boost/interprocess/mem_algo/detail/mem_algo_common.hpp

Lines changed: 100 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ class basic_multiallocation_chain
7777
}
7878
};
7979

80-
8180
//!This class implements several allocation functions shared by different algorithms
8281
//!(aligned allocation, multiple allocation...).
8382
template<class MemoryAlgorithm>
@@ -91,7 +90,6 @@ class memory_algorithm_common
9190
typedef typename MemoryAlgorithm::size_type size_type;
9291

9392
static const size_type Alignment = MemoryAlgorithm::Alignment;
94-
static const size_type MinBlockUnits = MemoryAlgorithm::MinBlockUnits;
9593
static const size_type AllocatedCtrlBytes = MemoryAlgorithm::AllocatedCtrlBytes;
9694
static const size_type AllocatedCtrlUnits = MemoryAlgorithm::AllocatedCtrlUnits;
9795
static const size_type BlockCtrlBytes = MemoryAlgorithm::BlockCtrlBytes;
@@ -116,6 +114,13 @@ class memory_algorithm_common
116114
static size_type floor_units(size_type size)
117115
{ return size/Alignment; }
118116

117+
static size_type user_buffer_ceil_units(size_type size)
118+
{
119+
if(size <= UsableByPreviousChunk)
120+
return 0;
121+
return ceil_units(size - UsableByPreviousChunk);
122+
}
123+
119124
static size_type multiple_of_units(size_type size)
120125
{ return get_rounded_size(size, Alignment); }
121126

@@ -236,133 +241,153 @@ class memory_algorithm_common
236241
}
237242

238243
static void* allocate_aligned
239-
(MemoryAlgorithm *memory_algo, size_type nbytes, size_type alignment)
244+
(MemoryAlgorithm * const memory_algo, const size_type nbytes, const size_type alignment)
240245
{
241246

242247
//Ensure power of 2
243-
if ((alignment & (alignment - size_type(1u))) != 0){
248+
const bool alignment_ok = (alignment & (alignment - 1u)) == 0;
249+
if (!alignment_ok){
244250
//Alignment is not power of two
245-
BOOST_ASSERT((alignment & (alignment - size_type(1u))) == 0);
251+
BOOST_ASSERT(alignment_ok);
246252
return 0;
247253
}
248254

249-
size_type real_size = nbytes;
250255
if(alignment <= Alignment){
256+
size_type real_size = nbytes;
251257
void *ignore_reuse = 0;
252258
return memory_algo->priv_allocate
253259
(boost::interprocess::allocate_new, nbytes, real_size, ignore_reuse);
254260
}
255261

256-
if(nbytes > UsableByPreviousChunk)
257-
nbytes -= UsableByPreviousChunk;
258-
259-
//We can find a aligned portion if we allocate a block that has alignment
260-
//nbytes + alignment bytes or more.
261-
size_type minimum_allocation = max_value
262-
(nbytes + alignment, size_type(MinBlockUnits*Alignment));
263-
//Since we will split that block, we must request a bit more memory
264-
//if the alignment is near the beginning of the buffer, because otherwise,
265-
//there is no space for a new block before the alignment.
266-
//
267-
// ____ Aligned here
268-
// |
269-
// -----------------------------------------------------
270-
// | MBU |
271-
// -----------------------------------------------------
272-
size_type request =
273-
minimum_allocation + (2*MinBlockUnits*Alignment - AllocatedCtrlBytes
274-
//prevsize - UsableByPreviousChunk
275-
);
262+
//To fulfill user's request we need at least min_user_units
263+
size_type needed_units = user_buffer_ceil_units(nbytes);
264+
//However, there is a minimum allocation unit count (BlockCtrlUnits) to be able to deallocate the buffer,
265+
//The allocation will give us a part of it (AllocatedCtrlUnits) so (BlockCtrlUnits - AllocatedCtrlUnits)
266+
//is the minimum ammount of blocks we need to allocate.
267+
needed_units += max_value(needed_units, BlockCtrlUnits - AllocatedCtrlUnits);
268+
//If we need to align, we need to at least move enough to create a new block at the beginning
269+
//that can be marked as free, so we need BlockCtrlUnits units for that
270+
needed_units += BlockCtrlUnits;
271+
//Finally, we need to add extra space to be sure we will find an aligned address
272+
needed_units += (alignment - Alignment)/Alignment;
273+
274+
//Transform units to bytes
275+
const size_type request = needed_units*Alignment + UsableByPreviousChunk;
276276

277277
//Now allocate the buffer
278-
real_size = request;
278+
size_type real_size = request;
279279
void *ignore_reuse = 0;
280-
void *buffer = memory_algo->priv_allocate(boost::interprocess::allocate_new, request, real_size, ignore_reuse);
280+
void *const buffer = memory_algo->priv_allocate(boost::interprocess::allocate_new, request, real_size, ignore_reuse);
281281
if(!buffer){
282282
return 0;
283283
}
284-
else if ((((std::size_t)(buffer)) % alignment) == 0){
284+
else if ((((std::size_t)(buffer)) & (alignment-1)) == 0){
285285
//If we are lucky and the buffer is aligned, just split it and
286286
//return the high part
287-
block_ctrl *first = memory_algo->priv_get_block(buffer);
288-
size_type old_size = first->m_size;
287+
block_ctrl *const first = memory_algo->priv_get_block(buffer);
288+
const size_type orig_first_units = first->m_size;
289289
const size_type first_min_units =
290-
max_value(ceil_units(nbytes) + AllocatedCtrlUnits, size_type(MinBlockUnits));
290+
max_value(user_buffer_ceil_units(nbytes) + AllocatedCtrlUnits, size_type(BlockCtrlUnits));
291291
//We can create a new block in the end of the segment
292-
if(old_size >= (first_min_units + MinBlockUnits)){
292+
if(orig_first_units >= (first_min_units + BlockCtrlUnits)){
293293
block_ctrl *second = move_detail::force_ptr<block_ctrl*>
294294
(reinterpret_cast<char*>(first) + Alignment*first_min_units);
295+
//Update first size
295296
first->m_size = first_min_units & block_ctrl::size_mask;
296-
second->m_size = (old_size - first->m_size) & block_ctrl::size_mask;
297-
BOOST_ASSERT(second->m_size >= MinBlockUnits);
298297
memory_algo->priv_mark_new_allocated_block(first);
298+
299+
//Deallocate the remaining memory
300+
second->m_size = (orig_first_units - first_min_units) & block_ctrl::size_mask;
299301
memory_algo->priv_mark_new_allocated_block(second);
300302
memory_algo->priv_deallocate(memory_algo->priv_get_user_buffer(second));
301303
}
302304
return buffer;
303305
}
304306

305-
//Buffer not aligned, find the aligned part.
307+
//Now obtain the address of the allocated block
308+
block_ctrl* const first = memory_algo->priv_get_block(buffer);
309+
//The block must be marked as allocated
310+
BOOST_ASSERT(memory_algo->priv_is_allocated_block(first));
311+
//Assert allocated block has at least the desired size
312+
BOOST_ASSERT(first->m_size >= (needed_units + AllocatedCtrlUnits));
313+
//Assert allocated block can be splitted in the two blocks
314+
BOOST_ASSERT(first->m_size >= 2 * BlockCtrlUnits);
315+
316+
//Buffer is not overaligned, so find the aligned part
317+
318+
// BCB: BlockControlBytes
319+
// ACB: AllocatedControlBytes (<= BlockControlBytes)
306320
//
307-
// ____ Aligned here
308-
// |
321+
// __________> Block control ("first")
322+
// | _________> Block control ("second")
323+
// | | ___> usr_buf, overaligned
324+
// | | |
309325
// -----------------------------------------------------
310-
// | MBU +more | ACB |
326+
// | BCB+more | ACB |
311327
// -----------------------------------------------------
312-
char *pos = reinterpret_cast<char*>
313-
(reinterpret_cast<std::size_t>(static_cast<char*>(buffer) +
314-
//This is the minimum size of (2)
315-
(MinBlockUnits*Alignment - AllocatedCtrlBytes) +
316-
//This is the next MBU for the aligned memory
317-
AllocatedCtrlBytes +
318-
//This is the alignment trick
319-
alignment - 1) & -alignment);
320-
321-
//Now obtain the address of the blocks
322-
block_ctrl *first = memory_algo->priv_get_block(buffer);
323-
block_ctrl *second = memory_algo->priv_get_block(pos);
324-
BOOST_ASSERT(pos <= (reinterpret_cast<char*>(first) + first->m_size*Alignment));
325-
BOOST_ASSERT(first->m_size >= 2*MinBlockUnits);
326-
BOOST_ASSERT((pos + MinBlockUnits*Alignment - AllocatedCtrlBytes + nbytes*Alignment/Alignment) <=
327-
(reinterpret_cast<char*>(first) + first->m_size*Alignment));
328-
//Set the new size of the first block
329-
size_type old_size = first->m_size;
330-
first->m_size = size_type(size_type(reinterpret_cast<char*>(second) - reinterpret_cast<char*>(first))/Alignment
331-
& block_ctrl::size_mask);
332-
memory_algo->priv_mark_new_allocated_block(first);
328+
char *const usr_buf = reinterpret_cast<char*>
329+
(reinterpret_cast<std::size_t>(static_cast<char*>(buffer)
330+
+ BlockCtrlBytes //Minimum to create a free block at the beginning
331+
+ alignment - 1) & -alignment); //This is the alignment trick
332+
333+
//Assert the user buffer is inside the allocated range
334+
BOOST_ASSERT(usr_buf <= (reinterpret_cast<char*>(first) + first->m_size*Alignment));
335+
//Assert all user data is inside the allocated range
336+
BOOST_ASSERT((usr_buf + nbytes) <= (reinterpret_cast<char*>(first) + first->m_size*Alignment + UsableByPreviousChunk));
337+
338+
//Set the new size of the secone block
339+
const size_type orig_first_units = first->m_size;
340+
341+
block_ctrl* const second = memory_algo->priv_get_block(usr_buf);
342+
343+
//Update first block size until second block starts and deallocate it
344+
const size_type final_first_units =
345+
size_type(reinterpret_cast<char*>(second) - reinterpret_cast<char*>(first))/Alignment & block_ctrl::size_mask;
333346

334347
//Now check if we can create a new buffer in the end
335348
//
336-
// __"second" block
337-
// | __Aligned here
338-
// | | __"third" block
339-
// -----------|-----|-----|------------------------------
340-
// | MBU +more | ACB | (3) | BCU |
349+
// _______________________> "first" (free block)
350+
// | ____________> "second" block
351+
// | | ______> user data aligned here (usr_buf)
352+
// | | | ____> optional "third" (free block)
353+
// ----------|-----|-----------|------------------------------
354+
// | BCB+more | ACB | user_data | BCB |
341355
// -----------------------------------------------------
342356
//This size will be the minimum size to be able to create a
343357
//new block in the end.
344-
const size_type second_min_units = max_value(size_type(MinBlockUnits),
345-
ceil_units(nbytes) + AllocatedCtrlUnits );
358+
const size_type orig_second_units = orig_first_units - final_first_units;
359+
const size_type second_min_units = max_value( size_type(BlockCtrlUnits)
360+
, user_buffer_ceil_units(nbytes) + AllocatedCtrlUnits );
346361

347-
//Check if we can create a new block (of size MinBlockUnits) in the end of the segment
348-
if((old_size - first->m_size) >= (second_min_units + MinBlockUnits)){
362+
//Check if we can create a new free block (of size BlockCtrlUnits) at the end of the segment
363+
if(orig_second_units >= (second_min_units + BlockCtrlUnits)){
349364
//Now obtain the address of the end block
350-
block_ctrl *third = new (reinterpret_cast<char*>(second) + Alignment*second_min_units)block_ctrl;
365+
block_ctrl *const third = ::new (reinterpret_cast<char*>(second) + Alignment*second_min_units, boost_container_new_t()) block_ctrl;
351366
second->m_size = second_min_units & block_ctrl::size_mask;
352-
third->m_size = (old_size - first->m_size - second->m_size) & block_ctrl::size_mask;
353-
BOOST_ASSERT(third->m_size >= MinBlockUnits);
367+
third->m_size = (orig_second_units - second->m_size) & block_ctrl::size_mask;
368+
BOOST_ASSERT(third->m_size >= BlockCtrlUnits);
354369
memory_algo->priv_mark_new_allocated_block(second);
355370
memory_algo->priv_mark_new_allocated_block(third);
371+
//We can deallocate third block because the previous "second" is properly set
356372
memory_algo->priv_deallocate(memory_algo->priv_get_user_buffer(third));
357373
}
358374
else{
359-
second->m_size = (old_size - first->m_size) & block_ctrl::size_mask;
360-
BOOST_ASSERT(second->m_size >= MinBlockUnits);
375+
second->m_size = orig_second_units & block_ctrl::size_mask;
376+
BOOST_ASSERT(second->m_size >= BlockCtrlUnits);
361377
memory_algo->priv_mark_new_allocated_block(second);
362378
}
363379

380+
//We can deallocate first block because the next "second" is properly set
381+
first->m_size = final_first_units & block_ctrl::size_mask;
382+
//Now mark second's previous allocated flag as allocated
383+
memory_algo->priv_mark_new_allocated_block(first);
364384
memory_algo->priv_deallocate(memory_algo->priv_get_user_buffer(first));
365-
return memory_algo->priv_get_user_buffer(second);
385+
386+
//Make sure all user data fits
387+
BOOST_ASSERT((reinterpret_cast<char*>(usr_buf) + nbytes) <= (reinterpret_cast<char*>(second) + second->m_size*Alignment + UsableByPreviousChunk));
388+
//Make sure user data is properly aligned
389+
BOOST_ASSERT(0 == ((std::size_t)usr_buf & (alignment-1u)));
390+
return usr_buf;
366391
}
367392

368393
static bool try_shrink

include/boost/interprocess/mem_algo/detail/simple_seq_fit_impl.hpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,6 @@ class simple_seq_fit_impl
280280
private:
281281
static const size_type BlockCtrlBytes = ipcdetail::ct_rounded_size<sizeof(block_ctrl), Alignment>::value;
282282
static const size_type BlockCtrlUnits = BlockCtrlBytes/Alignment;
283-
static const size_type MinBlockUnits = BlockCtrlUnits;
284-
static const size_type MinBlockSize = MinBlockUnits*Alignment;
285283
static const size_type AllocatedCtrlBytes = BlockCtrlBytes;
286284
static const size_type AllocatedCtrlUnits = BlockCtrlUnits;
287285
static const size_type UsableByPreviousChunk = 0;
@@ -359,7 +357,7 @@ inline void simple_seq_fit_impl<MutexFamily, VoidPointer>::grow(size_type extra_
359357
m_header.m_size += extra_size;
360358

361359
//We need at least MinBlockSize blocks to create a new block
362-
if((m_header.m_size - old_end) < MinBlockSize){
360+
if((m_header.m_size - old_end) < BlockCtrlBytes){
363361
return;
364362
}
365363

@@ -460,8 +458,8 @@ inline void simple_seq_fit_impl<MutexFamily, VoidPointer>::priv_add_segment(void
460458
{
461459
algo_impl_t::assert_alignment(addr);
462460
//Check size
463-
BOOST_ASSERT(!(segment_size < MinBlockSize));
464-
if(segment_size < MinBlockSize)
461+
BOOST_ASSERT(!(segment_size < BlockCtrlBytes));
462+
if(segment_size < BlockCtrlBytes)
465463
return;
466464
//Construct big block using the new segment
467465
block_ctrl *new_block = static_cast<block_ctrl *>(addr);
@@ -493,7 +491,7 @@ simple_seq_fit_impl<MutexFamily, VoidPointer>::
493491
{
494492
return ipcdetail::get_rounded_size((size_type)sizeof(simple_seq_fit_impl),Alignment) +
495493
ipcdetail::get_rounded_size(extra_hdr_bytes,Alignment)
496-
+ MinBlockSize;
494+
+ BlockCtrlBytes;
497495
}
498496

499497
template<class MutexFamily, class VoidPointer>

include/boost/interprocess/mem_algo/rbtree_best_fit.hpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,25 +108,35 @@ class rbtree_best_fit
108108
struct SizeHolder
109109
{
110110
static const size_type size_mask = size_type(-1) >> 2;
111+
//!Previous block's memory size (including block_ctrl
112+
//!header) in Alignment units. This field (UsableByPreviousChunk bytes)
113+
//!is OVERWRITTEN by the previous block if allocated (m_prev_allocated)
114+
size_type m_prev_size;
111115
//!This block's memory size (including block_ctrl
112116
//!header) in Alignment units
113-
size_type m_prev_size;
114117
size_type m_size : sizeof(size_type)*CHAR_BIT - 2;
115118
size_type m_prev_allocated : 1;
116119
size_type m_allocated : 1;
117120
};
118121

119122
//!Block control structure
120123
struct block_ctrl
121-
: public SizeHolder, public TreeHook
124+
: public SizeHolder
125+
//This tree hook is overwritten when this block is used
126+
, public TreeHook
122127
{
123128
block_ctrl()
124-
{ this->m_size = 0; this->m_allocated = 0, this->m_prev_allocated = 0; }
129+
{
130+
this->SizeHolder::m_size = 0;
131+
this->SizeHolder::m_allocated = 0;
132+
this->SizeHolder::m_prev_allocated = 0;
133+
}
125134

126135
friend bool operator<(const block_ctrl &a, const block_ctrl &b)
127-
{ return a.m_size < b.m_size; }
136+
{ return a.SizeHolder::m_size < b.SizeHolder::m_size; }
137+
128138
friend bool operator==(const block_ctrl &a, const block_ctrl &b)
129-
{ return a.m_size == b.m_size; }
139+
{ return a.SizeHolder::m_size == b.SizeHolder::m_size; }
130140
};
131141

132142
struct size_block_ctrl_compare
@@ -353,7 +363,6 @@ class rbtree_best_fit
353363
static const size_type AllocatedCtrlUnits = AllocatedCtrlBytes/Alignment;
354364
static const size_type EndCtrlBlockBytes = ipcdetail::ct_rounded_size<sizeof(SizeHolder), Alignment>::value;
355365
static const size_type EndCtrlBlockUnits = EndCtrlBlockBytes/Alignment;
356-
static const size_type MinBlockUnits = BlockCtrlUnits;
357366
static const size_type UsableByPreviousChunk = sizeof(size_type);
358367

359368
//Make sure the maximum alignment is power of two
@@ -401,7 +410,7 @@ void rbtree_best_fit<MutexFamily, VoidPointer, MemAlignment>::
401410
priv_mark_as_free_block (first_big_block);
402411
#ifdef BOOST_INTERPROCESS_RBTREE_BEST_FIT_ABI_V1_HPP
403412
first_big_block->m_prev_size = end_block->m_size =
404-
size_type(reinterpret_cast<char*>(first_big_block) - reinterpret_cast<char*>(end_block))/Alignmen) & block_ctrl::size_mask;
413+
size_type(reinterpret_cast<char*>(first_big_block) - reinterpret_cast<char*>(end_block))/Alignment) & block_ctrl::size_mask;
405414
#else
406415
first_big_block->m_prev_size = end_block->m_size =
407416
size_type(reinterpret_cast<char*>(end_block) - reinterpret_cast<char*>(first_big_block))/Alignment & block_ctrl::size_mask;
@@ -481,8 +490,8 @@ void rbtree_best_fit<MutexFamily, VoidPointer, MemAlignment>::grow(size_type ext
481490
//Update managed buffer's size
482491
m_header.m_size += extra_size;
483492

484-
//We need at least MinBlockUnits blocks to create a new block
485-
if((m_header.m_size - old_border_offset) < MinBlockUnits){
493+
//We need at least BlockCtrlBytes blocks to create a new block
494+
if((m_header.m_size - old_border_offset) < BlockCtrlBytes){
486495
return;
487496
}
488497

@@ -614,7 +623,7 @@ rbtree_best_fit<MutexFamily, VoidPointer, MemAlignment>::
614623
{
615624
return (algo_impl_t::ceil_units(sizeof(rbtree_best_fit)) +
616625
algo_impl_t::ceil_units(extra_hdr_bytes) +
617-
MinBlockUnits + EndCtrlBlockUnits)*Alignment;
626+
BlockCtrlUnits + EndCtrlBlockUnits)*Alignment;
618627
}
619628

620629
template<class MutexFamily, class VoidPointer, std::size_t MemAlignment>

0 commit comments

Comments
 (0)