minor fix
[cdsspec-compiler.git] / benchmark / cliffc-hashtable / cliffc_hashtable.h
index 2d3bd7075511d63039ec3c425e021ce680697b8e..c3cea0d20fef1ae5130a603201508c7acfedc279 100644 (file)
@@ -3,12 +3,21 @@
 
 #include <iostream>
 #include <atomic>
-#include <memory>
+//#include <common.h>
+#ifdef STANDALONE
 #include <assert.h>
+#define MODEL_ASSERT assert 
+#else
+#include <model-assert.h>
+#endif
 
-using namespace std;
-
+#include <spec_lib.h>
+#include <stdlib.h>
+#include <cdsannotate.h>
+#include <specannotation.h>
+#include <model_memory.h>
 
+using namespace std;
 
 /**
        This header file declares and defines a simplified version of Cliff Click's
@@ -31,7 +40,7 @@ struct kvs_data {
        
        kvs_data(int sz) {
                _size = sz;
-               int real_size = sizeof(atomic<void*>) * 2 + 2;
+               int real_size = sz * 2 + 2;
                _data = new atomic<void*>[real_size];
                // The control block should be initialized in resize()
                // Init the hash record array
@@ -40,11 +49,11 @@ struct kvs_data {
                for (i = 0; i < _size; i++) {
                        hashes[i] = 0;
                }
-               _data[1].store(hashes, memory_order_relaxed);
                // Init the data to Null slot
                for (i = 2; i < real_size; i++) {
                        _data[i].store(NULL, memory_order_relaxed);
                }
+               _data[1].store(hashes, memory_order_release);
        }
 
        ~kvs_data() {
@@ -56,13 +65,12 @@ struct kvs_data {
 
 struct slot {
        bool _prime;
-       shared_ptr<void> _ptr;
+       void *_ptr;
 
-       slot(bool prime, shared_ptr<void> ptr) {
+       slot(bool prime, void *ptr) {
                _prime = prime;
                _ptr = ptr;
        }
-
 };
 
 
@@ -71,9 +79,11 @@ struct slot {
        code for the its object, and "int equals(TypeK anotherKey)" which is
        used to judge equality.
        TypeK and TypeV should define their own copy constructor.
-       To make the memory management safe and similar to Cliff Click's Java
-       implementation, we use shared_ptr instead of normal pointer in terms of the
-       pointers that point to TypeK and TypeV.
+*/
+/**
+       @Begin
+       @Class_begin
+       @End
 */
 template<typename TypeK, typename TypeV>
 class cliffc_hashtable {
@@ -84,34 +94,59 @@ class cliffc_hashtable {
        
                @Begin
                @Options:
-                       LANG = C;
+                       LANG = CPP;
                        CLASS = cliffc_hashtable;
                @Global_define:
                        @DeclareVar:
-                       spec_hashtable<TypeK, TypeV*> map;
-                       spec_hashtable<TypeK, Tag> id_map;
-                       Tag tag;
+                       spec_table *map;
+                       spec_table *id_map;
+                       id_tag_t *tag;
                        @InitVar:
-                               map = spec_hashtable<TypeK, TypeV*>();
-                               id_map = spec_hashtable<TypeK, TypeV*>();
-                               tag = Tag();
+                               map = new_spec_table_default(equals_key);
+                               id_map = new_spec_table_default(equals_id);
+                               tag = new_id_tag();
+
                        @DefineFunc:
-                       bool equals_val(TypeV *ptr1, TypeV *ptr2) {
-                               // ...
+                       bool equals_key(void *ptr1, void *ptr2) {
+                               TypeK *key1 = (TypeK*) ptr1,
+                                       *key2 = (TypeK*) ptr2;
+                               if (key1 == NULL || key2 == NULL)
+                                       return false;
+                               return key1->equals(key2);
                        }
                        
+                       @DefineFunc:
+                       bool equals_val(void *ptr1, void *ptr2) {
+                               TypeV *val1 = (TypeV*) ptr1,
+                                       *val2 = (TypeV*) ptr2;
+                               if (val1 == NULL || val2 == NULL)
+                                       return false;
+                               return val1->equals(val2);
+                       }
+
+                       @DefineFunc:
+                       bool equals_id(void *ptr1, void *ptr2) {
+                               id_tag_t *id1 = (id_tag_t*) ptr1,
+                                       *id2 = (id_tag_t*) ptr2;
+                               if (id1 == NULL || id2 == NULL)
+                                       return false;
+                               return (*id1).tag == (*id2).tag;
+                       }
+
                        @DefineFunc:
                        # Update the tag for the current key slot if the corresponding tag
                        # is NULL, otherwise just return that tag. It will update the next
                        # available tag too if it requires a new tag for that key slot.
-                       Tag getKeyTag(TypeK &key) {
-                               if (id_map.get(key) == NULL) {
-                                       Tag cur_tag = tag.current();
-                                       id_map.put(key, cur_tag);
-                                       tag.next();
+                       id_tag_t getKeyTag(TypeK *key) {
+                               if (spec_table_contains(id_map, key)) {
+                                       id_tag_t *cur_tag = MODEL_MALLOC(sizeof(id_tag_t));
+                                       *cur_tag = current(tag);
+                                       spec_table_put(id_map, key, cur_tag);
+                                       next(tag);
                                        return cur_tag;
                                } else {
-                                       return id_map.get(key);
+                                       id_tag_t *res = (id_tag_t*) spec_table_get(id_map, key);
+                                       return *res;
                                }
                        }
                
@@ -134,7 +169,9 @@ class cliffc_hashtable {
                                ReplaceIfMatch(COND_ReplaceIfMatchSucc)
                        }
                @Happens_before:
-                       Write_interface -> Read_interface
+                       //Write_interface -> Read_interface
+                       Put->Get
+                       Put->Put
                @End
        */
 
@@ -162,6 +199,7 @@ friend class CHM;
        
                public:
                CHM(int size) {
+                       _newkvs.store(NULL, memory_order_relaxed);
                        _size.store(size, memory_order_relaxed);
                        _slots.store(0, memory_order_relaxed);
        
@@ -181,6 +219,7 @@ friend class CHM;
                }
        
                kvs_data* resize(cliffc_hashtable *topmap, kvs_data *kvs) {
+                       //model_print("resizing...\n");
                        kvs_data *newkvs = _newkvs.load(memory_order_acquire);
                        if (newkvs != NULL)
                                return newkvs;
@@ -208,7 +247,7 @@ friend class CHM;
        
                        newkvs = new kvs_data(newsz);
                        void *chm = (void*) new CHM(sz);
-                       newkvs->_data[0].store(chm, memory_order_relaxed);
+                       newkvs->_data[0].store(chm, memory_order_release);
        
                        kvs_data *cur_newkvs; 
                        // Another check after the slow allocation
@@ -228,7 +267,7 @@ friend class CHM;
        
                void help_copy_impl(cliffc_hashtable *topmap, kvs_data *oldkvs,
                        bool copy_all) {
-                       assert (get_chm(oldkvs) == this);
+                       MODEL_ASSERT (get_chm(oldkvs) == this);
                        kvs_data *newkvs = _newkvs.load(memory_order_acquire);
                        int oldlen = oldkvs->_size;
                        int min_copy_work = oldlen > 1024 ? 1024 : oldlen;
@@ -268,7 +307,7 @@ friend class CHM;
                        *oldkvs, int idx, void *should_help) {
                        kvs_data *newkvs = _newkvs.load(memory_order_acquire);
                        // We're only here cause the caller saw a Prime
-                       if (copy_slot(topmap, idx, oldkvs, _newkvs))
+                       if (copy_slot(topmap, idx, oldkvs, newkvs))
                                copy_check_and_promote(topmap, oldkvs, 1); // Record the slot copied
                        return (should_help == NULL) ? newkvs : topmap->help_copy(newkvs);
                }
@@ -288,9 +327,11 @@ friend class CHM;
        
                        // Promote the new table to the current table
                        if (copyDone + workdone == oldlen &&
-                               topmap->_kvs.load(memory_order_acquire) == oldkvs)
-                               topmap->_kvs.compare_exchange_strong(oldkvs, _newkvs, memory_order_release,
+                               topmap->_kvs.load(memory_order_acquire) == oldkvs) {
+                               kvs_data *newkvs = _newkvs.load(memory_order_acquire);
+                               topmap->_kvs.compare_exchange_strong(oldkvs, newkvs, memory_order_release,
                                        memory_order_release);
+                       }
                }
        
                bool copy_slot(cliffc_hashtable *topmap, int idx, kvs_data *oldkvs,
@@ -331,7 +372,7 @@ friend class CHM;
        
 
        private:
-       static const int Default_Init_Size = 8; // Intial table size
+       static const int Default_Init_Size = 4; // Intial table size
 
        static slot* const MATCH_ANY;
        static slot* const NO_MATCH_OLD;
@@ -358,6 +399,12 @@ friend class CHM;
                // Should initialize the CHM for the construction of the table
                // For other CHM in kvs_data, they should be initialzed in resize()
                // because the size is determined dynamically
+               /**
+                       @Begin
+                       @Entry_point
+                       @End
+               */
+
                kvs_data *kvs = new kvs_data(init_size);
                void *chm = (void*) new CHM(0);
                kvs->_data[0].store(chm, memory_order_relaxed);
@@ -367,141 +414,142 @@ friend class CHM;
        /**
                @Begin
                @Interface: Get
-               @Commit_point_set: Read_Val_Point1 | Read_Val_Point2 | Read_Val_Point3
-               @ID: __sequential.getKeyTag(key)
+               @Commit_point_set: Get_Success_Point1 | Get_Success_Point2 | Get_Success_Point3 
+               @ID: getKeyTag(key)
                @Action:
-                       TypeV *_Old_Val = __sequential.map.get(key)
+                       void *_Old_Val = spec_table_get(map, key);
                @Post_check:
-                       __sequential.equals_val(_Old_Val, __RET__)
+                       equals_val(_Old_Val, __RET__)
                @End
        */
-       shared_ptr<TypeV> get(TypeK& key) {
-               void *key_ptr = (void*) new TypeK(key);
-               slot *key_slot = new slot(false, shared_ptr<void>(key_ptr));
+       TypeV* get(TypeK *key) {
+               slot *key_slot = new slot(false, key);
                int fullhash = hash(key_slot);
-               slot *V = get_impl(this, _kvs, key_slot, fullhash);
+               kvs_data *kvs = _kvs.load(memory_order_acquire);
+               slot *V = get_impl(this, kvs, key_slot, fullhash);
                if (V == NULL) return NULL;
-               assert (!is_prime(V));
-               return static_pointer_cast<TypeV>(V->_ptr);
+               MODEL_ASSERT (!is_prime(V));
+               return (TypeV*) V->_ptr;
        }
 
        /**
                @Begin
                @Interface: Put
-               @Commit_point_set: Write_Val_Point
-               @ID: __sequential.getKeyTag(key)
+               @Commit_point_set: Write_Success_Point
+               @ID: getKeyTag(key)
                @Action:
                        # Remember this old value at checking point
-                       TypeV *_Old_Val = __sequential.map.get(key)
-                       __sequential.map.put(key, &val);
+                       void *_Old_Val = spec_table_get(map, key);
+                       spec_table_put(map, key, val);
                @Post_check:
-                       __sequential.equals_val(__RET__, _Old_Val)
+                       equals_val(__RET__, _Old_Val)
                @End
        */
-       shared_ptr<TypeV> put(TypeK& key, TypeV& val) {
+       TypeV* put(TypeK *key, TypeV *val) {
                return putIfMatch(key, val, NO_MATCH_OLD);
        }
 
        /**
-               @Begin
+//             @Begin
                @Interface: PutIfAbsent
                @Commit_point_set:
-                       Write_Val_Point | PutIfAbsent_Fail_Point
-               @Condition: __sequential.map.get(key) == NULL
+                       Write_Success_Point | PutIfAbsent_Fail_Point
+               @Condition: !spec_table_contains(map, key)
                @HB_condition:
                        COND_PutIfAbsentSucc :: __RET__ == NULL
-               @ID: __sequential.getKeyTag(key)
+               @ID: getKeyTag(key)
                @Action:
-                       TypeV *_Old_Val = __sequential.map.get(key)
+                       void *_Old_Val = spec_table_get(map, key);
                        if (__COND_SAT__)
-                               __sequential.map.put(key, &value);
+                               spec_table_put(map, key, value);
                @Post_check:
-                       __COND_SAT__ ? __RET__ == NULL : __sequential.equals_val(_Old_Val, __RET__) 
+                       __COND_SAT__ ? __RET__ == NULL : equals_val(_Old_Val, __RET__) 
                @End
        */
-       shared_ptr<TypeV> putIfAbsent(TypeK& key, TypeV& value) {
+       TypeV* putIfAbsent(TypeK *key, TypeV *value) {
                return putIfMatch(key, val, TOMBSTONE);
        }
 
        /**
-               @Begin
+//             @Begin
                @Interface: RemoveAny
-               @Commit_point_set: Write_Val_Point
-               @ID: __sequential.getKeyTag(key)
+               @Commit_point_set: Write_Success_Point
+               @ID: getKeyTag(key)
                @Action:
-                       TypeV *_Old_Val = __sequential.map.get(key)
-                       __sequential.map.put(key, NULL);
+                       void *_Old_Val = spec_table_get(map, key);
+                       spec_table_put(map, key, NULL);
                @Post_check:
-                       __sequential.equals_val(__RET__, _Old_Val)
+                       equals_val(__RET__, _Old_Val)
                @End
        */
-       shared_ptr<TypeV> remove(TypeK& key) {
+       TypeV* remove(TypeK *key) {
                return putIfMatch(key, TOMBSTONE, NO_MATCH_OLD);
        }
 
        /**
-               @Begin
+//             @Begin
                @Interface: RemoveIfMatch
                @Commit_point_set:
-                       Write_Val_Point | RemoveIfMatch_Fail_Point
+                       Write_Success_Point | RemoveIfMatch_Fail_Point
                @Condition:
-                       __sequential.equals_val(__sequential.map.get(key), &val)
+                       equals_val(spec_table_get(map, key), val)
                @HB_condition:
                        COND_RemoveIfMatchSucc :: __RET__ == true
-               @ID: __sequential.getKeyTag(key)
+               @ID: getKeyTag(key)
                @Action:
                        if (__COND_SAT__)
-                               __sequential.map.put(key, NULL);
+                               spec_table_put(map, key, NULL);
                @Post_check:
                        __COND_SAT__ ? __RET__ : !__RET__
                @End
        */
-       bool remove(TypeK& key, TypeV& val) {
+       bool remove(TypeK *key, TypeV *val) {
                slot *val_slot = val == NULL ? NULL : new slot(false, val);
                return putIfMatch(key, TOMBSTONE, val) == val;
 
        }
 
        /**
-               @Begin
+//             @Begin
                @Interface: ReplaceAny
                @Commit_point_set:
-                       Write_Val_Point
-               @ID: __sequential.getKeyTag(key)
+                       Write_Success_Point
+               @ID: getKeyTag(key)
                @Action:
-                       TypeV *_Old_Val = __sequential.map.get(key)
+                       void *_Old_Val = spec_table_get(map, key);
                @Post_check:
-                       __sequential.equals_val(__RET__, _Old_Val)
+                       equals_val(__RET__, _Old_Val)
                @End
        */
-       shared_ptr<TypeV> replace(TypeK& key, TypeV& val) {
+       TypeV* replace(TypeK *key, TypeV *val) {
                return putIfMatch(key, val, MATCH_ANY);
        }
 
        /**
-               @Begin
+//             @Begin
                @Interface: ReplaceIfMatch
                @Commit_point_set:
-                       Write_Val_Point | ReplaceIfMatch_Fail_Point
+                       Write_Success_Point | ReplaceIfMatch_Fail_Point
                @Condition:
-                       __sequential.equals_val(__sequential.map.get(key), &oldval)
+                       equals_val(spec_table_get(map, key), oldval)
                @HB_condition:
                        COND_ReplaceIfMatchSucc :: __RET__ == true
-               @ID: __sequential.getKeyTag(key)
+               @ID: getKeyTag(key)
                @Action:
                        if (__COND_SAT__)
-                               __sequential.map.put(key, &newval);
+                               spec_table_put(map, key, newval);
                @Post_check:
                        __COND_SAT__ ? __RET__ : !__RET__
                @End
        */
-       bool replace(TypeK& key, TypeV& oldval, TypeV& newval) {
+       bool replace(TypeK *key, TypeV *oldval, TypeV *newval) {
                return putIfMatch(key, newval, oldval) == oldval;
        }
 
        private:
        static CHM* get_chm(kvs_data* kvs) {
-               return (CHM*) kvs->_data[0].load(memory_order_relaxed);
+               CHM *res = (CHM*) kvs->_data[0].load(memory_order_relaxed);
+               return res;
        }
 
        static int* get_hashes(kvs_data *kvs) {
@@ -510,10 +558,11 @@ friend class CHM;
        
        // Preserve happens-before semantics on newly inserted keys
        static inline slot* key(kvs_data *kvs, int idx) {
-               assert (idx >= 0 && idx < kvs->_size);
+               MODEL_ASSERT (idx >= 0 && idx < kvs->_size);
                // Corresponding to the volatile read in get_impl() and putIfMatch in
                // Cliff Click's Java implementation
-               return (slot*) kvs->_data[idx * 2 + 2].load(memory_order_acquire);
+               slot *res = (slot*) kvs->_data[idx * 2 + 2].load(memory_order_acquire);
+               return res;
        }
 
        /**
@@ -526,7 +575,7 @@ friend class CHM;
        */
        // Preserve happens-before semantics on newly inserted values
        static inline slot* val(kvs_data *kvs, int idx) {
-               assert (idx >= 0 && idx < kvs->_size);
+               MODEL_ASSERT (idx >= 0 && idx < kvs->_size);
                // Corresponding to the volatile read in get_impl() and putIfMatch in
                // Cliff Click's Java implementation
                slot *res = (slot*) kvs->_data[idx * 2 + 3].load(memory_order_acquire);
@@ -544,8 +593,8 @@ friend class CHM;
        }
 
        static int hash(slot *key_slot) {
-               assert(key_slot != NULL && key_slot->_ptr != NULL);
-               shared_ptr<TypeK> key = static_pointer_cast<TypeK>(key_slot->_ptr);
+               MODEL_ASSERT(key_slot != NULL && key_slot->_ptr != NULL);
+               TypeK* key = (TypeK*) key_slot->_ptr;
                int h = key->hashCode();
                // Spread bits according to Cliff Click's code
                h += (h << 15) ^ 0xffffcd7d;
@@ -572,8 +621,8 @@ friend class CHM;
        static bool keyeq(slot *K, slot *key_slot, int *hashes, int hash,
                int fullhash) {
                // Caller should've checked this.
-               assert (K != NULL);
-               shared_ptr<TypeK> key_ptr = static_pointer_cast<TypeK>(key_slot->_ptr);
+               MODEL_ASSERT (K != NULL);
+               TypeK* key_ptr = (TypeK*) key_slot->_ptr;
                return
                        K == key_slot ||
                                ((hashes[hash] == 0 || hashes[hash] == fullhash) &&
@@ -582,8 +631,8 @@ friend class CHM;
        }
 
        static bool valeq(slot *val_slot1, slot *val_slot2) {
-               assert (val_slot1 != NULL);
-               shared_ptr<TypeK> ptr1 = static_pointer_cast<TypeV>(val_slot1->_ptr);
+               MODEL_ASSERT (val_slot1 != NULL);
+               TypeK* ptr1 = (TypeV*) val_slot1->_ptr;
                if (val_slot2 == NULL || ptr1 == NULL) return false;
                return ptr1->equals(val_slot2->_ptr);
        }
@@ -609,7 +658,7 @@ friend class CHM;
                        # If it is a successful put instead of a copy or any other internal
                        # operantions, expected != NULL
                        @Begin
-                       @Potential_commit_point_define: __ATOMIC_RET__ == true
+                       @Potential_commit_point_define: res == true
                        @Label: Write_Val_Point
                        @End
                */
@@ -635,7 +684,7 @@ friend class CHM;
                                @End
                        */
 
-                       if (V == NULL) return NULL; // A miss
+                       if (K == NULL) return NULL; // A miss
                        
                        if (keyeq(K, key_slot, hashes, idx, fullhash)) {
                                // Key hit! Check if table-resize in progress
@@ -674,21 +723,20 @@ friend class CHM;
        }
 
        // A wrapper of the essential function putIfMatch()
-       shared_ptr<TypeV> putIfMatch(TypeK& key, TypeV& value, slot *old_val) {
+       TypeV* putIfMatch(TypeK *key, TypeV *value, slot *old_val) {
                // TODO: Should throw an exception rather return NULL
                if (old_val == NULL) {
                        return NULL;
                }
-               void *key_ptr = (void*) new TypeK(key);
-               slot *key_slot = new slot(false, shared_ptr<void>(key_ptr));
+               slot *key_slot = new slot(false, key);
 
-               void *val_ptr = (void*) new TypeV(value);
-               slot *value_slot = new slot(false, shared_ptr<void>(val_ptr));
-               slot *res = putIfMatch(this, _kvs, key_slot, value_slot, old_val);
+               slot *value_slot = new slot(false, value);
+               kvs_data *kvs = _kvs.load(memory_order_acquire);
+               slot *res = putIfMatch(this, kvs, key_slot, value_slot, old_val);
                // Only when copy_slot() call putIfMatch() will it return NULL
-               assert (res != NULL); 
-               assert (!is_prime(res));
-               return res == TOMBSTONE ? NULL : static_pointer_cast<TypeV>(res->_ptr);
+               MODEL_ASSERT (res != NULL); 
+               MODEL_ASSERT (!is_prime(res));
+               return res == TOMBSTONE ? NULL : (TypeV*) res->_ptr;
        }
 
        /**
@@ -700,9 +748,9 @@ friend class CHM;
        */
        static slot* putIfMatch(cliffc_hashtable *topmap, kvs_data *kvs, slot
                *key_slot, slot *val_slot, slot *expVal) {
-               assert (val_slot != NULL);
-               assert (!is_prime(val_slot));
-               assert (!is_prime(expVal));
+               MODEL_ASSERT (val_slot != NULL);
+               MODEL_ASSERT (!is_prime(val_slot));
+               MODEL_ASSERT (!is_prime(expVal));
 
                int fullhash = hash(key_slot);
                int len = kvs->_size;
@@ -728,7 +776,7 @@ friend class CHM;
                                        break;
                                }
                                K = key(kvs, idx); // CAS failed, get updated value
-                               assert (K != NULL);
+                               MODEL_ASSERT (K != NULL);
                        }
 
                        // Key slot not null, there exists a Key here
@@ -765,7 +813,7 @@ friend class CHM;
                
                // Decided to update the existing table
                while (true) {
-                       assert (!is_prime(V));
+                       MODEL_ASSERT (!is_prime(V));
 
                        if (expVal != NO_MATCH_OLD &&
                                V != expVal &&
@@ -838,5 +886,10 @@ friend class CHM;
                return helper;
        }
 };
+/**
+       @Begin
+       @Class_end
+       @End
+*/
 
 #endif