Skip to content

File thread_lockfree_hash_map.hpp

File List > cubrid > src > thread > thread_lockfree_hash_map.hpp

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

//
//  specialize lock-free hash map to thread manager and its entries
//

#ifndef _THREAD_LOCKFREE_HASH_MAP_HPP_
#define _THREAD_LOCKFREE_HASH_MAP_HPP_

#include "lock_free.h"  // old implementation
#include "lockfree_hashmap.hpp"
#include "lockfree_transaction_def.hpp"
#include "system_parameter.h"
#include "thread_entry.hpp"

namespace cubthread
{
  template <class Key, class T>
  class lockfree_hashmap
  {
    public:
      lockfree_hashmap ();

      class iterator;

      void init (lf_tran_system &transys, int entry_idx, int hash_size, int freelist_block_size,
         int freelist_block_count, lf_entry_descriptor &edesc);

      void init_as_old (lf_tran_system &transys, int hash_size, int freelist_block_count, int freelist_block_size,
            lf_entry_descriptor &edesc, int entry_idx);
      void init_as_new (lockfree::tran::system &transys, size_t hash_size, size_t freelist_block_size,
            size_t freelist_block_count, lf_entry_descriptor &edesc);
      void destroy ();

      T *find (cubthread::entry *thread_p, Key &key);
      bool find_or_insert (cubthread::entry *thread_p, Key &key, T *&t);
      bool insert (cubthread::entry *thread_p, Key &key, T *&t);
      bool insert_given (cubthread::entry *thread_p, Key &key, T *&t);
      bool erase (cubthread::entry *thread_p, Key &key);
      bool erase_locked (cubthread::entry *thread_p, Key &key, T *&t);

      void unlock (cubthread::entry *thread_p, T *&t);

      void clear (cubthread::entry *thread_p);    // NOT LOCK-FREE

      T *freelist_claim (cubthread::entry *thread_p);
      void freelist_retire (cubthread::entry *thread_p, T *&t);

      void start_tran (cubthread::entry *thread_p);
      void end_tran (cubthread::entry *thread_p);

      size_t get_size () const;
      size_t get_element_count () const;
      size_t get_alloc_element_count () const;

    private:
      bool is_old_type () const;
      lf_tran_entry *get_tran_entry (cubthread::entry *thread_p);

      enum type
      {
    OLD,
    NEW,
    UNKNOWN
      };

      lf_hash_table_cpp<Key, T> m_old_hash;
      lockfree::hashmap<Key, T> m_new_hash;
      type m_type;
      int m_entry_idx;
  };

  template <class Key, class T>
  class lockfree_hashmap<Key, T>::iterator
  {
    public:
      iterator (cubthread::entry *thread_p, lockfree_hashmap &map);
      ~iterator () = default;

      T *iterate ();

      void restart ();

    private:
      // old stuff
      lockfree_hashmap &m_map;
      typename lf_hash_table_cpp<Key, T>::iterator m_old_iter;
      typename lockfree::hashmap<Key, T>::iterator m_new_iter;
  };

  lockfree::tran::system &get_thread_entry_lftransys ();
} // namespace cubthread

//
// implementation
//

