88using System ;
99using System . Collections . Concurrent ;
1010using System . Collections . Generic ;
11+ using System . Diagnostics ;
1112using System . Linq ;
1213using System . Runtime . CompilerServices ;
1314using System . Text ;
@@ -63,14 +64,16 @@ public sealed class Tag : IDisposable
6364 private uint ? _stringPadBytes ;
6465 private uint ? _stringTotalLength ;
6566
66- public Tag ( )
67+ public Tag ( ) : this ( new Native ( ) )
6768 {
68- _native = new Native ( ) ;
6969 }
7070
7171 internal Tag ( INative nativeMethods )
7272 {
7373 _native = nativeMethods ;
74+
75+ // Need to keep a reference to the delegate in memory so it doesn't get garbage collected
76+ coreLibCallbackFuncExDelegate = new callback_func_ex ( coreLibEventCallback ) ;
7477 }
7578
7679 ~ Tag ( )
@@ -79,9 +82,16 @@ internal Tag(INative nativeMethods)
7982 }
8083
8184 /// <summary>
82- /// True if <see cref="Initialize"/> or <see cref="InitializeAsync"/> has been called .
85+ /// True if <see cref="Initialize"/> or <see cref="InitializeAsync"/> has completed succesfully .
8386 /// </summary>
84- public bool IsInitialized => _isInitialized ;
87+ public bool IsInitialized
88+ {
89+ get
90+ {
91+ ThrowIfAlreadyDisposed ( ) ;
92+ return _isInitialized ;
93+ }
94+ }
8595
8696 /// <summary>
8797 /// <list type="table">
@@ -604,7 +614,21 @@ public uint? StringTotalLength
604614 set => SetField ( ref _stringTotalLength , value ) ;
605615 }
606616
607-
617+ /// <summary>
618+ /// [OPTIONAL | MODBUS-SPECIFIC]
619+ /// The Modbus specification allows devices to queue up to 16 requests at once.
620+ /// </summary>
621+ ///
622+ /// <remarks>
623+ /// The default is 1 and the maximum is 16.
624+ /// This allows the host to send multiple requests without waiting for the device to respond.
625+ /// Not all devices support up to 16 requests in flight.
626+ /// </remarks>
627+ public uint ? MaxRequestsInFlight
628+ {
629+ get => GetField ( ref _maxRequestsInFlight ) ;
630+ set => SetField ( ref _maxRequestsInFlight , value ) ;
631+ }
608632
609633
610634 public void Dispose ( )
@@ -613,10 +637,17 @@ public void Dispose()
613637 return ;
614638
615639 if ( _isInitialized )
616- RemoveEventsAndRemoveCallback ( ) ;
640+ {
641+ // These should always succeed unless bugs exist in this wrapper or the core library
642+ var removeCallbackResult = RemoveCallback ( ) ;
643+ var destroyResult = ( Status ) _native . plc_tag_destroy ( nativeTagHandle ) ;
617644
618- var result = ( Status ) _native . plc_tag_destroy ( nativeTagHandle ) ;
619- ThrowIfStatusNotOk ( result ) ;
645+ // However, we cannot recover if they do fail, so ignore except during development
646+ Debug . Assert ( removeCallbackResult == Status . Ok ) ;
647+ Debug . Assert ( destroyResult == Status . Ok ) ;
648+
649+ RemoveEvents ( ) ;
650+ }
620651
621652 _isDisposed = true ;
622653 }
@@ -654,7 +685,7 @@ public void Initialize()
654685 var result = _native . plc_tag_create_ex ( attributeString , coreLibCallbackFuncExDelegate , IntPtr . Zero , millisecondTimeout ) ;
655686 if ( result < 0 )
656687 {
657- RemoveEventsAndRemoveCallback ( ) ;
688+ RemoveEvents ( ) ;
658689 throw new LibPlcTagException ( ( Status ) result ) ;
659690 }
660691 else
@@ -666,22 +697,6 @@ public void Initialize()
666697 _isInitialized = true ;
667698 }
668699
669- /// <summary>
670- /// [OPTIONAL | MODBUS-SPECIFIC]
671- /// The Modbus specification allows devices to queue up to 16 requests at once.
672- /// </summary>
673- ///
674- /// <remarks>
675- /// The default is 1 and the maximum is 16.
676- /// This allows the host to send multiple requests without waiting for the device to respond.
677- /// Not all devices support up to 16 requests in flight.
678- /// </remarks>
679- public uint ? MaxRequestsInFlight
680- {
681- get => GetField ( ref _maxRequestsInFlight ) ;
682- set => SetField ( ref _maxRequestsInFlight , value ) ;
683- }
684-
685700 /// <summary>
686701 /// Creates the underlying data structures and references required before tag operations.
687702 /// </summary>
@@ -706,7 +721,8 @@ public async Task InitializeAsync(CancellationToken token = default)
706721 if ( createTasks . TryPop ( out var createTask ) )
707722 {
708723 Abort ( ) ;
709- RemoveEventsAndRemoveCallback ( ) ;
724+ RemoveCallback ( ) ;
725+ RemoveEvents ( ) ;
710726
711727 if ( token . IsCancellationRequested )
712728 createTask . SetCanceled ( ) ;
@@ -727,7 +743,7 @@ public async Task InitializeAsync(CancellationToken token = default)
727743 // Something went wrong locally
728744 if ( result < 0 )
729745 {
730- RemoveEventsAndRemoveCallback ( ) ;
746+ RemoveEvents ( ) ;
731747 throw new LibPlcTagException ( ( Status ) result ) ;
732748 }
733749 else
@@ -744,7 +760,8 @@ public async Task InitializeAsync(CancellationToken token = default)
744760 // On error, tear down and throw
745761 if ( createTask . Task . Result != Status . Ok )
746762 {
747- RemoveEventsAndRemoveCallback ( ) ;
763+ RemoveCallback ( ) ;
764+ RemoveEvents ( ) ;
748765 throw new LibPlcTagException ( createTask . Task . Result ) ;
749766 }
750767
@@ -1206,22 +1223,21 @@ void SetUpEvents()
12061223 WriteCompleted += WriteTaskCompleter ;
12071224 Created += CreatedTaskCompleter ;
12081225
1209- // Need to keep a reference to the delegate in memory so it doesn't get garbage collected
1210- coreLibCallbackFuncExDelegate = new callback_func_ex ( coreLibEventCallback ) ;
12111226
12121227 }
12131228
1214- void RemoveEventsAndRemoveCallback ( )
1229+ void RemoveEvents ( )
12151230 {
12161231
12171232 // Used to finalize the read/write task completion sources
12181233 ReadCompleted -= ReadTaskCompleter ;
12191234 WriteCompleted -= WriteTaskCompleter ;
12201235 Created -= CreatedTaskCompleter ;
1236+ }
12211237
1222- var callbackRemovalResult = ( Status ) _native . plc_tag_unregister_callback ( nativeTagHandle ) ;
1223- ThrowIfStatusNotOk ( callbackRemovalResult ) ;
1224-
1238+ Status RemoveCallback ( )
1239+ {
1240+ return ( Status ) _native . plc_tag_unregister_callback ( nativeTagHandle ) ;
12251241 }
12261242
12271243 private readonly ConcurrentStack < TaskCompletionSource < Status > > createTasks = new ConcurrentStack < TaskCompletionSource < Status > > ( ) ;
0 commit comments