Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix double free and double alloc #1

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 23 additions & 25 deletions redGrapes/util/chunked_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,7 @@ struct ChunkedList
*/
std::atomic< Item * > next_item;

/* counts the number of alive elements in this chunk.
* Whenever `item_count` reaches zero, the chunk will be deleted.
* `item_count` starts with 1 to keep the chunk at least until
* its full capacity is used. This initial offset is
* compensated by not increasing the count when the last
* element is inserted.
*/
std::atomic< chunk_offset_t > item_count{ 0 };
std::atomic< chunk_offset_t > freed_items{ 0 };

Chunk( memory::Block blk )
: first_item( (Item*) blk.ptr )
Expand Down Expand Up @@ -424,7 +417,9 @@ struct ChunkedList
public:
ChunkedList( Allocator && alloc )
: chunks( std::move(alloc), T_chunk_size * sizeof(Item) + sizeof(Chunk) )
{}
{
chunks.allocate_item();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I currently don't know how much the impact is, but this will decrease the speed of task-creation since every task will create four events , each containing a ChunkedList<EventPtr> to store the list of outgoing edges, but not all Events will actually have outgoing edges (this is also described in Issue ComputationalRadiationPhysics#42 . Maybe, to avoid this a possible condition for allocating a new chunk would be next_item == chunk_end ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

next_item == chunk_end

I think if you use this more than one thread can create the first chunk. Thats the reason why I added it to the constructor because this was the only point where I was sure that there will be no data race.

}

ChunkedList( ChunkedList && other ) = default;
ChunkedList( Allocator && alloc, ChunkedList const & other )
Expand All @@ -438,7 +433,7 @@ struct ChunkedList
*/
void release_chunk( typename memory::AtomicList< Chunk, Allocator >::MutBackwardIterator chunk )
{
if( chunk->item_count.fetch_sub(1) == 0 )
if( chunk->freed_items.fetch_add(1) == T_chunk_size - 1u )
chunks.erase( chunk );
}

Expand All @@ -451,27 +446,30 @@ struct ChunkedList
auto chunk = chunks.rbegin();
if( chunk != chunks.rend() )
{
if( chunk->item_count.fetch_add(1) < T_chunk_size )
{
Item * chunk_begin = chunk->first_item;
Item * chunk_end = chunk_begin + T_chunk_size;
Item * next_item = chunk->next_item.fetch_add(1);
Item * chunk_begin = chunk->first_item;
Item * chunk_end = chunk_begin + T_chunk_size;
Item * next_item = chunk->next_item.fetch_add(1);

if( (uintptr_t)next_item < (uintptr_t)chunk_end )
if( (uintptr_t)next_item < (uintptr_t)chunk_end )
{
*next_item = item;
chunk->last_item ++;
if( (uintptr_t)next_item == (uintptr_t)(chunk_end - 1u))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be moved to the position before the item is copied to provide faster new memory to other threads.

{
*next_item = item;
chunk->last_item ++;
return MutBackwardIterator( chunk, next_item );
// the thread who took the last item of this chunk must allocate
// the next batch of items
chunks.allocate_item();
}
return MutBackwardIterator( chunk, next_item );
}

release_chunk(chunk);
else
chunk->next_item.fetch_sub(1);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we increase a 64bit pointer there should be no strong need to add this overflow guard here.

}
else
{
throw std::runtime_error("chunk_list: invalid state, there should always be at least one chunk available.");
}

auto prev_chunk = chunks.allocate_item();

if( prev_chunk != chunks.rend() )
release_chunk( prev_chunk );
}
}

Expand Down
Loading