|
| 1 | +========================================== |
| 2 | +Refcounting Wrapper for External C Objects |
| 3 | +========================================== |
| 4 | + |
| 5 | +------- |
| 6 | +Context |
| 7 | +------- |
| 8 | + |
| 9 | +* From :url:`https://blog.adacore.com/the-road-to-a-thick-opengl-binding-for-ada-part-2` |
| 10 | +* OpenGL API create various objects like textures or vertex buffers |
| 11 | +* Creating them gives us an ID |
| 12 | + |
| 13 | + - Can then be used to refer to the object |
| 14 | + |
| 15 | +* Simple approach: Manually reclaiming them |
| 16 | + |
| 17 | + - Could cause leaks |
| 18 | + |
| 19 | +* Refcount approach: automatic ID management |
| 20 | + |
| 21 | + - From an Ada wrapper |
| 22 | + - Automatic reclaim once the last reference vanishes |
| 23 | + |
| 24 | +----------------- |
| 25 | +Wrapper Interface |
| 26 | +----------------- |
| 27 | + |
| 28 | +* :ada:`type GL_Object is abstract tagged private` |
| 29 | + |
| 30 | + - Implements smart pointer logic |
| 31 | + |
| 32 | +.. code:: Ada |
| 33 | +
|
| 34 | + procedure Initialize_Id (Object : in out GL_Object); |
| 35 | +
|
| 36 | + procedure Clear (Object : in out GL_Object); |
| 37 | +
|
| 38 | + function Initialized (Object : GL_Object) return Boolean; |
| 39 | +
|
| 40 | +* Derived by the **actual** object types |
| 41 | + |
| 42 | +.. code:: Ada |
| 43 | +
|
| 44 | + procedure Internal_Create_Id |
| 45 | + (Object : GL_Object; Id : out UInt) is abstract; |
| 46 | +
|
| 47 | + procedure Internal_Release_Id |
| 48 | + (Object : GL_Object; Id : UInt) is abstract; |
| 49 | +
|
| 50 | +* Example usage |
| 51 | + |
| 52 | +.. code:: Ada |
| 53 | +
|
| 54 | + type Shader (Kind : Shader_Type) is new GL_Object with null record; |
| 55 | +
|
| 56 | +------------------------------------ |
| 57 | +Wrapper Implementation: Private part |
| 58 | +------------------------------------ |
| 59 | + |
| 60 | +* Object ID's holder: :ada:`GL_Object_Reference` |
| 61 | + |
| 62 | + - All derived types have a handle to this |
| 63 | + |
| 64 | +.. code:: Ada |
| 65 | +
|
| 66 | + type GL_Object_Reference; |
| 67 | + type GL_Object_Reference_Access is access all GL_Object_Reference; |
| 68 | +
|
| 69 | + type GL_Object is abstract new Ada.Finalization.Controlled with record |
| 70 | + Reference : GL_Object_Reference_Access := null; |
| 71 | + end record; |
| 72 | +
|
| 73 | +* Controlled implements **ref-counting** |
| 74 | + |
| 75 | +.. code:: Ada |
| 76 | +
|
| 77 | + -- Increases reference count. |
| 78 | + overriding procedure Adjust (Object : in out GL_Object); |
| 79 | +
|
| 80 | + -- Decreases reference count. Destroys texture when it reaches zero. |
| 81 | + overriding procedure Finalize (Object : in out GL_Object); |
| 82 | +
|
| 83 | +------------------------------------ |
| 84 | +Wrapper Implementation: Full Picture |
| 85 | +------------------------------------ |
| 86 | + |
| 87 | +.. code:: Ada |
| 88 | +
|
| 89 | + type GL_Object_Reference is record |
| 90 | + GL_Id : UInt; |
| 91 | + Reference_Count : Natural; |
| 92 | + Is_Owner : Boolean; |
| 93 | + end record; |
| 94 | +
|
| 95 | +.. image:: controlled_gl_object.svg |
| 96 | + |
| 97 | +------------------------ |
| 98 | +:ada:`Adjust` Completion |
| 99 | +------------------------ |
| 100 | + |
| 101 | +* :ada:`Adjust` is called every time a new reference is **created** |
| 102 | +* Increments the ref-counter |
| 103 | + |
| 104 | +.. code:: Ada |
| 105 | +
|
| 106 | + overriding procedure Adjust (Object : in out GL_Object) is |
| 107 | + begin |
| 108 | + if Object.Reference /= null then |
| 109 | + Object.Reference.Reference_Count := @ + 1; |
| 110 | + end if; |
| 111 | + end Adjust; |
| 112 | +
|
| 113 | +-------------------------- |
| 114 | +:ada:`Finalize` Completion |
| 115 | +-------------------------- |
| 116 | + |
| 117 | +* :ada:`Finalize` should always be :dfn:`idempotent` |
| 118 | + |
| 119 | + - Compiler might call it multiple times on the same object |
| 120 | + - In particular when **exceptions** occur |
| 121 | + - Do **not** decrement the reference counter for every call |
| 122 | + |
| 123 | + + A given object will own **only one** reference |
| 124 | + |
| 125 | +.. code:: Ada |
| 126 | +
|
| 127 | + overriding procedure Finalize (Object : in out GL_Object) is |
| 128 | + Ref : GL_Object_Reference_Access |
| 129 | + renames Object.Reference; |
| 130 | + begin |
| 131 | + -- Idempotence: the next call to Finalize will have no effect |
| 132 | + Ref := null; |
| 133 | +
|
| 134 | + if Ref /= null then |
| 135 | + Ref.Reference_Count := @ - 1; |
| 136 | + if Ref.Reference_Count = 0 then |
| 137 | + Free (Ref.all); -- Call to user-defined primitive |
| 138 | + Unchecked_Free (Ref); |
| 139 | + end if; |
| 140 | + end if; |
| 141 | + end Finalize; |
0 commit comments