Skip to content
This repository was archived by the owner on Jul 24, 2025. It is now read-only.

Conversation

@archanox
Copy link
Owner

@archanox archanox commented Mar 15, 2023

This is an attempt to get to the bottom of a perceived race condition.

Duplicate messages are being raised as an event causing already expired tickets to be attempted to be reprocessed.

Locks during breakpoints are never released as a duplicate set breakpoints are coming through with no pending ticket, as it was parsed previously.

Summary by CodeRabbit

Release Notes

  • New Features

    • Transition to asynchronous event handling across various classes, enhancing responsiveness.
    • Improved parameter validation in asynchronous methods for better reliability.
    • New methods for handling game state events asynchronously.
  • Bug Fixes

    • Enhanced error handling and logging in message processing.
  • Documentation

    • Updated project version to 0.0.3-pre and enabled documentation generation.
  • Chores

    • Cleaned up obsolete event handling methods and improved resource management.

@archanox
Copy link
Owner Author

@archanox
Copy link
Owner Author

@archanox
Copy link
Owner Author

@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 1, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarCloud

@coderabbitai
Copy link

coderabbitai bot commented Oct 2, 2024

Walkthrough

The changes in this pull request involve a significant transition to asynchronous event handling across multiple classes in the ppsspp_api.Endpoints namespace. The Cpu, Game, and Input classes have replaced traditional event declarations with AsyncEvent<T> types, and several event invocation methods have been removed. Additionally, modifications to the MonitorFileOpen/Program.cs file enhance connection handling and game state management through asynchronous methods. The Ppsspp class has also been updated to support asynchronous operations and improved resource management, while the ResultMessage.cs file has undergone a reorganization of class hierarchies and property types. Lastly, the project file reflects updates to versioning and documentation properties.

Changes

File Change Summary
Endpoints/Cpu.cs Replaced OnStep and OnResume event declarations with AsyncEvent<CpuSteppingResult> and AsyncEvent<CpuResumeResult>. Added validation in GetRegisterAsync. Removed Resumed and Stepped methods.
Endpoints/Game.cs Replaced OnStarted, OnQuit, OnPaused, and OnResumed with AsyncEvent<GameResultEventArgs>. Removed internal methods for event invocation.
Endpoints/Input.cs Replaced OnButtonChange and OnAnalogChange with AsyncEvent<InputButtonResult> and AsyncEvent<InputAnalogResult>. Removed ButtonChanged and AnalogChanged methods.
MonitorFileOpen/Program.cs Introduced Game_OnStartedAsync and Game_OnQuitAsync methods for handling game events. Renamed CpuOnStep to Cpu_OnStep. Streamlined connection handling.
Ppsspp.cs Implemented IAsyncDisposable and IDisposable. Updated event handling to AsyncEvent<T>. Modified _pendingTickets to ConcurrentDictionary. Added several new asynchronous methods.
ResultMessage.cs Modified GameResultEventArgs to inherit from MessageEventArgs. Changed InputAnalogResult properties X and Y from int to double.
ppsspp-api.csproj Updated version from 0.0.2-pre to 0.0.3-pre. Enabled documentation generation and symbol inclusion. Set EnforceCodeStyleInBuild to True.

Sequence Diagram(s)

sequenceDiagram
    participant Game
    participant Monitor
    participant Cpu

    Monitor->>Game: Subscribe to OnStartedAsync
    Monitor->>Game: Subscribe to OnQuitAsync
    Game->>Monitor: OnStartedAsync
    Monitor->>Cpu: Handle CPU step
    Game->>Monitor: OnQuitAsync
    Monitor->>Cpu: Cleanup on quit
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

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

Sonarcsharp (reported by Codacy) found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🧹 Outside diff range comments (2)
ppsspp-api.csproj (2)

Line range hint 14-15: Ace! Including symbols for better debugging.

Setting IncludeSymbols to True and specifying the SymbolPackageFormat as 'snupkg' are top-notch changes. These will make debugging a breeze for developers using your package.

