model/action: bugfix - UNINIT actions do not have a Node
[model-checker.git] / model.cc
index dbca58e7394f5e3eb9a4f7b68a54c67aff4a5346..7bff598f974e762daecc27d7d58926bdfa466d68 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <algorithm>
 #include <mutex>
+#include <new>
 
 #include "model.h"
 #include "action.h"
@@ -286,8 +287,7 @@ void ModelChecker::execute_sleep_set() {
        for(unsigned int i=0;i<get_num_threads();i++) {
                thread_id_t tid=int_to_id(i);
                Thread *thr=get_thread(tid);
-               if ( scheduler->get_enabled(thr) == THREAD_SLEEP_SET &&
-                                thr->get_pending() == NULL ) {
+               if (scheduler->is_sleep_set(thr) && thr->get_pending() == NULL) {
                        thr->set_state(THREAD_RUNNING);
                        scheduler->next_thread(thr);
                        Thread::swap(&system_context, thr);
@@ -298,16 +298,15 @@ void ModelChecker::execute_sleep_set() {
        priv->current_action = NULL;
 }
 
-void ModelChecker::wake_up_sleeping_actions(ModelAction * curr) {
-       for(unsigned int i=0;i<get_num_threads();i++) {
-               thread_id_t tid=int_to_id(i);
-               Thread *thr=get_thread(tid);
-               if ( scheduler->get_enabled(thr) == THREAD_SLEEP_SET ) {
-                       ModelAction *pending_act=thr->get_pending();
-                       if ((!curr->is_rmwr())&&pending_act->could_synchronize_with(curr)) {
+void ModelChecker::wake_up_sleeping_actions(ModelAction *curr)
+{
+       for (unsigned int i = 0; i < get_num_threads(); i++) {
+               Thread *thr = get_thread(int_to_id(i));
+               if (scheduler->is_sleep_set(thr)) {
+                       ModelAction *pending_act = thr->get_pending();
+                       if ((!curr->is_rmwr()) && pending_act->could_synchronize_with(curr))
                                //Remove this thread from sleep set
                                scheduler->remove_sleep(thr);
-                       }
                }
        }
 }
@@ -714,7 +713,7 @@ bool ModelChecker::process_read(ModelAction *curr, bool second_part_of_rmw)
                        /* Read from future value */
                        value = curr->get_node()->get_future_value();
                        modelclock_t expiration = curr->get_node()->get_future_value_expiration();
-                       read_from(curr, NULL);
+                       curr->set_read_from(NULL);
                        Promise *valuepromise = new Promise(curr, value, expiration);
                        promises->push_back(valuepromise);
                }
@@ -858,6 +857,56 @@ bool ModelChecker::process_write(ModelAction *curr)
        return updated_mod_order || updated_promises;
 }
 
+/**
+ * Process a fence ModelAction
+ * @param curr The ModelAction to process
+ * @return True if synchronization was updated
+ */
+bool ModelChecker::process_fence(ModelAction *curr)
+{
+       /*
+        * fence-relaxed: no-op
+        * fence-release: only log the occurence (not in this function), for
+        *   use in later synchronization
+        * fence-acquire (this function): search for hypothetical release
+        *   sequences
+        */
+       bool updated = false;
+       if (curr->is_acquire()) {
+               action_list_t *list = action_trace;
+               action_list_t::reverse_iterator rit;
+               /* Find X : is_read(X) && X --sb-> curr */
+               for (rit = list->rbegin(); rit != list->rend(); rit++) {
+                       ModelAction *act = *rit;
+                       if (act == curr)
+                               continue;
+                       if (act->get_tid() != curr->get_tid())
+                               continue;
+                       /* Stop at the beginning of the thread */
+                       if (act->is_thread_start())
+                               break;
+                       /* Stop once we reach a prior fence-acquire */
+                       if (act->is_fence() && act->is_acquire())
+                               break;
+                       if (!act->is_read())
+                               continue;
+                       /* read-acquire will find its own release sequences */
+                       if (act->is_acquire())
+                               continue;
+
+                       /* Establish hypothetical release sequences */
+                       rel_heads_list_t release_heads;
+                       get_release_seq_heads(curr, act, &release_heads);
+                       for (unsigned int i = 0; i < release_heads.size(); i++)
+                               if (!curr->synchronize_with(release_heads[i]))
+                                       set_bad_synchronization();
+                       if (release_heads.size() != 0)
+                               updated = true;
+               }
+       }
+       return updated;
+}
+
 /**
  * @brief Process the current action for thread-related activity
  *
@@ -1173,6 +1222,9 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                        if (act->is_write() && process_write(act))
                                update = true;
 
+                       if (act->is_fence() && process_fence(act))
+                               update_all = true;
+
                        if (act->is_mutex_op() && process_mutex(act))
                                update_all = true;
 
@@ -1464,18 +1516,21 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
                                                *act < *last_sc_fence_thread_local) {
                                        mo_graph->addEdge(act, rf);
                                        added = true;
+                                       break;
                                }
                                /* C++, Section 29.3 statement 4 */
                                else if (act->is_seqcst() && last_sc_fence_local &&
                                                *act < *last_sc_fence_local) {
                                        mo_graph->addEdge(act, rf);
                                        added = true;
+                                       break;
                                }
                                /* C++, Section 29.3 statement 6 */
                                else if (last_sc_fence_thread_before &&
                                                *act < *last_sc_fence_thread_before) {
                                        mo_graph->addEdge(act, rf);
                                        added = true;
+                                       break;
                                }
                        }
 
@@ -1652,6 +1707,7 @@ bool ModelChecker::w_modification_order(ModelAction *curr)
                                        *act < *last_sc_fence_thread_before) {
                                mo_graph->addEdge(act, curr);
                                added = true;
+                               break;
                        }
 
                        /*
@@ -2046,18 +2102,32 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_
 void ModelChecker::add_action_to_lists(ModelAction *act)
 {
        int tid = id_to_int(act->get_tid());
-       action_trace->push_back(act);
+       ModelAction *uninit = NULL;
+       int uninit_id = -1;
+       action_list_t *list = get_safe_ptr_action(obj_map, act->get_location());
+       if (list->empty()) {
+               uninit = new_uninitialized_action(act->get_location());
+               uninit_id = id_to_int(uninit->get_tid());
+               list->push_back(uninit);
+       }
+       list->push_back(act);
 
-       get_safe_ptr_action(obj_map, act->get_location())->push_back(act);
+       action_trace->push_back(act);
+       if (uninit)
+               action_trace->push_front(uninit);
 
        std::vector<action_list_t> *vec = get_safe_ptr_vect_action(obj_thrd_map, act->get_location());
        if (tid >= (int)vec->size())
                vec->resize(priv->next_thread_id);
        (*vec)[tid].push_back(act);
+       if (uninit)
+               (*vec)[uninit_id].push_front(uninit);
 
        if ((int)thrd_last_action->size() <= tid)
                thrd_last_action->resize(get_num_threads());
        (*thrd_last_action)[tid] = act;
+       if (uninit)
+               (*thrd_last_action)[uninit_id] = uninit;
 
        if (act->is_fence() && act->is_release()) {
                if ((int)thrd_last_fence_release->size() <= tid)
@@ -2392,16 +2462,8 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
 
        ModelAction *last_sc_write = NULL;
 
-       /* Track whether this object has been initialized */
-       bool initialized = false;
-
-       if (curr->is_seqcst()) {
+       if (curr->is_seqcst())
                last_sc_write = get_last_seq_cst_write(curr);
-               /* We have to at least see the last sequentially consistent write,
-                        so we are initialized. */
-               if (last_sc_write != NULL)
-                       initialized = true;
-       }
 
        /* Iterate over all threads */
        for (i = 0; i < thrd_lists->size(); i++) {
@@ -2433,17 +2495,12 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
                        }
 
                        /* Include at most one act per-thread that "happens before" curr */
-                       if (act->happens_before(curr)) {
-                               initialized = true;
+                       if (act->happens_before(curr))
                                break;
-                       }
                }
        }
 
-       if (!initialized)
-               assert_bug("May read from uninitialized atomic");
-
-       if (DBG_ENABLED() || !initialized) {
+       if (DBG_ENABLED()) {
                model_print("Reached read action:\n");
                curr->print();
                model_print("Printing may_read_from\n");
@@ -2453,7 +2510,10 @@ void ModelChecker::build_reads_from_past(ModelAction *curr)
 }
 
 bool ModelChecker::sleep_can_read_from(ModelAction * curr, const ModelAction *write) {
-       while(true) {
+       while (true) {
+               /* UNINIT actions don't have a Node, and they never sleep */
+               if (write->is_uninitialized())
+                       return true;
                Node *prevnode=write->get_node()->get_parent();
 
                bool thread_sleep=prevnode->enabled_status(curr->get_tid())==THREAD_SLEEP_SET;
@@ -2468,6 +2528,19 @@ bool ModelChecker::sleep_can_read_from(ModelAction * curr, const ModelAction *wr
        }
 }
 
+/**
+ * @brief Create a new action representing an uninitialized atomic
+ * @param location The memory location of the atomic object
+ * @return A pointer to a new ModelAction
+ */
+ModelAction * ModelChecker::new_uninitialized_action(void *location) const
+{
+       ModelAction *act = (ModelAction *)snapshot_malloc(sizeof(class ModelAction));
+       act = new (act) ModelAction(ATOMIC_UNINIT, std::memory_order_relaxed, location, 0, model_thread);
+       act->create_cv(NULL);
+       return act;
+}
+
 static void print_list(action_list_t *list, int exec_num = -1)
 {
        action_list_t::iterator it;