// File: /workspace/github_cpp_makefile_star2_O3/dragazo[P]cpp-gc/GarbageCollection.cpp
// Function: GC::disjoint_module::collect()
bool GC::disjoint_module::collect()
{
	// -- begin the collection action -- //

	{
		std::lock_guard<std::mutex> internal_lock(internal_mutex);

		// if there are 1 or more ignore sentries acting on this module, do nothing and return true.
		// true is to prevent a deadlock case where we do a blocking collection is made to an ignoring module.
		if (ignore_collect_count > 0) return true;

		// if there's already a collection in progress for this module, we do nothing.
		// if the collector is us, return true - this is to prevent a deadlock case where we do a blocking collection from a router/destructor.
		// otherwise return false - someone else is doing something.
		if (collector_thread != std::thread::id()) return collector_thread == std::this_thread::get_id();

		// otherwise mark the calling thread as the collector thread
		collector_thread = std::this_thread::get_id();

		// begin caching ref count deletion events
		cache_ref_count_del_actions = true;

		// since we just came out of no-collect phase, all the caches should be empty
		assert(objs_add_cache.empty());

		assert(roots_add_cache.empty());
		assert(roots_remove_cache.empty());

		assert(handle_repoint_cache.empty());

		// ref count del cache should also be empty
		assert(ref_count_del_cache.empty());

		// the del list should also be empty
		assert(del_list.empty());
	}

	// -- initialize the collection data -- //

	// we've now started the collection action, so we have lock-free access to collector-only resources.

	// to ensure all unused objects are deleted in one pass, we need to unroot all mutables arcs.
	// this requires going through all the obj list entities, so we might as well clear their marks.

	// for each object we'll examine
	for (info *i = objs.front(); i; i = i->next)
	{
		// clear the marked flag
		i->marked = false;

		// route to mutable arcs and directly unroot from the collector-only root set.
		// this is only safe because we're guaranteed to be the (only) collector at this point.
		i->mutable_route(+[](const smart_handle &arc) { local()->roots.erase(&arc.raw_handle()); });
	}

	// clear the root objs set
	root_objs.clear();

	{
		std::lock_guard<std::mutex> lock(internal_mutex);

		// at this point we've directly unrooted all the mutables in the obj list.
		// however, other threads could have destroyed rooted handles.
		// this means the roots set at this point may contain dangling pointers.
		// thus, we need to apply the cached root/unroot actions.
		// this will ensure there are no dangling pointers that are represented.
		
		// the reason we can't just do the cached unroot actions is because it could exclude live objects.
		// for instance, suppose we have a pre-existing dynamic rooted handle A.
		// now, say a new dynamic rooted handle B is created and initialized to A, then A is destroyed.
		// thus, A was unrooted, but the object it refered to is still reachable through B.
		// this could happen e.g. on a std::vector<GC::ptr<T>> reallocation.
		// this of course could only happen for live objects, so we want to make sure we keep them that way.
		// thus we account for both root and unroot cached actions.

		// for much the same reasons, we need to apply the handle repoint cache.
		// and, because we now have roots that may point to objects in the obj add cache, we need to add those as well.
		// however, we need to clear their marks first.
		// we can't perform the routing step for them because we need the mutex to be locked during this process.
		// but that's not a problem as collect() only guarantees it will collect all objects unreachable prior to invocation.
		// the routing logic is just to ensure this happens in 1 pass and not 2 (uncommon but otherwise possible without this step).

		// so long story short we need to apply all the caches (aside from obj deletion), plus a tiny bit of extra logic.
		// we know this is safe because it's as if we took the graph snapshot later on and just routed to a subset of it for unrooting.

		// apply the obj add cache - also clear their marks (the ones in the obj list are already cleared)
		for (info *i : objs_add_cache)
		{
			i->marked = false;
			objs.add(i);
		}
		objs_add_cache.clear();

		// apply cached root actions
		for (auto i : roots_add_cache) roots.insert(i);
		roots_add_cache.clear();

		// apply cached unroot actions
		for (auto i : roots_remove_cache) roots.erase(i);
		roots_remove_cache.clear();

		// apply handle repoint actions
		for (auto i : handle_repoint_cache) *i.first = i.second;
		handle_repoint_cache.clear();

		// now that that's all done...

		// add the pointed-at objects of all remaining (valid) roots to a set of root objects.
		// we only include the non-null targets for convenience.
		for (auto root : roots)
			if (*root) root_objs.insert(*root);
	}

	// -----------------------------------------------------------

	#if DRAGAZO_GARBAGE_COLLECT_MSG
	std::size_t collect_count = 0; // number of objects that we scheduled for deletion
	#endif

	// -- mark and sweep -- //

	// perform a mark sweep from each root object
	for (info *i : root_objs) i->mark_sweep();

	// -- clean anything not marked -- //

	// for each item in the gc database
	for (info *i = objs.front(), *next; i; i = next)
	{
		next = i->next;

		// if it hasn't been marked, mark it for deletion
		if (!i->marked)
		{
			// mark it for deletion
			objs.remove(i);
			del_list.add(i);

			#if DRAGAZO_GARBAGE_COLLECT_MSG
			++collect_count;
			#endif
		}
	}

	#if DRAGAZO_GARBAGE_COLLECT_MSG
	std::cerr << "collecting - deleting: " << collect_count << '\n';
	#endif

	// -----------------------------------------------------------

	// we've now divided the old obj list into two partitions:
	// the reachable objects are still in the obj list.
	// the unreachable objects are now in the del list.
	// ref count deletion caching is still in effect.

	// destroy unreachable objects
	for (info *i = del_list.front(); i; i = i->next) i->destroy();

	// now we've destroyed the unreachable objects but there may be cached deletions from ref count logic.
	// we'll now resume immediate ref count deletions.
	// we can't resume immediate ref count deletions prior to destroying the unreachable objs because it could double delete.
	// e.g. could drop an unreachable obj ref count to 0 and insta-delete on its own before we get to it.
	// even if we made a check for double delete, it would still deallocate the info object as well and would cause even more headache.
	// we know this usage is safe because there's no way a reachable object could ref count delete an unreachable object.
		
	// resume immediate ref count deletions.
	{
		std::lock_guard<std::mutex> internal_lock(internal_mutex);

		// stop caching ref count deletion actions (i.e. resume immediate ref count deletions)
		cache_ref_count_del_actions = false;

		// if an unreachable object is in the ref count del cache purge it (to avoid the double delete issue for unreachable objs).
		// we know this'll work because the unreachable objects are unreachable from reachable objects (hence the name).
		// thus, since we already called unreachable destructors, there will be no further ref count logic for unreachables.

		// purge unreachable objects from the ref count del cache (to avoid double deletions - see above).
		for (info *i = del_list.front(); i; i = i->next) ref_count_del_cache.erase(i);

		// after the double-deletion purge, remove remaining ref count del cache objects from the obj list.
		// we do this now because enabling immediate ref count del logic means the obj list can be modified by any holder of the mutex.
		for (auto i : ref_count_del_cache) objs.remove(i);
	}

	// we now have lock-free exclusive ownership of the ref count del cache.

	// deallocate memory
	// done after calling ALL unreachable dtors so that the dtors can access the info objects safely.
	// this is because we might be deleting objects whose reference count is not zero.
	// which means they could potentially hold live gc references to other objects in del list and try to refer to their info objects.
	for (info *i = del_list.front(), *next; i; i = next)
	{
		next = i->next; // dealloc() will deallocate the info object as well, so grab the next object now
		i->dealloc();
	}

	// clear the del list (we already deallocated the resources above)
	del_list.unsafe_clear();
	assert(del_list.empty()); // just to make sure

	// remember, we still have lock-free exclusive ownership of the ref count del cache from above.
	// process the cached ref count deletions.
	for (auto i : ref_count_del_cache)
	{
		// we don't need to do all destructors before all deallocators like we did above.
		// this is because we know the ref count for these is zero (because they were cached ref count deletion).
		// this means we don't have the same risks as above (i.e. live references being forcibly severed).

		i->destroy();
		i->dealloc();
	}
	ref_count_del_cache.clear();

	// end the collection action
	// must be after dtors/deallocs to ensure that if they call collect() it'll no-op (otherwise very slow).
	// additionally, must be after those to ensure the caches are fully emptied as the last atomic step.
	// also, if this came before dtors, the reference count system could fall to 0 and result in double dtor.
	{
		std::lock_guard<std::mutex> internal_lock(internal_mutex);

		// mark that there is no longer a collector thread
		collector_thread = std::thread::id();

		// apply all the cached obj add actions that occurred during the collection action
		for (auto i : objs_add_cache) objs.add(i);
		objs_add_cache.clear();

		// apply cached root actions
		for (auto i : roots_add_cache) roots.insert(i);
		roots_add_cache.clear();

		// apply cached unroot actions
		for (auto i : roots_remove_cache) roots.erase(i);
		roots_remove_cache.clear();

		// apply all the cached handle repoint actions
		for (auto i : handle_repoint_cache) *i.first = i.second;
		handle_repoint_cache.clear();
	}

	// return that we did the collection
	return true;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x28, %rsp
movq %rdi, %rbx
callq 0x4380
testl %eax, %eax
jne 0x6a19
movb $0x1, %bpl
cmpq $0x0, 0x30(%rbx)
jne 0x6593
movq 0x28(%rbx), %r14
callq 0x4250
testq %r14, %r14
je 0x65ac
cmpq %rax, %r14
sete %bpl
movq %rbx, %rdi
callq 0x4200
movl %ebp, %eax
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, 0x28(%rbx)
movb $0x1, 0xc8(%rbx)
cmpq $0x0, 0x120(%rbx)
jne 0x6a20
cmpq $0x0, 0x158(%rbx)
jne 0x6a3f
cmpq $0x0, 0x190(%rbx)
jne 0x6a5e
cmpq $0x0, 0x1c8(%rbx)
jne 0x6a7d
cmpq $0x0, 0xe8(%rbx)
jne 0x6a9c
cmpq $0x0, 0xb8(%rbx)
jne 0x6abb
movq %rbx, %rdi
callq 0x4200
movq 0x38(%rbx), %r14
testq %r14, %r14
je 0x663e
leaq 0x18ef(%rip), %r15      # 0x7f12
movb $0x0, 0x28(%r14)
movq 0x10(%r14), %rax
movq %r14, %rdi
movq %r15, %rsi
callq *0x18(%rax)
movq 0x38(%r14), %r14
testq %r14, %r14
jne 0x6623
leaq 0x80(%rbx), %rbp
movq %rbp, %rdi
callq 0x82b4
movq %rbx, %rdi
callq 0x4380
testl %eax, %eax
jne 0x6a19
leaq 0x108(%rbx), %rdi
movq 0x118(%rbx), %rax
testq %rax, %rax
je 0x66a2
movq 0x40(%rbx), %rcx
movq 0x8(%rax), %rdx
movb $0x0, 0x28(%rdx)
movq %rcx, 0x30(%rdx)
movq $0x0, 0x38(%rdx)
testq %rcx, %rcx
cmoveq %rbx, %rcx
movq %rdx, 0x38(%rcx)
movq %rdx, 0x40(%rbx)
movq (%rax), %rax
movq %rdx, %rcx
testq %rax, %rax
jne 0x6674
movq %rdi, 0x20(%rsp)
callq 0x82b4
movq 0x150(%rbx), %r13
testq %r13, %r13
je 0x66e8
leaq 0x48(%rbx), %r14
leaq 0x8(%rsp), %r15
movq %rsp, %r12
movq 0x8(%r13), %rax
movq %rax, 0x8(%rsp)
movq %r14, (%rsp)
movq %r14, %rdi
movq %r15, %rsi
movq %r12, %rdx
callq 0x8478
movq (%r13), %r13
testq %r13, %r13
jne 0x66c4
leaq 0x140(%rbx), %r13
movq %r13, %rdi
callq 0x8244
movq 0x188(%rbx), %r12
testq %r12, %r12
je 0x6727
leaq 0x48(%rbx), %r14
movq %rsp, %r15
movq 0x8(%r12), %rax
movq %rax, (%rsp)
movq %r14, %rdi
movq %r15, %rsi
callq 0x8388
movq (%r12), %r12
testq %r12, %r12
jne 0x670a
movq %r13, 0x18(%rsp)
leaq 0x178(%rbx), %r13
movq %r13, %rdi
callq 0x8244
leaq 0x1b0(%rbx), %rdi
movq 0x1c0(%rbx), %rax
testq %rax, %rax
je 0x675e
movq 0x8(%rax), %rcx
movq 0x10(%rax), %rdx
movq %rdx, (%rcx)
movq (%rax), %rax
jmp 0x6749
movq %rdi, 0x10(%rsp)
callq 0x81d4
movq 0x58(%rbx), %r15
testq %r15, %r15
je 0x6795
movq %rsp, %r14
movq 0x8(%r15), %rsi
cmpq $0x0, (%rsi)
je 0x678d
movq %rbp, (%rsp)
movq %rbp, %rdi
movq %r14, %rdx
callq 0x8722
movq (%r15), %r15
testq %r15, %r15
jne 0x6774
leaq 0x38(%rbx), %rbp
movq %rbx, %rdi
callq 0x4200
movq 0x90(%rbx), %r15
testq %r15, %r15
je 0x67ce
leaq 0x173a(%rip), %r14      # 0x7eee
movq 0x8(%r15), %rdi
movb $0x1, 0x28(%rdi)
movq 0x10(%rdi), %rax
movq %r14, %rsi
callq *0x10(%rax)
movq (%r15), %r15
testq %r15, %r15
jne 0x67b4
leaq 0xb8(%rbx), %r12
movq (%rbp), %r14
testq %r14, %r14
je 0x6824
movq 0x38(%r14), %r15
cmpb $0x0, 0x28(%r14)
jne 0x681c
movq %rbp, %rdi
movq %r14, %rsi
callq 0x634c
movq 0xc0(%rbx), %rax
movq %rax, 0x30(%r14)
movq $0x0, 0x38(%r14)
leaq 0x38(%rax), %rcx
testq %rax, %rax
cmoveq %r12, %rcx
movq %r14, (%rcx)
movq %r14, 0xc0(%rbx)
movq %r15, %r14
testq %r15, %r15
jne 0x67de
movq (%r12), %r14
testq %r14, %r14
je 0x683c
movq 0x10(%r14), %rax
movq %r14, %rdi
callq *(%rax)
movq 0x38(%r14), %r14
jmp 0x6828
movq %rbx, %rdi
callq 0x4380
testl %eax, %eax
jne 0x6a19
movb $0x0, 0xc8(%rbx)
movq 0xb8(%rbx), %rax
movq %rax, (%rsp)
testq %rax, %rax
je 0x6889
leaq 0xd0(%rbx), %r14
movq %rsp, %r15
movq %r14, %rdi
movq %r15, %rsi
callq 0x89b0
movq (%rsp), %rax
movq 0x38(%rax), %rax
movq %rax, (%rsp)
testq %rax, %rax
jne 0x686d
movq 0xe0(%rbx), %r14
testq %r14, %r14
je 0x68a6
movq 0x8(%r14), %rsi
movq %rbp, %rdi
callq 0x634c
movq (%r14), %r14
jmp 0x6890
movq %rbx, %rdi
callq 0x4200
movq 0xb8(%rbx), %rdi
testq %rdi, %rdi
je 0x68cd
movq 0x10(%rdi), %rax
movq 0x38(%rdi), %r14
callq *0x8(%rax)
movq %r14, %rdi
testq %r14, %r14
jne 0x68ba
leaq 0xd0(%rbx), %r14
xorps %xmm0, %xmm0
movups %xmm0, (%r12)
movq 0xe0(%rbx), %r12
testq %r12, %r12
je 0x6906
movq 0x8(%r12), %r15
movq 0x10(%r15), %rax
movq %r15, %rdi
callq *(%rax)
movq 0x10(%r15), %rax
movq %r15, %rdi
callq *0x8(%rax)
movq (%r12), %r12
jmp 0x68e3
movq %r14, %rdi
callq 0x82b4
movq %rbx, %rdi
callq 0x4380
testl %eax, %eax
jne 0x6a19
movq $0x0, 0x28(%rbx)
movq 0x118(%rbx), %rax
testq %rax, %rax
movq 0x20(%rsp), %rdi
je 0x6965
movq 0x40(%rbx), %rcx
movq 0x8(%rax), %rdx
movq %rcx, 0x30(%rdx)
movq $0x0, 0x38(%rdx)
testq %rcx, %rcx
cmoveq %rbx, %rcx
movq %rdx, 0x38(%rcx)
movq %rdx, 0x40(%rbx)
movq (%rax), %rax
movq %rdx, %rcx
testq %rax, %rax
jne 0x693b
leaq 0x48(%rbx), %rbp
callq 0x82b4
movq 0x150(%rbx), %r12
testq %r12, %r12
je 0x69a7
leaq 0x8(%rsp), %r14
movq %rsp, %r15
movq 0x8(%r12), %rax
movq %rax, 0x8(%rsp)
movq %rbp, (%rsp)
movq %rbp, %rdi
movq %r14, %rsi
movq %r15, %rdx
callq 0x8478
movq (%r12), %r12
testq %r12, %r12
jne 0x6982
movq 0x18(%rsp), %rdi
callq 0x8244
movq 0x188(%rbx), %r15
testq %r15, %r15
je 0x69db
movq %rsp, %r14
movq 0x8(%r15), %rax
movq %rax, (%rsp)
movq %rbp, %rdi
movq %r14, %rsi
callq 0x8388
movq (%r15), %r15
testq %r15, %r15
jne 0x69c0
movq %r13, %rdi
callq 0x8244
movq 0x1c0(%rbx), %rax
testq %rax, %rax
je 0x69ff
movq 0x8(%rax), %rcx
movq 0x10(%rax), %rdx
movq %rdx, (%rcx)
movq (%rax), %rax
jmp 0x69ea
movq 0x10(%rsp), %rdi
callq 0x81d4
movq %rbx, %rdi
callq 0x4200
movb $0x1, %bpl
jmp 0x659b
movl %eax, %edi
callq 0x41a0
leaq 0x18718(%rip), %rdi     # 0x1f13f
leaq 0x18728(%rip), %rsi     # 0x1f156
leaq 0x18737(%rip), %rcx     # 0x1f16c
movl $0xd9, %edx
callq 0x4190
leaq 0x1874a(%rip), %rdi     # 0x1f190
leaq 0x18709(%rip), %rsi     # 0x1f156
leaq 0x18718(%rip), %rcx     # 0x1f16c
movl $0xdb, %edx
callq 0x4190
leaq 0x18743(%rip), %rdi     # 0x1f1a8
leaq 0x186ea(%rip), %rsi     # 0x1f156
leaq 0x186f9(%rip), %rcx     # 0x1f16c
movl $0xdc, %edx
callq 0x4190
leaq 0x1873f(%rip), %rdi     # 0x1f1c3
leaq 0x186cb(%rip), %rsi     # 0x1f156
leaq 0x186da(%rip), %rcx     # 0x1f16c
movl $0xde, %edx
callq 0x4190
leaq 0x1873d(%rip), %rdi     # 0x1f1e0
leaq 0x186ac(%rip), %rsi     # 0x1f156
leaq 0x186bb(%rip), %rcx     # 0x1f16c
movl $0xe1, %edx
callq 0x4190
leaq 0x1873a(%rip), %rdi     # 0x1f1fc
leaq 0x1868d(%rip), %rsi     # 0x1f156
leaq 0x1869c(%rip), %rcx     # 0x1f16c
movl $0xe4, %edx
callq 0x4190
jmp 0x6ae4
jmp 0x6ae4
jmp 0x6ae4
jmp 0x6ae4
jmp 0x6ae4
movq %rax, %r14
movq %rbx, %rdi
callq 0x4200
movq %r14, %rdi
callq 0x4430
nop