Just a small suggestion: Consider adding a comment explaining the benefits of including symbols and using the 'snupkg' format for future maintainers.


Line range hint 16-16: Fair dinkum! Enforcing code style during build.

Setting EnforceCodeStyleInBuild to True is a beauty. This will help maintain consistent code quality across the project by enforcing code style rules during the build process.

To complement this, you might want to consider adding an .editorconfig file to the project if you haven't already. This would ensure that all developers are using the same code style settings in their IDEs.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE

📥 Commits

Files that changed from the base of the PR and between 2257d83 and 62ba372.

📒 Files selected for processing (7)
  • Endpoints/Cpu.cs (3 hunks)
  • Endpoints/Game.cs (4 hunks)
  • Endpoints/Input.cs (1 hunks)
  • MonitorFileOpen/Program.cs (2 hunks)
  • Ppsspp.cs (8 hunks)
  • ResultMessage.cs (1 hunks)
  • ppsspp-api.csproj (1 hunks)
🔇 Additional comments (11)
ppsspp-api.csproj (3)

20-20: G'day! Let's verify if this version bump is appropriate.

The version has been updated from 0.0.2-pre to 0.0.3-pre. This minor version increment suggests notable changes or improvements in the project. Could you please confirm that this version bump aligns with the scope of changes in this PR, particularly the implementation of AsyncEvents?


Line range hint 9-9: Bonza! Enabling XML documentation generation.

Setting GenerateDocumentationFile to True is a ripper move. This will generate XML documentation files during the build, which can enhance IntelliSense support and make it easier for developers to understand and use your API. Good on ya for improving the project's documentation!


Line range hint 1-85: Strewth! Your project setup is looking schmick.

I've noticed a few more things that are worth a mention:

  1. You're using static code analyzers like Microsoft.VisualStudio.Threading.Analyzers and SonarAnalyzer.CSharp. That's a ripper idea for maintaining high code quality.

  2. The file exclusions for directories like HelloWorld, MonitorFileOpen, and Memory seem intentional. If these are test or example projects, you might consider moving them to a separate solution folder to keep your main project clean.

  3. The use of PackageReference for managing dependencies is spot on. It's the recommended approach for modern .NET projects.

Overall, your project structure and setup show good practices. Keep up the good work!

Endpoints/Cpu.cs (2)

1-2: Import statements are appropriate.

The necessary namespaces have been included.


8-9: Use of AsyncEvent enhances asynchronous handling.

The transition to AsyncEvent for OnStep and OnResume supports improved asynchronous event management.

MonitorFileOpen/Program.cs (3)

42-46: Looks good!

The Game_OnQuitAsync method correctly removes the breakpoint when the game quits, ensuring that resources are properly cleaned up.


71-71: Good practice changing 'async void' to 'async Task'

Updating the Cpu_OnStep method to return Task instead of void enhances exception handling and allows for better control over asynchronous operations.


Line range hint 71-95: Looks good!

The Cpu_OnStep method effectively handles CPU stepping logic with appropriate null checks and exception handling.

Endpoints/Game.cs (2)

18-18: Ensure AsyncEvent usage addresses race conditions appropriately.

Given the PR's objective to resolve race conditions related to event processing, confirm that AsyncEvent is suitable for this purpose and that its usage addresses the concurrency issues previously experienced. Ensure that all event invocations are correctly awaited and that subscribers handle events asynchronously as intended.

Also applies to: 27-27, 40-40, 53-53


18-18: Update event subscribers to handle AsyncEvent.

The events OnStarted, OnQuit, OnPaused, and OnResumed have been changed from traditional event handlers to AsyncEvent. Ensure that all existing event subscribers have been updated to handle AsyncEvent appropriately to prevent runtime issues.

Run the following script to identify any usages of the old event patterns:

Also applies to: 27-27, 40-40, 53-53

Ppsspp.cs (1)

202-205: ⚠️ Potential issue

Verify the asynchronous nature of 'SendInstant'

