llx_scx-inl.h 14.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 * Copyright (c) 2014, Siemens AG. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

27 28
#ifndef EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_
#define EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_
29

30
#include <embb/containers/internal/llx_scx.h>
31 32 33
#include <embb/base/thread.h>
#include <embb/base/atomic.h>
#include <embb/base/memory_allocation.h>
34
#include <algorithm>
35 36 37

namespace embb { 
namespace containers {
38
namespace internal {
39

40 41
template< typename UserData, typename ValuePool >
unsigned int LlxScx<UserData, ValuePool>::ThreadId() {
42 43 44
  unsigned int thread_index;
  int return_val = embb_internal_thread_index(&thread_index);
  if (return_val != EMBB_SUCCESS)
45
  EMBB_THROW(embb::base::ErrorException, "Could not get thread id");
46 47 48
  return thread_index;
}

49 50 51 52
template< typename UserData, typename ValuePool >
LlxScx<UserData, ValuePool>::LlxScx(size_t max_links)
: max_links_(max_links),
  max_threads_(embb::base::Thread::GetThreadsMaxCount()),
53 54 55 56 57 58 59 60 61 62 63 64 65
  scx_record_list_pool_(max_threads_ * max_threads_ * 2),
  scx_record_pool_(max_threads_ * max_threads_ * 2),
  // Disable "this is used in base member initializer" warning.
  // We explicitly want this.
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4355)
#endif
  delete_operation_callback(*this, &self_t::DeleteOperationCallback),
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(pop)
#endif
  hp(delete_operation_callback, NULL, 2) {
66 67 68 69
  typedef embb::containers::internal::FixedSizeList<LlxResult>
    llx_result_list_t;
  typedef embb::containers::internal::FixedSizeList<ScxRecord_t>
    scx_record_list_t;
70
  // Allocate a list of LLX results for every thread:
71
  thread_llx_results_ = static_cast<llx_result_list_t **>(
72
    embb::base::Allocation::AllocateCacheAligned(
73 74 75 76 77 78 79 80
      max_threads_ * sizeof(llx_result_list_t*)));
  // Using Allocation::New to create every list instance as FixedSizeList
  // does not provide a default constructor and Allocation::Allocate does
  // not allow constructor arguments.
  for (unsigned int thread_idx = 0; thread_idx < max_threads_; ++thread_idx) {
    thread_llx_results_[thread_idx] =
      embb::base::Allocation::New<llx_result_list_t>(max_links_);
  }
81 82
}

83 84
template< typename UserData, typename ValuePool >
LlxScx<UserData, ValuePool>::~LlxScx() {
85 86 87 88 89
  // Delete thread-specific lists of LLX results:
  for (unsigned int thread_idx = 0; thread_idx < max_threads_; ++thread_idx) {
    embb::base::Allocation::Delete(thread_llx_results_[thread_idx]);
  }
  // Delete array of list pointers:
90
  embb::base::Allocation::FreeAligned(thread_llx_results_);
91 92
}

93 94
template< typename UserData, typename ValuePool >
bool LlxScx<UserData, ValuePool>::TryLoadLinked(
95
  DataRecord_t * const data_record,
96
  UserData & user_data,
97 98
  bool & finalized) {
  finalized = false;
99
  unsigned int thread_id = ThreadId();
100
  // Order of initialization matters:
101 102
  volatile bool marked_1 = data_record->IsMarkedForFinalize();
  ScxRecord_t * curr_scx = data_record->ScxInfo().Load();
103 104 105 106 107
  // Guard active SCX record of this data record using guard 1.
  // When calling help with this SCX record, it will be guarded
  // using guard 0 again.
  // This hazard pointer is validated in the nested if-block below.
  hp.GuardPointer(1, curr_scx);
108
  volatile OperationState curr_state = curr_scx->state;
109
  bool marked_2 = data_record->IsMarkedForFinalize();
110 111 112 113
  if (curr_state == OperationState::Aborted ||
      (curr_state == OperationState::Comitted && !marked_2)) {
    // read mutable fields into local variable:
    UserData user_data_local(data_record->Data());
114
    if (data_record->ScxInfo().Load() == curr_scx) {
115 116 117 118 119
      // Active SCX record of data record did not change.
      // Store <r, curr_scx, user_data_local> in
      // the thread-specific table.
      // LLX results do not need to be guarded as they local to the
      // thread.
120 121 122 123
      LlxResult llx_result;
      llx_result.data_record = data_record;
      llx_result.scx_record  = curr_scx;
      llx_result.user_data   = user_data_local;
124
      assert(thread_llx_results_[thread_id]->PushBack(llx_result));
125 126 127 128 129
      // Set return value:
      user_data = user_data_local;
      return true;
    }
  }
130
  // Active SCX record of data record has been changed in between
131
  if (marked_1 && 
132 133
      (curr_scx->state == OperationState::Comitted ||
       (curr_scx->state == OperationState::InProgress && Help(curr_scx)))) {
134 135
    // Successfully completed the data record's active SCX but failed to
    // complete the LLX operation because the data record has been finalized:
136 137 138
    finalized = true;
    return false;
  }
139
  if (data_record->ScxInfo().Load()->state == OperationState::InProgress) {
140 141 142 143
    // Help active SCX.
    // This SCX record has been guarded above.
    ScxRecord_t * data_record_scx = data_record->ScxInfo().Load();
    Help(data_record_scx);
144 145 146 147
  }
  return false;
}

148
template< typename UserData, typename ValuePool >
149 150 151 152 153 154 155
void LlxScx<UserData, ValuePool>::ClearLinks() {
  // Clear thread-local list of LLX results
  unsigned int thread_id = ThreadId();
  thread_llx_results_[thread_id]->clear();
}

template< typename UserData, typename ValuePool >
156 157 158 159 160
bool LlxScx<UserData, ValuePool>::TryStoreConditional(
  embb::base::Atomic<cas_t> * cas_field,
  cas_t cas_value,
  embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
  embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps) {
161 162 163 164 165 166
  typedef embb::containers::internal::FixedSizeList<DataRecord_t *>
    dr_list_t;
  typedef embb::containers::internal::FixedSizeList<ScxRecord_t *>
    scx_op_list_t;
  typedef embb::containers::internal::FixedSizeList<LlxResult>
    llx_result_list_t;
167 168 169 170 171 172 173 174
  // Preconditions:
  // 1. For each r in linked_deps, this thread has performed an invocation
  //    I_r of LLX(r) linked to this SCX.
  // 2. Given value is not initial value of field.
  // 3. For each r in V, no SCX(V',R',field,value) has been linearized before
  //    any I_r was linearized.
  unsigned int thread_id = ThreadId();
  // Let info_fields be a table in shared memory containing for each r in V,
175
  // a copy of r's info value in this threads local table of LLX results.
176
  // In brief: A list of the SCX record of all linked deps.
177
  scx_op_list_t * info_fields = scx_record_list_pool_.Allocate(max_links_);
178 179 180 181
  if (info_fields == NULL) {    
    EMBB_THROW(embb::base::ErrorException,
      "Could not allocate SCX record list");
  }
182
  dr_list_t::const_iterator data_record;
183 184
  dr_list_t::const_iterator end;
  end = linked_deps.end();
185 186
  // Copy SCX operation of all LLX results of link dependencies into a list.
  // For each r in linked_deps ...
187 188 189 190 191
  for (data_record = linked_deps.begin(); data_record != end; ++data_record) {
    llx_result_list_t::iterator llx_result_it =
      thread_llx_results_[thread_id]->begin();
    llx_result_list_t::iterator llx_result_end =
      thread_llx_results_[thread_id]->end();
192
    // Find LLX result of data_record (r) in thread-local LLX results:
193 194 195 196 197
    while (llx_result_it != llx_result_end &&
           llx_result_it->data_record != *data_record) {
      ++llx_result_it;
    }
    if (llx_result_it->data_record != *data_record) {
198 199 200
      // Missing LLX result for given linked data record, user did not
      // load-link a data record this SCX depends on.
      EMBB_THROW(embb::base::ErrorException,
201
        "Missing preceding LLX on a data record used as SCX dependency");
202
    }
203
    // Copy SCX operation from LLX result of link dependency into list: 
204 205
    assert(info_fields->PushBack(
      llx_result_it->data_record->ScxInfo().Load()));
206
  }
207
  // Clear thread-local list of LLX results
208
  thread_llx_results_[thread_id]->clear();
209 210 211
  // Announce SCX operation. Lists linked_deps and finalize_dep are 
  // guaranteed to remain on the stack until this announced operation
  // is completed, so no allocation/pool is necessary.
212 213 214
  // The SCX operation description must be allocated from a pool as
  // LLX data records might reference it after this operation has been
  // completed.
215
  ScxRecord_t new_scx(
216 217 218
    linked_deps,
    finalize_deps,
    // target field:
219
    cas_field,
220
    // new value:
221
    cas_value,
222
    // old value:
223
    cas_field->Load(),
224
    // list of the SCX record of all linked deps
225
    info_fields,
226 227
    // initial operation state:
    OperationState::InProgress);
228 229
  // Allocate from pool as this operation description is global:
  ScxRecord_t * scx = scx_record_pool_.Allocate(new_scx);
230 231
  // Try to complete the operation. It will also be helped in failing
  // TryLoadLinked operations.
232
  bool result = Help(scx);
233
  // Release all load-links this SCX operation depended on
234
  ClearLinks();
235 236 237 238
  // Release guards, but do not enqueue instance scx for deletion as it
  // is still referenced in data records.
  hp.GuardPointer(0, NULL);
  hp.GuardPointer(1, NULL);;
239
  return result;
240 241
}

242 243
template< typename UserData, typename ValuePool >
bool LlxScx<UserData, ValuePool>::TryValidateLink(
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
  embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps) {
  typedef embb::containers::internal::FixedSizeList<DataRecord_t *>
    dr_list_t;
  typedef embb::containers::internal::FixedSizeList<LlxResult>
    llx_result_list_t;
  unsigned int thread_id = ThreadId();
  // Iterate over given list of data records V:
  dr_list_t::iterator linked_it  = linked_deps.begin();
  dr_list_t::iterator linked_end = linked_deps.end();
  // For each r in V ...
  for (; linked_it != linked_end; ++linked_it) {
    llx_result_list_t::iterator llx_result_it =
      thread_llx_results_[thread_id]->begin();
    llx_result_list_t::iterator llx_result_end =
      thread_llx_results_[thread_id]->end();
    // Find LLX result of data_record (r) in thread-local LLX results:
    while (llx_result_it != llx_result_end &&
           llx_result_it->data_record != *linked_it) {
      ++llx_result_it;
    }
    if (llx_result_it->data_record != *linked_it) {
      // Missing LLX result for given linked data record, user did not
      // load-link a data record this SCX depends on.
      EMBB_THROW(embb::base::ErrorException,
        "Missing preceding LLX on a data record used as SCX dependency");
    }
    if (llx_result_it->scx_record != (*linked_it)->ScxInfo()) {
      return false;
    }
  }
  return true;
275 276
}

277
// ScxRecord
278

279 280 281
template< typename UserData, typename ValuePool >
bool LlxScx<UserData, ValuePool>::Help(
  ScxRecord_t * scx) {
282
  hp.GuardPointer(0, scx);
283 284 285 286
  // We ensure that an SCX S does not change a data record 
  // while it is frozen for another SCX S'. Instead, S uses 
  // the information in the SCX record of S' to help S'
  // complete, so that the data record can be unfrozen.
287 288
  typedef embb::containers::internal::FixedSizeList<DataRecord_t *> dr_list_t;
  typedef embb::containers::internal::FixedSizeList<ScxRecord_t *> op_list_t;
289 290 291
  // Freeze all data records in data_records (i.e. reserve them for this 
  // SCX operation) to protect their mutable fields from being changed by 
  // other SCXs:
292 293 294 295
  dr_list_t::iterator linked_it  = scx->linked_data_records->begin();
  dr_list_t::iterator linked_end = scx->linked_data_records->end();
  op_list_t::iterator scx_op_it  = scx->scx_ops->begin();
  op_list_t::iterator scx_op_end = scx->scx_ops->end();
296 297
  for (; linked_it != linked_end && scx_op_it != scx_op_end;
       ++linked_it, ++scx_op_it) {
298 299
    DataRecord_t * r = *linked_it;
    ScxRecord<DataRecord_t> * rinfo_old = *scx_op_it;
300
    hp.GuardPointer(1, rinfo_old);
301
    // Try to freeze the data record by setting its SCX info field
302 303 304
    // to this SCX operation description.
    // r->ScxInfo() is not an ABA hazard as it is local to instance scx
    // which is already guarded.
305 306 307
    if (r->ScxInfo().CompareAndSwap(rinfo_old, scx)) {
      // Do not try to delete the sentinel scx record:
      if (rinfo_old != &DataRecord_t::DummyScx) {
308
        hp.EnqueuePointerForDeletion(rinfo_old);
309 310
      }
    } else {
311
      if (r->ScxInfo().Load() != scx) {
312
        // could not freeze r because it is frozen for another SCX:
313
        if (scx->all_frozen) {
314
          // SCX already completed by any other thread:
315 316
          return true;
        }
317
        // Atomically unfreeze all nodes frozen for this SCX (see LLX):
318
        scx->state = ScxRecord_t::Aborted;
319 320
        return false;
      }
321
    } 
322 323
  }
  // finished freezing data records
324 325
  assert(scx->state == ScxRecord_t::InProgress ||
         scx->state == ScxRecord_t::Comitted);
326
  // frozen step:
327
  scx->all_frozen = true;
328
  // mark step:
329 330
  dr_list_t::iterator finalize_it  = scx->finalize_data_records->begin();
  dr_list_t::iterator finalize_end = scx->finalize_data_records->end();
331 332
  for (; finalize_it != finalize_end; ++finalize_it) {
    (*finalize_it)->MarkForFinalize();
333 334
  }
  // update CAS:
335
  cas_t expected_old_value = scx->old_value;
336 337
  // scx->old_value_ is not an ABA hazard as it is local to the instance
  // scx which is already guarded.
338
  scx->field->CompareAndSwap(expected_old_value, scx->new_value);
339 340 341 342
  // Commit step.
  // Finalizes all r in data_records within finalize range and
  // unfreezes all r in data_records outside of finalize range. 
  // Linearization point of this operation.
343
  scx->state = ScxRecord_t::Comitted;
344 345 346
  return true;
}

347 348 349
template< typename UserData, typename ValuePool >
void LlxScx<UserData, ValuePool>::DeleteOperationCallback(
  ScxRecord_t * scx_record) {
350
  scx_record_list_pool_.Free(scx_record->scx_ops);
351 352 353
  scx_record_pool_.Free(scx_record);
}

354 355 356 357 358
// LlxScxRecord

template< typename UserData >
LlxScxRecord<UserData>::LlxScxRecord()
: marked_for_finalize_(false) {
359
  scx_op_.Store(&DummyScx);
360 361 362 363 364 365 366
}

template< typename UserData >
LlxScxRecord<UserData>::LlxScxRecord(
  const UserData & user_data)
: user_data_(user_data),
  marked_for_finalize_(false) {
367
  scx_op_.Store(&DummyScx);
368 369
}

370
template< typename UserData >
371
ScxRecord< LlxScxRecord<UserData> > 
372 373
LlxScxRecord<UserData>::DummyScx =
  ScxRecord< LlxScxRecord<UserData> >();
374

375
} // namespace internal
376 377 378
} // namespace containers
} // namespace embb

379
#endif  // EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_