         verify_modules(result, extra_warnings ++ warnings, [], state)
 
       {:compile, more, extra_warnings} ->
-        spawn_workers(more, %{}, [], [], result, extra_warnings ++ warnings, state)
+        spawn_workers(more, %{}, %{}, [], result, extra_warnings ++ warnings, state)
     end
   end
 
   # files x, waiting for x: POSSIBLE ERROR! Release processes so we get the failures
 
-  # Single entry, just release it.
-  defp spawn_workers(
-         [],
-         spawned,
-         [{_, pid, ref, _, _, _, _}] = waiting,
-         [%{pid: pid}] = files,
-         result,
-         warnings,
-         state
-       )
-       when map_size(spawned) == 1 do
-    spawn_workers([{ref, :not_found}], spawned, waiting, files, result, warnings, state)
-  end
-
-  # Multiple entries, try to release modules.
   defp spawn_workers([], spawned, waiting, files, result, warnings, state)
-       when length(waiting) == map_size(spawned) do
-    # There is potentially a deadlock. We will release modules with
-    # the following order:
-    #
-    #   1. Code.ensure_compiled/1 checks without a known definition (deadlock = soft)
-    #   2. Code.ensure_compiled/1 checks with a known definition (deadlock = soft)
-    #   3. Struct/import/require/ensure_compiled! checks without a known definition (deadlock = hard)
-    #   4. Modules without a known definition
-    #   5. Code invocation (deadlock = raise)
-    #
-    # The reason for step 3 and 4 is to not treat typos as deadlocks and
-    # help developers handle those sooner. However, this can have false
-    # positives in case multiple modules are defined in the same file
-    # and the module we are waiting for is defined later on.
-    #
-    # Finally, note there is no difference between hard and raise, the
-    # difference is where the raise is happening, inside the compiler
-    # or in the caller.
-
-    deadlocked =
-      deadlocked(waiting, :soft, false) ||
-        deadlocked(waiting, :soft, true) || deadlocked(waiting, :hard, false) ||
-        without_definition(waiting, files)
-
-    if deadlocked do
-      spawn_workers(deadlocked, spawned, waiting, files, result, warnings, state)
+       when map_size(waiting) == map_size(spawned) do
+    # Single entry, just release it.
+    with 1 <- map_size(waiting), [%{pid: pid}] <- files, %{^pid => _} <- waiting do
+      spawn_workers([{pid, :not_found}], spawned, waiting, files, result, warnings, state)
     else
-      errors = handle_deadlock(waiting, files)
-      {{:error, errors, warnings}, state}
+      _ ->
+        # There is potentially a deadlock. We will release modules with
+        # the following order:
+        #
+        #   1. Code.ensure_compiled/1 checks without a known definition (deadlock = soft)
+        #   2. Code.ensure_compiled/1 checks with a known definition (deadlock = soft)
+        #   3. Struct/import/require/ensure_compiled! checks without a known definition (deadlock = hard)
+        #   4. Modules without a known definition
+        #   5. Code invocation (deadlock = raise)
+        #
+        # The reason for step 3 and 4 is to not treat typos as deadlocks and
+        # help developers handle those sooner. However, this can have false
+        # positives in case multiple modules are defined in the same file
+        # and the module we are waiting for is defined later on.
+        #
+        # Finally, note there is no difference between hard and raise, the
+        # difference is where the raise is happening, inside the compiler
+        # or in the caller.
+        waiting_list = Map.to_list(waiting)
+
+        deadlocked =
+          deadlocked(waiting_list, :soft, false) ||
+            deadlocked(waiting_list, :soft, true) || deadlocked(waiting_list, :hard, false) ||
+            without_definition(waiting_list, files)
+
+        if deadlocked do
+          spawn_workers(deadlocked, spawned, waiting, files, result, warnings, state)
+        else
+          errors = handle_deadlock(waiting, files)
+          {{:error, errors, warnings}, state}
+        end
     end
   end
 

        {:current_stacktrace, stacktrace} = Process.info(pid, :current_stacktrace)
        Process.exit(pid, :kill)

        {kind, ^pid, _, _, on, _, _} = List.keyfind(waiting, pid, 1)
        description = "deadlocked waiting on #{kind} #{inspect(on)}"
        error = CompileError.exception(description: description, file: nil, line: nil)
        print_error(file, :error, error, stacktrace)

    timer_ref = Process.send_after(self(), :threshold_check, threshold)

    {outcome, state} =
      spawn_workers(files, %{}, %{}, [], %{}, [], %{
        dest: Keyword.get(options, :dest),
        each_cycle: Keyword.get(options, :each_cycle, fn -> {:runtime, [], []} end),
        each_file: Keyword.get(options, :each_file, fn _, _ -> :ok end) |> each_file(),
