// File: /workspace/github_cpp_makefile_star2_O2/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 $0x58, %rsp
movq %rdi, %r15
callq 0x7694
cmpq $0x0, 0x30(%r15)
je 0x591e
movb $0x1, %bpl
jmp 0x5937
movq 0x28(%r15), %rbx
callq 0x5280
testq %rbx, %rbx
je 0x5d52
cmpq %rax, %rbx
sete %bpl
xorl %ebx, %ebx
movq %r15, %rdi
callq 0x5220
testb %bl, %bl
je 0x5d3d
leaq 0x38(%r15), %r12
leaq 0x16e0(%rip), %r14      # 0x7034
movq %r12, 0x38(%rsp)
movq (%r12), %r12
testq %r12, %r12
je 0x597c
movb $0x0, 0x28(%r12)
movq 0x10(%r12), %rax
movq %r12, %rdi
movq %r14, %rsi
callq *0x18(%rax)
addq $0x38, %r12
jmp 0x5959
leaq 0x80(%r15), %r12
movq %r12, %rdi
callq 0x7492
movq %r15, %rdi
callq 0x7694
leaq 0x118(%r15), %rbp
movq %rbp, %rax
movq (%rax), %rax
testq %rax, %rax
je 0x59cb
movq 0x8(%rax), %rcx
movb $0x0, 0x28(%rcx)
movq 0x40(%r15), %rdx
movq %rdx, 0x30(%rcx)
andq $0x0, 0x38(%rcx)
testq %rdx, %rdx
cmoveq %r15, %rdx
movq %rcx, 0x38(%rdx)
movq %rcx, 0x40(%r15)
jmp 0x599d
leaq 0x108(%r15), %rdi
movq %rdi, 0x50(%rsp)
callq 0x7492
leaq 0x140(%r15), %rax
movq %rax, 0x30(%rsp)
leaq 0x150(%r15), %r14
movq %r15, 0x8(%rsp)
leaq 0x48(%r15), %rax
movq %rax, 0x20(%rsp)
leaq 0x10(%rsp), %r13
movq %r14, %rbx
movq (%rbx), %rbx
testq %rbx, %rbx
je 0x5a25
movq 0x8(%rbx), %rax
movq %rax, 0x10(%rsp)
movq 0x20(%rsp), %rdi
movq %r13, %rsi
callq 0x77f4
jmp 0x5a05
movq 0x30(%rsp), %rdi
callq 0x73ea
movq 0x8(%rsp), %rax
leaq 0x178(%rax), %rcx
movq %rcx, 0x28(%rsp)
leaq 0x188(%rax), %r13
leaq 0x10(%rsp), %r15
movq %r13, %rbx
movq (%rbx), %rbx
testq %rbx, %rbx
je 0x5a6f
movq 0x8(%rbx), %rax
movq %rax, 0x10(%rsp)
movq 0x20(%rsp), %rdi
movq %r15, %rsi
callq 0x76a8
jmp 0x5a4f
movq 0x28(%rsp), %rdi
callq 0x73ea
movq 0x8(%rsp), %rax
leaq 0x1c0(%rax), %rbx
movq %rbx, %rax
movq (%rax), %rax
testq %rax, %rax
je 0x5a9d
movq 0x8(%rax), %rcx
movq 0x10(%rax), %rdx
movq %rdx, (%rcx)
jmp 0x5a88
movq 0x8(%rsp), %r15
leaq 0x1b0(%r15), %rdi
movq %rdi, 0x40(%rsp)
callq 0x7342
addq $0x58, %r15
movq (%r15), %r15
testq %r15, %r15
je 0x5ad3
movq 0x8(%r15), %rsi
cmpq $0x0, (%rsi)
je 0x5ab7
movq %r12, %rdi
callq 0x7b20
jmp 0x5ab7
movq 0x8(%rsp), %r15
movq %r15, %rdi
callq 0x5220
addq $0x90, %r15
leaq 0x1522(%rip), %r12      # 0x7010
movq (%r15), %r15
testq %r15, %r15
je 0x5b0a
movq 0x8(%r15), %rdi
movb $0x1, 0x28(%rdi)
movq 0x10(%rdi), %rax
movq %r12, %rsi
callq *0x10(%rax)
jmp 0x5aee
movq 0x8(%rsp), %rax
movq 0x38(%rax), %r15
addq $0xb8, %rax
movq %rax, 0x18(%rsp)
testq %r15, %r15
je 0x5b6f
movq %r15, %r12
movq 0x38(%r15), %r15
cmpb $0x0, 0x28(%r12)
jne 0x5b1e
movq 0x38(%rsp), %rdi
movq %r12, %rsi
callq 0x5724
movq 0x8(%rsp), %rdx
movq 0xc0(%rdx), %rax
movq %rax, 0x30(%r12)
andq $0x0, 0x38(%r12)
leaq 0x38(%rax), %rcx
testq %rax, %rax
cmoveq 0x18(%rsp), %rcx
movq %r12, (%rcx)
movq %r12, 0xc0(%rdx)
jmp 0x5b1e
movq 0x18(%rsp), %r12
movq (%r12), %r12
testq %r12, %r12
je 0x5b8d
movq 0x10(%r12), %rax
movq %r12, %rdi
callq *(%rax)
addq $0x38, %r12
jmp 0x5b74
movq 0x8(%rsp), %r15
movq %r15, %rdi
callq 0x7694
movb $0x0, 0xc8(%r15)
leaq 0xd0(%r15), %r12
leaq 0x10(%rsp), %r15
movq 0x18(%rsp), %rax
movq (%rax), %rax
movq %rax, 0x10(%rsp)
testq %rax, %rax
je 0x5bd6
movq %r12, %rdi
movq %r15, %rsi
callq 0x7e66
movq 0x10(%rsp), %rax
addq $0x38, %rax
jmp 0x5bb3
movq %r12, 0x48(%rsp)
movq 0x8(%rsp), %rax
leaq 0xe0(%rax), %r12
movq %r12, %r15
movq (%r15), %r15
testq %r15, %r15
je 0x5c02
movq 0x8(%r15), %rsi
movq 0x38(%rsp), %rdi
callq 0x5724
jmp 0x5bea
movq 0x8(%rsp), %r15
movq %r15, %rdi
callq 0x5220
movq 0xb8(%r15), %rdi
testq %rdi, %rdi
je 0x5c2b
movq 0x10(%rdi), %rax
movq 0x38(%rdi), %r15
callq *0x8(%rax)
movq %r15, %rdi
jmp 0x5c16
xorps %xmm0, %xmm0
movq 0x18(%rsp), %rax
movups %xmm0, (%rax)
movq (%r12), %r12
testq %r12, %r12
je 0x5c59
movq 0x8(%r12), %r15
movq 0x10(%r15), %rax
movq %r15, %rdi
callq *(%rax)
movq 0x10(%r15), %rax
movq %r15, %rdi
callq *0x8(%rax)
jmp 0x5c36
movq 0x48(%rsp), %rdi
callq 0x7492
movq 0x8(%rsp), %r15
movq %r15, %rdi
callq 0x7694
andq $0x0, 0x28(%r15)
movq 0x8(%rsp), %rdx
movq (%rbp), %rbp
testq %rbp, %rbp
je 0x5ca5
movq 0x8(%rbp), %rax
movq 0x40(%rdx), %rcx
movq %rcx, 0x30(%rax)
andq $0x0, 0x38(%rax)
testq %rcx, %rcx
cmoveq %rdx, %rcx
movq %rax, 0x38(%rcx)
movq %rax, 0x40(%rdx)
jmp 0x5c7a
movq 0x50(%rsp), %rdi
callq 0x7492
leaq 0x10(%rsp), %r15
movq (%r14), %r14
testq %r14, %r14
je 0x5cd4
movq 0x8(%r14), %rax
movq %rax, 0x10(%rsp)
movq 0x20(%rsp), %rdi
movq %r15, %rsi
callq 0x77f4
jmp 0x5cb4
movq 0x30(%rsp), %rdi
callq 0x73ea
leaq 0x10(%rsp), %r15
movq (%r13), %r13
testq %r13, %r13
je 0x5d04
movq 0x8(%r13), %rax
movq %rax, 0x10(%rsp)
movq 0x20(%rsp), %rdi
movq %r15, %rsi
callq 0x76a8
jmp 0x5ce3
movq 0x28(%rsp), %rdi
callq 0x73ea
movq 0x8(%rsp), %r14
movq (%rbx), %rbx
testq %rbx, %rbx
je 0x5d28
movq 0x8(%rbx), %rax
movq 0x10(%rbx), %rcx
movq %rcx, (%rax)
jmp 0x5d13
movq 0x40(%rsp), %rdi
callq 0x7342
movq %r14, %rdi
callq 0x5220
movb $0x1, %bpl
andb $0x1, %bpl
movl %ebp, %eax
addq $0x58, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, 0x28(%r15)
movb $0x1, 0xc8(%r15)
cmpq $0x0, 0x120(%r15)
jne 0x5dad
cmpq $0x0, 0x158(%r15)
jne 0x5dcc
cmpq $0x0, 0x190(%r15)
jne 0x5deb
cmpq $0x0, 0x1c8(%r15)
jne 0x5e0a
cmpq $0x0, 0xe8(%r15)
jne 0x5e29
cmpq $0x0, 0xb8(%r15)
jne 0x5e48
movb $0x1, %bl
jmp 0x5939
leaq 0x1838b(%rip), %rdi     # 0x1e13f
leaq 0x1839b(%rip), %rsi     # 0x1e156
leaq 0x183aa(%rip), %rcx     # 0x1e16c
movl $0xd9, %edx
callq 0x51b0
leaq 0x183bd(%rip), %rdi     # 0x1e190
leaq 0x1837c(%rip), %rsi     # 0x1e156
leaq 0x1838b(%rip), %rcx     # 0x1e16c
movl $0xdb, %edx
callq 0x51b0
leaq 0x183b6(%rip), %rdi     # 0x1e1a8
leaq 0x1835d(%rip), %rsi     # 0x1e156
leaq 0x1836c(%rip), %rcx     # 0x1e16c
movl $0xdc, %edx
callq 0x51b0
leaq 0x183b2(%rip), %rdi     # 0x1e1c3
leaq 0x1833e(%rip), %rsi     # 0x1e156
leaq 0x1834d(%rip), %rcx     # 0x1e16c
movl $0xde, %edx
callq 0x51b0
leaq 0x183b0(%rip), %rdi     # 0x1e1e0
leaq 0x1831f(%rip), %rsi     # 0x1e156
leaq 0x1832e(%rip), %rcx     # 0x1e16c
movl $0xe1, %edx
callq 0x51b0
leaq 0x183ad(%rip), %rdi     # 0x1e1fc
leaq 0x18300(%rip), %rsi     # 0x1e156
leaq 0x1830f(%rip), %rcx     # 0x1e16c
movl $0xe4, %edx
callq 0x51b0
jmp 0x5e71
jmp 0x5e71
jmp 0x5e71
jmp 0x5e71
jmp 0x5e71
movq %rax, %r14
movq 0x8(%rsp), %rdi
callq 0x5220
movq %r14, %rdi
callq 0x5450