namespace cubthread
{
  //
  //  lockfree_hashmap
  //
  template <class Key, class T>
  lockfree_hashmap<Key, T>::lockfree_hashmap ()
    : m_old_hash {}
    , m_new_hash {}
    , m_type (UNKNOWN)
  {
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::init (lf_tran_system &transys, int entry_idx, int hash_size, int freelist_block_size,
                  int freelist_block_count, lf_entry_descriptor &edesc)
  {
    if (prm_get_bool_value (PRM_ID_ENABLE_NEW_LFHASH))
      {
    init_as_new (get_thread_entry_lftransys (), (size_t) hash_size, (size_t) freelist_block_size,
             (size_t) freelist_block_count, edesc);
      }
    else
      {
    init_as_old (transys, hash_size, freelist_block_count, freelist_block_size, edesc, entry_idx);
      }
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::init_as_old (lf_tran_system &transys, int hash_size, int freelist_block_count,
                     int freelist_block_size, lf_entry_descriptor &edesc, int entry_idx)
  {
    m_type = OLD;
    m_old_hash.init (transys, hash_size, freelist_block_count, freelist_block_size, edesc);
    m_entry_idx = entry_idx;
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::init_as_new (lockfree::tran::system &transys, size_t hash_size, size_t freelist_block_size,
                     size_t freelist_block_count, lf_entry_descriptor &edesc)
  {
    m_type = NEW;
    m_new_hash.init (transys, hash_size, freelist_block_size, freelist_block_count, edesc);
  }

#define lockfree_hashmap_forward_func(f_, tp_, ...) \
  is_old_type () ? \
  m_old_hash.f_ (get_tran_entry (tp_), __VA_ARGS__) : \
  m_new_hash.f_ ((tp_)->get_lf_tran_index (), __VA_ARGS__)
#define lockfree_hashmap_forward_func_noarg(f_, tp_) \
  is_old_type () ? \
  m_old_hash.f_ (get_tran_entry (tp_)) : \
  m_new_hash.f_ ((tp_)->get_lf_tran_index ())

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::destroy ()
  {
    if (m_type == UNKNOWN)
      {
    // was not initialized
    return;
      }
    if (is_old_type ())
      {
    m_old_hash.destroy ();
      }
    else
      {
    m_new_hash.destroy ();
      }
  }

  template <class Key, class T>
  T *
  lockfree_hashmap<Key, T>::find (cubthread::entry *thread_p, Key &key)
  {
    return lockfree_hashmap_forward_func (find, thread_p, key);
  }

  template <class Key, class T>
  bool
  lockfree_hashmap<Key, T>::find_or_insert (cubthread::entry *thread_p, Key &key, T *&t)
  {
    return lockfree_hashmap_forward_func (find_or_insert, thread_p, key, t);
  }

  template <class Key, class T>
  bool
  lockfree_hashmap<Key, T>::insert (cubthread::entry *thread_p, Key &key, T *&t)
  {
    return lockfree_hashmap_forward_func (insert, thread_p, key, t);
  }

  template <class Key, class T>
  bool
  lockfree_hashmap<Key, T>::insert_given (cubthread::entry *thread_p, Key &key, T *&t)
  {
    return lockfree_hashmap_forward_func (insert_given, thread_p, key, t);
  }

  template <class Key, class T>
  bool
  lockfree_hashmap<Key, T>::erase (cubthread::entry *thread_p, Key &key)
  {
    return lockfree_hashmap_forward_func (erase, thread_p, key);
  }

  template <class Key, class T>
  bool
  lockfree_hashmap<Key, T>::erase_locked (cubthread::entry *thread_p, Key &key, T *&t)
  {
    return lockfree_hashmap_forward_func (erase_locked, thread_p, key, t);
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::unlock (cubthread::entry *thread_p, T *&t)
  {
    lockfree_hashmap_forward_func (unlock, thread_p, t);
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::clear (cubthread::entry *thread_p)
  {
    lockfree_hashmap_forward_func_noarg (clear, thread_p);
  }

  template <class Key, class T>
  T *
  lockfree_hashmap<Key, T>::freelist_claim (cubthread::entry *thread_p)
  {
    return lockfree_hashmap_forward_func_noarg (freelist_claim, thread_p);
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::freelist_retire (cubthread::entry *thread_p, T *&t)
  {
    lockfree_hashmap_forward_func (freelist_retire, thread_p, t);
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::start_tran (cubthread::entry *thread_p)
  {
    lockfree_hashmap_forward_func_noarg (start_tran, thread_p);
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::end_tran (cubthread::entry *thread_p)
  {
    lockfree_hashmap_forward_func_noarg (end_tran, thread_p);
  }

  template <class Key, class T>
  size_t
  lockfree_hashmap<Key, T>::get_size () const
  {
    return is_old_type () ? m_old_hash.get_size () : m_new_hash.get_size ();
  }

  template <class Key, class T>
  size_t
  lockfree_hashmap<Key, T>::get_element_count () const
  {
    return is_old_type () ? m_old_hash.get_element_count () : m_new_hash.get_element_count ();
  }

  template <class Key, class T>
  size_t
  lockfree_hashmap<Key, T>::get_alloc_element_count () const
  {
    return is_old_type () ? m_old_hash.get_alloc_element_count () : m_new_hash.get_alloc_element_count ();
  }

#undef lockfree_hashmap_forward_func
#undef lockfree_hashmap_forward_func_noarg

  template <class Key, class T>
  bool
  lockfree_hashmap<Key, T>::is_old_type () const
  {
    assert (m_type == OLD || m_type == NEW);
    return m_type == OLD;
  }

  template <class Key, class T>
  lf_tran_entry *
  lockfree_hashmap<Key, T>::get_tran_entry (cubthread::entry *thread_p)
  {
    return thread_p->tran_entries[m_entry_idx];
  }

  //
  // lockfree_hashmap::iterator
  //
  template <class Key, class T>
  lockfree_hashmap<Key, T>::iterator::iterator (cubthread::entry *thread_p, lockfree_hashmap &map)
    : m_map (map)
    , m_old_iter ()
    , m_new_iter ()
  {
    if (m_map.is_old_type ())
      {
    m_old_iter = { m_map.get_tran_entry (thread_p), m_map.m_old_hash };
      }
    else
      {
    m_new_iter = { thread_p->get_lf_tran_index (), m_map.m_new_hash };
      }
  }

  template <class Key, class T>
  T *
  lockfree_hashmap<Key, T>::iterator::iterate ()
  {
    if (m_map.is_old_type ())
      {
    return m_old_iter.iterate ();
      }
    else
      {
    return m_new_iter.iterate ();
      }
  }

  template <class Key, class T>
  void
  lockfree_hashmap<Key, T>::iterator::restart ()
  {
    if (m_map.is_old_type ())
      {
    m_old_iter.restart ();
      }
    else
      {
    m_new_iter.restart ();
      }
  }
}

#endif // !_THREAD_LOCKFREE_HASH_MAP_HPP_