summaryrefslogtreecommitdiff
path: root/pages/develop/tutorials/c/eo-refcount.md.txt
blob: b3f272bf4c78b18add6cb9e809dff15af94e1175 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
---
~~Title: Reference Counting in Eo~~
---

# Reference Counting in Eo #

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 (and tutorial) purposes.

## Prerequisites ##

* Read the [Introduction to Eo](eo-intro.md) tutorial, which is the basis for this one.

## Step One: Print the Reference Counts ##

The previous tutorial talked a lot about reference counts, but it never printed one on the screen. This tutorial will begin by doing just that. It turns out this is not a trivial task, and that is why it has been moved to this second tutorial.

Without further ado: the method to retrieve the reference count of an object is ``efl_ref_count()``. It takes an ``Eo *`` as parameter and returns an integer.

This tutorial starts where the previous one left off, so here's the final listing from the [Introduction to Eo](eo-intro.md) tutorial:

```c
#define EFL_BETA_API_SUPPORT 1

#include <Eina.h>
#include <Efl_Core.h>

#include <stdlib.h>

Eo *_root, *_child1, *_child2;

// Create our test hierarchy
static void
_obj_create()
{
   // First create a root element
   _root = efl_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));

   // Create the first child element
   _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root,
                     efl_name_set(efl_added, "Child1"));

   // Create the second child element, this time, with an extra reference
   _child2 = efl_add_ref(EFL_MODEL_ITEM_CLASS, _root,
                         efl_name_set(efl_added, "Child2"));
}

// Destroy the test hierarchy
static void
_obj_destroy()
{
   // Destroy the root element
   printf ("Deleting root...\n");
   efl_unref(_root);

   // Destroy the child2 element, for which we were keeping an extra reference
   printf ("Deleting Child2...\n");
   efl_unref(_child2);
}

EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   // Create all objects
   _obj_create();

   // Destroy all objects
   _obj_destroy();

   // Exit
   efl_exit(0);
}
EFL_MAIN()
```

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()
{
   printf("Object:   %6s %6s %6s\n", "ROOT", "CHILD1", "CHILD2");
   printf("Refcount: %6d %6d %6d\n",
          efl_ref_count(_root),
          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
   [...]
   // Create all objects
   _obj_create();

   printf ("Initial state:\n");
   _status_print();

   // Destroy all objects
   _obj_destroy();

   printf ("Final state:\n");
   _status_print();
   [...]
```

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 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**.

In your applications, you should not try to use an object after you returned the reference you had to it, so you should not encounter this problem. In some rare cases, though, for debugging or didactic purposes, you might want to examine the state of an object, even after it has been destroyed.

## Step Two: Weak References ##

There is a mechanism in EFL that avoids invalid pointers, by turning them into ``NULL`` pointers. You can then check your object pointer to see if it is ``NULL`` before trying to access it, and some EFL functions will accept ``NULL`` as an object pointer and return appropriate results.

For instance, ``efl_ref_count()`` (used in ``_status_print()``) returns a reference count of 0 when the object parameter is ``NULL``, meaning that the object has already been destroyed.

To achieve this, EFL uses *Weak References*. The following code snippets explain how to use them and how they work.

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.

In fact, you could get rid of the previous plain object pointers (``_root``, ``_child1`` and ``_child2``) and only use the wrefs from now on. This tutorial keeps them separate to highlight that plain object pointers will become invalid, whereas wrefs will become ``NULL``.

Now, replace the previous usage of the plain object pointers in the ``printf()`` call in ``_status_print()`` with the wrefs:

```c
   [...]
   printf("Refcount: %6d %6d %6d\n",
          efl_ref_count(_root_ref),
          efl_ref_count(_child1_ref),
          efl_ref_count(_child2_ref));
   [...]
```

Finally, create the wrefs by adding a call to ``efl_wref_add()`` after each object is created in the ``_obj_create()`` method. It should look like this:

```c
[...]
static void
_obj_create()
{
   // First create a root element
   _root = efl_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_root, &_root_ref);

   // Create the first child element
   _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root,
                     efl_name_set(efl_added, "Child1"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_child1, &_child1_ref);

   // Create the second child element, this time, with an extra reference
   _child2 = efl_add_ref(EFL_MODEL_ITEM_CLASS, _root,
                         efl_name_set(efl_added, "Child2"));
   // 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.

> **NOTE:**
> ``efl_wref_add()`` does not get any extra reference to the object. After these changes, ``_child1`` still has 1 reference which belongs to its parent and ``_child2`` still has two references (one belongs to the parent and the other to the program).

Now you can run the program, and it should not crash anymore. You should see this in your terminal:

```
Initial state:
Object:     ROOT CHILD1 CHILD2
Refcount:      1      1      2

Deleting Root...
Deleting Child2...
Final state:
Object:     ROOT CHILD1 CHILD2
Refcount:      0      0      0
```

Which finally comes to show visually what the previous tutorial tried to explain with words: You can see the reference counts that ``_obj_create()`` has produced, and you can verify that all of them have been returned after calling ``_obj_destroy()``.

Try not returning the extra reference you hold for ``_child2`` and see what happens.

## Step Three: Print Some More ##

Just for completeness' sake, the tutorial will now call ``_status_print()`` in another place and add the parent of each object in the information.

Call ``_status_print()`` after returning the ``_root`` reference but before returning the ``_child2`` reference. ``_obj_destroy()`` should look like this:

```c
   [...]
   // Destroy the root element
   printf ("Deleting root...\n");
   efl_unref(_root);
   printf ("After deleting root:\n");
   _status_print();
   [...]
```

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*
_parent_name_get(Eo *obj)
{
   // Object has been destroyed
   if (efl_ref_count(obj) == 0) return "-";
   // Object has no parent
   if (efl_parent_get(obj) == NULL) return "none";
   // 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 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 but passing in ``NULL`` is OK.

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()``.

This step of the tutorial is now complete. The whole program should look like this:

```c
#define EFL_BETA_API_SUPPORT 1

#include <Eina.h>
#include <Efl_Core.h>

#include <stdlib.h>

Eo *_root, *_child1, *_child2;
Eo *_root_ref, *_child1_ref, *_child2_ref;

// 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*
_parent_name_get(Eo *obj)
{
   // Object has been destroyed
   if (efl_ref_count(obj) == 0) return "-";
   // Object has no parent
   if (efl_parent_get(obj) == NULL) return "none";
   // Otherwise, return parent's name
   return efl_name_get(efl_parent_get(obj));
}

// Prints status of all our objects in a pretty table
static void
_status_print()
{
   printf("Object:   %6s %6s %6s\n", "ROOT", "CHILD1", "CHILD2");
   printf("Refcount: %6d %6d %6d\n",
          efl_ref_count(_root_ref),
          efl_ref_count(_child1_ref),
          efl_ref_count(_child2_ref));
   printf("Parent:   %6s %6s %6s\n\n",
          _parent_name_get(_root_ref),
          _parent_name_get(_child1_ref),
          _parent_name_get(_child2_ref));
}

// Create our test hierarchy
static void
_obj_create()
{
   // First create a root element
   _root = efl_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_root, &_root_ref);

   // Create the first child element
   _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root,
                     efl_name_set(efl_added, "Child1"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_child1, &_child1_ref);

   // Create the second child element, this time, with an extra reference
   _child2 = efl_add_ref(EFL_MODEL_ITEM_CLASS, _root,
                         efl_name_set(efl_added, "Child2"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_child2, &_child2_ref);
}

// Destroy the test hierarchy
static void
_obj_destroy()
{
   // Destroy the root element
   printf ("Deleting root...\n");
   efl_unref(_root);
   printf ("After deleting root:\n");
   _status_print();

   // Destroy the child2 element, for which we were keeping an extra reference
   printf ("Deleting Child2...\n");
   efl_unref(_child2);
}

EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   // Create all objects
   _obj_create();

   printf ("Initial state:\n");
   _status_print();

   // Destroy all objects
   _obj_destroy();

   printf ("Final state:\n");
   _status_print();

   // Exit
   efl_exit(0);
}
EFL_MAIN()
```

If you run it, you should get this on the terminal:

```
Initial state:
Object:     ROOT CHILD1 CHILD2
Refcount:      1      1      2
Parent:     none   Root   Root

Deleting Root...
After deleting Root:
Object:     ROOT CHILD1 CHILD2
Refcount:      0      0      1
Parent:        -      -   none

Deleting Child2...
Final state:
Object:     ROOT CHILD1 CHILD2
Refcount:      0      0      0
Parent:        -      -      -
```

Many interesting things to see here. Note how, after returning the reference for ``_root`` (but before returning the one for ``_child2``), the ``_root`` and ``_child1`` objects have already been destroyed. This is expected, because the only reference to ``_child1`` was held by ``_root``, so upon destroying ``_root``, ``_child1`` disappears too.

Notice also how ``_child2`` survives the destruction of its parent, because of the extra reference you are holding to it.

Finally, notice how the parent of ``_child2`` is not ``_root`` anymore after ``_root`` is destroyed.

## Step Four: Being Notified ##

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 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 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_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_root, &_root_ref);
   // Register a callback for DELETION events
   efl_event_callback_add(_root, EFL_EVENT_DEL, _obj_destroy_cb, NULL);

   // Create the first child element
   _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root,
                     efl_name_set(efl_added, "Child1"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_child1, &_child1_ref);
   // Register a callback for DELETION events
   efl_event_callback_add(_child1, EFL_EVENT_DEL, _obj_destroy_cb, NULL);

   // Create the second child element, this time, with an extra reference
   _child2 = efl_add_ref(EFL_MODEL_ITEM_CLASS, _root,
                         efl_name_set(efl_added, "Child2"));
   // Add a weak reference so we can keep track of its state
   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).

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)
{
   Eo *obj = event->object;

   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.

In this case, you are interested in the object that is about to be deleted, and that is found in ``event->object``. The method prints its name, and then the status of all objects.

This tutorial is now complete. It should look like this:

```c
#define EFL_BETA_API_SUPPORT 1

#include <Eina.h>
#include <Efl_Core.h>

#include <stdlib.h>

Eo *_root, *_child1, *_child2;
Eo *_root_ref, *_child1_ref, *_child2_ref;

// 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*
_parent_name_get(Eo *obj)
{
   // Object has been destroyed
   if (efl_ref_count(obj) == 0) return "-";
   // Object has no parent
   if (efl_parent_get(obj) == NULL) return "none";
   // Otherwise, return parent's name
   return efl_name_get(efl_parent_get(obj));
}

// Prints status of all our objects in a pretty table
static void
_status_print()
{
   printf("Object:   %6s %6s %6s\n", "ROOT", "CHILD1", "CHILD2");
   printf("Refcount: %6d %6d %6d\n",
          efl_ref_count(_root_ref),
          efl_ref_count(_child1_ref),
          efl_ref_count(_child2_ref));
   printf("Parent:   %6s %6s %6s\n\n",
          _parent_name_get(_root_ref),
          _parent_name_get(_child1_ref),
          _parent_name_get(_child2_ref));
}

// Gets called whenever an object is deleted
static void
_obj_destroy_cb(void *data EINA_UNUSED, const Efl_Event *event)
{
   Eo *obj = event->object;

   printf("Object named \"%s\" is about to be deleted\n", efl_name_get(obj));
   _status_print();
}

// Create our test hierarchy
static void
_obj_create()
{
   // First create a root element
   _root = efl_new(EFL_MODEL_ITEM_CLASS,
                   efl_name_set(efl_added, "Root"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_root, &_root_ref);
   // Register a callback for DELETION events
   efl_event_callback_add(_root, EFL_EVENT_DEL, _obj_destroy_cb, NULL);

   // Create the first child element
   _child1 = efl_add(EFL_MODEL_ITEM_CLASS, _root,
                     efl_name_set(efl_added, "Child1"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_child1, &_child1_ref);
   // Register a callback for DELETION events
   efl_event_callback_add(_child1, EFL_EVENT_DEL, _obj_destroy_cb, NULL);

   // Create the second child element, this time, with an extra reference
   _child2 = efl_add_ref(EFL_MODEL_ITEM_CLASS, _root,
                         efl_name_set(efl_added, "Child2"));
   // Add a weak reference so we can keep track of its state
   efl_wref_add(_child2, &_child2_ref);
   // Register a callback for DELETION events
   efl_event_callback_add(_child2, EFL_EVENT_DEL, _obj_destroy_cb, NULL);
}

// Destroy the test hierarchy
static void
_obj_destroy()
{
   // Destroy the root element
   printf ("Deleting root...\n");
   efl_unref(_root);
   printf ("After deleting root:\n");
   _status_print();

   // Destroy the child2 element, for which we were keeping an extra reference
   printf ("Deleting Child2...\n");
   efl_unref(_child2);
}

EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   // Create all objects
   _obj_create();

   printf ("Initial state:\n");
   _status_print();

   // Destroy all objects
   _obj_destroy();

   printf ("Final state:\n");
   _status_print();

   // Exit
   efl_exit(0);
}
EFL_MAIN()
```

Build and run it, and you should get this on the terminal:

```
Initial state:
Object:     ROOT CHILD1 CHILD2
Refcount:      1      1      2
Parent:     none   Root   Root

Deleting Root...
Object named "Root" is about to be deleted
Object:     ROOT CHILD1 CHILD2
Refcount:      0      1      2
Parent:        -   Root   Root

Object named "Child1" is about to be deleted
Object:     ROOT CHILD1 CHILD2
Refcount:      0      0      2
Parent:        -      -   Root

After deleting Root:
Object:     ROOT CHILD1 CHILD2
Refcount:      0      0      1
Parent:        -      -   none

Deleting Child2...
Object named "Child2" is about to be deleted
Object:     ROOT CHILD1 CHILD2
Refcount:      0      0      0
Parent:        -      -      -

Final state:
Object:     ROOT CHILD1 CHILD2
Refcount:      0      0      0
Parent:        -      -      -
```

You can see that the ``_obj_destroy_cb()`` is called for each of the objects, in more or less the expected order (``_child2`` is the last one).

It is also worth noting that, inside a destruction callback, only the information regarding the object being destroyed is reliable. Note how, when being notified of the destruction of ``_root``, ``_root`` has already been destroyed, but not its children (``_child1`` still has a reference count of 1, and its parent is ``_root``).

## Summary ##

At the end of this tutorial you have learned:

* You can find out the reference count of an object with ``efl_ref_count()``.
* Object pointers **should never be used after returning the reference** you held to that object.
* If you need to examine an object which might have already been destroyed, you can use a Weak Reference obtained with ``efl_wref_add()``.
* Do not expect ``efl_ref_count()`` to return 0 if you are not using weak references (expect undefined behavior instead).
* You can be notified of some events, like object destruction, by registering a callback method with ``efl_event_callback_add()``.

## Further Reading ##
[Introduction to Eo](eo-intro.md)
:   Part one of this tutorial

[Setting up the Development Environment](/develop/setup/c/)
:    Read this before trying to develop with the EFL