Browse Source

Reduce mutex usage and convert things to queues from arrays

gingerBill 4 years ago
parent
commit
7a9b7af078
5 changed files with 204 additions and 118 deletions
  1. 1 3
      src/check_decl.cpp
  2. 5 13
      src/check_expr.cpp
  3. 104 82
      src/checker.cpp
  4. 23 16
      src/checker.hpp
  5. 71 4
      src/queue.cpp

+ 1 - 3
src/check_decl.cpp

@@ -901,9 +901,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
 	}
 
 	if (ac.require_declaration) {
-		gb_mutex_lock(&ctx->info->entity_mutex);
-		array_add(&ctx->info->required_global_variables, e);
-		gb_mutex_unlock(&ctx->info->entity_mutex);
+		mpmc_enqueue(&ctx->info->required_global_variable_queue, e);
 	}
 
 

+ 5 - 13
src/check_expr.cpp

@@ -225,15 +225,13 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
 		return false;
 	}
 
-
-	gb_mutex_lock(&info->gen_procs_mutex);
-	defer (gb_mutex_unlock(&info->gen_procs_mutex));
-
 	String name = base_entity->token.string;
 
 	Type *src = base_type(base_entity->type);
 	Type *dst = nullptr;
-	if (type != nullptr) dst = base_type(type);
+	if (type != nullptr) {
+		dst = base_type(type);
+	}
 
 	if (param_operands == nullptr) {
 		GB_ASSERT(dst != nullptr);
@@ -242,6 +240,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
 		GB_ASSERT(dst == nullptr);
 	}
 