The method _socket.SendInstant(...) is being awaited, but if it's synchronous, using await is unnecessary and may cause confusion. Verify whether SendInstant is asynchronous. If it's synchronous, remove the await keyword.

Run the following script to verify if SendInstant is asynchronous:

<IncludeSymbols>True</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<LangVersion>default</LangVersion>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Heads up on the LangVersion setting, mate.

The LangVersion is currently set to 'default', which is fair enough as it allows the use of the latest C# features compatible with your target framework. However, for better control and clarity, you might want to consider explicitly specifying the C# version you're targeting. For example, if you're using C# 11 features, you could set it to:

<LangVersion>11.0</LangVersion>

This can help prevent unexpected behaviour if the project is built on a system with a different default C# version.

Comment on lines +11 to +12
public AsyncEvent<InputButtonResult> OnButtonChange = new();
public AsyncEvent<InputAnalogResult> OnAnalogChange = new();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Encapsulate AsyncEvent instances to protect event integrity

Currently, OnButtonChange and OnAnalogChange are declared as public fields. This allows external code to modify or replace the event instances, which could lead to unintended behaviour. To maintain encapsulation and safeguard your event handling mechanism, consider making these fields private and exposing them via public read-only properties.

You might refactor the code as follows:

-public AsyncEvent<InputButtonResult> OnButtonChange = new();
-public AsyncEvent<InputAnalogResult> OnAnalogChange = new();
+private readonly AsyncEvent<InputButtonResult> _onButtonChange = new();
+public AsyncEvent<InputButtonResult> OnButtonChange => _onButtonChange;
+
+private readonly AsyncEvent<InputAnalogResult> _onAnalogChange = new();
+public AsyncEvent<InputAnalogResult> OnAnalogChange => _onAnalogChange;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public AsyncEvent<InputButtonResult> OnButtonChange = new();
public AsyncEvent<InputAnalogResult> OnAnalogChange = new();
private readonly AsyncEvent<InputButtonResult> _onButtonChange = new();
public AsyncEvent<InputButtonResult> OnButtonChange => _onButtonChange;
private readonly AsyncEvent<InputAnalogResult> _onAnalogChange = new();
public AsyncEvent<InputAnalogResult> OnAnalogChange => _onAnalogChange;


public async ValueTask<CpuGetReg> GetRegisterAsync(string register)
{
ArgumentException.ThrowIfNullOrEmpty(register);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace ArgumentException with ArgumentNullException in ThrowIfNullOrEmpty.

The method ThrowIfNullOrEmpty is a static method of ArgumentNullException, not ArgumentException. Using ArgumentException will result in a compilation error.

Apply this diff to correct the exception type:

- ArgumentException.ThrowIfNullOrEmpty(register);
+ ArgumentNullException.ThrowIfNullOrEmpty(register);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ArgumentException.ThrowIfNullOrEmpty(register);
ArgumentNullException.ThrowIfNullOrEmpty(register);

throw new Exception("No functions, not playing any game?");
}

functionAddress = result2.Functions.SingleOrDefault(x => x.Name == "zz_sceIoOpen")?.Address;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function name inconsistency: 'zz_sceIoOpen' vs 'sceIoOpen'

There's a mismatch between the function name used in the search and the exception message. You're searching for "zz_sceIoOpen" but the exception refers to "sceIoOpen". Please confirm the correct function name and ensure consistency.

If "sceIoOpen" is the correct function name, apply this diff:

-functionAddress = result2.Functions.SingleOrDefault(x => x.Name == "zz_sceIoOpen")?.Address;
+functionAddress = result2.Functions.SingleOrDefault(x => x.Name == "sceIoOpen")?.Address;

Alternatively, if "zz_sceIoOpen" is correct, update the exception message:

-throw new Exception("Function stub for sceIoOpen not found... game never calls it?");
+throw new Exception("Function stub for zz_sceIoOpen not found... game never calls it?");

Also applies to: 59-62

