I am trying to implement a shared cache for arrays.  It must support two operations: set(owner, idx, value) and fetch(owner, idx) where `idx` is the index into the array and `owner` is an opaque handle to an owning object -- `fetch(owner_1, idx)` should return the value stored by `set(owner_1, idx)` only if the `owner` argument matches.  The cache must be thread - safe but I do not want to rely on locking, i.e. mutexes.  Failure in looking up cached values is fine - it is OK and expected that other threads will overwrite existing values in the shared cache, in which case `fetch` should just fail.

So the `fetch` operation has to read the cache slot's `owner` field to check against its argument, and if it matches return the cache slot's `value` field.  The problem is, without locks, another thread could overwrite one of these fields during that operation.  This approach tries to get around that by assigning a `version` field to each cache slot.  It only increases.  The `fetch` operation reads the version (atomically) at the start of the operation and at the end; if these are not the same, something changed during the read and the result is invalid, even if the `owner` field apparently matched.

The code below ensures that `version` is always incremented before `value` is updated, thus preventing `fetch` from returning a value from a different owner.  (The functions `g_atomic_...` are provided by glib.) It "seems to work" - but can it be proven correct or incorrect?

    struct _cache_slot
    {
      void*        owner;
      gint         version;
      gdouble      value;
    };

    struct _cache_slot cache[SIZE];

    int
    point_cache_fetch(void *owner, gdouble* ret, gsize idx)
    {
       struct _cache_slot = cache[idx];
    
       gint version_start  = g_atomic_int_get(&(slot.version));
       void* slot_owner    = g_atomic_pointer_get(&(slot.owner));
       gdouble value       = slot.value;
       gint version_finish = g_atomic_int_get(&(slot.version));
    
       if ((version_start == version_finish) && (slot_owner == owner))
       {
         *ret = value;
         return 1;
       }
       else
       {
         return 0;
       }
    }
    
    void
    point_cache_store(void *owner, gsize idx, gdouble value)
    {
       struct slot = cache[idx];
    
       g_atomic_pointer_set(&(slot.owner), NULL);
       slot.version++;
       g_atomic_pointer_set(&(slot.owner), owner);
       slot.value = value;
    }