+	gb_mutex_lock(&info->gen_procs_mutex);
+	defer (gb_mutex_unlock(&info->gen_procs_mutex));
 
 	if (!src->Proc.is_polymorphic || src->Proc.is_poly_specialized) {
 		return false;
@@ -314,9 +314,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
 
 	auto *found_gen_procs = map_get(&info->gen_procs, hash_pointer(base_entity->identifier));
 	if (found_gen_procs) {
-		// gb_mutex_lock(&info->gen_procs_mutex);
-		// defer (gb_mutex_unlock(&info->gen_procs_mutex));
-
 		auto procs = *found_gen_procs;
 		for_array(i, procs) {
 			Entity *other = procs[i];
@@ -353,9 +350,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
 		}
 
 		if (found_gen_procs) {
-			// gb_mutex_lock(&info->gen_procs_mutex);
-			// defer (gb_mutex_unlock(&info->gen_procs_mutex));
-
 			auto procs = *found_gen_procs;
 			for_array(i, procs) {
 				Entity *other = procs[i];
@@ -425,7 +419,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
 	proc_info->generated_from_polymorphic = true;
 	proc_info->poly_def_node = poly_def_node;
 
-	// gb_mutex_lock(&info->gen_procs_mutex);
 	if (found_gen_procs) {
 		array_add(found_gen_procs, entity);
 	} else {
@@ -433,7 +426,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
 		array_add(&array, entity);
 		map_set(&info->gen_procs, hash_pointer(base_entity->identifier), array);
 	}
-	// gb_mutex_unlock(&info->gen_procs_mutex);
 
 	GB_ASSERT(entity != nullptr);
 

+ 104 - 82
src/checker.cpp

@@ -233,6 +233,7 @@ Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capaci
 	s->delayed_directives.allocator = heap_allocator();
 
 	if (parent != nullptr && parent != builtin_pkg->scope) {
+		// TODO(bill): make this an atomic operation
 		if (info) gb_mutex_lock(&info->scope_mutex);
 		DLIST_APPEND(parent->first_child, parent->last_child, s);
 		if (info) gb_mutex_unlock(&info->scope_mutex);
@@ -844,9 +845,7 @@ void init_checker_info(CheckerInfo *i) {
 	string_map_init(&i->packages, a);
 	array_init(&i->variable_init_order, a);
 	array_init(&i->required_foreign_imports_through_force, a);
-	array_init(&i->required_global_variables, a);
 	array_init(&i->testing_procedures, a, 0, 0);
-	mpmc_init(&i->untyped_queue, heap_allocator(), 1<<20);
 
 
 	i->allow_identifier_uses = build_context.query_data_set_settings.kind == QueryDataSet_GoToDefinitions;
@@ -854,12 +853,15 @@ void init_checker_info(CheckerInfo *i) {
 		array_init(&i->identifier_uses, a);
 	}
 
+	mpmc_init(&i->entity_queue, a, 1<<20);
+	mpmc_init(&i->definition_queue, a, 1<<20);
+	mpmc_init(&i->required_global_variable_queue, a, 1<<10);
+
 	gb_mutex_init(&i->gen_procs_mutex);
 	gb_mutex_init(&i->gen_types_mutex);
 	gb_mutex_init(&i->type_info_mutex);
 	gb_mutex_init(&i->deps_mutex);
 	gb_mutex_init(&i->identifier_uses_mutex);
-	gb_mutex_init(&i->entity_mutex);
 	gb_mutex_init(&i->foreign_mutex);
 	gb_mutex_init(&i->scope_mutex);
 
@@ -879,15 +881,16 @@ void destroy_checker_info(CheckerInfo *i) {
 	array_free(&i->variable_init_order);
 	array_free(&i->identifier_uses);
 	array_free(&i->required_foreign_imports_through_force);
-	array_free(&i->required_global_variables);
-	mpmc_destroy(&i->untyped_queue);
+
+	mpmc_destroy(&i->entity_queue);
+	mpmc_destroy(&i->definition_queue);
+	mpmc_destroy(&i->required_global_variable_queue);
 
 	gb_mutex_destroy(&i->gen_procs_mutex);
 	gb_mutex_destroy(&i->gen_types_mutex);
 	gb_mutex_destroy(&i->type_info_mutex);
 	gb_mutex_destroy(&i->deps_mutex);
 	gb_mutex_destroy(&i->identifier_uses_mutex);
-	gb_mutex_destroy(&i->entity_mutex);
 	gb_mutex_destroy(&i->foreign_mutex);
 	gb_mutex_destroy(&i->scope_mutex);
 }
@@ -958,6 +961,8 @@ bool init_checker(Checker *c, Parser *parser) {
 	mpmc_init(&c->procs_to_check_queue, heap_allocator(), 1<<20);
 	gb_semaphore_init(&c->procs_to_check_semaphore);
 
+	mpmc_init(&c->global_untyped_queue, a, 1<<20);
+
 	gb_mutex_init(&c->poly_type_mutex);
 	gb_mutex_init(&c->poly_proc_mutex);
 	return true;
@@ -970,6 +975,8 @@ void destroy_checker(Checker *c) {
 
 	mpmc_destroy(&c->procs_to_check_queue);
 	gb_semaphore_destroy(&c->procs_to_check_semaphore);
+
+	mpmc_destroy(&c->global_untyped_queue);
 }
 
 
@@ -1165,9 +1172,7 @@ void add_entity_definition(CheckerInfo *i, Ast *identifier, Entity *entity) {
 	GB_ASSERT(entity != nullptr);
 	identifier->Ident.entity = entity;
 	entity->identifier = identifier;
-	gb_mutex_lock(&i->entity_mutex);
-	array_add(&i->definitions, entity);
-	gb_mutex_unlock(&i->entity_mutex);
+	mpmc_enqueue(&i->definition_queue, entity);
 }
 
 bool redeclaration_error(String name, Entity *prev, Entity *found) {
@@ -1290,13 +1295,12 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec
 	CheckerInfo *info = c->info;
 	add_entity_definition(info, identifier, e);
 	GB_ASSERT(e->decl_info == nullptr);
-	gb_mutex_lock(&info->entity_mutex);
 	e->decl_info = d;
 	d->entity = e;
-	array_add(&info->entities, e);
-	e->order_in_src = info->entities.count; // Is this even correct?
 	e->pkg = c->pkg;
-	gb_mutex_unlock(&info->entity_mutex);
+
+	// Is this even correct?
+	e->order_in_src = 1+mpmc_enqueue(&info->entity_queue, e);
 }
 
 
@@ -1307,11 +1311,7 @@ void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) {
 	clause->CaseClause.implicit_entity = e;
 }
 
-
-
-
-
-void add_type_info_type(CheckerContext *c, Type *t) {
+void add_type_info_type_internal(CheckerContext *c, Type *t) {
 	if (t == nullptr) {
 		return;
 	}
@@ -1363,12 +1363,12 @@ void add_type_info_type(CheckerContext *c, Type *t) {
 
 	if (t->kind == Type_Named) {
 		// NOTE(bill): Just in case
-		add_type_info_type(c, t->Named.base);
+		add_type_info_type_internal(c, t->Named.base);
 		return;
 	}
 
 	Type *bt = base_type(t);
-	add_type_info_type(c, bt);
+	add_type_info_type_internal(c, bt);
 
 	switch (bt->kind) {
 	case Type_Invalid:
@@ -1376,84 +1376,84 @@ void add_type_info_type(CheckerContext *c, Type *t) {
 	case Type_Basic:
 		switch (bt->Basic.kind) {
 		case Basic_cstring:
-			add_type_info_type(c, t_u8_ptr);
+			add_type_info_type_internal(c, t_u8_ptr);
 			break;
 		case Basic_string:
-			add_type_info_type(c, t_u8_ptr);
-			add_type_info_type(c, t_int);
+			add_type_info_type_internal(c, t_u8_ptr);
+			add_type_info_type_internal(c, t_int);
 			break;
 		case Basic_any:
-			add_type_info_type(c, t_type_info_ptr);
-			add_type_info_type(c, t_rawptr);
+			add_type_info_type_internal(c, t_type_info_ptr);
+			add_type_info_type_internal(c, t_rawptr);
 			break;
 		case Basic_typeid:
 			break;
 
 		case Basic_complex64:
-			add_type_info_type(c, t_type_info_float);
-			add_type_info_type(c, t_f32);
+			add_type_info_type_internal(c, t_type_info_float);
+			add_type_info_type_internal(c, t_f32);
 			break;
 		case Basic_complex128:
-			add_type_info_type(c, t_type_info_float);
-			add_type_info_type(c, t_f64);
+			add_type_info_type_internal(c, t_type_info_float);
+			add_type_info_type_internal(c, t_f64);
 			break;
 		case Basic_quaternion128:
-			add_type_info_type(c, t_type_info_float);
-			add_type_info_type(c, t_f32);
+			add_type_info_type_internal(c, t_type_info_float);
+			add_type_info_type_internal(c, t_f32);
 			break;
 		case Basic_quaternion256:
-			add_type_info_type(c, t_type_info_float);
-			add_type_info_type(c, t_f64);
+			add_type_info_type_internal(c, t_type_info_float);
+			add_type_info_type_internal(c, t_f64);
 			break;
 		}
 		break;
 
 	case Type_BitSet:
-		add_type_info_type(c, bt->BitSet.elem);
-		add_type_info_type(c, bt->BitSet.underlying);
+		add_type_info_type_internal(c, bt->BitSet.elem);
+		add_type_info_type_internal(c, bt->BitSet.underlying);
 		break;
 
 	case Type_Pointer:
-		add_type_info_type(c, bt->Pointer.elem);
+		add_type_info_type_internal(c, bt->Pointer.elem);
 		break;
 
 	case Type_Array:
-		add_type_info_type(c, bt->Array.elem);
-		add_type_info_type(c, alloc_type_pointer(bt->Array.elem));
-		add_type_info_type(c, t_int);
+		add_type_info_type_internal(c, bt->Array.elem);
+		add_type_info_type_internal(c, alloc_type_pointer(bt->Array.elem));
+		add_type_info_type_internal(c, t_int);
 		break;
 
 	case Type_EnumeratedArray:
-		add_type_info_type(c, bt->EnumeratedArray.index);
-		add_type_info_type(c, t_int);
-		add_type_info_type(c, bt->EnumeratedArray.elem);
-		add_type_info_type(c, alloc_type_pointer(bt->EnumeratedArray.elem));
+		add_type_info_type_internal(c, bt->EnumeratedArray.index);
+		add_type_info_type_internal(c, t_int);
+		add_type_info_type_internal(c, bt->EnumeratedArray.elem);
+		add_type_info_type_internal(c, alloc_type_pointer(bt->EnumeratedArray.elem));
 		break;
 
 	case Type_DynamicArray:
-		add_type_info_type(c, bt->DynamicArray.elem);
-		add_type_info_type(c, alloc_type_pointer(bt->DynamicArray.elem));
-		add_type_info_type(c, t_int);
-		add_type_info_type(c, t_allocator);
+		add_type_info_type_internal(c, bt->DynamicArray.elem);
+		add_type_info_type_internal(c, alloc_type_pointer(bt->DynamicArray.elem));
+		add_type_info_type_internal(c, t_int);
+		add_type_info_type_internal(c, t_allocator);
 		break;
 	case Type_Slice:
-		add_type_info_type(c, bt->Slice.elem);
-		add_type_info_type(c, alloc_type_pointer(bt->Slice.elem));
-		add_type_info_type(c, t_int);
+		add_type_info_type_internal(c, bt->Slice.elem);
+		add_type_info_type_internal(c, alloc_type_pointer(bt->Slice.elem));
+		add_type_info_type_internal(c, t_int);
 		break;
 
 	case Type_Enum:
-		add_type_info_type(c, bt->Enum.base_type);
+		add_type_info_type_internal(c, bt->Enum.base_type);
 		break;
 
 	case Type_Union:
 		if (union_tag_size(t) > 0) {
-			add_type_info_type(c, union_tag_type(t));
+			add_type_info_type_internal(c, union_tag_type(t));
 		} else {
-			add_type_info_type(c, t_type_info_ptr);
+			add_type_info_type_internal(c, t_type_info_ptr);
 		}
 		for_array(i, bt->Union.variants) {
-			add_type_info_type(c, bt->Union.variants[i]);
+			add_type_info_type_internal(c, bt->Union.variants[i]);
 		}
 		break;
 
@@ -1463,56 +1463,56 @@ void add_type_info_type(CheckerContext *c, Type *t) {
 				Entity *e = bt->Struct.scope->elements.entries[i].value;
 				switch (bt->Struct.soa_kind) {
 				case StructSoa_Dynamic:
-					add_type_info_type(c, t_allocator);
+					add_type_info_type_internal(c, t_allocator);
 					/*fallthrough*/
 				case StructSoa_Slice:
 				case StructSoa_Fixed:
-					add_type_info_type(c, alloc_type_pointer(e->type));
+					add_type_info_type_internal(c, alloc_type_pointer(e->type));
 					break;
 				default:
-					add_type_info_type(c, e->type);
+					add_type_info_type_internal(c, e->type);
 					break;
 				}
 			}
 		}
 		for_array(i, bt->Struct.fields) {
 			Entity *f = bt->Struct.fields[i];
-			add_type_info_type(c, f->type);
+			add_type_info_type_internal(c, f->type);
 		}
 		add_comparison_procedures_for_fields(c, bt);
 		break;
 
 	case Type_Map:
 		init_map_internal_types(bt);
-		add_type_info_type(c, bt->Map.key);
-		add_type_info_type(c, bt->Map.value);
-		add_type_info_type(c, bt->Map.generated_struct_type);
+		add_type_info_type_internal(c, bt->Map.key);
+		add_type_info_type_internal(c, bt->Map.value);
+		add_type_info_type_internal(c, bt->Map.generated_struct_type);
 		break;
 
 	case Type_Tuple:
 		for_array(i, bt->Tuple.variables) {
 			Entity *var = bt->Tuple.variables[i];
-			add_type_info_type(c, var->type);
+			add_type_info_type_internal(c, var->type);
 		}
 		break;
 
 	case Type_Proc:
-		add_type_info_type(c, bt->Proc.params);
-		add_type_info_type(c, bt->Proc.results);
+		add_type_info_type_internal(c, bt->Proc.params);
+		add_type_info_type_internal(c, bt->Proc.results);
 		break;
 
 	case Type_SimdVector:
-		add_type_info_type(c, bt->SimdVector.elem);
+		add_type_info_type_internal(c, bt->SimdVector.elem);
 		break;
 
 	case Type_RelativePointer:
-		add_type_info_type(c, bt->RelativePointer.pointer_type);
-		add_type_info_type(c, bt->RelativePointer.base_integer);
+		add_type_info_type_internal(c, bt->RelativePointer.pointer_type);
+		add_type_info_type_internal(c, bt->RelativePointer.base_integer);
 		break;
 
 	case Type_RelativeSlice:
-		add_type_info_type(c, bt->RelativeSlice.slice_type);
-		add_type_info_type(c, bt->RelativeSlice.base_integer);
+		add_type_info_type_internal(c, bt->RelativeSlice.slice_type);
+		add_type_info_type_internal(c, bt->RelativeSlice.base_integer);
 		break;
 
 	default:
@@ -1521,6 +1521,13 @@ void add_type_info_type(CheckerContext *c, Type *t) {
 	}
 }
 
+void add_type_info_type(CheckerContext *c, Type *t) {
+	gb_mutex_lock(&c->info->type_info_mutex);
+	add_type_info_type_internal(c, t);
+	gb_mutex_unlock(&c->info->type_info_mutex);
+}
+
+
 gb_global bool global_procedure_body_in_worker_queue = false;
 
 void check_procedure_later(CheckerContext *c, ProcInfo *info) {
@@ -1887,8 +1894,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		add_dependency_to_set(c, e);
 	}
 
-	for_array(i, c->info.required_global_variables) {
-		Entity *e = c->info.required_global_variables[i];
+	for (Entity *e; mpmc_dequeue(&c->info.required_global_variable_queue, &e); /**/) {
 		e->flags |= EntityFlag_Used;
 		add_dependency_to_set(c, e);
 	}
@@ -4532,11 +4538,6 @@ void check_procedure_bodies(Checker *c) {
 	gbThread dummy_main_thread = {};
 	dummy_main_thread.user_data = thread_data+worker_count;
 	thread_proc_body(&dummy_main_thread);
-	gb_semaphore_release(&c->procs_to_check_semaphore);
-
-	for (isize i = 0; i < worker_count; i++) {
-		gb_thread_join(threads+i);
-	}
 
 	gb_semaphore_wait(&c->procs_to_check_semaphore);
 
@@ -4544,10 +4545,8 @@ void check_procedure_bodies(Checker *c) {
 		gb_thread_destroy(threads+i);
 	}
 
-	{
-		isize remaining = c->procs_to_check_queue.count.load(std::memory_order_relaxed);
-		GB_ASSERT(remaining == 0);
-	}
+	isize global_remaining = c->procs_to_check_queue.count.load(std::memory_order_relaxed);
+	GB_ASSERT(global_remaining == 0);
 
 	debugf("Total Procedure Bodies Checked: %td\n", total_bodies_checked.load(std::memory_order_relaxed));
 
@@ -4561,7 +4560,7 @@ void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped) {
 		Ast *expr = cast(Ast *)cast(uintptr)untyped->entries[i].key.key;
 		ExprInfo *info = untyped->entries[i].value;
 		if (expr != nullptr && info != nullptr) {
-			mpmc_enqueue(&cinfo->untyped_queue, UntypedExprInfo{expr, info});
+			mpmc_enqueue(&cinfo->checker->global_untyped_queue, UntypedExprInfo{expr, info});
 		}
 	}
 	map_clear(untyped);
@@ -4747,6 +4746,23 @@ void check_unique_package_names(Checker *c) {
 	}
 }
 
+void check_add_entities_from_queues(Checker *c) {
+	{
+		isize cap = c->info.entities.count + c->info.entity_queue.count.load(std::memory_order_relaxed);
+		array_reserve(&c->info.entities, cap);
+		for (Entity *e; mpmc_dequeue(&c->info.entity_queue, &e); /**/) {
+			array_add(&c->info.entities, e);
+		}
+	}
+	{
+		isize cap = c->info.definitions.count + c->info.definition_queue.count.load(std::memory_order_relaxed);
+		array_reserve(&c->info.definitions, cap);
+		for (Entity *e; mpmc_dequeue(&c->info.definition_queue, &e); /**/) {
+			array_add(&c->info.definitions, e);
+		}
+	}
+}
+
 
 void check_parsed_files(Checker *c) {
 #define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
@@ -4795,6 +4811,9 @@ void check_parsed_files(Checker *c) {
 	TIME_SECTION("import entities");
 	check_import_entities(c);
 
+	TIME_SECTION("add entities from packages");
+	check_add_entities_from_queues(c);
+
 	TIME_SECTION("check all global entities");
 	check_all_global_entities(c);
 
@@ -4811,6 +4830,9 @@ void check_parsed_files(Checker *c) {
 	TIME_SECTION("check procedure bodies");
 	check_procedure_bodies(c);
 
+	TIME_SECTION("add entities from procedure bodiess");
+	check_add_entities_from_queues(c);
+
 	TIME_SECTION("check scope usage");
 	for_array(i, c->info.files.entries) {
 		AstFile *f = c->info.files.entries[i].value;
@@ -4832,7 +4854,7 @@ void check_parsed_files(Checker *c) {
 
 	TIME_SECTION("add untyped expression values");
 	// Add untyped expression values
-	for (UntypedExprInfo u = {}; mpmc_dequeue(&c->info.untyped_queue, &u); /**/) {
+	for (UntypedExprInfo u = {}; mpmc_dequeue(&c->global_untyped_queue, &u); /**/) {
 		GB_ASSERT(u.expr != nullptr && u.info != nullptr);
 		if (is_type_typed(u.info->type)) {
 			compiler_error("%s (type %s) is typed!", expr_to_string(u.expr), type_to_string(u.info->type));

+ 23 - 16
src/checker.hpp

@@ -288,18 +288,20 @@ struct CheckerInfo {
 
 	Array<Entity *> testing_procedures;
 
+	Array<Entity *> definitions;
+	Array<Entity *> entities;
+
+
 	// Below are accessed within procedures
 	// NOTE(bill): If the semantic checker (check_proc_body) is to ever to be multithreaded,
 	// these variables will be of contention
 
-	gbMutex gen_procs_mutex;
-	gbMutex gen_types_mutex;
-	gbMutex type_info_mutex;
-	gbMutex deps_mutex;
-	gbMutex identifier_uses_mutex;
-	gbMutex entity_mutex;
-	gbMutex foreign_mutex;
-	gbMutex scope_mutex;
+	gbMutex gen_procs_mutex; // Possibly recursive
+	gbMutex gen_types_mutex; // Possibly recursive
+	gbMutex type_info_mutex; // NOT recursive
+	gbMutex deps_mutex;      // NOT recursive & Only used in `check_proc_body`
+	gbMutex foreign_mutex;   // NOT recursive
+	gbMutex scope_mutex;     // NOT recursive & Only used in `create_scope`
 
 	Map<Array<Entity *> > gen_procs;       // Key: Ast * | Identifier -> Entity
 	Map<Array<Entity *> > gen_types;       // Key: Type *
@@ -307,17 +309,20 @@ struct CheckerInfo {
 	Array<Type *>         type_info_types;
 	Map<isize>            type_info_map;   // Key: Type *
 
-	bool allow_identifier_uses;
-	Array<Ast *> identifier_uses; // only used by 'odin query'
+	StringMap<Entity *> foreigns;
+	Array<Entity *>     required_foreign_imports_through_force;
 
-	Array<Entity *>       definitions;
-	Array<Entity *>       entities;
-	StringMap<Entity *>   foreigns;
+	// only used by 'odin query'
+	bool         allow_identifier_uses;
+	gbMutex      identifier_uses_mutex;
+	Array<Ast *> identifier_uses;
 
-	Array<Entity *>       required_global_variables;
-	Array<Entity *>       required_foreign_imports_through_force;
+	// NOTE(bill): These are actually MPSC queues
+	// TODO(bill): Convert them to be MPSC queues
+	MPMCQueue<Entity *> definition_queue;
+	MPMCQueue<Entity *> entity_queue;
+	MPMCQueue<Entity *> required_global_variable_queue;
 
-	MPMCQueue<UntypedExprInfo> untyped_queue;
 };
 
 struct CheckerContext {
@@ -363,6 +368,7 @@ struct CheckerContext {
 	ProcBodyQueue *procs_to_check_queue;
 };
 
+
 struct Checker {
 	Parser *    parser;
 	CheckerInfo info;
@@ -373,6 +379,7 @@ struct Checker {
 
 	ProcBodyQueue procs_to_check_queue;
 	gbSemaphore procs_to_check_semaphore;
+	MPMCQueue<UntypedExprInfo> global_untyped_queue;
 
 	gbMutex poly_type_mutex;
 	gbMutex poly_proc_mutex;

+ 71 - 4
src/queue.cpp

@@ -46,7 +46,7 @@ void mpmc_destroy(MPMCQueue<T> *q) {
 
 
 template <typename T>
-bool mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
+isize mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 	isize head_idx = q->head_idx.load(std::memory_order_relaxed);
 
 	for (;;) {
@@ -59,8 +59,7 @@ bool mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 			if (q->head_idx.compare_exchange_weak(head_idx, next_head_idx)) {
 				node->data = data;
 				node->idx.store(next_head_idx, std::memory_order_release);
-				q->count.fetch_add(1, std::memory_order_release);
-				return true;
+				return q->count.fetch_add(1, std::memory_order_release);
 			}
 		} else if (diff < 0) {
 			gb_mutex_lock(&q->mutex);
@@ -70,7 +69,7 @@ bool mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 			if (q->buffer.data == nullptr) {
 				GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
 				gb_mutex_unlock(&q->mutex);
-				return false;
+				return -1;
 			}
 			for (isize i = old_size; i < new_size; i++) {
 				q->buffer.data[i].idx.store(i, std::memory_order_relaxed);
@@ -108,3 +107,71 @@ bool mpmc_dequeue(MPMCQueue<T> *q, T *data_) {
 		}
 	}
 }
+
+
+template <typename T>
+struct MPSCQueueNode {
+	std::atomic<MPSCQueueNode<T> *> next;
+	T data;
+};
+
+template <typename T>
+struct MPSCQueue {
+	gbAllocator allocator;
+
+	std::atomic<isize> count;
+	std::atomic<MPSCQueueNode<T> *> head;
+	std::atomic<MPSCQueueNode<T> *> tail;
+};
+
+template <typename T>
+void mpsc_init(MPSCQueue<T> *q, gbAllocator a) {
+	using Node = MPSCQueueNode<T>;
+
+	q->allocator = a;
+	Node *front = cast(Node *)gb_alloc_align(q->allocator, gb_size_of(Node), 64);
+	front->next.store(nullptr, std::memory_order_relaxed);
+	q->head.store(front, std::memory_order_relaxed);
+	q->tail.store(front, std::memory_order_relaxed);
+}
+
+
+template <typename T>
+isize mpsc_enqueue(MPSCQueue<T> *q, T const &value) {
+	using Node = MPSCQueueNode<T>;
+
+	Node *node = cast(Node *)gb_alloc_align(q->allocator, gb_size_of(Node), 64);
+	node->data = value;
+	node->next.store(nullptr, std::memory_order_relaxed);
+
+	auto *prev_head = q->head.exchange(node, std::memory_order_acq_rel);
+	prev_head->next.store(node, std::memory_order_release);
+	return q->count.fetch_add(1, std::memory_order_release);
+}
+
+template <typename T>
+bool mpsc_dequeue(MPSCQueue<T> *q, T *value_) {
+	auto *tail = q->tail.load(std::memory_order_relaxed);
+	auto *next = tail->next.load(std::memory_order_acquire);
+	if (next == nullptr) {
+		return false;
+	}
+
+	if (value_) *value_ = next->data;
+	q->tail.store(next, std::memory_order_release);
+	q->count.fetch_sub(1, std::memory_order_release);
+	gb_free(q->allocator, tail);
+	return true;
+}
+
+
+template <typename T>
+void mpsc_destroy(MPSCQueue<T> *q) {
+	T output = {};
+	while (mpsc_dequeue(q, &output)) {
+		// okay
+	}
+	auto *front = q->head.load(std::memory_order_relaxed);
+	gb_free(q->allocator, front);
+}
+