Skip to content

The allegedly synchronous Mount() uses asynchronous MountAsync() internally, spawning threads, and killing my mono-thread program #306

@tresabhi

Description

@tresabhi

In CUE4Parse\FileProvider\Vfs\AbstractVfsFileProvider.cs, the Mount method just invokes the MountAsync method internally, spawning threads even though I was promised a synchronous behaviour:

        public int Mount() => MountAsync().Result;
        public async Task<int> MountAsync()
        {
            var countNewMounts = 0;
            var tasks = new LinkedList<Task>();
            foreach (var reader in _unloadedVfs.Keys)
            {
                VerifyGlobalData(reader);

                if (reader.IsEncrypted && CustomEncryption == null || !reader.HasDirectoryIndex)
                    continue;

                tasks.AddLast(Task.Run(() =>
                {
                    try
                    {
                        reader.MountTo(Files, PathComparer, VfsMounted);
                        _unloadedVfs.TryRemove(reader, out _);
                        _mountedVfs[reader] = null;
                        Interlocked.Increment(ref countNewMounts);
                        return reader;
                    }
                    catch (InvalidAesKeyException)
                    {
                        // Ignore this
                    }
                    catch (Exception e)
                    {
                        Log.Warning(e, $"Uncaught exception while loading file {reader.Path.SubstringAfterLast('/')}");
                    }
                    return null;
                }));
            }

            await Task.WhenAll(tasks).ConfigureAwait(false);
            return countNewMounts;
        }

This is bad because I am developing a mono-thread program, and it throws an exception. Why am I using just 1 thread? https://github.com/microsoft/node-api-dotnet

I recommend making Mount truly synchronous by moving some of the Task.Run calls into a method and calling it differently in Mount and MountAsync. Please let me know if you would like me to implement this solution as I described, or if you would prefer to handle it yourself. Because currently, I am just hacking together my own provider:

using CUE4Parse.FileProvider;
using CUE4Parse.MappingsProvider;
using CUE4Parse.UE4.Exceptions;
using CUE4Parse.UE4.Versions;
using CUE4Parse.Utils;

namespace game.src.classes;

public class BlitzFileProvider : DefaultFileProvider
{
  public BlitzFileProvider(string directory)
    : base(
      directory: directory,
      searchOption: SearchOption.AllDirectories,
      versions: new(EGame.GAME_UE5_5),
      pathComparer: StringComparer.OrdinalIgnoreCase
    )
  {
    MappingsContainer = new FileUsmapTypeMappingsProvider("../../packages/closed/blitz.usmap");
  }

  public new int Mount()
  {
    Console.WriteLine("what?");

    var countNewMounts = 0;

    foreach (var reader in _unloadedVfs.Keys)
    {
      if (reader.IsEncrypted && CustomEncryption == null || !reader.HasDirectoryIndex)
        continue;

      try
      {
        reader.MountTo(Files, PathComparer);
        _unloadedVfs.TryRemove(reader, out _);
        countNewMounts++;
      }
      catch (InvalidAesKeyException) { }
      catch (Exception e)
      {
        Log.Warning(
          e,
          $"Uncaught exception while loading file {reader.Path.SubstringAfterLast('/')}"
        );
      }
    }

    return countNewMounts;
  }
}

This is a hack. Let's change that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions