Wiki page eo-refcount.md changed with summary [] by Paul

This commit is contained in:
Paul 2017-11-10 00:21:04 -08:00 committed by apache
parent ec2efd1a82
commit bd24a0f67a
1 changed files with 26 additions and 9 deletions

View File

@ -4,7 +4,7 @@
# Reference Counting in Eo #
The previous tutorial ([Introduction to Eo](eo-intro.md)) explained how Eo objects should be created and destroyed in order to avoid *memory leaks*. The present tutorial shows graphically the inner workings of the reference counting mechanism.
The previous tutorial ([Introduction to Eo](eo-intro.md)) explained how you should create and destroy Eo objects in order to avoid *memory leaks*. The present tutorial shows graphically the inner workings of the reference counting mechanism.
To do so, some new *instrumentation* techniques are introduced. These techniques allow collecting information about the state of your program, and, although not frequently used in normal applications, they can be useful for debugging purposes.
@ -77,9 +77,10 @@ efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
EFL_MAIN()
```
Add a new method before ``_obj_create()`` that uses ``efl_ref_count()`` to print the counts of all the objects in a nice table:
Add a new method before ``_obj_create()`` that uses ``efl_ref_count()`` to print the counts of all the objects into a nice table:
```c
[...]
// Prints status of all our objects in a pretty table
static void
_status_print()
@ -90,8 +91,12 @@ _status_print()
efl_ref_count(_child1),
efl_ref_count(_child2));
}
[...]
```
> **NOTE:**
> ``[...]`` in a Code Block indicates existing code which has been excluded for the sake of brevity. There is no need to type ``[...]`` into your program.
And call this method in a couple of interesting places. For example, in your main function (``efl_main()``) after creating all the objects (``_obj_create()``) and after destroying them (``_obj_destroy()``):
```c
@ -110,11 +115,11 @@ And call this method in a couple of interesting places. For example, in your mai
[...]
```
What do you think is going to happen if you run this program? Go ahead and try.
What do you think is going to happen if you run this program? Go ahead and try it.
The expected behavior is *undefined*, which usually means your program will abort abnormally (commonly known as *crash*). This is what happened:
You can always ask an object for its internal reference count... as long as the object still exists. The problem with the above code is that when a reference count reaches 0, the object is destroyed. If you still hold a pointer to that object (like ``_root`` or ``_child1``) that pointer is *invalid*, and trying to use it as a parameter to any EFL call will result in *undefined behavior*.
You can always ask an object for its internal reference count... as long as the object still exists. The problem with the above code is that when a reference count reaches 0, the object is destroyed. If you still hold a pointer to that object (like ``_root`` or ``_child1``) that pointer is *invalid*, and trying to use it as a parameter to any EFL call will result in an *undefined behavior*.
In summary, the first call to ``_status_print()`` succeeded because the objects where alive, but the second one, after calling ``_obj_destroy()``, crashed because all the objects had been destroyed, and hence their pointers where invalid.
@ -131,7 +136,9 @@ To achieve this, EFL uses *Weak References*. The following code snippets explain
First, add 3 more ``Eo *`` global variables to keep track of the weak references (*wref*'s for short). Put this line below the other 3 global variables:
```c
[...]
Eo *_root_ref, *_child1_ref, *_child2_ref;
[...]
```
As you can see, wrefs are also regular ``Eo`` pointers; you can use them anywhere you would use an object pointer.
@ -152,6 +159,7 @@ Now, replace the previous usage of the plain object pointers in the ``printf()``
Finally, create the wrefs by adding a call to ``efl_wref_add()`` after each call to ``efl_add()`` in the ``_obj_create()`` method. It should look like this:
```c
[...]
static void
_obj_create()
{
@ -173,6 +181,7 @@ _obj_create()
// Add a weak reference so we can keep track of its state
efl_wref_add(_child2, &_child2_ref);
}
[...]
```
Notice how ``efl_wref_add()`` has two parameters: The first one is the object to keep track of, and the second one is a pointer to an ``Eo *`` which will receive the wref.
@ -217,15 +226,18 @@ Call ``_status_print()`` after returning the ``_root`` reference but before retu
And now, print the name of the parent of every object in the ``_status_print()`` method, right after printing the reference counts:
```c
[...]
printf("Parent: %6s %6s %6s\n\n",
_parent_name_get(_root_ref),
_parent_name_get(_child1_ref),
_parent_name_get(_child2_ref));
[...]
```
Now, the ``_parent_name_get()`` method does not exist; you need to create it. It has been moved to a different method because it deals with a few special cases. Add this snippet before ``_status_print()``:
```c
[...]
// Retrieves the name of an object's parent, handling special cases like not
// having any parent, or the object having been already destroyed.
static const char*
@ -238,13 +250,14 @@ _parent_name_get(Eo *obj)
// Otherwise, return parent's name
return efl_name_get(efl_parent_get(obj));
}
[...]
```
Remember you will be calling this method in situations where some of the objects might have already been destroyed. Therefore, the first thing this method does is checking whether the reference count is 0 with ``efl_ref_count()``. In that case it returns a simple dash "-".
Remember you will be calling this method in situations where some of the objects might have already been destroyed. Therefore, the first thing this method does is check whether the reference count is 0 with ``efl_ref_count()``. In that case it returns a simple dash "-".
Remember also that you can do this, because you are using weak references. Passing an invalid pointer to ``efl_ref_count()`` would result in undefined behavior.
The second thing the method does is checking whether the object has a parent or not by calling ``efl_parent_get()``. If it has no parent, it returns "none".
The second thing the method does is check whether the object has a parent or not by calling ``efl_parent_get()``. If it has no parent, it returns "none".
Finally, the method returns the name of the parent obtained with ``efl_name_get()``.
@ -381,13 +394,14 @@ Finally, notice how the parent of ``_child2`` is not ``_root`` anymore after ``_
There's still one more thing you can do with reference counting, and that is being notified when objects are finally destroyed.
As it is apparent by now, you are aware of the references **you** hold to an object, but, generally speaking, you don't know how many references do other parts of the code hold. Therefore, in some situations it can be complicated to know when an object is **finally destroyed**.
As it is apparent by now, you are aware of the references **you** hold to an object, but, generally speaking, you don't know how many references other parts of the code hold. Therefore, in some situations it can be complicated to know when an object is **finally destroyed**.
If you really need to know, you can register an event callback with ``efl_event_callback_add()`` that will be triggered and that happens.
If you really need to know, you can register an event callback with ``efl_event_callback_add()`` that gets triggered when that happens.
Use it to be notified of the destruction of each object. ``_obj_create()`` should look like this:
```c
[...]
// First create a root element
_root = efl_add(EFL_MODEL_ITEM_CLASS, NULL,
efl_name_set(efl_added, "Root"));
@ -411,6 +425,7 @@ Use it to be notified of the destruction of each object. ``_obj_create()`` shoul
efl_wref_add(_child2, &_child2_ref);
// Register a callback for DELETION events
efl_event_callback_add(_child2, EFL_EVENT_DEL, _obj_destroy_cb, NULL);
[...]
```
The first parameter to ``efl_event_callback_add()`` is the object you want to track. The second one is the type of event you want to be notified of (``EFL_EVENT_DEL``). The third parameter is the callback you need to write (``_obj_destroy_cb``), and the fourth one is extra data you might want to pass to your callback (none, in this case).
@ -418,6 +433,7 @@ The first parameter to ``efl_event_callback_add()`` is the object you want to tr
Write now the notification callback, right above ``_obj_create()``:
```c
[...]
// Gets called whenever an object is deleted
static void
_obj_destroy_cb(void *data EINA_UNUSED, const Efl_Event *event)
@ -427,6 +443,7 @@ _obj_destroy_cb(void *data EINA_UNUSED, const Efl_Event *event)
printf("Object named \"%s\" is about to be deleted\n", efl_name_get(obj));
_status_print();
}
[...]
```
All callback methods in EFL are generic, in that they receive the custom data you passed when you registered the callback, and then an ``Efl_Event *`` with the details of the event that triggered the callback.
@ -606,4 +623,4 @@ At the end of this tutorial you have learned:
: Part one of this tutorial
[Setting up the Development Environment](/develop/setup/c/)
: Read this before trying to develop with the EFL
: Read this before trying to develop with the EFL