// File: /workspace/github_cpp_makefile_star2_O1/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 $0x38, %rsp
movq %rdi, %r12
callq 0x5380
testl %eax, %eax
jne 0x7aa9
cmpq $0x0, 0x30(%r12)
je 0x75a5
movb $0x1, %bpl
jmp 0x75bf
movq 0x28(%r12), %rbx
callq 0x5250
testq %rbx, %rbx
je 0x7a46
cmpq %rax, %rbx
sete %bpl
xorl %ebx, %ebx
movq %r12, %rdi
callq 0x5200
testb %bl, %bl
je 0x7a31
movq 0x38(%r12), %r14
testq %r14, %r14
je 0x75fd
leaq 0x19f6(%rip), %r15      # 0x8fd8
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 0x75e2
leaq 0x80(%r12), %r14
movq %r14, %rdi
callq 0x937a
movq %r12, %rdi
callq 0x5380
testl %eax, %eax
jne 0x7aa9
leaq 0x38(%r12), %r15
leaq 0x108(%r12), %rdi
movq 0x118(%r12), %rax
testq %rax, %rax
je 0x7678
leaq 0x40(%r12), %rcx
movq 0x40(%r12), %rdx
movq 0x8(%rax), %rsi
movb $0x0, 0x28(%rsi)
movq %rdx, 0x30(%rsi)
movq $0x0, 0x38(%rsi)
leaq 0x38(%rdx), %r8
testq %rdx, %rdx
cmoveq %rcx, %r8
movq %rcx, %rdx
cmoveq %r15, %rdx
movq %rsi, (%r8)
movq %rsi, (%rdx)
movq (%rax), %rax
movq %rsi, %rdx
testq %rax, %rax
jne 0x7641
movq %rdi, 0x30(%rsp)
callq 0x937a
movq %r12, 0x10(%rsp)
movq 0x150(%r12), %rbx
testq %rbx, %rbx
je 0x76cb
movq 0x10(%rsp), %rax
leaq 0x48(%rax), %r12
leaq 0x18(%rsp), %r13
leaq 0x8(%rsp), %rbp
movq 0x8(%rbx), %rax
movq %rax, 0x18(%rsp)
movq %r12, 0x8(%rsp)
movq %r12, %rdi
movq %r13, %rsi
movq %rbp, %rdx
callq 0x957c
movq (%rbx), %rbx
testq %rbx, %rbx
jne 0x76a7
movq 0x10(%rsp), %r13
leaq 0x140(%r13), %rdi
movq %rdi, 0x28(%rsp)
callq 0x930a
movq 0x188(%r13), %rbx
testq %rbx, %rbx
je 0x7712
leaq 0x48(%r13), %r12
leaq 0x8(%rsp), %rbp
movq 0x8(%rbx), %rax
movq %rax, 0x8(%rsp)
movq %r12, %rdi
movq %rbp, %rsi
callq 0x944e
movq (%rbx), %rbx
testq %rbx, %rbx
jne 0x76f6
leaq 0x178(%r13), %rbp
movq %rbp, %rdi
callq 0x930a
leaq 0x1b0(%r13), %rbx
movq 0x1c0(%r13), %rax
testq %rax, %rax
je 0x7744
movq 0x8(%rax), %rcx
movq 0x10(%rax), %rdx
movq %rdx, (%rcx)
movq (%rax), %rax
jmp 0x772f
movq %rbx, %rdi
callq 0x929a
movq 0x58(%r13), %r13
testq %r13, %r13
je 0x777d
leaq 0x8(%rsp), %r12
movq 0x8(%r13), %rsi
cmpq $0x0, (%rsi)
je 0x7774
movq %r14, 0x8(%rsp)
movq %r14, %rdi
movq %r12, %rdx
callq 0x9816
movq (%r13), %r13
testq %r13, %r13
jne 0x775a
movq %rbx, 0x20(%rsp)
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
callq 0x5200
movq 0x90(%rbx), %rbx
testq %rbx, %rbx
je 0x77bc
leaq 0x1812(%rip), %r14      # 0x8fb4
movq 0x8(%rbx), %rdi
movb $0x1, 0x28(%rdi)
movq 0x10(%rdi), %rax
movq %r14, %rsi
callq *0x10(%rax)
movq (%rbx), %rbx
testq %rbx, %rbx
jne 0x77a2
movq (%r15), %r14
testq %r14, %r14
je 0x781d
movq 0x10(%rsp), %rax
leaq 0xc0(%rax), %r12
leaq 0xb8(%rax), %r13
movq 0x38(%r14), %rbx
cmpb $0x0, 0x28(%r14)
jne 0x7815
movq %r15, %rdi
movq %r14, %rsi
callq 0x7366
movq (%r12), %rax
movq %rax, 0x30(%r14)
movq $0x0, 0x38(%r14)
leaq 0x38(%rax), %rcx
testq %rax, %rax
cmoveq %r12, %rcx
movq %r12, %rax
cmoveq %r13, %rax
movq %r14, (%rcx)
movq %r14, (%rax)
movq %rbx, %r14
testq %rbx, %rbx
jne 0x77d7
movq 0x10(%rsp), %r13
movq 0xb8(%r13), %r14
testq %r14, %r14
je 0x783d
movq 0x10(%r14), %rax
movq %r14, %rdi
callq *(%rax)
movq 0x38(%r14), %r14
jmp 0x7829
movq %r13, %rdi
callq 0x5380
testl %eax, %eax
jne 0x7aa9
movb $0x0, 0xc8(%r13)
movq 0xb8(%r13), %rax
movq %rax, 0x8(%rsp)
testq %rax, %rax
je 0x7890
leaq 0xd0(%r13), %r14
leaq 0x8(%rsp), %r12
movq %r14, %rdi
movq %r12, %rsi
callq 0x9adc
movq 0x8(%rsp), %rax
movq 0x38(%rax), %rax
movq %rax, 0x8(%rsp)
testq %rax, %rax
jne 0x7872
movq 0xe0(%r13), %rbx
testq %rbx, %rbx
je 0x78ad
movq 0x8(%rbx), %rsi
movq %r15, %rdi
callq 0x7366
movq (%rbx), %rbx
jmp 0x7897
leaq 0xb8(%r13), %rbx
movq %r13, %rdi
callq 0x5200
movq 0xb8(%r13), %rdi
testq %rdi, %rdi
je 0x78db
movq 0x10(%rdi), %rax
movq 0x38(%rdi), %r14
callq *0x8(%rax)
movq %r14, %rdi
testq %r14, %r14
jne 0x78c8
leaq 0xd0(%r13), %r14
xorps %xmm0, %xmm0
movups %xmm0, (%rbx)
movq 0xe0(%r13), %rbx
testq %rbx, %rbx
je 0x7912
movq 0x8(%rbx), %r12
movq 0x10(%r12), %rax
movq %r12, %rdi
callq *(%rax)
movq 0x10(%r12), %rax
movq %r12, %rdi
callq *0x8(%rax)
movq (%rbx), %rbx
jmp 0x78ef
movq %r14, %rdi
callq 0x937a
movq %r13, %rdi
callq 0x5380
testl %eax, %eax
jne 0x7aa9
movq $0x0, 0x28(%r13)
movq 0x118(%r13), %rax
testq %rax, %rax
movq 0x30(%rsp), %rdi
je 0x797e
leaq 0x40(%r13), %rcx
movq 0x40(%r13), %rdx
movq 0x8(%rax), %rsi
movq %rdx, 0x30(%rsi)
movq $0x0, 0x38(%rsi)
leaq 0x38(%rdx), %r8
testq %rdx, %rdx
cmoveq %rcx, %r8
movq %rcx, %rdx
cmoveq %r15, %rdx
movq %rsi, (%r8)
movq %rsi, (%rdx)
movq (%rax), %rax
movq %rsi, %rdx
testq %rax, %rax
jne 0x794b
leaq 0x48(%r13), %r14
callq 0x937a
movq 0x150(%r13), %rbx
testq %rbx, %rbx
je 0x79c1
leaq 0x18(%rsp), %r15
leaq 0x8(%rsp), %r12
movq 0x8(%rbx), %rax
movq %rax, 0x18(%rsp)
movq %r14, 0x8(%rsp)
movq %r14, %rdi
movq %r15, %rsi
movq %r12, %rdx
callq 0x957c
movq (%rbx), %rbx
testq %rbx, %rbx
jne 0x799d
movq 0x28(%rsp), %rdi
callq 0x930a
movq 0x188(%r13), %rbx
testq %rbx, %rbx
je 0x79f8
leaq 0x8(%rsp), %r15
movq 0x8(%rbx), %rax
movq %rax, 0x8(%rsp)
movq %r14, %rdi
movq %r15, %rsi
callq 0x944e
movq (%rbx), %rbx
testq %rbx, %rbx
jne 0x79dc
movq %rbp, %rdi
callq 0x930a
movq 0x1c0(%r13), %rax
testq %rax, %rax
je 0x7a1c
movq 0x8(%rax), %rcx
movq 0x10(%rax), %rdx
movq %rdx, (%rcx)
movq (%rax), %rax
jmp 0x7a07
movq 0x20(%rsp), %rdi
callq 0x929a
movq %r13, %rdi
callq 0x5200
movb $0x1, %bpl
andb $0x1, %bpl
movl %ebp, %eax
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, 0x28(%r12)
movb $0x1, 0xc8(%r12)
cmpq $0x0, 0x120(%r12)
jne 0x7ab0
cmpq $0x0, 0x158(%r12)
jne 0x7acf
cmpq $0x0, 0x190(%r12)
jne 0x7aee
cmpq $0x0, 0x1c8(%r12)
jne 0x7b0d
cmpq $0x0, 0xe8(%r12)
jne 0x7b2c
cmpq $0x0, 0xb8(%r12)
jne 0x7b4b
movb $0x1, %bl
jmp 0x75c1
movl %eax, %edi
callq 0x51a0
leaq 0x18688(%rip), %rdi     # 0x2013f
leaq 0x18698(%rip), %rsi     # 0x20156
leaq 0x186a7(%rip), %rcx     # 0x2016c
movl $0xd9, %edx
callq 0x5190
leaq 0x186ba(%rip), %rdi     # 0x20190
leaq 0x18679(%rip), %rsi     # 0x20156
leaq 0x18688(%rip), %rcx     # 0x2016c
movl $0xdb, %edx
callq 0x5190
leaq 0x186b3(%rip), %rdi     # 0x201a8
leaq 0x1865a(%rip), %rsi     # 0x20156
leaq 0x18669(%rip), %rcx     # 0x2016c
movl $0xdc, %edx
callq 0x5190
leaq 0x186af(%rip), %rdi     # 0x201c3
leaq 0x1863b(%rip), %rsi     # 0x20156
leaq 0x1864a(%rip), %rcx     # 0x2016c
movl $0xde, %edx
callq 0x5190
leaq 0x186ad(%rip), %rdi     # 0x201e0
leaq 0x1861c(%rip), %rsi     # 0x20156
leaq 0x1862b(%rip), %rcx     # 0x2016c
movl $0xe1, %edx
callq 0x5190
leaq 0x186aa(%rip), %rdi     # 0x201fc
leaq 0x185fd(%rip), %rsi     # 0x20156
leaq 0x1860c(%rip), %rcx     # 0x2016c
movl $0xe4, %edx
callq 0x5190
jmp 0x7b74
jmp 0x7b74
jmp 0x7b74
jmp 0x7b74
jmp 0x7b74
movq %rax, %r14
movq 0x10(%rsp), %rdi
callq 0x5200
movq %r14, %rdi
callq 0x5430
nop