Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug ComponentTypeHandle.GlobalSystemVersion does not indicate if it's safe to use

Discussion in 'Entity Component System' started by half_voxel, Jan 10, 2023.

  1. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    TL;DR: The documentation for GlobalSystemVersion is either wrong, or the ECS package is buggy and doesn't update the GlobalSystemVersion when it should.

    I'm accessing a lot of entities outside systems. This is necessary to integrate with non-ecs code.

    For this, I'm using

    Code (CSharp):
    1. var handle = entityManager.GetComponentTypeHandle<T>(readOnly);
    To avoid updating this handle unnecessarily, I only do it when the GlobalSystemVersion indicates that this should be done:

    Code (CSharp):
    1. public void Update(EntityManager entityManager) {
    2.     if (readOnly) entityManager.CompleteDependencyBeforeRO<T>();
    3.     else entityManager.CompleteDependencyBeforeRW<T>();
    4.     if (handle.GlobalSystemVersion != entityManager.GlobalSystemVersion) {
    5.         handle = entityManager.GetComponentTypeHandle<T>(readOnly);
    6.     }
    7. }
    The documentation for ComponentTypeHandle<T>.GlobalSystemVersion states

    The global system version for which this handle is valid.
    Attempting to use this type handle with a different system version indicates that the handle is no longer valid; use the Update(SystemBase) method to incrementally update the version just before use.

    However, even though I check the GlobalSystemVersion, and use the handle immediately afterward:

    Code (CSharp):
    1. myHandleWrapper.Update(entityManager);
    2. myHandleWrapper.AccessTheEntity(entity);
    I often get exceptions on the form

    ObjectDisposedException: Attempted to access ComponentTypeHandle<T> which has been invalidated by a structural change.

    This indicates to me that the documentation is lying. The ComponentTypeHandle is in fact not safe to use even though the GlobalSystemVersion matches the GlobalSystemVersion of the entity manager. I would very much like to be able to cache the ComponentTypeHandle when possible, as creating a new one is pretty slow. I also do not want to use EntityManager.GetComponentData<T> since this is very inefficient, especially for larger components (it easily doubles or triples my processing time).

    Either this must be a documentation error, or the ECS package is buggy and doesn't update the GlobalSystemVersion when it should.

    Is there anyone else who is experiencing the same problem, or can shed some light on it?

    Using entities 1.0.0-pre.15
     
    Last edited: Jan 10, 2023
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    In addition to the GlobalSystemVersion being out-of-date, the safety system will also invalidate the handle after every structural change. Simply checking the GlobalSystemVersion is not enough. You also need to call Update() pm the handle every time a structural change was made since the last update.
     
  3. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    So if I understand you correctly. The above could happen if a structural change happens from outside a system. In that case the handle will be invalidated, but the GlobalSystemVersion will not increment?

    If so, please @unitydevs, can we get another way of checking if a ComponentTypeHandle is up-to-date?
    Or at least a `handle.Update` method which can be used outside systems? Right now, all
    ComponentTypeHandle.Update methods take either a SystemBase or SystemState, so they can only be used inside systems.
     
  4. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    Further testing indicates that EntityManager.EntityOrderVersion should be used instead. This is documented as being incremented on every structural change. But still, even though I update the component type handle every time this value changes, it causes the Attempted to access ComponentTypeHandle<T> which has been invalidated by a structural change. exception sometimes.