rubinius/rubinius

View on GitHub
machine/memory/header.cpp

Summary

Maintainability
Test Coverage
#include "memory/header.hpp"

#include "bug.hpp"
#include "configuration.hpp"
#include "memory.hpp"
#include "on_stack.hpp"
#include "thread_phase.hpp"
#include "type_info.hpp"

#include "class/object.hpp"
#include "class/class.hpp"
#include "class/exception.hpp"
#include "class/fixnum.hpp"
#include "class/thread.hpp"

#include "memory/collector.hpp"

#include "diagnostics/memory.hpp"

#include "capi/ruby.h"

#include <assert.h>
#include <chrono>
#include <string>
#include <sys/time.h>

namespace rubinius {
  std::atomic<uintptr_t> MemoryHeader::object_id_counter;

  void MemoryHeader::bootstrap(STATE) {
    object_id_counter = 1;
  }

  void MemoryHeader::track_reference(STATE) {
    if(reference_p()) {
      state->collector()->add_reference(this);
    }
  }

  void MemoryHeader::track_weakref(STATE) {
    if(reference_p()) {
      state->collector()->add_weakref(this);
    }
  }

  MemoryHeaderBits::MemoryHeaderBits(const MemoryHeader* header) {
    extended = header->extended_header_p();
    forwarded = header->forwarded_p();
    thread_id = header->thread_id();
    region = header->region();
    referenced = header->referenced_count();
    weakref = header->weakref_p();
    finalizer = header->finalizer_p();
    remembered = header->remembered_p();
    pinned = header->pinned_p();
    visited = header->visited();
    marked = header->marked();
    scanned = header->scanned_p();
    type_id = header->type_id();
    data = header->data_p();
    frozen = header->frozen_p();
    tainted = header->tainted_p();
    locked_count = header->lock_count();
    lock_extended = header->lock_extended_p();
    lock_owner = header->lock_owner();
    object_id = header->object_id();
    type_specific = header->type_specific();
  }

  MemoryHeaderBits MemoryHeader::header_bits() const {
    return MemoryHeaderBits(this);
  }

  bool MemoryLock::locked_p(STATE) {
    return locked_count > 0;
  }

  bool MemoryLock::lock_owned_p(STATE) {
    return thread_id == state->thread_id() && locked_count > 0;
  }

  bool MemoryLock::try_lock(STATE) {
    if(lock.try_lock()) {
      thread_id = state->thread_id();
      locked_count = 1;
      return true;
    } else {
      return false;
    }
  }

  void MemoryLock::unlock(STATE) {
    if(thread_id == state->thread_id()) {
      if(locked_count > 0) {
        locked_count--;

        if(locked_count == 0) {
          thread_id = 0;
          lock.unlock();
        }
      } else {
        Exception::raise_runtime_error(state,
            "unlocking owned, extended-header non-locked object");
      }
    } else {
      Exception::raise_runtime_error(state,
          "unlocking non-owned, extended-header object");
    }
  }

  void ExtendedHeader::track_memory_header(STATE) {
    state->collector()->add_memory_header(this);
  }

  ExtendedHeader* ExtendedHeader::create_lock(STATE, const MemoryFlags h, unsigned int count) {
    ExtendedHeader* nh = create(h);

    MemoryLock* lock = new MemoryLock(state->thread_id(), count);
    nh->words[0].set_lock(lock);

    return nh;
  }

  ExtendedHeader* ExtendedHeader::create_lock(
      STATE, const MemoryFlags h, const ExtendedHeader* eh, unsigned int count)
  {
    ExtendedHeader* nh = create(h, eh);

    MemoryLock* lock = new MemoryLock(state->thread_id(), count);
    nh->words[eh->size()].set_lock(lock);

    return nh;
  }

  ExtendedHeader* ExtendedHeader::replace_lock(STATE, const MemoryFlags h,
      const ExtendedHeader* eh, unsigned int count)
  {
    ExtendedHeader* nh = create_copy(h, eh);

    MemoryLock* lock = new MemoryLock(state->thread_id(), count);

    for(int i = 0; i < nh->size(); i++) {
      if(nh->words[i].lock_p()) {
        nh->words[i].set_lock(lock);
      }
    }

    return nh;
  }

  void MemoryHeader::lock(STATE) {
    static int i = 0;
    static int delay[] = {
      133, 464, 254, 306, 549, 287, 358, 638, 496, 81,
      472, 288, 131, 31, 435, 258, 221, 73, 537, 854
    };
    static int modulo = sizeof(delay) / sizeof(int);

    while(!try_lock(state)) {
      state->set_thread_sleep();

      uint64_t ns = delay[i++ % modulo];

      // TODO: MemoryHeader standardize this for waiting
      std::this_thread::sleep_for(std::chrono::nanoseconds(ns));
    }

    state->set_thread_run();
  }

  bool MemoryHeader::try_lock(STATE) {
    while(true) {
      MemoryFlags h = header.load(std::memory_order_acquire);
      MemoryFlags nh;
      ExtendedHeader* hh = nullptr;
      ExtendedHeader* eh = nullptr;

      if(thread_id() == state->thread_id()) {
        if(extended_header_p()) {
          hh = extended_header();
          int locked_count = locked_count_field.get(hh->header);

          if(locked_count < max_locked_count()) {
            eh = ExtendedHeader::create_copy(
                locked_count_field.set(hh->header, locked_count + 1), hh);

            nh = extended_flags(eh);
          } else if(hh->lock_extended_p()) {
            MemoryLock* lock = hh->get_lock();

            if(lock->lock_owned_p(state)) {
              lock->locked_count++;
              return true;
            }

            if(lock->try_lock(state)) return true;

            if(state->valid_thread_p(lock->thread_id)) {
              return false;
            }

            eh = ExtendedHeader::replace_lock(state,
                locked_count_field.set(hh->header, lock_extended()), hh, 1);
            nh = extended_flags(eh);
          } else {
            eh = ExtendedHeader::create_lock(state,
                locked_count_field.set(hh->header, lock_extended()), hh, locked_count + 1);

            nh = extended_flags(eh);
          }
        } else {
          int locked_count = locked_count_field.get(h);

          if(locked_count < max_locked_count()) {
            nh = locked_count_field.set(h, locked_count + 1);
          } else {
            ExtendedHeader* eh = ExtendedHeader::create_lock(
                state, locked_count_field.set(h, lock_extended()), locked_count + 1);

            nh = extended_flags(eh);
          }
        }

        if(header.compare_exchange_strong(h, nh, std::memory_order_release)) {
          if(eh) eh->track_memory_header(state);

          if(hh) hh->unsynchronized_set_zombie();

          return true;
        }

        if(eh) eh->delete_zombie_header();
      } else {
        ExtendedHeader* hh = nullptr;

        if(extended_header_p()) {
          hh = extended_header();

          if(hh->lock_extended_p()) {
            MemoryLock* lock = hh->get_lock();

            if(lock->lock_owned_p(state)) {
              lock->locked_count++;
              return true;
            }

            if(lock->try_lock(state)) return true;

            if(state->valid_thread_p(lock->thread_id)) {
              return false;
            }

            eh = ExtendedHeader::replace_lock(
                state, locked_count_field.set(hh->header, lock_extended()), hh, 1);
            nh = extended_flags(eh);
          } else if(locked_count_field.get(hh->header) == 0) {
            eh = ExtendedHeader::create_lock(
                state, locked_count_field.set(hh->header, lock_extended()), hh, 1);
            nh = extended_flags(eh);
          } else {
            if(state->valid_thread_p(thread_id_field.get(hh->header))) {
              return false;
            }

            eh = ExtendedHeader::create_lock(
                state, locked_count_field.set(hh->header, lock_extended()), hh, 1);
            nh = extended_flags(eh);
          }
        } else {
          if(locked_count_field.get(h) == 0) {
            ExtendedHeader* eh = ExtendedHeader::create_lock(
                state, locked_count_field.set(h, lock_extended()), 1);

            nh = extended_flags(eh);
          } else {
            if(state->valid_thread_p(thread_id_field.get(h))) {
              return false;
            }

            eh = ExtendedHeader::create_lock(
                state, locked_count_field.set(h, lock_extended()), 1);
            nh = extended_flags(eh);
          }
        }

        if(header.compare_exchange_strong(h, nh, std::memory_order_release)) {
          if(eh) eh->track_memory_header(state);

          if(hh) hh->unsynchronized_set_zombie();

          return true;
        }

        if(eh) eh->delete_zombie_header();
      }
    }
  }

  void MemoryHeader::unlock(STATE) {
    while(true) {
      MemoryFlags h = header.load(std::memory_order_acquire);

      if(thread_id() == state->thread_id()) {
        if(extended_header_p()) {
          ExtendedHeader* hh = extended_header();

          if(hh->lock_extended_p()) {
            hh->get_lock()->unlock(state);
            return;
          } else {
            int locked_count = locked_count_field.get(hh->header);

            if(locked_count > 0) {
              ExtendedHeader* eh = ExtendedHeader::create_copy(
                  locked_count_field.set(hh->header, locked_count - 1), hh);

              MemoryFlags nh = extended_flags(eh);

              if(header.compare_exchange_strong(h, nh, std::memory_order_release)) {
                eh->track_memory_header(state);

                hh->unsynchronized_set_zombie();

                return;
              }

              eh->delete_zombie_header();
            } else {
              Exception::raise_runtime_error(state,
                  "unlocking owned, extended-header, non-locked object");
            }
          }
        } else {
          int locked_count = locked_count_field.get(h);

          if(locked_count > 0) {
            MemoryFlags nh = locked_count_field.set(h, locked_count - 1);

            if(header.compare_exchange_strong(h, nh, std::memory_order_release)) {
              return;
            }
          } else {
            Exception::raise_runtime_error(state, "unlocking owned, non-locked object");
          }
        }
      } else {
        if(extended_header_p()) {
          ExtendedHeader* hh = extended_header();

          if(hh->lock_extended_p()) {
            hh->get_lock()->unlock(state);
            return;
          } else {
            Exception::raise_runtime_error(state,
                "unlocking non-owned, extended-header, non-locked object");
          }
        } else {
          Exception::raise_runtime_error(state, "unlocking non-owned, non-locked object");
        }
      }
    }
  }

