Использование std :: atomicкак забор - PullRequest
0 голосов
/ 23 мая 2019

Я написал класс Resource ниже для асинхронного менеджера ресурсов. Во время загрузки объект Resource создается с ресурсом по умолчанию (например, черной текстурой), поэтому основной поток, который будет использовать этот ресурс, не должен ждать, даже если он является пустым ресурсом. Как только загрузка закончится, поток загрузчика вызовет setResource () и установит базовый ресурс для вновь загруженного. Но есть и право собственности. Таким образом, когда загрузка будет завершена, вновь назначенный ресурс будет принадлежать классу Resource, чтобы его можно было освободить при уничтожении Resource или при вызове другого setResource, поскольку файл на диске изменился и нуждается в перезагрузке.

template <typename T>
class Resource {
 protected:
  // Underlying object
  std::atomic<T*> resource;

  // Do I own resource
  bool isOwner;

  // Id of resource for faster mapping
  uint64_t id;

  // Name
  std::string name;

 public:
  Resource(T* res) : resource(res), isOwner(false), id(0), name("non") {}

  Resource(std::unique_ptr<T>&& res)
      : resource(res.release()), isOwner(true), id(0), name("non") {}

  ~Resource() {
    if (isOwner) delete resource.load(std::memory_order_acquire);
  }

  Resource(Resource&&) = default;

  Resource(const Resource&) = delete;
  Resource& operator=(const Resource&) = delete;

  T* getResource() { 
    return resource.load(std::memory_order_acquire);
  }

  void setResource(T* res, bool own = false) {
    if (isOwner) { 
      delete resource.load(std::memory_order_acquire);
    }

    isOwner = own;
    resource.store(res, std::memory_order_release);
  }

  void setResource(std::unique_ptr<T>&& res) {
    if (isOwner) { 
      delete resource.load(std::memory_order_acquire);
    }

    isOwner = true;
    resource.store(res.release(), std::memory_order_release);
  }

  void setId(uint64_t idd) { id = idd; }
};

Есть ли гонка данных на isOwner или в этом случае atomic.store () выступает в качестве ограждения? Или я должен изменить весь подход к владению и просто использовать std::atomic<std::shared_ptr<T>>, который я не знаю, выполнимо ли?

1 Ответ

1 голос
/ 23 мая 2019

Когда код выполняет delete resource.load(std::memory_order_acquire);, другой ресурс все еще может использовать ресурс, так что это условие гонки.

Один простой обходной путь - никогда не удалять существующий ресурс.Вы можете иметь эту черную текстуру в качестве глобального объекта со статической продолжительностью хранения, которая остается активной в течение всего выполнения вашего приложения.старый ресурс автоматически уничтожается, когда его использует последний пользователь.Например:

struct NullDeleter {
    template<class T>
    void operator()(T const&) {}
};

template<typename T>
class Resource {
protected:
    std::shared_ptr<T> resource;
public:
    Resource(T& res) : resource(&res, NullDeleter{}) {}

    Resource(std::shared_ptr<T> const& res) : resource(res) {}

    std::shared_ptr<T> getResource() {
        return atomic_load(&resource);
    }

    void setResource(std::shared_ptr<T> const& res) {
        atomic_store(&resource, res);
    }
};
...