summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <mail@marcel-hollerbach.de>2020-03-04 08:28:42 +0100
committerMarcel Hollerbach <mail@marcel-hollerbach.de>2020-03-20 19:46:06 +0100
commit93a701d06585e7138e41361cdfa76735049ec53b (patch)
treed984877a377da41e19df3a074656022498412cb4
parenta935695aad71d6acb8ecd7b53a5e0ae91427adea (diff)
eo: rework vtable allocation scheme
with this commit a new way of allocating vtables arrived. The old mechnism was to allocate a table big enough to carry *all* functions at once, in order to not allocate that much memory for functions that are not implemented on a specific klass, dichchains have been used, which can be seens as a 2D matrix, where columns are only allocated if min 1 entry needs to be written, this may have been a good way to allocate back in the day when all this with eo started, however, it showed to not pay off. With this new way, we allocate a array of arrays. the first lvl array is carrying enough slots, that *all* up to the time defined interfaces/classes/abstracts/mixins can be implemented. The second lvl array then has exactly the size of the defined APIs. The second lvl array is obviously only allocated if needed. When comparing the two methods, i messured two things, the usage based on memory allocation for vtable-way-1 and vtable-way-2. Additionally, i checked the overall memory usage of elementary_test using pmap. The first messurement is a little bit more exact. The second messurement is more biased, but captures the whole picture. Memory allocation tracking: vtable-way-1 - vtable-way-2 = 74680 Byte Pmap memory tracking: vtable-way1 - vtable-way-2 = 217088 Byte The second messurement shows a bigger impact, likely because this is also showing off all the sideeffects that we are taking place due to fewer allocations. Differential Revision: https://phab.enlightenment.org/D11535
-rw-r--r--src/lib/eo/eo.c495
-rw-r--r--src/lib/eo/eo_private.h32
-rw-r--r--src/tests/eo/suite/eo_test_general.c1
3 files changed, 285 insertions, 243 deletions
diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c
index 8ca94bf8fd..b28cb178c3 100644
--- a/src/lib/eo/eo.c
+++ b/src/lib/eo/eo.c
@@ -90,7 +90,6 @@ static _Efl_Class **_eo_classes = NULL;
90static Eo_Id _eo_classes_last_id = 0; 90static Eo_Id _eo_classes_last_id = 0;
91static Eo_Id _eo_classes_alloc = 0; 91static Eo_Id _eo_classes_alloc = 0;
92static int _efl_object_init_count = 0; 92static int _efl_object_init_count = 0;
93static Efl_Object_Op _eo_ops_last_id = 0;
94static Eina_Hash *_ops_storage = NULL; 93static Eina_Hash *_ops_storage = NULL;
95static Eina_Spinlock _ops_storage_lock; 94static Eina_Spinlock _ops_storage_lock;
96 95
@@ -104,7 +103,6 @@ static void _eo_condtor_reset(_Eo_Object *obj);
104static inline void *_efl_data_scope_get(const _Eo_Object *obj, const _Efl_Class *klass); 103static inline void *_efl_data_scope_get(const _Eo_Object *obj, const _Efl_Class *klass);
105static inline void *_efl_data_xref_internal(const char *file, int line, _Eo_Object *obj, const _Efl_Class *klass, const _Eo_Object *ref_obj); 104static inline void *_efl_data_xref_internal(const char *file, int line, _Eo_Object *obj, const _Efl_Class *klass, const _Eo_Object *ref_obj);
106static inline void _efl_data_xunref_internal(_Eo_Object *obj, void *data, const _Eo_Object *ref_obj); 105static inline void _efl_data_xunref_internal(_Eo_Object *obj, void *data, const _Eo_Object *ref_obj);
107static void _vtable_init(Eo_Vtable *vtable, size_t size);
108 106
109static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void *api_func); 107static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void *api_func);
110 108
@@ -120,96 +118,175 @@ static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void *api_f
120 (_eo_classes[_UNMASK_ID(id) - 1]) : NULL); \ 118 (_eo_classes[_UNMASK_ID(id) - 1]) : NULL); \
121 }) 119 })
122 120
123static inline void 121#define EFL_OBJECT_OP_CLASS_PART(op) op >> 16
124_vtable_chain2_unref(Dich_Chain2 *chain) 122#define EFL_OBJECT_OP_FUNC_PART(op) op & 0xffff
123#define EFL_OBJECT_OP_CREATE_OP_ID(class_id, func_id) ((unsigned short)class_id)<<16|((unsigned short)func_id&0xffff)
124
125static const _Efl_Class *
126_eo_op_class_get(Efl_Object_Op op)
125{ 127{
126 if (--(chain->refcount) == 0) 128 short class_id = EFL_OBJECT_OP_CLASS_PART(op);
127 { 129 return _eo_classes[class_id];
128 free(chain);
129 }
130} 130}
131 131
132static inline void 132/**
133_vtable_chain_alloc(Dich_Chain1 *chain1) 133 * This inits the vtable wit hthe current size of allocated tables
134 */
135static void
136_vtable_init(Eo_Vtable *vtable)
134{ 137{
135 chain1->chain2 = calloc(1, sizeof(*(chain1->chain2))); 138 //we assume here that _eo_classes_last_id was called before
136 chain1->chain2->refcount = 1; 139 vtable->size = _eo_classes_last_id;
140 vtable->chain = calloc(vtable->size, sizeof(Eo_Vtable_Node));
137} 141}
138 142
139static inline void _vtable_chain_write_prepare(Dich_Chain1 *dst); 143/**
140 144 * This removes all nodes from the klass that are copied from mro
141static inline void 145 */
142_vtable_chain_merge(Dich_Chain1 *dst, const Dich_Chain1 *src) 146static void
147_vtable_mro_free(const _Efl_Class *klass)
143{ 148{
144 size_t j; 149 const _Efl_Class **mro_itr = klass->mro;
145 const op_type_funcs *sf = src->chain2->funcs; 150 const Eo_Vtable *vtable = &klass->vtable;
146 op_type_funcs *df = dst->chain2->funcs;
147 151
148 if (df == sf) 152 for ( ; *mro_itr ; mro_itr++)
149 { 153 {
150 /* Skip if the chain is the same. */ 154 const Eo_Vtable *mro_vtable = &(*mro_itr)->vtable;
151 return; 155 for (int i = 0; i < mro_vtable->size; ++i)
156 {
157 if (mro_vtable->chain[i].funcs == vtable->chain[i].funcs)
158 vtable->chain[i].funcs = NULL;
159 }
160 }
161}
162
163static void
164_vtable_free(Eo_Vtable *vtable, const Eo_Vtable *root)
165{
166 if (root)
167 {
168 EINA_SAFETY_ON_FALSE_RETURN(vtable->size == root->size);
152 } 169 }
153 170
154 for (j = 0 ; j < DICH_CHAIN_LAST_SIZE ; j++, df++, sf++) 171 for (int i = 0; i < vtable->size; ++i)
155 { 172 {
156 if (sf->func && memcmp(df, sf, sizeof(*df))) 173 if (root && root->chain[i].funcs == vtable->chain[i].funcs)
174 vtable->chain[i].count = 0;
175
176 if (vtable->chain[i].count)
157 { 177 {
158 _vtable_chain_write_prepare(dst); 178 free(vtable->chain[i].funcs);
159 df = dst->chain2->funcs + j;
160 memcpy(df, sf, sizeof(*df));
161 } 179 }
162 } 180 }
181 free(vtable->chain);
163} 182}
164 183
165static inline void 184/**
166_vtable_chain_write_prepare(Dich_Chain1 *dst) 185 * This takes over all set chains of the src to dest.
186 * This should only be called on Eo_Vtables, which are initialized with this value.
187 * Previous setted values are going to be overwritten.
188 */
189static void
190_vtable_take_over(Eo_Vtable *dest, const Eo_Vtable *src)
167{ 191{
168 if (!dst->chain2) 192 for (int i = 0; i < src->size; ++i)
169 {
170 _vtable_chain_alloc(dst);
171 return;
172 }
173 else if (dst->chain2->refcount == 1)
174 { 193 {
175 /* We own it, no need to duplicate */ 194 if (src->chain[i].funcs)
176 return; 195 {
196 dest->chain[i] = src->chain[i];
197 }
177 } 198 }
199}
178 200
179 Dich_Chain1 old; 201/**
180 old.chain2 = dst->chain2; 202 * Fills the node of the passed class id with a empty none NULL pointer.
203 * This is used to indicate that a specific node has a normal 0 size, but is set.
204 */
205static void
206_vtable_insert_empty_funcs(Eo_Vtable *vtable, unsigned short class_id)
207{
208 vtable->chain[class_id].funcs = (void*)0x1010101;
209 vtable->chain[class_id].count = 0;
210}
181 211
182 _vtable_chain_alloc(dst); 212/**
183 _vtable_chain_merge(dst, &old); 213 * duplicate the source node, and write the duplicated values to the destination
214 * No logical changes are applied to src.
215 */
216static void
217_vtable_copy_node(Eo_Vtable_Node *dest, const Eo_Vtable_Node *src)
218{
219 dest->count = src->count;
220 dest->funcs = calloc(sizeof(op_type_funcs), src->count);
221 memcpy(dest->funcs, src->funcs, sizeof(op_type_funcs) * src->count);
222}
184 223
185 _vtable_chain2_unref(old.chain2); 224/**
225 * Initialize a node with a empty funcs array of the passed length
226 */
227static void
228_vtable_prepare_empty_node(Eo_Vtable *dest, unsigned int length, unsigned int class_id)
229{
230 dest->chain[class_id].count = length;
231 dest->chain[class_id].funcs = calloc(sizeof(op_type_funcs), dest->chain[class_id].count);
186} 232}
187 233
188static inline void 234/**
189_vtable_chain_copy_ref(Dich_Chain1 *dst, const Dich_Chain1 *src) 235 * Copy all setted APIs from src to dest.
236 * Already set function slots are going to be replaced.
237 */
238static void
239_vtable_merge_defined_api(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool *hitmap)
190{ 240{
191 if (dst->chain2) 241 for (unsigned int i = 0; i < src->size; ++i)
192 { 242 {
193 _vtable_chain_merge(dst, src); 243 //if there is a source node evalulate if we need to copy it
194 } 244 if (src->chain[i].funcs)
195 else 245 {
196 { 246 if (!dest->chain[i].funcs)
197 dst->chain2 = src->chain2; 247 {
198 dst->chain2->refcount++; 248 dest->chain[i] = src->chain[i];
249 EINA_SAFETY_ON_FALSE_RETURN(hitmap[i] == EINA_FALSE);
250 }
251 else
252 {
253 if (!hitmap[i])
254 {
255 const Eo_Vtable_Node node = dest->chain[i];
256 _vtable_copy_node(&dest->chain[i], &node); //we copy what we have, and overwrite in the later for loop
257 hitmap[i] = EINA_TRUE;
258 }
259 for (int j = 0; j < src->chain[i].count; ++j)
260 {
261 if (src->chain[i].funcs[j].func)
262 dest->chain[i].funcs[j] = src->chain[i].funcs[j];
263 }
264 }
265 }
199 } 266 }
200} 267}
201 268
202static inline void 269/**
203_vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src) 270 * Ensure that all set nodes from src are also set on dest.
271 * No real values are copied, the newly taken or allocated slots will be empty.
272 */
273static void
274_vtable_merge_empty(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool *hitmap)
204{ 275{
205 Efl_Object_Op i; 276 for (unsigned int i = 0; i < src->size; ++i)
206 const Dich_Chain1 *sc1 = src->chain;
207 Dich_Chain1 *dc1 = dst->chain;
208 for (i = 0 ; i < src->size ; i++, sc1++, dc1++)
209 { 277 {
210 if (sc1->chain2) 278 if (src->chain[i].funcs && !dest->chain[i].funcs)
211 { 279 {
212 _vtable_chain_copy_ref(dc1, sc1); 280 if (!src->chain[i].count)
281 {
282 dest->chain[i].funcs = src->chain[i].funcs;
283 dest->chain[i].count = src->chain[i].count;
284 }
285 else
286 {
287 _vtable_prepare_empty_node(dest, src->chain[i].count, i);
288 hitmap[i] = EINA_TRUE;
289 }
213 } 290 }
214 } 291 }
215} 292}
@@ -217,37 +294,15 @@ _vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src)
217static inline const op_type_funcs * 294static inline const op_type_funcs *
218_vtable_func_get(const Eo_Vtable *vtable, Efl_Object_Op op) 295_vtable_func_get(const Eo_Vtable *vtable, Efl_Object_Op op)
219{ 296{
220 size_t idx1 = DICH_CHAIN1(op); 297 unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
221 if (EINA_UNLIKELY(idx1 >= vtable->size)) 298 unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
222 return NULL;
223 Dich_Chain1 *chain1 = &vtable->chain[idx1];
224 if (EINA_UNLIKELY(!chain1->chain2))
225 return NULL;
226 return &chain1->chain2->funcs[DICH_CHAIN_LAST(op)];
227}
228
229/* XXX: Only used for a debug message below. Doesn't matter that it's slow. */
230static const _Efl_Class *
231_eo_op_class_get(Efl_Object_Op op)
232{
233 _Efl_Class **itr = _eo_classes;
234 int mid, max, min;
235
236 min = 0;
237 max = _eo_classes_last_id - 1;
238 while (min <= max)
239 {
240 mid = (min + max) / 2;
241 299
242 if (itr[mid]->base_id + itr[mid]->ops_count < op) 300 if (EINA_UNLIKELY(vtable->size <= class_id))
243 min = mid + 1; 301 return NULL;
244 else if (itr[mid]->base_id > op) 302 if (EINA_UNLIKELY(vtable->chain[class_id].count <= func_id))
245 max = mid - 1; 303 return NULL;
246 else
247 return itr[mid];
248 }
249 304
250 return NULL; 305 return &vtable->chain[class_id].funcs[func_id];
251} 306}
252 307
253static inline Eina_Bool 308static inline Eina_Bool
@@ -256,24 +311,30 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class *klass,
256 Eo_Op_Func_Type func, Eina_Bool allow_same_override) 311 Eo_Op_Func_Type func, Eina_Bool allow_same_override)
257{ 312{
258 op_type_funcs *fsrc; 313 op_type_funcs *fsrc;
259 size_t idx1 = DICH_CHAIN1(op); 314 unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
260 Dich_Chain1 *chain1; 315 unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
316 Eo_Vtable_Node *hirachy_node = NULL;
317 Eo_Vtable_Node *node = NULL;
318
319 EINA_SAFETY_ON_FALSE_RETURN_VAL(vtable->size >= class_id, EINA_FALSE);
261 320
262 EINA_SAFETY_ON_FALSE_RETURN_VAL(idx1 < vtable->size, EINA_FALSE); 321 if (klass->parent && klass->parent->vtable.size > class_id)
263 chain1 = &vtable->chain[idx1]; 322 hirachy_node = &klass->parent->vtable.chain[class_id];
264 _vtable_chain_write_prepare(chain1);
265 fsrc = &chain1->chain2->funcs[DICH_CHAIN_LAST(op)];
266 if (hierarchy_klass) 323 if (hierarchy_klass)
324 hirachy_node = &hierarchy_klass->vtable.chain[class_id];
325 node = &vtable->chain[class_id];
326
327 EINA_SAFETY_ON_NULL_RETURN_VAL(node->funcs, EINA_FALSE);
328 EINA_SAFETY_ON_FALSE_RETURN_VAL(node->count >= func_id, EINA_FALSE);
329 fsrc = &node->funcs[func_id];
330
331 if (hierarchy_klass && !func)
267 { 332 {
268 if (!func) 333 if (!func)
269 { 334 {
270 op_type_funcs *fsrc_orig; 335 op_type_funcs funcs = hirachy_node->funcs[func_id];
271 Dich_Chain1 *chain1_orig; 336 klass = funcs.src;
272 337 func = funcs.func;
273 chain1_orig = &hierarchy_klass->vtable.chain[idx1];
274 fsrc_orig = &chain1_orig->chain2->funcs[DICH_CHAIN_LAST(op)];
275 func = fsrc_orig->func;
276 klass = fsrc_orig->src;
277 } 338 }
278 } 339 }
279 else 340 else
@@ -287,27 +348,12 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class *klass,
287 } 348 }
288 } 349 }
289 350
290 fsrc->func = func;
291 fsrc->src = klass; 351 fsrc->src = klass;
352 fsrc->func = func;
292 353
293 return EINA_TRUE; 354 return EINA_TRUE;
294} 355}
295 356
296void
297_vtable_func_clean_all(Eo_Vtable *vtable)
298{
299 size_t i;
300 Dich_Chain1 *chain1 = vtable->chain;
301
302 for (i = 0 ; i < vtable->size ; i++, chain1++)
303 {
304 if (chain1->chain2)
305 _vtable_chain2_unref(chain1->chain2);
306 }
307 free(vtable->chain);
308 vtable->chain = NULL;
309}
310
311/* END OF DICH */ 357/* END OF DICH */
312 358
313#define _EO_ID_GET(Id) ((Eo_Id) (Id)) 359#define _EO_ID_GET(Id) ((Eo_Id) (Id))
@@ -478,7 +524,7 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
478 524
479 obj = _obj; 525 obj = _obj;
480 klass = _obj->klass; 526 klass = _obj->klass;
481 vtable = EO_VTABLE(obj); 527 vtable = EO_VTABLE2(obj);
482 if (EINA_UNLIKELY(_obj->cur_klass != NULL)) 528 if (EINA_UNLIKELY(_obj->cur_klass != NULL))
483 { 529 {
484 // YES this is a goto with a label to return. this is a 530 // YES this is a goto with a label to return. this is a
@@ -544,7 +590,7 @@ end:
544 EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj); 590 EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj);
545 if (EINA_UNLIKELY(!emb_obj)) continue; 591 if (EINA_UNLIKELY(!emb_obj)) continue;
546 592
547 func = _vtable_func_get(EO_VTABLE(emb_obj), op); 593 func = _vtable_func_get(&emb_obj->klass->vtable, op);
548 if (func == NULL) goto composite_continue; 594 if (func == NULL) goto composite_continue;
549 595
550 if (EINA_LIKELY(func->func && func->src)) 596 if (EINA_LIKELY(func->func && func->src))
@@ -727,23 +773,25 @@ _efl_object_op_api_id_get(const void *api_func, const Eo *eo_obj, const char *ap
727/* klass is the klass we are working on. hierarchy_klass is the class whe should 773/* klass is the klass we are working on. hierarchy_klass is the class whe should
728 * use when validating. */ 774 * use when validating. */
729static Eina_Bool 775static Eina_Bool
730_eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Class *hierarchy_klass, const _Efl_Class *klass, Efl_Object_Op id_offset, Eina_Bool override_only) 776_eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Class *hierarchy_klass, const _Efl_Class *klass, Eina_Bool override_only, unsigned int class_id, Eina_Bool *hitmap)
731{ 777{
732 unsigned int i, j; 778 unsigned int i, j;
733 Efl_Object_Op op_id; 779 unsigned int number_of_new_functions = 0;
734 const Efl_Op_Description *op_desc; 780 const Efl_Op_Description *op_desc;
735 const Efl_Op_Description *op_descs; 781 const Efl_Op_Description *op_descs;
736 const _Efl_Class *override_class; 782 const _Efl_Class *override_class;
737 const void **api_funcs; 783 const void **api_funcs;
738 Eina_Bool check_equal; 784 Eina_Bool check_equal;
739 785
740 op_id = hierarchy_klass->base_id + id_offset;
741 op_descs = ops->descs; 786 op_descs = ops->descs;
742 override_class = override_only ? hierarchy_klass : NULL; 787 override_class = override_only ? hierarchy_klass : NULL;
743 788
744 DBG("Set functions for class '%s':%p", klass->desc->name, klass); 789 DBG("Set functions for class '%s':%p", klass->desc->name, klass);
745 790
746 if (!op_descs || !ops->count) return EINA_TRUE; 791 if (!override_only)
792 _vtable_insert_empty_funcs(vtable, class_id);
793 if (!op_descs || !ops->count)
794 return EINA_TRUE;
747 795
748#ifdef EO_DEBUG 796#ifdef EO_DEBUG
749 check_equal = EINA_TRUE; 797 check_equal = EINA_TRUE;
@@ -776,43 +824,61 @@ _eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Cla
776 824
777 api_funcs[i] = op_desc->api_func; 825 api_funcs[i] = op_desc->api_func;
778 } 826 }
827 if (_efl_object_api_op_id_get_internal(op_desc->api_func) == EFL_NOOP)
828 {
829 number_of_new_functions ++;
830 }
779 } 831 }
780 832
781 for (i = 0, op_desc = op_descs; i < ops->count; i++, op_desc++) 833 if (!override_only)
782 { 834 {
783 Efl_Object_Op op = EFL_NOOP; 835 //Before setting any real functions, allocate the node that will contain all the functions
836 _vtable_prepare_empty_node(vtable, number_of_new_functions, class_id);
837 hitmap[class_id] = EINA_TRUE;
838 }
839
840 for (i = 0, j = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
841 {
842 Efl_Object_Op op2 = EFL_NOOP;
843 short op2_class_id;
784 844
785 /* Get the opid for the function. */ 845 /* Get the opid for the function. */
786 op = _efl_object_api_op_id_get_internal(op_desc->api_func); 846 op2 = _efl_object_api_op_id_get_internal(op_desc->api_func);
787 847
788 if (op == EFL_NOOP) 848 if (op2 == EFL_NOOP)
789 { 849 {
850 //functions that do not have a op yet, are considered to be belonging to this class
790 if (override_only) 851 if (override_only)
791 { 852 {
792 ERR("Class '%s': Tried overriding a previously undefined function.", klass->desc->name); 853 ERR("Class '%s': Tried overriding a previously undefined function.", klass->desc->name);
793 return EINA_FALSE; 854 return EINA_FALSE;
794 } 855 }
795 856
796 op = op_id; 857 op2 = EFL_OBJECT_OP_CREATE_OP_ID(class_id, j);
797 eina_spinlock_take(&_ops_storage_lock); 858 eina_spinlock_take(&_ops_storage_lock);
798#ifndef _WIN32 859#ifndef _WIN32
799 eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op); 860 eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op2);
800#else 861#else
801 eina_hash_add(_ops_storage, op_desc->api_func, (void *) (uintptr_t) op); 862 eina_hash_add(_ops_storage, op_desc->api_func, (void *) (uintptr_t) op2);
802#endif 863#endif
803 eina_spinlock_release(&_ops_storage_lock); 864 eina_spinlock_release(&_ops_storage_lock);
804 865 j ++;
805 op_id++;
806 } 866 }
807 867
808#ifdef EO_DEBUG 868#ifdef EO_DEBUG
809 DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc)); 869 DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
810#endif 870#endif
811 871 op2_class_id = EFL_OBJECT_OP_CLASS_PART(op2);
812 if (!_vtable_func_set(vtable, klass, override_class, op, op_desc->func, EINA_TRUE)) 872 //in case we are having a function overwrite for a specific type, copy the relevant vtable
873 if (!hitmap[op2_class_id])
874 {
875 const Eo_Vtable_Node node = vtable->chain[op2_class_id];
876 _vtable_copy_node(&vtable->chain[op2_class_id], &node);
877 hitmap[op2_class_id] = EINA_TRUE;
878 }
879 if (!_vtable_func_set(vtable, klass, override_class, op2, op_desc->func, EINA_TRUE))
813 return EINA_FALSE; 880 return EINA_FALSE;
814 } 881 }
815
816 return EINA_TRUE; 882 return EINA_TRUE;
817} 883}
818 884
@@ -821,6 +887,7 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
821{ 887{
822 EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass); 888 EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass);
823 Efl_Object_Ops empty_ops = { 0 }; 889 Efl_Object_Ops empty_ops = { 0 };
890 Eina_Bool *hitmap;
824 891
825 // not likely so use goto to alleviate l1 instruction cache of rare code 892 // not likely so use goto to alleviate l1 instruction cache of rare code
826 if (klass->functions_set) goto err_funcs; 893 if (klass->functions_set) goto err_funcs;
@@ -832,11 +899,12 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
832 899
833 klass->ops_count = object_ops->count; 900 klass->ops_count = object_ops->count;
834 901
835 klass->base_id = _eo_ops_last_id; 902 klass->class_id = _UNMASK_ID(klass->header.id) - 1;
836 _eo_ops_last_id += klass->ops_count + 1;
837 903
838 _vtable_init(&klass->vtable, DICH_CHAIN1(_eo_ops_last_id) + 1); 904 _vtable_init(&klass->vtable);
839 905
906 hitmap = alloca(klass->vtable.size);
907 memset(hitmap, 0, klass->vtable.size);
840 /* Flatten the function array */ 908 /* Flatten the function array */
841 { 909 {
842 const _Efl_Class **mro_itr = klass->mro; 910 const _Efl_Class **mro_itr = klass->mro;
@@ -844,11 +912,44 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
844 912
845 /* Skip ourselves. */ 913 /* Skip ourselves. */
846 for ( mro_itr-- ; mro_itr > klass->mro ; mro_itr--) 914 for ( mro_itr-- ; mro_itr > klass->mro ; mro_itr--)
847 _vtable_copy_all(&klass->vtable, &(*mro_itr)->vtable); 915 {
916 _vtable_merge_defined_api(&klass->vtable, &(*mro_itr)->vtable, hitmap);
917 }
918 /*add slots for the interfaces we are inheriting from*/
919 for (int i = 0; klass->extensions[i]; i++)
920 {
921 const _Efl_Class *ext = klass->extensions[i];
922 /*for all extensions of the class, ensure that *at least* empty vtables are available, so the efl_isa calls do succeed*/
923 _vtable_merge_empty(&klass->vtable, &ext->vtable, hitmap);
924 }
848 } 925 }
926 {
927 unsigned int i;
849 928
850 return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, 0, EINA_FALSE); 929 for (i = 0; i < object_ops->count; i++)
930 {
931 Efl_Object_Op op = _efl_object_api_op_id_get_internal(object_ops->descs[i].api_func);
932 if (op == EFL_NOOP) continue; //EFL_NOOP means that this function is not yet defined, this will be handled later
933 short class_id = EFL_OBJECT_OP_CLASS_PART(op);
934 if (klass->vtable.chain[class_id].count == 0)
935 {
936 const _Efl_Class *required_klass = _eo_classes[class_id];
937 /* in case this type is not already inherited, error on everything that is not a mixin */
938 if (klass->desc->type == EFL_CLASS_TYPE_MIXIN)
939 {
940 /* this is when a mixin implemets a regular api, we just prepare a empty node, the rest will be implemented later */
941 _vtable_prepare_empty_node(&klass->vtable, required_klass->vtable.chain[class_id].count, class_id);
942 }
943 else
944 {
945 ERR("There is an API implemented, whoms type is not part of this class. %s vs. %s", klass->desc->name, required_klass->desc->name);
946 _vtable_take_over(&klass->vtable, &required_klass->vtable);
947 }
851 948
949 }
950 }
951 }
952 return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, EINA_FALSE, klass->class_id, hitmap);
852err_funcs: 953err_funcs:
853 ERR("Class %s already had its functions set..", klass->desc->name); 954 ERR("Class %s already had its functions set..", klass->desc->name);
854 return EINA_FALSE; 955 return EINA_FALSE;
@@ -1098,8 +1199,8 @@ _eo_free(_Eo_Object *obj, Eina_Bool manual_free EINA_UNUSED)
1098#endif 1199#endif
1099 if (_obj_is_override(obj)) 1200 if (_obj_is_override(obj))
1100 { 1201 {
1101 _vtable_func_clean_all(obj->opt->vtable); 1202 if (obj->opt)
1102 eina_freeq_ptr_main_add(obj->opt->vtable, free, 0); 1203 _vtable_free(obj->opt->vtable, &obj->klass->vtable);
1103 EO_OPTIONAL_COW_SET(obj, vtable, NULL); 1204 EO_OPTIONAL_COW_SET(obj, vtable, NULL);
1104 } 1205 }
1105 1206
@@ -1190,22 +1291,6 @@ err_obj:
1190 return 0; 1291 return 0;
1191} 1292}
1192 1293
1193
1194static void
1195_vtable_init(Eo_Vtable *vtable, size_t size)
1196{
1197 vtable->size = size;
1198 vtable->chain = calloc(vtable->size, sizeof(*vtable->chain));
1199}
1200
1201static void
1202_vtable_free(Eo_Vtable *vtable)
1203{
1204 if (!vtable) return;
1205 _vtable_func_clean_all(vtable);
1206 eina_freeq_ptr_main_add(vtable, free, sizeof(*vtable));
1207}
1208
1209static Eina_Bool 1294static Eina_Bool
1210_eo_class_mro_has(const _Efl_Class *klass, const _Efl_Class *find) 1295_eo_class_mro_has(const _Efl_Class *klass, const _Efl_Class *find)
1211{ 1296{
@@ -1360,8 +1445,8 @@ eo_class_free(_Efl_Class *klass)
1360 { 1445 {
1361 if (klass->desc->class_destructor) 1446 if (klass->desc->class_destructor)
1362 klass->desc->class_destructor(_eo_class_id_get(klass)); 1447 klass->desc->class_destructor(_eo_class_id_get(klass));
1363 1448 _vtable_mro_free(klass);
1364 _vtable_func_clean_all(&klass->vtable); 1449 _vtable_free(&klass->vtable, NULL);
1365 } 1450 }
1366 1451
1367 EINA_TRASH_CLEAN(&klass->objects.trash, data) 1452 EINA_TRASH_CLEAN(&klass->objects.trash, data)
@@ -1376,32 +1461,6 @@ eo_class_free(_Efl_Class *klass)
1376 eina_freeq_ptr_main_add(klass, free, 0); 1461 eina_freeq_ptr_main_add(klass, free, 0);
1377} 1462}
1378 1463
1379/* Not really called, just used for the ptr... */
1380static void
1381_eo_class_isa_func(Eo *eo_id EINA_UNUSED, void *class_data EINA_UNUSED)
1382{
1383 /* Do nonthing. */
1384}
1385
1386static void
1387_eo_class_isa_recursive_set(_Efl_Class *klass, const _Efl_Class *cur)
1388{
1389 const _Efl_Class **extn_itr;
1390
1391 _vtable_func_set(&klass->vtable, klass, NULL, cur->base_id + cur->ops_count,
1392 _eo_class_isa_func, EINA_TRUE);
1393
1394 for (extn_itr = cur->extensions ; *extn_itr ; extn_itr++)
1395 {
1396 _eo_class_isa_recursive_set(klass, *extn_itr);
1397 }
1398
1399 if (cur->parent)
1400 {
1401 _eo_class_isa_recursive_set(klass, cur->parent);
1402 }
1403}
1404
1405static inline void 1464static inline void
1406_eo_classes_release(void) 1465_eo_classes_release(void)
1407{ 1466{
@@ -1710,12 +1769,6 @@ efl_class_new(const Efl_Class_Description *desc, const Efl_Class *parent_id, ...
1710 efl_class_functions_set(_eo_class_id_get(klass), NULL, NULL); 1769 efl_class_functions_set(_eo_class_id_get(klass), NULL, NULL);
1711 } 1770 }
1712 1771
1713 /* Mark which classes we implement */
1714 if (klass->vtable.size)
1715 {
1716 _eo_class_isa_recursive_set(klass, klass);
1717 }
1718
1719 _eo_class_constructor(klass); 1772 _eo_class_constructor(klass);
1720 1773
1721 DBG("Finished building class '%s'", klass->desc->name); 1774 DBG("Finished building class '%s'", klass->desc->name);
@@ -1732,32 +1785,41 @@ efl_object_override(Eo *eo_id, const Efl_Object_Ops *ops)
1732 if (ops) 1785 if (ops)
1733 { 1786 {
1734 Eo_Vtable *vtable = obj->opt->vtable; 1787 Eo_Vtable *vtable = obj->opt->vtable;
1788 //copy all the vtable nodes that we are going to change later on
1789 Eina_Bool *hitmap;
1735 1790
1736 if (!vtable) 1791 if (!vtable)
1737 { 1792 {
1738 vtable = calloc(1, sizeof(*vtable)); 1793 vtable = calloc(1, sizeof(*vtable));
1739 _vtable_init(vtable, obj->klass->vtable.size); 1794 _vtable_init(vtable);
1740 _vtable_copy_all(vtable, &obj->klass->vtable); 1795 _vtable_take_over(vtable, &obj->klass->vtable);
1741 } 1796 }
1742 1797
1743 if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, 0, EINA_TRUE)) 1798 hitmap = alloca(vtable->size * sizeof(Eina_Bool));
1799 memset(hitmap, 0, vtable->size);
1800
1801 if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, EINA_TRUE, obj->klass->class_id, hitmap))
1744 { 1802 {
1745 ERR("Failed to override functions for %s@%p. All previous " 1803 ERR("Failed to override functions for %s@%p. All previous "
1746 "overrides have been reset.", obj->klass->desc->name, eo_id); 1804 "overrides have been reset.", obj->klass->desc->name, eo_id);
1747 if (obj->opt->vtable == vtable) 1805 if (obj->opt->vtable == vtable)
1748 EO_OPTIONAL_COW_SET(obj, vtable, NULL); 1806 {
1807 EO_OPTIONAL_COW_SET(obj, vtable, NULL);
1808 }
1749 else 1809 else
1750 _vtable_free(vtable); 1810 {
1811 _vtable_free(vtable, &obj->klass->vtable);
1812 }
1813
1751 goto err; 1814 goto err;
1752 } 1815 }
1753
1754 EO_OPTIONAL_COW_SET(obj, vtable, vtable); 1816 EO_OPTIONAL_COW_SET(obj, vtable, vtable);
1755 } 1817 }
1756 else 1818 else
1757 { 1819 {
1758 if (obj->opt->vtable) 1820 if (obj->opt->vtable)
1759 { 1821 {
1760 _vtable_free(obj->opt->vtable); 1822 _vtable_free(obj->opt->vtable, &obj->klass->vtable);
1761 EO_OPTIONAL_COW_SET(obj, vtable, NULL); 1823 EO_OPTIONAL_COW_SET(obj, vtable, NULL);
1762 } 1824 }
1763 } 1825 }
@@ -1791,10 +1853,10 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
1791 EO_CLASS_POINTER_GOTO(klass_id, klass, err_class); 1853 EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
1792 EO_CLASS_POINTER_GOTO(eo_id, lookinto, err_class0); 1854 EO_CLASS_POINTER_GOTO(eo_id, lookinto, err_class0);
1793 1855
1794 const op_type_funcs *func = _vtable_func_get 1856 if (EINA_UNLIKELY(lookinto->vtable.size <= klass->class_id))
1795 (&lookinto->vtable, klass->base_id + klass->ops_count); 1857 return EINA_FALSE;
1796 1858
1797 return (func && (func->func == _eo_class_isa_func));; 1859 return !!lookinto->vtable.chain[klass->class_id].funcs;
1798 } 1860 }
1799 1861
1800 domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN; 1862 domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
@@ -1813,15 +1875,17 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
1813 1875
1814 EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj); 1876 EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj);
1815 EO_CLASS_POINTER_GOTO(klass_id, klass, err_class); 1877 EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
1816 const op_type_funcs *func = _vtable_func_get 1878
1817 (EO_VTABLE(obj), klass->base_id + klass->ops_count); 1879 const Eo_Vtable vtable = obj->klass->vtable;
1880 if (EINA_UNLIKELY(vtable.size <= klass->class_id))
1881 return EINA_FALSE;
1882
1883 isa = !!vtable.chain[klass->class_id].funcs;
1818 1884
1819 // Caching the result as we do a lot of serial efl_isa due to evas_object_image using it. 1885 // Caching the result as we do a lot of serial efl_isa due to evas_object_image using it.
1820 tdata->cache.isa_id = eo_id; 1886 tdata->cache.isa_id = eo_id;
1821 tdata->cache.klass = klass_id; 1887 tdata->cache.klass = klass_id;
1822 // Currently implemented by reusing the LAST op id. Just marking it with 1888 tdata->cache.isa = isa;
1823 // _eo_class_isa_func.
1824 isa = tdata->cache.isa = (func && (func->func == _eo_class_isa_func));
1825 } 1889 }
1826 else 1890 else
1827 { 1891 {
@@ -1841,15 +1905,15 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
1841 1905
1842 EO_OBJ_POINTER_GOTO(eo_id, obj, err_shared_obj); 1906 EO_OBJ_POINTER_GOTO(eo_id, obj, err_shared_obj);
1843 EO_CLASS_POINTER_GOTO(klass_id, klass, err_shared_class); 1907 EO_CLASS_POINTER_GOTO(klass_id, klass, err_shared_class);
1844 const op_type_funcs *func = _vtable_func_get 1908 if (EINA_UNLIKELY(obj->klass->vtable.size <= klass->class_id))
1845 (EO_VTABLE(obj), klass->base_id + klass->ops_count); 1909 return EINA_FALSE;
1910
1911 isa = !!obj->klass->vtable.chain[klass->class_id].funcs;
1846 1912
1847 // Caching the result as we do a lot of serial efl_isa due to evas_object_image using it. 1913 // Caching the result as we do a lot of serial efl_isa due to evas_object_image using it.
1848 tdata->cache.isa_id = eo_id; 1914 tdata->cache.isa_id = eo_id;
1849 tdata->cache.klass = klass_id; 1915 tdata->cache.klass = klass_id;
1850 // Currently implemented by reusing the LAST op id. Just marking it with 1916 tdata->cache.isa = isa;
1851 // _eo_class_isa_func.
1852 isa = tdata->cache.isa = (func && (func->func == _eo_class_isa_func));
1853 EO_OBJ_DONE(eo_id); 1917 EO_OBJ_DONE(eo_id);
1854 eina_lock_release(&(_eo_table_data_shared_data->obj_lock)); 1918 eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
1855 } 1919 }
@@ -2326,7 +2390,6 @@ efl_object_init(void)
2326 2390
2327 _eo_classes = NULL; 2391 _eo_classes = NULL;
2328 _eo_classes_last_id = EO_CLASS_IDS_FIRST - 1; 2392 _eo_classes_last_id = EO_CLASS_IDS_FIRST - 1;
2329 _eo_ops_last_id = EFL_OBJECT_OP_IDS_FIRST;
2330 _eo_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_LIGHTBLUE); 2393 _eo_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_LIGHTBLUE);
2331 if (_eo_log_dom < 0) 2394 if (_eo_log_dom < 0)
2332 { 2395 {
@@ -2384,12 +2447,6 @@ efl_object_init(void)
2384 eina_tls_set(_eo_table_data, data); 2447 eina_tls_set(_eo_table_data, data);
2385 _efl_object_main_thread = eina_thread_self(); 2448 _efl_object_main_thread = eina_thread_self();
2386 2449
2387#ifdef EO_DEBUG
2388 /* Call it just for coverage purposes. Ugly I know, but I like it better than
2389 * casting everywhere else. */
2390 _eo_class_isa_func(NULL, NULL);
2391#endif
2392
2393 efl_object_optional_cow = 2450 efl_object_optional_cow =
2394 eina_cow_add("Efl Object Optional Data", sizeof(Efl_Object_Optional), 2451 eina_cow_add("Efl Object Optional Data", sizeof(Efl_Object_Optional),
2395 64, &efl_object_optional_cow_default, EINA_TRUE); 2452 64, &efl_object_optional_cow_default, EINA_TRUE);
diff --git a/src/lib/eo/eo_private.h b/src/lib/eo/eo_private.h
index c64dee5f5e..2c902d8456 100644
--- a/src/lib/eo/eo_private.h
+++ b/src/lib/eo/eo_private.h
@@ -70,17 +70,14 @@ static inline void _eo_id_release(const Eo_Id obj_id);
70 70
71void _eo_condtor_done(Eo *obj); 71void _eo_condtor_done(Eo *obj);
72 72
73typedef struct _Dich_Chain1 Dich_Chain1; 73typedef struct _Eo_Vtable_Node Eo_Vtable_Node;
74 74
75typedef struct _Eo_Vtable 75typedef struct _Eo_Vtable
76{ 76{
77 Dich_Chain1 *chain; 77 Eo_Vtable_Node *chain;
78 unsigned int size; 78 unsigned short size;
79} Eo_Vtable; 79} Eo_Vtable;
80 80
81/* Clean the vtable. */
82void _vtable_func_clean_all(Eo_Vtable *vtable);
83
84struct _Eo_Header 81struct _Eo_Header
85{ 82{
86 Eo_Id id; 83 Eo_Id id;
@@ -88,7 +85,7 @@ struct _Eo_Header
88 85
89struct _Efl_Object_Optional 86struct _Efl_Object_Optional
90{ 87{
91 Eo_Vtable *vtable; 88 Eo_Vtable *vtable;
92 Eina_List *composite_objects; 89 Eina_List *composite_objects;
93 Efl_Del_Intercept del_intercept; 90 Efl_Del_Intercept del_intercept;
94}; 91};
@@ -129,12 +126,6 @@ struct _Eo_Object
129 Eina_Bool ownership_track:1; 126 Eina_Bool ownership_track:1;
130}; 127};
131 128
132/* How we search and store the implementations in classes. */
133#define DICH_CHAIN_LAST_BITS 5
134#define DICH_CHAIN_LAST_SIZE (1 << DICH_CHAIN_LAST_BITS)
135#define DICH_CHAIN1(x) ((x) >> DICH_CHAIN_LAST_BITS)
136#define DICH_CHAIN_LAST(x) ((x) & ((1 << DICH_CHAIN_LAST_BITS) - 1))
137
138extern Eina_Cow *efl_object_optional_cow; 129extern Eina_Cow *efl_object_optional_cow;
139#define EO_OPTIONAL_COW_WRITE(_obj) ({ Efl_Object_Optional *_cow = eina_cow_write(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt)); _cow; }) 130#define EO_OPTIONAL_COW_WRITE(_obj) ({ Efl_Object_Optional *_cow = eina_cow_write(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt)); _cow; })
140#define EO_OPTIONAL_COW_END(_cow, _obj) eina_cow_done(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt), _cow, EINA_TRUE) 131#define EO_OPTIONAL_COW_END(_cow, _obj) eina_cow_done(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt), _cow, EINA_TRUE)
@@ -146,6 +137,7 @@ extern Eina_Cow *efl_object_optional_cow;
146 EO_OPTIONAL_COW_END(_obj##_cow, _obj); \ 137 EO_OPTIONAL_COW_END(_obj##_cow, _obj); \
147 }} while (0) 138 }} while (0)
148#define EO_VTABLE(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable)) 139#define EO_VTABLE(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
140#define EO_VTABLE2(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
149 141
150typedef void (*Eo_Op_Func_Type)(Eo *, void *class_data); 142typedef void (*Eo_Op_Func_Type)(Eo *, void *class_data);
151 143
@@ -155,15 +147,9 @@ typedef struct
155 const _Efl_Class *src; 147 const _Efl_Class *src;
156} op_type_funcs; 148} op_type_funcs;
157 149
158typedef struct _Dich_Chain2 150struct _Eo_Vtable_Node{
159{ 151 op_type_funcs *funcs;
160 op_type_funcs funcs[DICH_CHAIN_LAST_SIZE]; 152 unsigned short count;
161 unsigned short refcount;
162} Dich_Chain2;
163
164struct _Dich_Chain1
165{
166 Dich_Chain2 *chain2;
167}; 153};
168 154
169typedef struct 155typedef struct
@@ -203,7 +189,7 @@ struct _Efl_Class
203 } iterators; 189 } iterators;
204 190
205 unsigned int obj_size; /**< size of an object of this class */ 191 unsigned int obj_size; /**< size of an object of this class */
206 unsigned int base_id; 192 unsigned int class_id; /**< the id which can be used to find the slot in _eo_classes and vtables chains */
207 unsigned int data_offset; /* < Offset of the data within object data. */ 193 unsigned int data_offset; /* < Offset of the data within object data. */
208 unsigned int ops_count; /* < Offset of the data within object data. */ 194 unsigned int ops_count; /* < Offset of the data within object data. */
209 195
diff --git a/src/tests/eo/suite/eo_test_general.c b/src/tests/eo/suite/eo_test_general.c
index 1494f46c95..ae026a27f4 100644
--- a/src/tests/eo/suite/eo_test_general.c
+++ b/src/tests/eo/suite/eo_test_general.c
@@ -329,7 +329,6 @@ EFL_END_TEST
329 329
330EFL_START_TEST(efl_data_safe_fetch) 330EFL_START_TEST(efl_data_safe_fetch)
331{ 331{
332
333 Eo *obj = efl_add_ref(SIMPLE2_CLASS, NULL); 332 Eo *obj = efl_add_ref(SIMPLE2_CLASS, NULL);
334 fail_if(!obj || !efl_data_scope_safe_get(obj, SIMPLE2_CLASS)); 333 fail_if(!obj || !efl_data_scope_safe_get(obj, SIMPLE2_CLASS));
335 efl_unref(obj); 334 efl_unref(obj);