  void MemoryHeader::unset_lock(STATE) {
    unset(state, locked_count_field);
  }

  bool MemoryHeader::locked_p(STATE) {
    if(extended_header_p()) {
      ExtendedHeader* hh = extended_header();

      if(hh->lock_extended_p()) {
        MemoryLock* lock = hh->get_lock();

        if(!lock->locked_p(state)) return false;

        if(state->thread_id() == lock->thread_id) return true;

        if(state->valid_thread_p(lock->thread_id)) {
          return true;
        }
      } else {
        if(locked_count_field.get(hh->header) > 0) {
          if(state->thread_id() == thread_id()) return true;

          if(state->valid_thread_p(thread_id())) {
            return true;
          }
        }
      }
    } else {
      if(locked_count_field.get(header.load(std::memory_order_acquire)) > 0) {
        if(state->thread_id() == thread_id()) return true;

        if(state->valid_thread_p(thread_id())) {
          return true;
        }
      }
    }

    return false;
  }

  bool MemoryHeader::lock_owned_p(STATE) {
    return false;
  }

  void MemoryHeader::write_barrier(STATE, MemoryHeader* value) {
#ifdef RBX_LOG_CONCURRENT_UPDATE
    if(state->configuration()->machine_concurrent_update_log) {
      if(thread_id() != state->thread_id()) {
        TypeInfo* ti = state->memory()->type_info[type_id()];

        logger::warn("Therad id: %d updating an instance of %s created by Thread id: %d",
            state->thread_id(), ti->type_name.c_str(), thread_id());
      }
    }
#endif

#ifdef RBX_RAISE_CONCURRENT_UPDATE
    if(state->configuration()->machine_concurrent_update_raise) {
      if(thread_id() != state->thread_id()) {
        TypeInfo* ti = state->memory()->type_info[type_id()];

        std::ostringstream msg;
        msg << "Thread id: " << state->thread_id() <<
          " updating an instance of " << ti->type_name <<
          " created by Thread id: " << thread_id();

        Exception::raise_concurrent_update_error(state, msg.str().c_str());
      }
    }
#endif
  }

  MemoryHandle::~MemoryHandle() {
    if(rarray_p()) {
      delete reinterpret_cast<RArray*>(data());
    } else if(rstring_p()) {
      delete reinterpret_cast<RString*>(data());
    } else if(rdata_p()) {
      // RData objects have a finalizer defined
    } else if (rfloat_p()) {
      delete reinterpret_cast<RFloat*>(data());
    } else if (rio_p()) {
      // RIO objects have a finalizer defined
    } else if (rfile_p()) {
      delete reinterpret_cast<RFile*>(data());
    }

    _type_ = eReleased;
  }

  /* TODO: write_barrier
    inline void write_barrier(ObjectHeader* target, Fixnum* val) {
      // No-op
    }

    inline void write_barrier(ObjectHeader* target, Symbol* val) {
      // No-op
    }

    inline void write_barrier(ObjectHeader* target, ObjectHeader* val) {
      // memory::WriteBarrier::write_barrier(target, val, mark_);
    }

    inline void write_barrier(ObjectHeader* target, Class* val) {
      // memory::WriteBarrier::write_barrier(target, reinterpret_cast<Object*>(val), mark_);
    }
  */

  size_t ObjectHeader::compute_size_in_bytes(STATE) const {
    return state->memory()->type_info[type_id()]->object_size(this);
  }

  size_t DataHeader::compute_size_in_bytes(STATE) const {
    return state->memory()->type_info[type_id()]->object_size(this);
  }

  void ObjectHeader::initialize_copy(STATE, Object* other) {
    klass(state, other->klass());
    ivars(state, other->ivars());
  }

  void ObjectHeader::copy_body(STATE, Object* other) {
    void* src = other->__body__;
    void* dst = this->__body__;

    memcpy(dst, src, other->body_in_bytes(state));
  }

  namespace memory {
    void write_barrier(STATE, ObjectHeader* object, Fixnum* value) {
      // No-op
    }

    void write_barrier(STATE, ObjectHeader* object, Symbol* value) {
      // No-op
    }

    void write_barrier(STATE, ObjectHeader* object, ObjectHeader* value) {
      object->write_barrier(state, value);
    }

    void write_barrier(STATE, ObjectHeader* object, Class* value) {
      object->write_barrier(state, value);
    }
  }
}