Skip to content

Commit

Permalink
removed onDispose due to issues with fb_exit
Browse files Browse the repository at this point in the history
  • Loading branch information
benhar-dev committed Mar 16, 2023
1 parent 9214173 commit 25f73c0
Show file tree
Hide file tree
Showing 9 changed files with 39 additions and 206 deletions.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ mobject's goal is to be a lightweight solution to typical oop problems.

The mobject-disposable library provides a consistent way to handle the lifecycle of objects in an industrial control system. It includes an abstract class Disposable and an interface I_Disposable that can be implemented by classes that need to handle the deletion of objects.

Using this library ensures that objects are properly disposed of, which helps to prevent memory leaks and resource allocation issues. The Disposable class provides a consistent implementation of the OnDispose() method, which can be used by derived classes to handle the release of any resources or objects they own. The I_Disposable interface provides a common interface for objects that have a responsibility to delete others. This makes it easy to manage the lifecycle of objects in a complex system.
The I_Disposable interface provides a common interface for objects that have a responsibility to delete others. This makes it easy to manage the lifecycle of objects in a complex system.

Overall, the mobject-disposable library helps to improve the reliability and maintainability of industrial control systems by providing a consistent way to handle the deletion of objects.
6 changes: 6 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.3.0

### Disposable

- Removed OnDispose. Due to new information being provided in regards to how fb_exit operates it is no longer possible to implement the OnDispose method in this way. fb_exit is not follow the same rules as an inherited method, and as such would have only called OnDispose at the point when the disposable abstract class was destroyed. NOT at the point that a child class is destroyed. This is a subtle issue which is worth removing this functionality for. If left in could have resulted in some very hard to find bugs in the user code.

## 0.2.1

### Disposable
Expand Down
54 changes: 0 additions & 54 deletions docs/disposable.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,6 @@ END_VAR
//... no code should go here.
```

```declaration
METHOD OnDispose : BOOL
VAR_INPUT
CalledExplicitly : BOOL; // true if disposal was triggered from Dispose()
END_VAR
```

```body
// Here you should dispose of any objects your object owns or manages.
// ...
```

## Methods

### Dispose()
Expand All @@ -58,43 +44,3 @@ N/A
```example
myObject.Dispose()
```

### Protected OnDispose() : BOOL;

This method should be implemented by the concrete class if that class needs to dispose of other components.

OnDispose will be called explicitly by the Dispose() method, or from someone calling \_\_DELETE on the object, or by TwinCAT when it destroys the object.

#### Parameters

| Parameters | Datatype | Description |
| ---------------- | -------- | ---------------------------------------------------------------------- |
| CalledExplicitly | BOOL | This boolean will return True if disposal was triggered from Dispose() |

#### Return

| Datatype | Description |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| BOOL | Returning true will cancel the disposal. If you cancel it, then you must call Dispose or \_\_DELETE when you are ready for the final disposal. |

#### Usage

```declaration
METHOD OnDispose : BOOL
VAR_INPUT
CalledExplicitly : BOOL; // true if disposal was triggered from Dispose()
END_VAR
```

```body
// your code goes here...
// you should dispose any dynamic objects your object is managing
// in most cases you can simply return from this method.
RETURN;
// however...
// if the releasing of objects is not possible in a single cycle then you must return TRUE
OnDispose := true;
// This will suppress the final deletion and a second call of Dispose() will be required.
// The second call of Dispose() will not re-trigger this method.
```
11 changes: 0 additions & 11 deletions docs/i-disposable.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,6 @@ END_VAR
//... no code should go here.
```

```declaration
METHOD Dispose
```

```body
// Here you should dispose of any objects your object owns or manages.
// ...
```

## Methods

### Dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,18 @@ VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[OnDisposeCalledFromDisposeMethod();
OnDisposeCalledByManuallyDeleting();
OnDisposeOnlyCalledOnceDuringCancel();
OnDisposeCalledWithExplicitFlagFalse();
OnDisposeCalledWithExplicitFlagTrue();]]></ST>
<ST><![CDATA[DisposedFromDisposeMethod();
DisposedByManuallyDeleting();]]></ST>
</Implementation>
<Method Name="OnDisposeCalledByManuallyDeleting" Id="{53f28239-f13a-41de-aa9f-47d3ff4c2a4d}">
<Declaration><![CDATA[METHOD PUBLIC OnDisposeCalledByManuallyDeleting
<Method Name="DisposedByManuallyDeleting" Id="{53f28239-f13a-41de-aa9f-47d3ff4c2a4d}">
<Declaration><![CDATA[METHOD PUBLIC DisposedByManuallyDeleting
VAR
mockDispose : POINTER TO MockDisposable;
disposed : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('OnDisposeCalledByManuallyDeleting');
<ST><![CDATA[TEST('DisposedByManuallyDeleting');
// @TEST-FIXTURE
mockDispose := __NEW(MockDisposable(DisposedFlag:=disposed));
Expand All @@ -37,15 +34,15 @@ AssertTrue(
TEST_FINISHED();]]></ST>
</Implementation>
</Method>
<Method Name="OnDisposeCalledFromDisposeMethod" Id="{f6d78085-e7eb-482f-b15d-638e7798de7e}">
<Declaration><![CDATA[METHOD PUBLIC OnDisposeCalledFromDisposeMethod
<Method Name="DisposedFromDisposeMethod" Id="{f6d78085-e7eb-482f-b15d-638e7798de7e}">
<Declaration><![CDATA[METHOD PUBLIC DisposedFromDisposeMethod
VAR
mockDispose : POINTER TO MockDisposable;
disposed : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('OnDisposeCalledFromDisposeMethod');
<ST><![CDATA[TEST('DisposedFromDisposeMethod');
// @TEST-FIXTURE
mockDispose := __NEW(MockDisposable(DisposedFlag:=disposed));
Expand All @@ -59,84 +56,6 @@ AssertTrue(
Message := 'OnDispose was not correctly triggered in the MockDisposable'
);
TEST_FINISHED();]]></ST>
</Implementation>
</Method>
<Method Name="OnDisposeCalledWithExplicitFlagFalse" Id="{3a5dcb77-aaad-459c-8895-b710f6e010c5}">
<Declaration><![CDATA[METHOD PUBLIC OnDisposeCalledWithExplicitFlagFalse
VAR
mockDispose : POINTER TO MockDisposableWithExplicitFeedback;
explicit : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('OnDisposeCalledWithExplicitFlagFalse');
// @TEST-FIXTURE
mockDispose := __NEW(MockDisposableWithExplicitFeedback(ExplicitFlag:=explicit));
// @TEST-RUN
__DELETE(mockDispose);
// @TEST-ASSSERT
AssertFalse(
Condition := explicit,
Message := 'OnDispose argument CalledExplicitly was not false when called implicitly'
);
TEST_FINISHED();]]></ST>
</Implementation>
</Method>
<Method Name="OnDisposeCalledWithExplicitFlagTrue" Id="{1c28e0a8-a446-43bb-8ecc-5edeb1700d70}">
<Declaration><![CDATA[METHOD PUBLIC OnDisposeCalledWithExplicitFlagTrue
VAR
mockDispose : POINTER TO MockDisposableWithExplicitFeedback;
explicit : BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('OnDisposeCalledWithExplicitFlagTrue');
// @TEST-FIXTURE
mockDispose := __NEW(MockDisposableWithExplicitFeedback(ExplicitFlag:=explicit));
// @TEST-RUN
mockDispose^.Dispose();
// @TEST-ASSSERT
AssertTrue(
Condition := explicit,
Message := 'OnDispose argument CalledExplicitly was not true when called explicitly'
);
TEST_FINISHED();]]></ST>
</Implementation>
</Method>
<Method Name="OnDisposeOnlyCalledOnceDuringCancel" Id="{20e38352-90f3-4ba0-9c2b-439ece13ab5d}">
<Declaration><![CDATA[METHOD PUBLIC OnDisposeOnlyCalledOnceDuringCancel
VAR
mockDispose : POINTER TO MockDisposableWithCancel;
disposedCount : INT;
expected : INT := 1;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[TEST('OnDisposeOnlyCalledOnceDuringCancel');
// @TEST-FIXTURE
mockDispose := __NEW(MockDisposableWithCancel(DisposedCounterFlag:=disposedCount));
// @TEST-RUN
mockDispose^.Dispose(); // should trigger OnDisposed which will be cancelled;
mockDispose^.Dispose(); // should trigger __Delete
// @TEST-ASSSERT
AssertEquals(
Expected := expected,
Actual := disposedCount,
Message := 'OnDispose was incorrectly triggered more than once following a cancel'
);
TEST_FINISHED();]]></ST>
</Implementation>
</Method>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@ END_VAR
<ST><![CDATA[]]></ST>
</Implementation>
<Folder Name="Constructor" Id="{53162d61-c8fa-0c98-298f-bdd8fb2ca230}" />
<Method Name="FB_init" Id="{610da5a1-3b4a-0935-385a-dc588635f05c}" FolderPath="Constructor\">
<Declaration><![CDATA[METHOD FB_init : BOOL
<Folder Name="Destructor" Id="{8534e866-0d8b-4843-95b1-abd7bc1d43dc}" />
<Method Name="FB_exit" Id="{0e91c4ee-35e1-4c12-affe-8655ee47ca02}" FolderPath="Destructor\">
<Declaration><![CDATA[METHOD FB_exit : BOOL
VAR_INPUT
bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
bInCopyCode : BOOL; // if TRUE, the instance afterwards gets moved into the copy code (online change)
DisposedFlag : REFERENCE TO BOOL;
bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[THIS^.disposedFlag REF= DisposedFlag;]]></ST>
<ST><![CDATA[disposedFlag := TRUE;]]></ST>
</Implementation>
</Method>
<Method Name="OnDispose" Id="{66d42422-07ef-0aac-08dc-54d96cf1e697}">
<Declaration><![CDATA[METHOD PROTECTED OnDispose : BOOL
<Method Name="FB_init" Id="{610da5a1-3b4a-0935-385a-dc588635f05c}" FolderPath="Constructor\">
<Declaration><![CDATA[METHOD FB_init : BOOL
VAR_INPUT
CalledExplicitly : BOOL;
bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
bInCopyCode : BOOL; // if TRUE, the instance afterwards gets moved into the copy code (online change)
DisposedFlag : REFERENCE TO BOOL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[disposedFlag := TRUE;]]></ST>
<ST><![CDATA[THIS^.disposedFlag REF= DisposedFlag;]]></ST>
</Implementation>
</Method>
</POU>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</System>
<Plc>
<Project GUID="{99410A04-EDBC-4BE9-8215-58B13429C019}" Name="Main" PrjFilePath="Main\Main.plcproj" TmcFilePath="Main\Main.tmc" ReloadTmc="true" AmsPort="851" FileArchiveSettings="#x000e" SymbolicMapping="true">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcPath="Main\Main.tmc" TmcHash="{BCBF54D0-9EBA-5801-397A-81BAADEFBFEB}">
<Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcPath="Main\Main.tmc" TmcHash="{8DA6D5F0-0B8C-DEB7-4161-4E337FBB0457}">
<Name>Main Instance</Name>
<CLSID ClassFactory="TcPlc30">{08500001-0000-0000-F000-000000000064}</CLSID>
<Contexts>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,17 @@
{attribute 'enable_dynamic_creation'}
FUNCTION_BLOCK ABSTRACT Disposable IMPLEMENTS I_Disposable
VAR
disposeState : (IDLE, IMPLICITLY_CALLED, IMPLICITLY_CALLED_ON_DISPOSE, EXPLICITLY_CALLED_ON_DISPOSE, ON_DISPOSE_CANCELLED);
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
</Implementation>
<Folder Name="Destructor" Id="{a244c56d-214c-0539-02cc-30986d16b566}" />
<Folder Name="Protected" Id="{6667bda1-abca-422c-8122-12e92228bc13}" />
<Method Name="Dispose" Id="{6c98f403-5dd6-4f9d-bc0a-e406c48a07a8}">
<Declaration><![CDATA[METHOD Dispose
VAR
cancel : BOOL;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[CASE disposeState OF
IDLE:
disposeState := EXPLICITLY_CALLED_ON_DISPOSE;
cancel := OnDispose(TRUE);
IMPLICITLY_CALLED:
disposeState := IMPLICITLY_CALLED_ON_DISPOSE;
OnDispose(FALSE);
RETURN;
END_CASE
IF cancel THEN
disposeState := ON_DISPOSE_CANCELLED;
RETURN;
END_IF
__DELETE(THIS);]]></ST>
<ST><![CDATA[__DELETE(THIS);]]></ST>
</Implementation>
</Method>
<Method Name="FB_exit" Id="{0d1dd945-173c-0626-2abd-474b81e0db04}" FolderPath="Destructor\">
Expand All @@ -49,20 +26,16 @@ VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF disposeState = IDLE THEN
disposeState := IMPLICITLY_CALLED;
Dispose();
END_IF]]></ST>
</Implementation>
</Method>
<Method Name="OnDispose" Id="{349cfa6c-1d32-4721-9a07-8f628551b64b}" FolderPath="Protected\">
<Declaration><![CDATA[METHOD PROTECTED OnDispose : BOOL
VAR_INPUT
CalledExplicitly : BOOL; // true if disposal was triggered from Dispose()
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Returning true will cancel the disposal. If you cancel it, then you must call Dispose or __DELETE when you are ready for the final disposal.]]></ST>
<ST><![CDATA[// by default you would return if this is an in copy code, i.e. online change to this class
IF bInCopyCode THEN
RETURN;
END_IF
// A word of warning. fb_exit does not NOT operate in the same way as a typical inherited method. If a child class does not implement fb_exit then it does not
// gain this one for itself.
// The system will automatically call each fb_exit on each layer of inheritance one by one, starting with the child class and making it's way to the parent.
// Therefore code in here will only be triggered once the system reaches this layer of the object's deletion. So by this point, the child class and
// all of it's variables will already have had fb_exit called by the time we reach this point!]]></ST>
</Implementation>
</Method>
</POU>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<Company>mobject</Company>
<Released>false</Released>
<Title>mobject-disposable</Title>
<ProjectVersion>0.2.1</ProjectVersion>
<ProjectVersion>0.3.0</ProjectVersion>
<DefaultNamespace>mobject-disposable</DefaultNamespace>
<Author>mobject dev team</Author>
<LibraryCategories>
Expand Down

0 comments on commit 25f73c0

Please sign in to comment.