Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework SingleProcess app code #1345

Merged
merged 23 commits into from
May 15, 2024
Merged

Rework SingleProcess app code #1345

merged 23 commits into from
May 15, 2024

Conversation

halgari
Copy link
Collaborator

@halgari halgari commented May 13, 2024

What this PR does:

  • Copies the .SingleProcess project into the main process code. Maintaining this in two codebases was a major waste of time, being able to swap between our app code and the single process code has resulted in the deletion of a lot of code and the reduction of some cognitive overhead
  • CliSettings are now ISettings, and we can turn the TCP server on and off
  • CliServer now listens for TCP connections and is a IHostedService. No crazy task management code, if the server is enabled it starts when the app starts
  • CLI no longer spawns main process apps, the main process never spawns a main process.
  • Many layers of indirection were removed. Now the CliServer takes a CommandLineConfigurator and hands it any CLI requests it gets. No bouncing between threads handlers, etc. Infact the entire Director/handler system is gone

The workflow (at least for now) is:

  1. Launch the app, this enables the CLI listener
  2. Launch any CLI commands you want while the main window stays open

Error cases:

  • App is running and you run it again, you get an error about the app locking the sync file
  • App is not running and you run a CLI command, you get an error.
    • We may update this in the future but the workflow here is so simple I'm fine with it existing in this way until we get a feature request

Tested on Windows and OSX to make sure we still run the main window from the main thread (this is a unique error case in OSX)

Resolves #965.

Copy link
Contributor

This PR conflicts with main. You need to rebase the PR before it can be merged.

Copy link
Contributor

This PR doesn't conflict with main anymore. It can be merged after all status checks have passed and it has been reviewed.

@halgari halgari requested a review from a team May 13, 2024 22:05
@halgari halgari self-assigned this May 13, 2024
@erri120 erri120 self-requested a review May 13, 2024 22:50
src/NexusMods.App/Startup.cs Outdated Show resolved Hide resolved
src/NexusMods.SingleProcess/CliSettings.cs Outdated Show resolved Hide resolved
src/NexusMods.SingleProcess/SyncFile.cs Outdated Show resolved Hide resolved
src/NexusMods.SingleProcess/CliServer.cs Outdated Show resolved Hide resolved
src/NexusMods.SingleProcess/CliServer.cs Outdated Show resolved Hide resolved
src/NexusMods.SingleProcess/CliServer.cs Outdated Show resolved Hide resolved
src/NexusMods.SingleProcess/CliServer.cs Show resolved Hide resolved
src/NexusMods.SingleProcess/CliServer.cs Outdated Show resolved Hide resolved
@erri120
Copy link
Member

erri120 commented May 14, 2024

On Linux, the process doesn't exit after closing the main window.

Copy link
Contributor

@Al12rs Al12rs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the plan was to:

  • If no arguments and no main ui process -> make current process Main and show UI
  • If no arguments and a main ui process exists -> do nothing (we can have this open a new window in the future)
  • If has cli arguments and there is no UI process -> start a second process to become main but don't show window, connect and wait for results
  • if has cli arguments and there is a main UI process -> connect to that and wait for results.

Rather than requiring app to have UI running when CLI is invoked, I would actually do the opposite. CLI commands only work if UI is not present, which seems to me the more likely use case.

Then we do a separate executable for NXM Handling (I think you mentioned MacOS requires a separate binary anyways).

halgari and others added 7 commits May 14, 2024 05:56
….Abstractions.csproj

Co-authored-by: erri120 <erri120@fossmailer.de>
Co-authored-by: erri120 <erri120@fossmailer.de>
Co-authored-by: erri120 <erri120@fossmailer.de>
Co-authored-by: erri120 <erri120@fossmailer.de>
Co-authored-by: erri120 <erri120@fossmailer.de>
…s.csproj

Co-authored-by: erri120 <erri120@fossmailer.de>
@halgari
Copy link
Collaborator Author

halgari commented May 14, 2024

On Linux, the process doesn't exit after closing the main window.

Dang it! You're right, there's nothing in these changes that should make that happen, so back to figuring that out I guess.

@halgari
Copy link
Collaborator Author

halgari commented May 14, 2024

  • If no arguments and no main ui process -> make current process Main and show UI
    This PR does that
  • If no arguments and a main ui process exists -> do nothing (we can have this open a new window in the future)
    This PR does that, except it prints an error as well
  • If has cli arguments and there is no UI process -> start a second process to become main but don't show window, connect and wait for results
    We don't do that currently with this PR, mostly for simplicity reasons, we could add this in, but I'm sure what that we have a solid usecase for this behavior yet
  • if has cli arguments and there is a main UI process -> connect to that and wait for results.
    This PR does this

--

So really it's about starting a new app when run from the commandline. I'm fine with getting some input on that from others.

@Al12rs
Copy link
Contributor

Al12rs commented May 14, 2024

So really it's about starting a new app when run from the commandline. I'm fine with getting some input on that from others.

Excluding NXM links, other CLI commands basically replicate what UI can do but through CLI. Main use case would be that you don't want to use UI or can't use UI.

E.g. "uninstall" command that @Sewer56 is reimplementing that is required to clean up game installs at uninstall time. That command can't work if it requires app to be open during uninstall.

Other CLI commands can be useful for Tests, where we don't really want the UI to be running.

I think we want to support CLI commands without UI being already open, not necessarily in this PR, but specifically for Uninstall we are going to need this.

Co-authored-by: erri120 <erri120@fossmailer.de>
@halgari
Copy link
Collaborator Author

halgari commented May 14, 2024

Okay the app exits now, I had replaced the Avalonia lifetime before, and didn't remove it with this PR. Switched back to the normal lifetime and made sure that the app calls .StopAsync when it terminates.

