I have encountered a very weird issue with some code I have been writing; a segmentation fault is occurring in std::deque::emplace_back when pushing a struct:
struct ChunkMeshTask {
union {
ChunkID id; //< ui64
ChunkRectilinearWorldPosition pos; //< i24, i16, i24
};
Chunk* chunk;
};
to a queue stored as a member of the same class as the method in which the push is performed:
namespace hvox {
class ChunkGrid {
...
public:
void handleBlockChange(Sender sender, BlockChangeEvent event);
...
protected:
std::queue<ChunkMeshTask> m_meshTasks; // = std::queue<ChunkMeshTask>(); was tried as a sanity check.
...
};
}
void hvox::ChunkGrid::handleBlockChange(h::Sender sender, BlockChangeEvent event) {
// TODO(Matthew): We really shouldn't be submitting a mesh task for each block change...
// We want AT MOST one mesh task per chunk on the queue at any given time regardless of number of blocks changed.
m_meshTasks.push(ChunkMeshTask{
{ event.chunkPos },
(Chunk*)sender
});
}
This segmentation fault doesn't occur in MSVC, but does in GCC and Clang, debugging with GDB shows that all values (including the pointer sender) are valid at the call of hvox::ChunkGrid::handleBlockChange.
hvox::ChunkGrid::handleBlockChange is called from an event system, that in this particular case is invoking a delegate that wraps the function. I mention this as to try to dig deeper I ran my program through valgrind. In addition to fixing various unrelated bugs, it has yielded two distinct errors that seem to be related but that I cannot get an understanding of with respect to the problem I'm facing:
==7788== Use of uninitialised value of size 8
==7788== at 0x4249F9: void std::deque<hemlock::voxel::ChunkMeshTask, std::allocator<hemlock::voxel::ChunkMeshTask> >::emplace_back<hemlock::voxel::ChunkMeshTask>(hemlock::voxel::ChunkMeshTask&&) (deque.tcc:158)
==7788== by 0x423FB5: std::deque<hemlock::voxel::ChunkMeshTask, std::allocator<hemlock::voxel::ChunkMeshTask> >::push_back(hemlock::voxel::ChunkMeshTask&&) (stl_deque.h:1533)
==7788== by 0x4236DD: std::queue<hemlock::voxel::ChunkMeshTask, std::deque<hemlock::voxel::ChunkMeshTask, std::allocator<hemlock::voxel::ChunkMeshTask> > >::push(hemlock::voxel::ChunkMeshTask&&) (stl_queue.h:248)
==7788== by 0x422BB2: hemlock::voxel::ChunkGrid::handleBlockChange(void const*, hemlock::voxel::BlockChangeEvent) (ChunkGrid.cpp:52)
==7788== by 0x424E3C: void hemlock::Delegate<void, void const*, hemlock::voxel::BlockChangeEvent>::executeWithObject<hemlock::voxel::ChunkGrid, void>(void const*, void (hemlock::TypelessMember::*)(), void const*, hemlock::voxel::BlockChangeEvent) (Delegate.hpp:167)
==7788== by 0x425C09: hemlock::Delegate<void, void const*, hemlock::voxel::BlockChangeEvent>::operator()(void const*, hemlock::voxel::BlockChangeEvent) const (Delegate.hpp:112)
==7788== by 0x424F02: void hemlock::executeDelegate<void, hemlock::voxel::BlockChangeEvent>(stx::basic_any<5ul>, void const*, hemlock::voxel::BlockChangeEvent) (Event.hpp:303)
==7788== by 0x4222C4: hemlock::Event<hemlock::voxel::BlockChangeEvent>::trigger(hemlock::voxel::BlockChangeEvent) (Event.hpp:130)
==7788== by 0x42218D: hemlock::Event<hemlock::voxel::BlockChangeEvent>::operator()(hemlock::voxel::BlockChangeEvent) (Event.hpp:135)
==7788== by 0x422020: hemlock::voxel::Chunk::setBlock(hemlock::voxel::BlockChunkPosition, hemlock::voxel::Block) (Chunk.cpp:25)
==7788== by 0x40B138: ChunkGenerator::runGenTask(hemlock::voxel::ChunkGenTask, unsigned short) (ChunkGenerator.cpp:51)
==7788== by 0x422A49: hemlock::voxel::ChunkGrid::update() (ChunkGrid.cpp:35)
==7788== Uninitialised value was created by a stack allocation
==7788== at 0x424EB8: void hemlock::executeDelegate<void, hemlock::voxel::BlockChangeEvent>(stx::basic_any<5ul>, void const*, hemlock::voxel::BlockChangeEvent) (Event.hpp:299)
This error implies there's something uninitialised in hemlock::executeDelegate<void, hemlock::voxel::BlockChangeEvent> but again, using GDB I have verified that delegate (stored in a 5-byte-wide std::any), the pointer sender and the struct in parameters are all valid here. The function looks like:
template<typename ReturnType, typename ...Parameters>
ReturnType executeDelegate(DelegateAny callback, Sender sender, Parameters... parameters) {
using MyDelegate = Delegate<ReturnType, Sender, Parameters...>;
MyDelegate delegate = delegateAnyCast<MyDelegate>(callback); // delegateAnyCast is just a wrapper of std::any_cast specifically for an internal 5-byte wide stack storage in std::any.
return delegate(sender, parameters...);
}
Using GDB again, everything is valid inside the delegate, and the wrapped function is called with the expected parameters.
The last error valgrind yields before the seg fault is:
==7788== Invalid write of size 8
==7788== at 0x4260C5: void __gnu_cxx::new_allocator<hemlock::voxel::ChunkMeshTask>::construct<hemlock::voxel::ChunkMeshTask, hemlock::voxel::ChunkMeshTask>(hemlock::voxel::ChunkMeshTask*, hemlock::voxel::ChunkMeshTask&&) (new_allocator.h:120)
==7788== by 0x4257EC: void std::allocator_traits<std::allocator<hemlock::voxel::ChunkMeshTask> >::construct<hemlock::voxel::ChunkMeshTask, hemlock::voxel::ChunkMeshTask>(std::allocator<hemlock::voxel::ChunkMeshTask>&, hemlock::voxel::ChunkMeshTask*, hemlock::voxel::ChunkMeshTask&&) (alloc_traits.h:475)
==7788== by 0x4249E8: void std::deque<hemlock::voxel::ChunkMeshTask, std::allocator<hemlock::voxel::ChunkMeshTask> >::emplace_back<hemlock::voxel::ChunkMeshTask>(hemlock::voxel::ChunkMeshTask&&) (deque.tcc:155)
==7788== by 0x423FB5: std::deque<hemlock::voxel::ChunkMeshTask, std::allocator<hemlock::voxel::ChunkMeshTask> >::push_back(hemlock::voxel::ChunkMeshTask&&) (stl_deque.h:1533)
==7788== by 0x4236DD: std::queue<hemlock::voxel::ChunkMeshTask, std::deque<hemlock::voxel::ChunkMeshTask, std::allocator<hemlock::voxel::ChunkMeshTask> > >::push(hemlock::voxel::ChunkMeshTask&&) (stl_queue.h:248)
==7788== by 0x422BB2: hemlock::voxel::ChunkGrid::handleBlockChange(void const*, hemlock::voxel::BlockChangeEvent) (ChunkGrid.cpp:52)
==7788== by 0x424E3C: void hemlock::Delegate<void, void const*, hemlock::voxel::BlockChangeEvent>::executeWithObject<hemlock::voxel::ChunkGrid, void>(void const*, void (hemlock::TypelessMember::*)(), void const*, hemlock::voxel::BlockChangeEvent) (Delegate.hpp:167)
==7788== by 0x425C09: hemlock::Delegate<void, void const*, hemlock::voxel::BlockChangeEvent>::operator()(void const*, hemlock::voxel::BlockChangeEvent) const (Delegate.hpp:112)
==7788== by 0x424F02: void hemlock::executeDelegate<void, hemlock::voxel::BlockChangeEvent>(stx::basic_any<5ul>, void const*, hemlock::voxel::BlockChangeEvent) (Event.hpp:303)
==7788== by 0x4222C4: hemlock::Event<hemlock::voxel::BlockChangeEvent>::trigger(hemlock::voxel::BlockChangeEvent) (Event.hpp:130)
==7788== by 0x42218D: hemlock::Event<hemlock::voxel::BlockChangeEvent>::operator()(hemlock::voxel::BlockChangeEvent) (Event.hpp:135)
==7788== by 0x422020: hemlock::voxel::Chunk::setBlock(hemlock::voxel::BlockChunkPosition, hemlock::voxel::Block) (Chunk.cpp:25)
==7788== Address 0x18 is not stack'd, malloc'd or (recently) free'd
Which I cannot parse in this case at all, it's something to do with an invalid pointer, but on my end there are no pointers involved except for the hvox::Chunk pointer, which I have confirmed to be valid using GDB on push.
The seg fault occurs inside deque.tcc at the first line of std::deque::emplace_back:
if (this->_M_impl._M_finish._M_cur != this->_M_impl._M_finish._M_last - 1) { ... }
specifically the iterator this->_M_impl._M_finish appears to be invalid.
This is all single-threaded, so nothing else is happening to the queue to cause iterators to invalidate (though I assume internal iterators are always valid?).
Aucun commentaire:
Enregistrer un commentaire