evas: Add some safety code to evas_clip

I'm trying to fix a crash that seems to happens in some very odd
circumstances under stress testing. I have absolutely no idea
what is going wrong... So let's just add some extra safety.
This commit is contained in:
Jean-Philippe Andre 2016-11-02 19:51:20 +09:00
parent 7f57e23670
commit d0333561be
3 changed files with 91 additions and 104 deletions

View File

@ -29,6 +29,7 @@ evas_object_recalc_clippees(Evas_Object_Protected_Data *obj)
Evas_Object_Protected_Data *clipee;
Eina_List *l;
EVAS_OBJECT_DATA_VALID_CHECK(obj);
if (obj->cur->cache.clip.dirty)
{
evas_object_clip_recalc(obj);
@ -193,7 +194,8 @@ evas_object_mapped_clip_across_mark(Evas_Object *eo_obj, Evas_Object_Protected_D
static void
_efl_canvas_object_clip_mask_unset(Evas_Object_Protected_Data *obj)
{
if (!obj || !obj->mask->is_mask) return;
EVAS_OBJECT_DATA_VALID_CHECK(obj);
if (!obj->mask->is_mask) return;
if (obj->clip.clipees) return;
/* this frees the clip surface. is this correct? */
@ -258,19 +260,77 @@ err_type:
return EINA_TRUE;
}
static inline void
_efl_canvas_object_clip_unset_common(Evas_Object_Protected_Data *obj, Eina_Bool warn)
{
Evas_Object_Protected_Data *clip = obj->cur->clipper;
if (!clip) return;
if (EVAS_OBJECT_DATA_VALID(clip))
{
clip->clip.cache_clipees_answer = eina_list_free(clip->clip.cache_clipees_answer);
clip->clip.clipees = eina_list_remove(clip->clip.clipees, obj);
if (!clip->clip.clipees)
{
EINA_COW_STATE_WRITE_BEGIN(clip, state_write, cur)
{
state_write->have_clipees = 0;
if (warn && clip->is_static_clip)
{
WRN("You override static clipper, it may be dangled! "
"obj(%p) type(%s) new clip(%p)",
obj->object, obj->type, clip->object);
}
}
EINA_COW_STATE_WRITE_END(clip, state_write, cur);
/* i know this was to handle a case where a clip stops having
* children and becomes a solid colored box - no one ever does
* that... they hide the clip so dont add damages,
* But, if the clipper could affect color to its clipees, the
* clipped area should be redrawn. */
if (((clip->cur) && (clip->cur->visible)) &&
(((clip->cur->color.r != 255) || (clip->cur->color.g != 255) ||
(clip->cur->color.b != 255) || (clip->cur->color.a != 255)) ||
(clip->mask->is_mask)))
{
if (clip->layer)
{
Evas_Public_Data *e = clip->layer->evas;
evas_damage_rectangle_add(e->evas,
clip->cur->geometry.x + e->framespace.x,
clip->cur->geometry.y + e->framespace.y,
clip->cur->geometry.w,
clip->cur->geometry.h);
}
}
_efl_canvas_object_clip_mask_unset(clip);
}
evas_object_change(clip->object, clip);
if (obj->prev->clipper != clip)
efl_event_callback_del(clip->object, EFL_EVENT_DEL, _clipper_del_cb, obj->object);
}
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
state_write->clipper = NULL;
EINA_COW_STATE_WRITE_END(obj, state_write, cur);
}
EOLIAN void
_efl_canvas_object_clip_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Object *eo_clip)
{
Evas_Object_Protected_Data *clip;
Evas_Public_Data *e;
if (!eo_clip)
EVAS_OBJECT_DATA_ALIVE_CHECK(obj);
clip = EVAS_OBJECT_DATA_SAFE_GET(eo_clip);
if (!EVAS_OBJECT_DATA_ALIVE(clip))
{
_clip_unset(eo_obj, obj);
return;
}
clip = efl_data_scope_get(eo_clip, EFL_CANVAS_OBJECT_CLASS);
if (_efl_canvas_object_clip_set_block(eo_obj, obj, eo_clip, clip)) return;
if (_evas_object_intercept_call(eo_obj, EVAS_OBJECT_INTERCEPT_CB_CLIP_SET, 1, eo_clip)) return;
@ -279,54 +339,9 @@ _efl_canvas_object_clip_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Ob
{
obj->smart.smart->smart_class->clip_set(eo_obj, eo_clip);
}
if (obj->cur->clipper)
{
Evas_Object_Protected_Data *old_clip = obj->cur->clipper;
/* unclip */
obj->cur->clipper->clip.cache_clipees_answer = eina_list_free(obj->cur->clipper->clip.cache_clipees_answer);
obj->cur->clipper->clip.clipees = eina_list_remove(obj->cur->clipper->clip.clipees, obj);
if (!obj->cur->clipper->clip.clipees)
{
EINA_COW_STATE_WRITE_BEGIN(obj->cur->clipper, state_write, cur)
{
state_write->have_clipees = 0;
if (obj->cur->clipper->is_static_clip)
WRN("You override static clipper, it may be dangled! obj(%p) type(%s) new clip(%p)", eo_obj, obj->type, eo_clip);
}
EINA_COW_STATE_WRITE_END(obj->cur->clipper, state_write, cur);
/* i know this was to handle a case where a clip stops having children and
* becomes a solid colored box - no one ever does that... they hide the clip
* so dont add damages.
* But, if the clipper could affect color to its clipees,
* the clipped area should be redrawn. */
if (((obj->cur->clipper->cur) && (obj->cur->clipper->cur->visible)) &&
(((obj->cur->clipper->cur->color.r != 255) || (obj->cur->clipper->cur->color.g != 255) ||
(obj->cur->clipper->cur->color.b != 255) || (obj->cur->clipper->cur->color.a != 255)) ||
(obj->cur->clipper->mask->is_mask)))
{
if (obj->cur->clipper->layer)
{
e = obj->cur->clipper->layer->evas;
evas_damage_rectangle_add(e->evas,
obj->cur->clipper->cur->geometry.x + e->framespace.x,
obj->cur->clipper->cur->geometry.y + e->framespace.y,
obj->cur->clipper->cur->geometry.w,
obj->cur->clipper->cur->geometry.h);
}
}
_efl_canvas_object_clip_mask_unset(obj->cur->clipper);
}
evas_object_change(obj->cur->clipper->object, obj->cur->clipper);
evas_object_change(eo_obj, obj);
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
state_write->clipper = NULL;
EINA_COW_STATE_WRITE_END(obj, state_write, cur);
if (obj->prev->clipper != old_clip)
efl_event_callback_del(old_clip->object, EFL_EVENT_DEL, _clipper_del_cb, eo_obj);
}
/* unset cur clipper */
_efl_canvas_object_clip_unset_common(obj, EINA_TRUE);
/* image object clipper */
if (clip->type == o_image_type)
@ -396,6 +411,7 @@ _efl_canvas_object_clip_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Ob
EOLIAN Evas_Object *
_efl_canvas_object_clip_get(Eo *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj)
{
EVAS_OBJECT_DATA_ALIVE_CHECK(obj, NULL);
if (obj->cur->clipper)
return obj->cur->clipper->object;
return NULL;
@ -423,50 +439,7 @@ _clip_unset(Eo *eo_obj, Evas_Object_Protected_Data *obj)
{
obj->smart.smart->smart_class->clip_unset(eo_obj);
}
if (obj->cur->clipper)
{
Evas_Object_Protected_Data *old_clip = obj->cur->clipper;
obj->cur->clipper->clip.clipees = eina_list_remove(obj->cur->clipper->clip.clipees, obj);
if (!obj->cur->clipper->clip.clipees)
{
EINA_COW_STATE_WRITE_BEGIN(obj->cur->clipper, state_write, cur)
{
state_write->have_clipees = 0;
}
EINA_COW_STATE_WRITE_END(obj->cur->clipper, state_write, cur);
/* i know this was to handle a case where a clip stops having children and
* becomes a solid colored box - no one ever does that... they hide the clip
* so dont add damages.
* But, if the clipper could affect color to its clipees,
* the clipped area should be redrawn. */
if (((obj->cur->clipper->cur) && (obj->cur->clipper->cur->visible)) &&
(((obj->cur->clipper->cur->color.r != 255) || (obj->cur->clipper->cur->color.g != 255) ||
(obj->cur->clipper->cur->color.b != 255) || (obj->cur->clipper->cur->color.a != 255)) ||
(obj->cur->clipper->mask->is_mask)))
{
if (obj->cur->clipper->layer)
{
Evas_Public_Data *e = obj->cur->clipper->layer->evas;
evas_damage_rectangle_add(e->evas,
obj->cur->clipper->cur->geometry.x + e->framespace.x,
obj->cur->clipper->cur->geometry.y + e->framespace.y,
obj->cur->clipper->cur->geometry.w,
obj->cur->clipper->cur->geometry.h);
}
}
_efl_canvas_object_clip_mask_unset(obj->cur->clipper);
}
evas_object_change(obj->cur->clipper->object, obj->cur->clipper);
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, cur)
state_write->clipper = NULL;
EINA_COW_STATE_WRITE_END(obj, state_write, cur);
if (obj->prev->clipper != old_clip)
efl_event_callback_del(old_clip->object, EFL_EVENT_DEL, _clipper_del_cb, eo_obj);
}
_efl_canvas_object_clip_unset_common(obj, EINA_FALSE);
evas_object_update_bounding_box(eo_obj, obj, NULL);
evas_object_change(eo_obj, obj);
evas_object_clip_dirty(eo_obj, obj);
@ -489,10 +462,8 @@ _clip_unset(Eo *eo_obj, Evas_Object_Protected_Data *obj)
EAPI void
evas_object_clip_unset(Evas_Object *eo_obj)
{
Evas_Object_Protected_Data *obj;
if (!efl_isa(eo_obj, EFL_CANVAS_OBJECT_CLASS)) return;
obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
Evas_Object_Protected_Data *obj = EVAS_OBJECT_DATA_SAFE_GET(eo_obj);
EVAS_OBJECT_DATA_ALIVE_CHECK(obj);
_clip_unset(eo_obj, obj);
}
@ -501,11 +472,12 @@ _clipper_del_cb(void *data, const Efl_Event *event)
{
Evas_Object *eo_obj = data;
Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
Evas_Object_Protected_Data *clip = efl_data_scope_get(event->object, EFL_CANVAS_OBJECT_CLASS);
if (!obj) return;
EVAS_OBJECT_DATA_ALIVE_CHECK(obj);
_clip_unset(eo_obj, obj);
if (obj->prev->clipper && (obj->prev->clipper->object == event->object))
if (obj->prev->clipper && (obj->prev->clipper == clip))
{
// not removing cb since it's the del cb... it can't be called again!
EINA_COW_STATE_WRITE_BEGIN(obj, state_write, prev)

View File

@ -96,7 +96,7 @@ _evas_object_intercept_call(Evas_Object *eo_obj, Evas_Object_Intercept_Cb_Type c
int r, g, b, a, i, j;
va_list args;
if (!obj || obj->delete_me || !obj->layer) return 1;
EVAS_OBJECT_DATA_ALIVE_CHECK(obj, 1);
evas_object_async_block(obj);
va_start(args, internal);

View File

@ -3,6 +3,19 @@
#include "evas_private.h"
/* Paranoid safety checks.
* This can avoid lots of SEGV with dangling pointers to deleted objects.
* Two variants: valid or alive (extra check on delete_me).
*/
#define EVAS_OBJECT_DATA_VALID(o) ((o) && (o)->layer && (o)->layer->evas)
#define EVAS_OBJECT_DATA_ALIVE(o) (EVAS_OBJECT_DATA_VALID(o) && !(o)->delete_me)
#define EVAS_OBJECT_DATA_VALID_CHECK(o, ...) do { \
if (EINA_UNLIKELY(!EVAS_OBJECT_DATA_VALID(o))) return __VA_ARGS__; } while (0)
#define EVAS_OBJECT_DATA_ALIVE_CHECK(o, ...) do { \
if (EINA_UNLIKELY(!EVAS_OBJECT_DATA_ALIVE(o))) return __VA_ARGS__; } while (0)
#define EVAS_OBJECT_DATA_SAFE_GET(eo_o) \
(((eo_o) && efl_isa((eo_o), EFL_CANVAS_OBJECT_CLASS)) ? efl_data_scope_get((eo_o), EFL_CANVAS_OBJECT_CLASS) : NULL)
static inline Eina_Bool
_evas_render_has_map(Evas_Object_Protected_Data *obj)
{
@ -263,11 +276,13 @@ evas_object_clip_recalc(Evas_Object_Protected_Data *obj)
Eina_Bool cvis, nvis;
Evas_Object *eo_obj;
EVAS_OBJECT_DATA_ALIVE_CHECK(obj);
eo_obj = obj->object;
clipper = obj->cur->clipper;
if ((!obj->cur->cache.clip.dirty) &&
!(!obj->cur->clipper || clipper->cur->cache.clip.dirty)) return;
!(!clipper || clipper->cur->cache.clip.dirty)) return;
if (obj->layer->evas->is_frozen) return;
@ -309,7 +324,7 @@ evas_object_clip_recalc(Evas_Object_Protected_Data *obj)
cr = obj->cur->color.r; cg = obj->cur->color.g;
cb = obj->cur->color.b; ca = obj->cur->color.a;
if (clipper)
if (EVAS_OBJECT_DATA_VALID(clipper))
{
// this causes problems... hmmm ?????
evas_object_clip_recalc(clipper);
@ -397,7 +412,7 @@ evas_object_clip_recalc(Evas_Object_Protected_Data *obj)
static inline void
evas_object_async_block(Evas_Object_Protected_Data *obj)
{
if ((obj) && (obj->layer) && (obj->layer->evas))
if (EVAS_OBJECT_DATA_VALID(obj))
{
eina_lock_take(&(obj->layer->evas->lock_objects));
eina_lock_release(&(obj->layer->evas->lock_objects));