@halgari halgari requested review from Al12rs and erri120 May 14, 2024 13:43
Copy link
Member

@erri120 erri120 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exception when starting the App.

The capacity may not be smaller than the file size. (Parameter 'capacity'))
 ---> System.ArgumentOutOfRangeException: The capacity may not be smaller than the file size. (Parameter 'capacity')
   at System.IO.MemoryMappedFiles.MemoryMappedFile.VerifyMemoryMappedFileAccess(MemoryMappedFileAccess access, Int64 capacity, SafeFileHandle fileHandle, Int64 fileSize, Boolean& isRegularFile)
   at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateCore(SafeFileHandle fileHandle, String mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, MemoryMappedFileOptions options, Int64 capacity, Int64 fileSize)
   at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile(FileStream fileStream, String mapName, Int64 capacity, MemoryMappedFileAccess access, HandleInheritability inheritability, Boolean leaveOpen)
   at NexusMods.SingleProcess.MultiProcessSharedArray..ctor(AbsolutePath path, Int32 itemCount) in /ssd-pool/projects/NexusMods/app/code-review/src/NexusMods.SingleProcess/MultiprocessSharedArray.cs:line 65
   at NexusMods.SingleProcess.SyncFile..ctor(ILogger`1 logger, ISettingsManager settingsManager) in /ssd-pool/projects/NexusMods/app/code-review/src/NexusMods.SingleProcess/SyncFile.cs:line 21
@erri120
Copy link
Member

erri120 commented May 14, 2024

I think this code doesn't work:

// Make sure the a previous process didn't exit without setting the file size
if (path.FileExists)
{
var tmpStream =
path.Open(FileMode.Open, FileAccess.ReadWrite);
tmpStream.Position = _totalSize;
tmpStream.Close();
}

@erri120
Copy link
Member

erri120 commented May 14, 2024

The exception gets thrown when the file already exists, but the file size is greater than what we desire. Ideally, the code should resize the file, but it doesn't do that.

@erri120
Copy link
Member

erri120 commented May 14, 2024

NXM handler also doesn't work anymore. It did before.

@halgari
Copy link
Collaborator Author

halgari commented May 15, 2024

Exception when starting the App.

The capacity may not be smaller than the file size. (Parameter 'capacity'))
 ---> System.ArgumentOutOfRangeException: The capacity may not be smaller than the file size. (Parameter 'capacity')
   at System.IO.MemoryMappedFiles.MemoryMappedFile.VerifyMemoryMappedFileAccess(MemoryMappedFileAccess access, Int64 capacity, SafeFileHandle fileHandle, Int64 fileSize, Boolean& isRegularFile)
   at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateCore(SafeFileHandle fileHandle, String mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, MemoryMappedFileOptions options, Int64 capacity, Int64 fileSize)
   at System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile(FileStream fileStream, String mapName, Int64 capacity, MemoryMappedFileAccess access, HandleInheritability inheritability, Boolean leaveOpen)
   at NexusMods.SingleProcess.MultiProcessSharedArray..ctor(AbsolutePath path, Int32 itemCount) in /ssd-pool/projects/NexusMods/app/code-review/src/NexusMods.SingleProcess/MultiprocessSharedArray.cs:line 65
   at NexusMods.SingleProcess.SyncFile..ctor(ILogger`1 logger, ISettingsManager settingsManager) in /ssd-pool/projects/NexusMods/app/code-review/src/NexusMods.SingleProcess/SyncFile.cs:line 21

This was a strange one, but I got it fixed, it really has nothing to do with the size of the file, but that the memory map routine demands that we map the whole file. Switched that out and it no logner errors

@halgari
Copy link
Collaborator Author

halgari commented May 15, 2024

Also did a ton of testing of how the protocol handler code worked, we were putting the sync file in a strange place (temp folder) so I moved it into app local on windows. Also cleaned up how we did the mapping and got rid of a struct. It all works now, or at least it does on Windows and it didn't when I started.

@halgari halgari requested a review from erri120 May 15, 2024 04:21
@halgari
Copy link
Collaborator Author

halgari commented May 15, 2024

So really it's about starting a new app when run from the commandline. I'm fine with getting some input on that from others.

Excluding NXM links, other CLI commands basically replicate what UI can do but through CLI. Main use case would be that you don't want to use UI or can't use UI.

E.g. "uninstall" command that @Sewer56 is reimplementing that is required to clean up game installs at uninstall time. That command can't work if it requires app to be open during uninstall.

Other CLI commands can be useful for Tests, where we don't really want the UI to be running.

I think we want to support CLI commands without UI being already open, not necessarily in this PR, but specifically for Uninstall we are going to need this.

Good point on the uninstall command, I'll add in the options for no-gui and starting the main app from the CLI in the morning

Copy link
Member

@erri120 erri120 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NXM handler seems to be working again, and the process now exists properly 5 seconds after the window closes.

{
var lower = (ulong)port;
var upper = (ulong)processId << 32;
Value = lower & upper;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that this should've been | which is why the code wasn't working 😅. Sad to see the struct go, but whatever.

Copy link
Contributor

This PR conflicts with main. You need to rebase the PR before it can be merged.

@halgari
Copy link
Collaborator Author

halgari commented May 15, 2024

Since this PR has been open for 2 days at this point, I'm going to merge it and open the remaining two items as a new item/PR: #1360

Copy link
Contributor

This PR doesn't conflict with main anymore. It can be merged after all status checks have passed and it has been reviewed.

@halgari halgari merged commit 03ddcf0 into main May 15, 2024
7 checks passed
@erri120 erri120 deleted the fix-single-process branch May 15, 2024 12:08
This was referenced May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants