-
-
Notifications
You must be signed in to change notification settings - Fork 18
Improvements for async device management and exclusive access to device #376
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
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
2432653
.editorconfig
58b3a3b
Local locks for global NanoFrameworkDevices collection because of dis…
29d3197
Hopefully locking of NanoFrameworkDevices was flagged by sonarcloud b…
7e7e683
Might have misread the locking issue. Fixed this one.
463de39
Some corrections:
500f6bb
Changes as a result of review comments
3faf575
Use per-port lock for wire protocol communication rather than a syste…
5bf0262
Minor fixes and improvements in comments
josesimoes 839145b
Next round of comments
bc708a7
Merged changes (auto-merge failed because of a wrong codepage???)
75560bb
Hope everything is merged
1df5b19
GetPortNames made static
dc2fe16
Port in now closed on device removal
josesimoes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| # EditorConfig for Visual Studio 2022: https://learn.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2022 | ||
|
|
||
| # This is a top-most .editorconfig file | ||
| root = true | ||
|
|
||
| #===================================================== | ||
| # | ||
| # nanoFramework specific settings | ||
| # | ||
| # | ||
| #===================================================== | ||
| [*] | ||
| # Generic EditorConfig settings | ||
| end_of_line = crlf | ||
| charset = utf-8-bom | ||
|
|
||
| # Visual Studio spell checker | ||
| spelling_languages = en-us | ||
| spelling_checkable_types = strings,identifiers,comments | ||
| spelling_error_severity = information | ||
| spelling_exclusion_path = spelling_exclusion.dic | ||
|
|
||
| #===================================================== | ||
| # | ||
| # Settings copied from the .NET runtime | ||
| # | ||
| # https://github.com/dotnet/runtime | ||
| # | ||
| #===================================================== | ||
| # Default settings: | ||
| # A newline ending every file | ||
| # Use 4 spaces as indentation | ||
| insert_final_newline = true | ||
| indent_style = space | ||
| indent_size = 4 | ||
| trim_trailing_whitespace = true | ||
|
|
||
| # Generated code | ||
| [*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}] | ||
| generated_code = true | ||
|
|
||
| # C# files | ||
| [*.cs] | ||
| # New line preferences | ||
| csharp_new_line_before_open_brace = all | ||
| csharp_new_line_before_else = true | ||
| csharp_new_line_before_catch = true | ||
| csharp_new_line_before_finally = true | ||
| csharp_new_line_before_members_in_object_initializers = true | ||
| csharp_new_line_before_members_in_anonymous_types = true | ||
| csharp_new_line_between_query_expression_clauses = true | ||
|
|
||
| # Indentation preferences | ||
| csharp_indent_block_contents = true | ||
| csharp_indent_braces = false | ||
| csharp_indent_case_contents = true | ||
| csharp_indent_case_contents_when_block = false | ||
| csharp_indent_switch_labels = true | ||
| csharp_indent_labels = one_less_than_current | ||
|
|
||
| # Modifier preferences | ||
| csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion | ||
|
|
||
| # avoid this. unless absolutely necessary | ||
| dotnet_style_qualification_for_field = false:suggestion | ||
| dotnet_style_qualification_for_property = false:suggestion | ||
| dotnet_style_qualification_for_method = false:suggestion | ||
| dotnet_style_qualification_for_event = false:suggestion | ||
|
|
||
| # Types: use keywords instead of BCL types, and permit var only when the type is clear | ||
| csharp_style_var_for_built_in_types = false:suggestion | ||
| csharp_style_var_when_type_is_apparent = false:none | ||
| csharp_style_var_elsewhere = false:suggestion | ||
| dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion | ||
| dotnet_style_predefined_type_for_member_access = true:suggestion | ||
|
|
||
| # name all constant fields using PascalCase | ||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion | ||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields | ||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style | ||
| dotnet_naming_symbols.constant_fields.applicable_kinds = field | ||
| dotnet_naming_symbols.constant_fields.required_modifiers = const | ||
| dotnet_naming_style.pascal_case_style.capitalization = pascal_case | ||
|
|
||
| # static fields should have s_ prefix | ||
| dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion | ||
| dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields | ||
| dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style | ||
| dotnet_naming_symbols.static_fields.applicable_kinds = field | ||
| dotnet_naming_symbols.static_fields.required_modifiers = static | ||
| dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected | ||
| dotnet_naming_style.static_prefix_style.required_prefix = s_ | ||
| dotnet_naming_style.static_prefix_style.capitalization = camel_case | ||
|
|
||
| # internal and private fields should be _camelCase | ||
| dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion | ||
| dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields | ||
| dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style | ||
| dotnet_naming_symbols.private_internal_fields.applicable_kinds = field | ||
| dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal | ||
| dotnet_naming_style.camel_case_underscore_style.required_prefix = _ | ||
| dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case | ||
|
|
||
| # Code style defaults | ||
| csharp_using_directive_placement = outside_namespace:suggestion | ||
| dotnet_sort_system_directives_first = true | ||
| csharp_prefer_braces = true:silent | ||
| csharp_preserve_single_line_blocks = true:none | ||
| csharp_preserve_single_line_statements = false:none | ||
| csharp_prefer_static_local_function = true:suggestion | ||
| csharp_prefer_simple_using_statement = false:none | ||
| csharp_style_prefer_switch_expression = true:suggestion | ||
| dotnet_style_readonly_field = true:suggestion | ||
|
|
||
| # Expression-level preferences | ||
| dotnet_style_object_initializer = true:suggestion | ||
| dotnet_style_collection_initializer = true:suggestion | ||
| dotnet_style_prefer_collection_expression = when_types_exactly_match | ||
| dotnet_style_explicit_tuple_names = true:suggestion | ||
| dotnet_style_coalesce_expression = true:suggestion | ||
| dotnet_style_null_propagation = true:suggestion | ||
| dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion | ||
| dotnet_style_prefer_inferred_tuple_names = true:suggestion | ||
| dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion | ||
| dotnet_style_prefer_auto_properties = true:suggestion | ||
| dotnet_style_prefer_conditional_expression_over_assignment = true:silent | ||
| dotnet_style_prefer_conditional_expression_over_return = true:silent | ||
| csharp_prefer_simple_default_expression = true:suggestion | ||
|
|
||
| # Expression-bodied members | ||
| csharp_style_expression_bodied_methods = true:silent | ||
| csharp_style_expression_bodied_constructors = true:silent | ||
| csharp_style_expression_bodied_operators = true:silent | ||
| csharp_style_expression_bodied_properties = true:silent | ||
| csharp_style_expression_bodied_indexers = true:silent | ||
| csharp_style_expression_bodied_accessors = true:silent | ||
| csharp_style_expression_bodied_lambdas = true:silent | ||
| csharp_style_expression_bodied_local_functions = true:silent | ||
|
|
||
| # Pattern matching | ||
| csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion | ||
| csharp_style_pattern_matching_over_as_with_null_check = true:suggestion | ||
| csharp_style_inlined_variable_declaration = true:suggestion | ||
|
|
||
| # Null checking preferences | ||
| csharp_style_throw_expression = true:suggestion | ||
| csharp_style_conditional_delegate_call = true:suggestion | ||
|
|
||
| # Other features | ||
| csharp_style_prefer_index_operator = false:none | ||
| csharp_style_prefer_range_operator = false:none | ||
| csharp_style_pattern_local_over_anonymous_function = false:none | ||
|
|
||
| # Space preferences | ||
| csharp_space_after_cast = false | ||
| csharp_space_after_colon_in_inheritance_clause = true | ||
| csharp_space_after_comma = true | ||
| csharp_space_after_dot = false | ||
| csharp_space_after_keywords_in_control_flow_statements = true | ||
| csharp_space_after_semicolon_in_for_statement = true | ||
| csharp_space_around_binary_operators = before_and_after | ||
| csharp_space_around_declaration_statements = do_not_ignore | ||
| csharp_space_before_colon_in_inheritance_clause = true | ||
| csharp_space_before_comma = false | ||
| csharp_space_before_dot = false | ||
| csharp_space_before_open_square_brackets = false | ||
| csharp_space_before_semicolon_in_for_statement = false | ||
| csharp_space_between_empty_square_brackets = false | ||
| csharp_space_between_method_call_empty_parameter_list_parentheses = false | ||
| csharp_space_between_method_call_name_and_opening_parenthesis = false | ||
| csharp_space_between_method_call_parameter_list_parentheses = false | ||
| csharp_space_between_method_declaration_empty_parameter_list_parentheses = false | ||
| csharp_space_between_method_declaration_name_and_open_parenthesis = false | ||
| csharp_space_between_method_declaration_parameter_list_parentheses = false | ||
| csharp_space_between_parentheses = false | ||
| csharp_space_between_square_brackets = false | ||
|
|
||
| # License header | ||
| file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| # C++ Files | ||
| [*.{cpp,h,in}] | ||
| curly_bracket_next_line = true | ||
| indent_brace_style = Allman | ||
|
|
||
| # Xml project files | ||
| [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] | ||
| indent_size = 2 | ||
|
|
||
| [*.{csproj,vbproj,proj,nativeproj,locproj}] | ||
| charset = utf-8 | ||
|
|
||
| # Xml build files | ||
| [*.builds] | ||
| indent_size = 2 | ||
|
|
||
| # Xml files | ||
| [*.{xml,stylecop,resx,ruleset}] | ||
| indent_size = 2 | ||
|
|
||
| # Xml config files | ||
| [*.{props,targets,config,nuspec}] | ||
| indent_size = 2 | ||
|
|
||
| # YAML config files | ||
| [*.{yml,yaml}] | ||
| indent_size = 2 | ||
|
|
||
| # Shell scripts | ||
| [*.sh] | ||
| end_of_line = lf | ||
| [*.{cmd,bat}] | ||
| end_of_line = crlf |
12 changes: 7 additions & 5 deletions
12
nanoFramework.Tools.DebugLibrary.Net/PortDefinitions/PortBase.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
nanoFramework.Tools.DebugLibrary.Shared/NFDevice/GlobalExclusiveDeviceAccess.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Threading; | ||
| using nanoFramework.Tools.Debugger.PortTcpIp; | ||
|
|
||
| namespace nanoFramework.Tools.Debugger.NFDevice | ||
| { | ||
| /// <summary> | ||
| /// Code that wants to access a device should use this system-wide exclusive access while | ||
| /// communicating to a device to prevent that another nanoFramework tool also wants to | ||
| /// communicate with the device. | ||
| /// </summary> | ||
| public static class GlobalExclusiveDeviceAccess | ||
| { | ||
| #region Fields | ||
| /// <summary> | ||
| /// Base name for the system-wide mutex that controls access to a device connected to a COM port. | ||
| /// </summary> | ||
| private const string MutexBaseName = "276545121198496AADD346A60F14EF8D_"; | ||
josesimoes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #endregion | ||
|
|
||
| #region Methods | ||
| /// <summary> | ||
| /// Communicate with a serial device and ensure the code to be executed as exclusive access to the device. | ||
| /// </summary> | ||
| /// <param name="serialPort">The serial port the device is connected to.</param> | ||
| /// <param name="communication">Code to execute while having exclusive access to the device</param> | ||
| /// <param name="millisecondsTimeout">Maximum time in milliseconds to wait for exclusive access</param> | ||
| /// <param name="cancellationToken">Cancellation token that can be cancelled to stop/abort running the <paramref name="communication"/>. | ||
| /// This method does not stop/abort execution of <paramref name="communication"/> after it has been started.</param> | ||
| /// <returns>Indicates whether the <paramref name="communication"/> has been executed. Returns <c>false</c> if exclusive access | ||
| /// cannot be obtained within <paramref name="millisecondsTimeout"/>, or if <paramref name="cancellationToken"/> was cancelled | ||
| /// before the <paramref name="communication"/> has been started.</returns> | ||
| public static bool CommunicateWithDevice(string serialPort, Action communication, int millisecondsTimeout = Timeout.Infinite, CancellationToken? cancellationToken = null) | ||
| { | ||
| return DoCommunicateWithDevice(serialPort, communication, millisecondsTimeout, cancellationToken); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Communicate with a device accessible via the network and ensure the code to be executed as exclusive access to the device. | ||
| /// </summary> | ||
| /// <param name="address">The network address the device is connected to.</param> | ||
| /// <param name="communication">Code to execute while having exclusive access to the device</param> | ||
| /// <param name="millisecondsTimeout">Maximum time in milliseconds to wait for exclusive access</param> | ||
| /// <param name="cancellationToken">Cancellation token that can be cancelled to stop/abort running the <paramref name="communication"/>. | ||
| /// This method does not stop/abort execution of <paramref name="communication"/> after it has been started.</param> | ||
| /// <returns>Indicates whether the <paramref name="communication"/> has been executed. Returns <c>false</c> if exclusive access | ||
| /// cannot be obtained within <paramref name="millisecondsTimeout"/>, or if <paramref name="cancellationToken"/> was cancelled | ||
| /// before the <paramref name="communication"/> has been started.</returns> | ||
| public static bool CommunicateWithDevice(NetworkDeviceInformation address, Action communication, int millisecondsTimeout = Timeout.Infinite, CancellationToken? cancellationToken = null) | ||
| { | ||
| return DoCommunicateWithDevice($"{address.Host}:{address.Port}", communication, millisecondsTimeout, cancellationToken); | ||
| } | ||
| #endregion | ||
|
|
||
| #region Implementation | ||
| private static bool DoCommunicateWithDevice(string connectionKey, Action communication, int millisecondsTimeout, CancellationToken? cancellationToken) | ||
| { | ||
| for (bool retry = true; retry;) | ||
| { | ||
| retry = false; | ||
|
|
||
| var waitHandles = new List<WaitHandle>(); | ||
| var mutex = new Mutex(false, $"{MutexBaseName}{connectionKey}"); | ||
| waitHandles.Add(mutex); | ||
|
|
||
| CancellationTokenSource timeOutToken = null; | ||
| if (millisecondsTimeout > 0 && millisecondsTimeout != Timeout.Infinite) | ||
| { | ||
| timeOutToken = new CancellationTokenSource(millisecondsTimeout); | ||
| waitHandles.Add(timeOutToken.Token.WaitHandle); | ||
| } | ||
|
|
||
| if (cancellationToken.HasValue) | ||
josesimoes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| waitHandles.Add(cancellationToken.Value.WaitHandle); | ||
| } | ||
|
|
||
| try | ||
josesimoes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| if (WaitHandle.WaitAny(waitHandles.ToArray()) == 0) | ||
| { | ||
| communication(); | ||
| return true; | ||
| } | ||
| } | ||
| catch (AbandonedMutexException) | ||
| { | ||
| // While this process is waiting on a mutex, the process that owned the mutex has been terminated | ||
| // without properly releasing the mutex. | ||
| // Try again, if this is the only remaining process it will re-create the mutex and get exclusive access. | ||
| retry = true; | ||
| } | ||
| finally | ||
| { | ||
| mutex.ReleaseMutex(); | ||
| timeOutToken?.Dispose(); | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
josesimoes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| #endregion | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.