0

キーと値の型が決定されていない小さな連想配列 (TimedMap など) を提供する必要があります。この連想配列には、そのすべての要素の持続時間が固定されています。たとえば、キーの型が「int」、値の型が「Foo」、期間が 5 秒であるとします。(0, Foo インスタンス) を挿入すると、5 秒後に値がなくなるはずです。

この連想配列を使用する同僚は、配列の取得/設定操作のためにロック/ロック解除したくないので、他に選択肢がないと仮定します。また、boost::shared_ptr を除いて、boost を使用することはなぜか許可されていません。

TimedMap インスタンスはいくつかの場所でのみ使用する必要があるため、インスタンスごとにスレッドを作成し、すべての要素を定期的にチェックして、キーと値のペアが期限切れになった場合に削除できるようにすることにしました。

これは、TimedMap のユーザーが使用する方法です。

typedef TimedMap<int, boost::shared_ptr<Foo> > TMAP;
TMAP m(5);      // each key, value pair will have 5 seconds lifetime.

m.set(0, boost::shared_ptr<Foo>(new Foo()));

{ 
  TMAP::Ptr ptr = m.get(0);  // index 0, and value is locked at this time
  if (ptr) { // if it was in the map,
    // use (*ptr).xxx() to access Foo::xxx.
  }
} // now, 'ptr' is gone, so the lock will be gone.

今のところ、スレッドのことを除いて、残りは実装したと思います。ロックには 2 つのタイプがあり、各 TimedMap インスタンスごとに 1 つのロックがあり、要素ごとにロックがあり、各要素を保護するために使用されます。

問題は、TimedMap インスタンス デストラクタが呼び出されたときに、不明な理由でメイン ロックがロック状態になることがあるということです。しばらく調べましたが、何も見つかりませんでした。

標準コンテナの要素にロックを配置することは知っていますが、今のところより良い解決策は考えられません。

これで私を助けてもらえますか?それをコンパイルするには、

$ g++ timedmap.cpp

申し訳ありませんが、ソースを短く要約することはできませんでした。

timedmap.h:

#include <map>
#include <stdexcept>

#include <string>

#include <string.h>
#include <pthread.h>
#include <errno.h>

#define M_LOCK(m)       ((m).lock())
#define M_UNLOCK(m)     ((m).unlock())
#define M_TRYLOCK(m)    ((m).trylock())

template <typename K, typename V>
class TimedMap {
  class MUTEX {
    ::pthread_mutex_t lck_;

  public:
    MUTEX(const MUTEX &m) {
      // copy CTOR
      int type = PTHREAD_MUTEX_DEFAULT;
      ::pthread_mutexattr_t attr;
      ::pthread_mutexattr_init(&attr);
      ::pthread_mutexattr_settype(&attr, type);
      ::pthread_mutex_init(&lck_, &attr);
      ::pthread_mutexattr_destroy(&attr);
    }

    MUTEX(int type = PTHREAD_MUTEX_DEFAULT) {
      ::pthread_mutexattr_t attr;
      ::pthread_mutexattr_init(&attr);
      ::pthread_mutexattr_settype(&attr, type);
      ::pthread_mutex_init(&lck_, &attr);
      ::pthread_mutexattr_destroy(&attr);
    }

    ~MUTEX() {
      int ret = ::pthread_mutex_destroy(&lck_);
      if (ret) {
        // pthread_mutex_destroy() failed
        if (ret == EBUSY) {
          // locked?
        }
        //abort();
      }
    }

    int lock() {
      int ret = pthread_mutex_lock(&lck_);
      if (ret) {                // lock failed
        abort();
      }
      return ret;
    }

    int unlock() {
      int ret = pthread_mutex_unlock(&lck_);
      if (ret) {                // unlock failed
        abort();
      }
      return ret;
    }

    bool trylock() {
      int ret = pthread_mutex_trylock(&lck_);
      if (ret != 0 && ret != EBUSY) { // trylock failed
        abort();
      }
      return ret ? false : true;
    }
  };

  struct TMENT {
    time_t tm_;
    MUTEX m_;
    V val_;

    TMENT() : tm_(time(0)), m_(), val_() {}
    TMENT(const V &val) : tm_(time(0)), m_(), val_(val) {
    }
    void refresh() { tm_ = time(0); }
    bool expired(time_t expiration) {
      time_t now = ::time(0);
      return (now - tm_) > expiration;
    }
  };

  typedef std::map<K, TMENT> map_type;

  typedef typename map_type::key_type key_type;
  typedef typename map_type::mapped_type mapped_type;
  typedef typename map_type::value_type value_type;

  TimedMap(const TimedMap &);
  map_type map_;
  MUTEX mlock_;
  int expiration_;
  bool refresh_;

public:
  explicit TimedMap(int expiration)
    : map_(), mlock_(PTHREAD_MUTEX_ERRORCHECK),
      expiration_(expiration), refresh_(false) {
  }

  ~TimedMap() {
    clear();
  }

  void clear() {
    M_LOCK(mlock_);
    typename map_type::iterator i = map_.begin();
    while (i != map_.end()) {
      TMENT &ent = (*i).second;
      M_LOCK(ent.m_);
      M_UNLOCK(ent.m_);
      map_.erase(i++);
    }
    M_UNLOCK(mlock_);
  }

  class Ptr {
    TMENT *ent_;

    mutable bool owned_;

    bool yield() const {
      if (!owned_) {
        // Ptr can't yield ownership when it doesn't");
        abort();
      }
      owned_ = false;
      return true;
    }

    Ptr &operator=(const Ptr &ptr);

  public:
    Ptr(const Ptr &ptr) : ent_(ptr.ent_) {
      owned_ = ptr.yield();
    }
    Ptr() : ent_(0), owned_(false) {}

    Ptr(TMENT *ent) : ent_(ent), owned_(true) {}

    ~Ptr() {
      if (owned_)
        M_UNLOCK(ent_->m_);
    }

    V *operator->() {
      if (!owned_)
        throw std::out_of_range("not found");
      return &(ent_->val_);
    }

    V &operator*() {
      if (!owned_)
        throw std::out_of_range("not found");
      return ent_->val_;
    }

    operator bool() {
      if (owned_)
        return true;
      return false;
    }
  };

  void set(const key_type &k, const V &v) {
    M_LOCK(mlock_);
    typename map_type::iterator i = map_.find(k);
    if (i == map_.end()) {
      map_[k] = TMENT(v);
    }
    else {
      TMENT &ent = (*i).second;
      M_LOCK(ent.m_);
      ent.refresh();
      ent.val_ = v;
      M_UNLOCK(ent.m_);
    }
    M_UNLOCK(mlock_);
  }

  Ptr get(const key_type &k) {
    M_LOCK(mlock_);

    typename map_type::iterator i = map_.find(k);

    if (i == map_.end()) {
      // key not found
      M_UNLOCK(mlock_);
      return Ptr();
    }

    TMENT &ent = (*i).second;

    M_LOCK(ent.m_);

    if (ent.expired(expiration_)) {
      // key expired
      M_UNLOCK(ent.m_);
      return Ptr();
    }
    else if (refresh_)
      ent.refresh();

    M_UNLOCK(mlock_);

    // Intentionally leave the 'ent' as locked state.
    return Ptr(&ent);
  }
};

timedmap.cpp:

#include <cstdio>
#include <boost/shared_ptr.hpp>
#include <time.h>

#include "timedmap.h"

class Foo {
public:
  Foo() {
  }
  ~Foo() {
  }

  const char *what() {
    return "foo";
  }

};

typedef TimedMap<int, boost::shared_ptr<Foo> > TMAP;

int
main(void)
{
  {
    TMAP m(5);

    int key = 0;
    {
      m.set(key, boost::shared_ptr<Foo>(new Foo()));
    }

    bool loop = true;

    {
      while (loop) {
        {
          TMAP::Ptr ptr = m.get(key);

          if (ptr) {
            fprintf(stderr, "main: key(%d) = %s\n", key, (*ptr)->what());
          }
          else {
            fprintf(stderr, "main: key(%d) was not there\n", key);
            break;
          }
        }
        //break;
        usleep(100000);
        //sleep(1);
      }
    }
    m.clear();
  }
  return 0;
}
4

1 に答える 1