Comment on lines +12 to +15
//await ppsspp.ConnectAsync(new Uri("ws://127.0.0.1:45333/debugger"));
//await ppsspp.ConnectAsync(new Uri("ws://127.0.0.1:52809/debugger"));
//await ppsspp.ConnectAsync(new Uri("ws://10.211.55.3:52809/debugger")); //WindowsVM
//await ppsspp.ConnectAsync(new Uri("ws://192.168.1.31:45333/debugger"));
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove commented-out code for a cleaner codebase

The commented-out connection lines are redundant now that AutoConnectAsync() is in use. Removing them will improve code readability and maintainability.

Apply this diff to remove the unnecessary code:

-//await ppsspp.ConnectAsync(new Uri("ws://127.0.0.1:45333/debugger"));
-//await ppsspp.ConnectAsync(new Uri("ws://127.0.0.1:52809/debugger"));
-//await ppsspp.ConnectAsync(new Uri("ws://10.211.55.3:52809/debugger")); //WindowsVM
-//await ppsspp.ConnectAsync(new Uri("ws://192.168.1.31:45333/debugger"));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//await ppsspp.ConnectAsync(new Uri("ws://127.0.0.1:45333/debugger"));
//await ppsspp.ConnectAsync(new Uri("ws://127.0.0.1:52809/debugger"));
//await ppsspp.ConnectAsync(new Uri("ws://10.211.55.3:52809/debugger")); //WindowsVM
//await ppsspp.ConnectAsync(new Uri("ws://192.168.1.31:45333/debugger"));

Comment on lines +67 to +69
[JsonIgnore]
public JsonElement Data { get; set; }
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Review accessibility of the Data property in MessageEventArgs

Since the Data property is marked with [JsonIgnore], consider making it protected or providing a private setter to prevent unintended external modification. This enhances encapsulation and prevents potential misuse.

Apply the following diff to adjust the property's accessibility:

 public class MessageEventArgs : EventArgs
 {
     [JsonPropertyName("event")]
     public string Event { get; set; }
     
     [JsonPropertyName("message")]
     public string Message { get; set; }
     
     [JsonPropertyName("ticket")]
     public string Ticket { get; set; }

     [JsonIgnore]
-    public JsonElement Data { get; set; }
+    public JsonElement Data { get; protected set; }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[JsonIgnore]
public JsonElement Data { get; set; }
}
[JsonIgnore]
public JsonElement Data { get; protected set; }
}

Comment on lines +498 to +517
public void Dispose()
{
if (_socket != null && (_socket.IsStarted || _socket.IsRunning))
{
_socket.StopOrFail(WebSocketCloseStatus.NormalClosure, "Disconnected from PPSSPP").RunSynchronously();
}
FailAllPendingAsync("Disconnected from PPSSPP").RunSynchronously();
if (!disposedValue)
{
// TODO: dispose managed state (managed objects)
_socket?.Dispose();

// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
_socket = null;
disposedValue = true;
}
OnClose?.InvokeAsync(this, EventArgs.Empty).RunSynchronously();
//GC.SuppressFinalize(this);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid synchronously blocking in 'Dispose' method

In the Dispose method, asynchronous methods are being invoked synchronously using .RunSynchronously(), which can lead to deadlocks or exceptions. Since Dispose cannot be asynchronous, consider refactoring to ensure that resources are disposed of properly without blocking.

One possible solution is to perform all necessary asynchronous cleanup in DisposeAsync and avoid calling asynchronous methods in Dispose. Alternatively, you could use the DisposeAsync().AsTask().GetAwaiter().GetResult() pattern, but this still risks blocking the thread.

["ReceivedMessage"] = args.GetRawText(),
},
});
tcs.SetException(new Exception(args.GetProperty("message").GetString()) { Data = { ["ReceivedMessage"] = args.GetRawText() }, Source = sender.ToString()});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Cannot set the 'Source' property of an Exception

The 'Source' property of an Exception is read-only and cannot be set using an object initialiser. Assigning to it will result in a runtime exception. Consider removing the assignment to 'Source' or use another method to include sender information.

Apply this diff to fix the issue:

-tcs.SetException(new Exception(args.GetProperty("message").GetString()) { Data = { ["ReceivedMessage"] = args.GetRawText() }, Source = sender.ToString()});
+tcs.SetException(new Exception(args.GetProperty("message").GetString())
+{
+    Data = { ["ReceivedMessage"] = args.GetRawText(), ["Sender"] = sender.ToString() }
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tcs.SetException(new Exception(args.GetProperty("message").GetString()) { Data = { ["ReceivedMessage"] = args.GetRawText() }, Source = sender.ToString()});
tcs.SetException(new Exception(args.GetProperty("message").GetString())
{
Data = { ["ReceivedMessage"] = args.GetRawText(), ["Sender"] = sender.ToString() }
});

Comment on lines +436 to +448
private static readonly ThreadLocal<Random> Random = new (() => new Random());

private static string MakeTicket(IReadOnlyDictionary<string, AsyncEvent<JsonElement>> pendingTickets)
{
const string chars = "0123456789abcdefghijklmnopqrstuvwxyz";
var random = new Random();


while (true)
{
var ticket = new string(Enumerable.Repeat(chars, 11)
.Select(s => s[random.Next(s.Length)])
.Select(s => s[Random.Value!.Next(s.Length)])
.ToArray());

if (_pendingTickets.ContainsKey(ticket))
if (pendingTickets.ContainsKey(ticket))
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use 'Guid.NewGuid()' for unique ticket generation

Using ThreadLocal<Random> and manual string generation may lead to collisions and isn't the most efficient method. Consider using Guid.NewGuid() to generate unique tickets reliably and thread-safely.

Apply this diff to refactor the ticket generation:

-private static readonly ThreadLocal<Random> Random = new (() => new Random());
...
-    const string chars = "0123456789abcdefghijklmnopqrstuvwxyz";
...
-    var ticket = new string(Enumerable.Repeat(chars, 11)
-       .Select(s => s[Random.Value!.Next(s.Length)])
-       .ToArray());
+    var ticket = Guid.NewGuid().ToString();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static readonly ThreadLocal<Random> Random = new (() => new Random());
private static string MakeTicket(IReadOnlyDictionary<string, AsyncEvent<JsonElement>> pendingTickets)
{
const string chars = "0123456789abcdefghijklmnopqrstuvwxyz";
var random = new Random();
while (true)
{
var ticket = new string(Enumerable.Repeat(chars, 11)
.Select(s => s[random.Next(s.Length)])
.Select(s => s[Random.Value!.Next(s.Length)])
.ToArray());
if (_pendingTickets.ContainsKey(ticket))
if (pendingTickets.ContainsKey(ticket))
private static string MakeTicket(IReadOnlyDictionary<string, AsyncEvent<JsonElement>> pendingTickets)
{
while (true)
{
var ticket = Guid.NewGuid().ToString();
if (pendingTickets.ContainsKey(ticket))

Comment on lines +251 to +253
OnClose?.InvokeAsync(this, EventArgs.Empty).Wait();

FailAllPending($"PPSSPP disconnected {info.Exception?.Message}");
FailAllPendingAsync($"PPSSPP disconnected {info.Exception?.Message}").Wait();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid blocking with '.Wait()' on asynchronous methods

Using .Wait() on asynchronous methods like InvokeAsync and FailAllPendingAsync can cause deadlocks or block the calling thread. It's better to use await to asynchronously wait for these methods to complete.

Apply this diff to fix the issue:

-websocketClient?.DisconnectionHappened.Subscribe(info =>
-{
-    OnClose?.InvokeAsync(this, EventArgs.Empty).Wait();
-    FailAllPendingAsync($"PPSSPP disconnected {info.Exception?.Message}").Wait();
-});
+websocketClient?.DisconnectionHappened.Subscribe(async info =>
+{
+    await OnClose?.InvokeAsync(this, EventArgs.Empty);
+    await FailAllPendingAsync($"PPSSPP disconnected {info.Exception?.Message}");
+});

Committable suggestion was skipped due to low confidence.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants