<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/media_transfer_protocol/media_transfer_protocol_manager.h"

#include <map>
#include <queue>
#include <set>
#include <utility>

#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/stl_util.h"
#include "chrome/browser/media_transfer_protocol/media_transfer_protocol_daemon_client.h"
#include "chrome/browser/media_transfer_protocol/mtp_file_entry.pb.h"
#include "chrome/browser/media_transfer_protocol/mtp_storage_info.pb.h"
#include "content/public/browser/browser_thread.h"

#if defined(OS_CHROMEOS)
#include "chromeos/dbus/dbus_thread_manager.h"
#else
#include "dbus/bus.h"
#endif

using content::BrowserThread;

namespace chrome {

namespace {

MediaTransferProtocolManager* g_media_transfer_protocol_manager = NULL;

// The MediaTransferProtocolManager implementation.
class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager {
 public:
  MediaTransferProtocolManagerImpl() : weak_ptr_factory_(this) {
    dbus::Bus* bus = NULL;
#if defined(OS_CHROMEOS)
    chromeos::DBusThreadManager* dbus_thread_manager =
        chromeos::DBusThreadManager::Get();
    bus = dbus_thread_manager->GetSystemBus();
    if (!bus)
      return;
#else
    dbus::Bus::Options options;
    options.bus_type = dbus::Bus::SYSTEM;
    options.connection_type = dbus::Bus::PRIVATE;
    options.dbus_thread_message_loop_proxy =
        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
    session_bus_ = new dbus::Bus(options);
    bus = session_bus_.get();
#endif

    DCHECK(bus);
    mtp_client_.reset(
        MediaTransferProtocolDaemonClient::Create(bus, false /* not stub */));

    // Set up signals and start initializing |storage_info_map_|.
    mtp_client_->SetUpConnections(
        base::Bind(&MediaTransferProtocolManagerImpl::OnStorageChanged,
                   weak_ptr_factory_.GetWeakPtr()));
    mtp_client_->EnumerateStorage(
        base::Bind(&MediaTransferProtocolManagerImpl::OnEnumerateStorage,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&base::DoNothing));
  }

  virtual ~MediaTransferProtocolManagerImpl() {
  }

  // MediaTransferProtocolManager override.
  virtual void AddObserver(Observer* observer) OVERRIDE {
    observers_.AddObserver(observer);
  }

  // MediaTransferProtocolManager override.
  virtual void RemoveObserver(Observer* observer) OVERRIDE {
    observers_.RemoveObserver(observer);
  }

  // MediaTransferProtocolManager override.
  const std::vector<std::string> GetStorages() const OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    std::vector<std::string> storages;
    for (StorageInfoMap::const_iterator it = storage_info_map_.begin();
         it != storage_info_map_.end();
         ++it) {
      storages.push_back(it->first);
    }
    return storages;
  }

  // MediaTransferProtocolManager override.
  virtual const MtpStorageInfo* GetStorageInfo(
      const std::string& storage_name) const OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    StorageInfoMap::const_iterator it = storage_info_map_.find(storage_name);
    if (it == storage_info_map_.end())
      return NULL;
    return &it->second;
  }

  // MediaTransferProtocolManager override.
  virtual void OpenStorage(const std::string& storage_name,
                           const std::string& mode,
                           const OpenStorageCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(storage_info_map_, storage_name)) {
      callback.Run("", true);
      return;
    }
    open_storage_callbacks_.push(callback);
    mtp_client_->OpenStorage(
        storage_name,
        mode,
        base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorage,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorageError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void CloseStorage(const std::string& storage_handle,
                            const CloseStorageCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(true);
      return;
    }
    close_storage_callbacks_.push(std::make_pair(callback, storage_handle));
    mtp_client_->CloseStorage(
        storage_handle,
        base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorage,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorageError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadDirectoryByPath(
      const std::string& storage_handle,
      const std::string& path,
      const ReadDirectoryCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::vector<MtpFileEntry>(), true);
      return;
    }
    read_directory_callbacks_.push(callback);
    mtp_client_->ReadDirectoryByPath(
        storage_handle,
        path,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadDirectoryById(
      const std::string& storage_handle,
      uint32 file_id,
      const ReadDirectoryCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::vector<MtpFileEntry>(), true);
      return;
    }
    read_directory_callbacks_.push(callback);
    mtp_client_->ReadDirectoryById(
        storage_handle,
        file_id,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadFileByPath(const std::string& storage_handle,
                              const std::string& path,
                              const ReadFileCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::string(), true);
      return;
    }
    read_file_callbacks_.push(callback);
    mtp_client_->ReadFileByPath(
        storage_handle,
        path,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadFileById(const std::string& storage_handle,
                            uint32 file_id,
                            const ReadFileCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::string(), true);
      return;
    }
    read_file_callbacks_.push(callback);
    mtp_client_->ReadFileById(
        storage_handle,
        file_id,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  virtual void GetFileInfoByPath(const std::string& storage_handle,
                                 const std::string& path,
                                 const GetFileInfoCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(MtpFileEntry(), true);
      return;
    }
    get_file_info_callbacks_.push(callback);
    mtp_client_->GetFileInfoByPath(
        storage_handle,
        path,
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  virtual void GetFileInfoById(const std::string& storage_handle,
                               uint32 file_id,
                               const GetFileInfoCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(MtpFileEntry(), true);
      return;
    }
    get_file_info_callbacks_.push(callback);
    mtp_client_->GetFileInfoById(
        storage_handle,
        file_id,
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

 private:
  // Map of storage names to storage info.
  typedef std::map<std::string, MtpStorageInfo> StorageInfoMap;
  // Callback queues - DBus communication is in-order, thus callbacks are
  // received in the same order as the requests.
  typedef std::queue<OpenStorageCallback> OpenStorageCallbackQueue;
  // (callback, handle)
  typedef std::queue<std::pair<CloseStorageCallback, std::string>
                    > CloseStorageCallbackQueue;
  typedef std::queue<ReadDirectoryCallback> ReadDirectoryCallbackQueue;
  typedef std::queue<ReadFileCallback> ReadFileCallbackQueue;
  typedef std::queue<GetFileInfoCallback> GetFileInfoCallbackQueue;

  void OnStorageChanged(bool is_attach, const std::string& storage_name) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (is_attach) {
      mtp_client_->GetStorageInfo(
          storage_name,
          base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo,
                     weak_ptr_factory_.GetWeakPtr()),
          base::Bind(&base::DoNothing));
      return;
    }

    // Detach case.
    StorageInfoMap::iterator it = storage_info_map_.find(storage_name);
    if (it == storage_info_map_.end()) {
      // This might happen during initialization when |storage_info_map_| has
      // not been fully populated yet?
      return;
    }
    storage_info_map_.erase(it);
    FOR_EACH_OBSERVER(Observer,
                      observers_,
                      StorageChanged(false /* detach */, storage_name));
  }

  void OnEnumerateStorage(const std::vector<std::string>& storage_names) {
    for (size_t i = 0; i < storage_names.size(); ++i) {
      mtp_client_->GetStorageInfo(
          storage_names[i],
          base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo,
                     weak_ptr_factory_.GetWeakPtr()),
          base::Bind(&base::DoNothing));
    }
  }

  void OnGetStorageInfo(const MtpStorageInfo& storage_info) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    const std::string& storage_name = storage_info.storage_name();
    if (ContainsKey(storage_info_map_, storage_name)) {
      // This should not happen, since MediaTransferProtocolManagerImpl should
      // only call EnumerateStorage() once, which populates |storage_info_map_|
      // with the already-attached devices.
      // After that, all incoming signals are either for new storage
      // attachments, which should not be in |storage_info_map_|, or for
      // storage detachements, which do not add to |storage_info_map_|.
      NOTREACHED();
      return;
    }

    // New storage. Add it and let the observers know.
    storage_info_map_.insert(std::make_pair(storage_name, storage_info));
    FOR_EACH_OBSERVER(Observer,
                      observers_,
                      StorageChanged(true /* is attach */, storage_name));
  }

  void OnOpenStorage(const std::string& handle) {
    if (!ContainsKey(handles_, handle)) {
      handles_.insert(handle);
      open_storage_callbacks_.front().Run(handle, false);
    } else {
      NOTREACHED();
      open_storage_callbacks_.front().Run("", true);
    }
    open_storage_callbacks_.pop();
  }

  void OnOpenStorageError() {
    open_storage_callbacks_.front().Run("", true);
    open_storage_callbacks_.pop();
  }

  void OnCloseStorage() {
    const std::string& handle = close_storage_callbacks_.front().second;
    if (ContainsKey(handles_, handle)) {
      handles_.erase(handle);
      close_storage_callbacks_.front().first.Run(false);
    } else {
      NOTREACHED();
      close_storage_callbacks_.front().first.Run(true);
    }
    close_storage_callbacks_.pop();
  }

  void OnCloseStorageError() {
    close_storage_callbacks_.front().first.Run(true);
    close_storage_callbacks_.pop();
  }

  void OnReadDirectory(const std::vector<MtpFileEntry>& file_entries) {
    read_directory_callbacks_.front().Run(file_entries, false);
    read_directory_callbacks_.pop();
  }

  void OnReadDirectoryError() {
    read_directory_callbacks_.front().Run(std::vector<MtpFileEntry>(), true);
    read_directory_callbacks_.pop();
  }

  void OnReadFile(const std::string& data) {
    read_file_callbacks_.front().Run(data, false);
    read_file_callbacks_.pop();
  }

  void OnReadFileError() {
    read_file_callbacks_.front().Run(std::string(), true);
    read_file_callbacks_.pop();
  }

  void OnGetFileInfo(const MtpFileEntry& entry) {
    get_file_info_callbacks_.front().Run(entry, false);
    get_file_info_callbacks_.pop();
  }

  void OnGetFileInfoError() {
    get_file_info_callbacks_.front().Run(MtpFileEntry(), true);
    get_file_info_callbacks_.pop();
  }

  // Mtpd DBus client.
  scoped_ptr<chrome::MediaTransferProtocolDaemonClient> mtp_client_;

#if !defined(OS_CHROMEOS)
  // And a D-Bus session for talking to mtpd.
  scoped_refptr<dbus::Bus> session_bus_;
#endif

  // Device attachment / detachment observers.
  ObserverList<Observer> observers_;

  base::WeakPtrFactory<MediaTransferProtocolManagerImpl> weak_ptr_factory_;

  // Everything below is only accessed on the UI thread.

  // Map to keep track of attached storages by name.
  StorageInfoMap storage_info_map_;

  // Set of open storage handles.
  std::set<std::string> handles_;

  // Queued callbacks.
  OpenStorageCallbackQueue open_storage_callbacks_;
  CloseStorageCallbackQueue close_storage_callbacks_;
  ReadDirectoryCallbackQueue read_directory_callbacks_;
  ReadFileCallbackQueue read_file_callbacks_;
  GetFileInfoCallbackQueue get_file_info_callbacks_;

  DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolManagerImpl);
};

}  // namespace

// static
void MediaTransferProtocolManager::Initialize() {
  if (g_media_transfer_protocol_manager) {
    LOG(WARNING) << "MediaTransferProtocolManager was already initialized";
    return;
  }
  g_media_transfer_protocol_manager = new MediaTransferProtocolManagerImpl();
  VLOG(1) << "MediaTransferProtocolManager initialized";
}

// static
void MediaTransferProtocolManager::Shutdown() {
  if (!g_media_transfer_protocol_manager) {
    LOG(WARNING) << "MediaTransferProtocolManager::Shutdown() called with "
                 << "NULL manager";
    return;
  }
  delete g_media_transfer_protocol_manager;
  g_media_transfer_protocol_manager = NULL;
  VLOG(1) << "MediaTransferProtocolManager Shutdown completed";
}

// static
MediaTransferProtocolManager* MediaTransferProtocolManager::GetInstance() {
  return g_media_transfer_protocol_manager;
}

}  // namespace chrome
<commit_msg>Linux: Do not attempt to talk to mtpd during tests. It causes too much error spew in bot logs.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/media_transfer_protocol/media_transfer_protocol_manager.h"

#include <map>
#include <queue>
#include <set>
#include <utility>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/stl_util.h"
#include "chrome/browser/media_transfer_protocol/media_transfer_protocol_daemon_client.h"
#include "chrome/browser/media_transfer_protocol/mtp_file_entry.pb.h"
#include "chrome/browser/media_transfer_protocol/mtp_storage_info.pb.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/browser_thread.h"

#if defined(OS_CHROMEOS)
#include "chromeos/dbus/dbus_thread_manager.h"
#else
#include "dbus/bus.h"
#endif

using content::BrowserThread;

namespace chrome {

namespace {

MediaTransferProtocolManager* g_media_transfer_protocol_manager = NULL;

// The MediaTransferProtocolManager implementation.
class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager {
 public:
  MediaTransferProtocolManagerImpl() : weak_ptr_factory_(this) {
    if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType))
      return;

    dbus::Bus* bus = NULL;
#if defined(OS_CHROMEOS)
    chromeos::DBusThreadManager* dbus_thread_manager =
        chromeos::DBusThreadManager::Get();
    bus = dbus_thread_manager->GetSystemBus();
    if (!bus)
      return;
#else
    dbus::Bus::Options options;
    options.bus_type = dbus::Bus::SYSTEM;
    options.connection_type = dbus::Bus::PRIVATE;
    options.dbus_thread_message_loop_proxy =
        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
    session_bus_ = new dbus::Bus(options);
    bus = session_bus_.get();
#endif

    DCHECK(bus);
    mtp_client_.reset(
        MediaTransferProtocolDaemonClient::Create(bus, false /* not stub */));

    // Set up signals and start initializing |storage_info_map_|.
    mtp_client_->SetUpConnections(
        base::Bind(&MediaTransferProtocolManagerImpl::OnStorageChanged,
                   weak_ptr_factory_.GetWeakPtr()));
    mtp_client_->EnumerateStorage(
        base::Bind(&MediaTransferProtocolManagerImpl::OnEnumerateStorage,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&base::DoNothing));
  }

  virtual ~MediaTransferProtocolManagerImpl() {
  }

  // MediaTransferProtocolManager override.
  virtual void AddObserver(Observer* observer) OVERRIDE {
    observers_.AddObserver(observer);
  }

  // MediaTransferProtocolManager override.
  virtual void RemoveObserver(Observer* observer) OVERRIDE {
    observers_.RemoveObserver(observer);
  }

  // MediaTransferProtocolManager override.
  const std::vector<std::string> GetStorages() const OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    std::vector<std::string> storages;
    for (StorageInfoMap::const_iterator it = storage_info_map_.begin();
         it != storage_info_map_.end();
         ++it) {
      storages.push_back(it->first);
    }
    return storages;
  }

  // MediaTransferProtocolManager override.
  virtual const MtpStorageInfo* GetStorageInfo(
      const std::string& storage_name) const OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    StorageInfoMap::const_iterator it = storage_info_map_.find(storage_name);
    if (it == storage_info_map_.end())
      return NULL;
    return &it->second;
  }

  // MediaTransferProtocolManager override.
  virtual void OpenStorage(const std::string& storage_name,
                           const std::string& mode,
                           const OpenStorageCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(storage_info_map_, storage_name)) {
      callback.Run("", true);
      return;
    }
    open_storage_callbacks_.push(callback);
    mtp_client_->OpenStorage(
        storage_name,
        mode,
        base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorage,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnOpenStorageError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void CloseStorage(const std::string& storage_handle,
                            const CloseStorageCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(true);
      return;
    }
    close_storage_callbacks_.push(std::make_pair(callback, storage_handle));
    mtp_client_->CloseStorage(
        storage_handle,
        base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorage,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnCloseStorageError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadDirectoryByPath(
      const std::string& storage_handle,
      const std::string& path,
      const ReadDirectoryCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::vector<MtpFileEntry>(), true);
      return;
    }
    read_directory_callbacks_.push(callback);
    mtp_client_->ReadDirectoryByPath(
        storage_handle,
        path,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadDirectoryById(
      const std::string& storage_handle,
      uint32 file_id,
      const ReadDirectoryCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::vector<MtpFileEntry>(), true);
      return;
    }
    read_directory_callbacks_.push(callback);
    mtp_client_->ReadDirectoryById(
        storage_handle,
        file_id,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectory,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadDirectoryError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadFileByPath(const std::string& storage_handle,
                              const std::string& path,
                              const ReadFileCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::string(), true);
      return;
    }
    read_file_callbacks_.push(callback);
    mtp_client_->ReadFileByPath(
        storage_handle,
        path,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  // MediaTransferProtocolManager override.
  virtual void ReadFileById(const std::string& storage_handle,
                            uint32 file_id,
                            const ReadFileCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(std::string(), true);
      return;
    }
    read_file_callbacks_.push(callback);
    mtp_client_->ReadFileById(
        storage_handle,
        file_id,
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFile,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnReadFileError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  virtual void GetFileInfoByPath(const std::string& storage_handle,
                                 const std::string& path,
                                 const GetFileInfoCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(MtpFileEntry(), true);
      return;
    }
    get_file_info_callbacks_.push(callback);
    mtp_client_->GetFileInfoByPath(
        storage_handle,
        path,
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  virtual void GetFileInfoById(const std::string& storage_handle,
                               uint32 file_id,
                               const GetFileInfoCallback& callback) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!ContainsKey(handles_, storage_handle)) {
      callback.Run(MtpFileEntry(), true);
      return;
    }
    get_file_info_callbacks_.push(callback);
    mtp_client_->GetFileInfoById(
        storage_handle,
        file_id,
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfo,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&MediaTransferProtocolManagerImpl::OnGetFileInfoError,
                   weak_ptr_factory_.GetWeakPtr()));
  }

 private:
  // Map of storage names to storage info.
  typedef std::map<std::string, MtpStorageInfo> StorageInfoMap;
  // Callback queues - DBus communication is in-order, thus callbacks are
  // received in the same order as the requests.
  typedef std::queue<OpenStorageCallback> OpenStorageCallbackQueue;
  // (callback, handle)
  typedef std::queue<std::pair<CloseStorageCallback, std::string>
                    > CloseStorageCallbackQueue;
  typedef std::queue<ReadDirectoryCallback> ReadDirectoryCallbackQueue;
  typedef std::queue<ReadFileCallback> ReadFileCallbackQueue;
  typedef std::queue<GetFileInfoCallback> GetFileInfoCallbackQueue;

  void OnStorageChanged(bool is_attach, const std::string& storage_name) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (is_attach) {
      mtp_client_->GetStorageInfo(
          storage_name,
          base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo,
                     weak_ptr_factory_.GetWeakPtr()),
          base::Bind(&base::DoNothing));
      return;
    }

    // Detach case.
    StorageInfoMap::iterator it = storage_info_map_.find(storage_name);
    if (it == storage_info_map_.end()) {
      // This might happen during initialization when |storage_info_map_| has
      // not been fully populated yet?
      return;
    }
    storage_info_map_.erase(it);
    FOR_EACH_OBSERVER(Observer,
                      observers_,
                      StorageChanged(false /* detach */, storage_name));
  }

  void OnEnumerateStorage(const std::vector<std::string>& storage_names) {
    for (size_t i = 0; i < storage_names.size(); ++i) {
      mtp_client_->GetStorageInfo(
          storage_names[i],
          base::Bind(&MediaTransferProtocolManagerImpl::OnGetStorageInfo,
                     weak_ptr_factory_.GetWeakPtr()),
          base::Bind(&base::DoNothing));
    }
  }

  void OnGetStorageInfo(const MtpStorageInfo& storage_info) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    const std::string& storage_name = storage_info.storage_name();
    if (ContainsKey(storage_info_map_, storage_name)) {
      // This should not happen, since MediaTransferProtocolManagerImpl should
      // only call EnumerateStorage() once, which populates |storage_info_map_|
      // with the already-attached devices.
      // After that, all incoming signals are either for new storage
      // attachments, which should not be in |storage_info_map_|, or for
      // storage detachements, which do not add to |storage_info_map_|.
      NOTREACHED();
      return;
    }

    // New storage. Add it and let the observers know.
    storage_info_map_.insert(std::make_pair(storage_name, storage_info));
    FOR_EACH_OBSERVER(Observer,
                      observers_,
                      StorageChanged(true /* is attach */, storage_name));
  }

  void OnOpenStorage(const std::string& handle) {
    if (!ContainsKey(handles_, handle)) {
      handles_.insert(handle);
      open_storage_callbacks_.front().Run(handle, false);
    } else {
      NOTREACHED();
      open_storage_callbacks_.front().Run("", true);
    }
    open_storage_callbacks_.pop();
  }

  void OnOpenStorageError() {
    open_storage_callbacks_.front().Run("", true);
    open_storage_callbacks_.pop();
  }

  void OnCloseStorage() {
    const std::string& handle = close_storage_callbacks_.front().second;
    if (ContainsKey(handles_, handle)) {
      handles_.erase(handle);
      close_storage_callbacks_.front().first.Run(false);
    } else {
      NOTREACHED();
      close_storage_callbacks_.front().first.Run(true);
    }
    close_storage_callbacks_.pop();
  }

  void OnCloseStorageError() {
    close_storage_callbacks_.front().first.Run(true);
    close_storage_callbacks_.pop();
  }

  void OnReadDirectory(const std::vector<MtpFileEntry>& file_entries) {
    read_directory_callbacks_.front().Run(file_entries, false);
    read_directory_callbacks_.pop();
  }

  void OnReadDirectoryError() {
    read_directory_callbacks_.front().Run(std::vector<MtpFileEntry>(), true);
    read_directory_callbacks_.pop();
  }

  void OnReadFile(const std::string& data) {
    read_file_callbacks_.front().Run(data, false);
    read_file_callbacks_.pop();
  }

  void OnReadFileError() {
    read_file_callbacks_.front().Run(std::string(), true);
    read_file_callbacks_.pop();
  }

  void OnGetFileInfo(const MtpFileEntry& entry) {
    get_file_info_callbacks_.front().Run(entry, false);
    get_file_info_callbacks_.pop();
  }

  void OnGetFileInfoError() {
    get_file_info_callbacks_.front().Run(MtpFileEntry(), true);
    get_file_info_callbacks_.pop();
  }

  // Mtpd DBus client.
  scoped_ptr<chrome::MediaTransferProtocolDaemonClient> mtp_client_;

#if !defined(OS_CHROMEOS)
  // And a D-Bus session for talking to mtpd.
  scoped_refptr<dbus::Bus> session_bus_;
#endif

  // Device attachment / detachment observers.
  ObserverList<Observer> observers_;

  base::WeakPtrFactory<MediaTransferProtocolManagerImpl> weak_ptr_factory_;

  // Everything below is only accessed on the UI thread.

  // Map to keep track of attached storages by name.
  StorageInfoMap storage_info_map_;

  // Set of open storage handles.
  std::set<std::string> handles_;

  // Queued callbacks.
  OpenStorageCallbackQueue open_storage_callbacks_;
  CloseStorageCallbackQueue close_storage_callbacks_;
  ReadDirectoryCallbackQueue read_directory_callbacks_;
  ReadFileCallbackQueue read_file_callbacks_;
  GetFileInfoCallbackQueue get_file_info_callbacks_;

  DISALLOW_COPY_AND_ASSIGN(MediaTransferProtocolManagerImpl);
};

}  // namespace

// static
void MediaTransferProtocolManager::Initialize() {
  if (g_media_transfer_protocol_manager) {
    LOG(WARNING) << "MediaTransferProtocolManager was already initialized";
    return;
  }
  g_media_transfer_protocol_manager = new MediaTransferProtocolManagerImpl();
  VLOG(1) << "MediaTransferProtocolManager initialized";
}

// static
void MediaTransferProtocolManager::Shutdown() {
  if (!g_media_transfer_protocol_manager) {
    LOG(WARNING) << "MediaTransferProtocolManager::Shutdown() called with "
                 << "NULL manager";
    return;
  }
  delete g_media_transfer_protocol_manager;
  g_media_transfer_protocol_manager = NULL;
  VLOG(1) << "MediaTransferProtocolManager Shutdown completed";
}

// static
MediaTransferProtocolManager* MediaTransferProtocolManager::GetInstance() {
  return g_media_transfer_protocol_manager;
}

}  // namespace chrome
<|endoftext|>


<commit_before>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/notifications/login_state_notification_blocker_chromeos.h"

#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/system/system_notifier.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "ash/wm/window_properties.h"
#include "chrome/browser/chrome_notification_types.h"
#include "content/public/browser/notification_service.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/message_center/message_center.h"

LoginStateNotificationBlockerChromeOS::LoginStateNotificationBlockerChromeOS(
    message_center::MessageCenter* message_center)
    : NotificationBlocker(message_center),
      locked_(false),
      observing_(true) {
  // This class is created in the ctor of NotificationUIManager which is created
  // when a notification is created, so ash::Shell should be initialized.
  ash::Shell::GetInstance()->AddShellObserver(this);
}

LoginStateNotificationBlockerChromeOS::
    ~LoginStateNotificationBlockerChromeOS() {
  // In some tests, the notification blockers may be removed without calling
  // OnAppTerminating().
  if (observing_ && ash::Shell::HasInstance())
    ash::Shell::GetInstance()->RemoveShellObserver(this);
}

bool LoginStateNotificationBlockerChromeOS::ShouldShowNotificationAsPopup(
    const message_center::NotifierId& notifier_id) const {
  if (ash::system_notifier::ShouldAlwaysShowPopups(notifier_id))
    return true;

  if (!locked_)
    return true;

  ash::user::LoginStatus login_status =
      ash::Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
  return login_status != ash::user::LOGGED_IN_NONE &&
      login_status != ash::user::LOGGED_IN_LOCKED;
}

void LoginStateNotificationBlockerChromeOS::OnLoginStateChanged(
    ash::user::LoginStatus login_status) {
  FOR_EACH_OBSERVER(
      NotificationBlocker::Observer, observers(), OnBlockingStateChanged());
}

void LoginStateNotificationBlockerChromeOS::OnLockStateChanged(bool locked) {
  locked_ = locked;
  FOR_EACH_OBSERVER(
      NotificationBlocker::Observer, observers(), OnBlockingStateChanged());
}

void LoginStateNotificationBlockerChromeOS::OnAppTerminating() {
  ash::Shell::GetInstance()->RemoveShellObserver(this);
  observing_ = false;
}
<commit_msg>Fixes a lock state condition of a NotificationBlocker in ChromeOS<commit_after>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/notifications/login_state_notification_blocker_chromeos.h"

#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/system/system_notifier.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "ash/wm/window_properties.h"
#include "chrome/browser/chrome_notification_types.h"
#include "content/public/browser/notification_service.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/message_center/message_center.h"

LoginStateNotificationBlockerChromeOS::LoginStateNotificationBlockerChromeOS(
    message_center::MessageCenter* message_center)
    : NotificationBlocker(message_center),
      locked_(false),
      observing_(true) {
  // This class is created in the ctor of NotificationUIManager which is created
  // when a notification is created, so ash::Shell should be initialized.
  ash::Shell::GetInstance()->AddShellObserver(this);
}

LoginStateNotificationBlockerChromeOS::
    ~LoginStateNotificationBlockerChromeOS() {
  // In some tests, the notification blockers may be removed without calling
  // OnAppTerminating().
  if (observing_ && ash::Shell::HasInstance())
    ash::Shell::GetInstance()->RemoveShellObserver(this);
}

bool LoginStateNotificationBlockerChromeOS::ShouldShowNotificationAsPopup(
    const message_center::NotifierId& notifier_id) const {
  if (ash::system_notifier::ShouldAlwaysShowPopups(notifier_id))
    return true;

  ash::user::LoginStatus login_status =
      ash::Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
  return login_status != ash::user::LOGGED_IN_NONE &&
      login_status != ash::user::LOGGED_IN_LOCKED &&
      !locked_;
}

void LoginStateNotificationBlockerChromeOS::OnLoginStateChanged(
    ash::user::LoginStatus login_status) {
  FOR_EACH_OBSERVER(
      NotificationBlocker::Observer, observers(), OnBlockingStateChanged());
}

void LoginStateNotificationBlockerChromeOS::OnLockStateChanged(bool locked) {
  locked_ = locked;
  FOR_EACH_OBSERVER(
      NotificationBlocker::Observer, observers(), OnBlockingStateChanged());
}

void LoginStateNotificationBlockerChromeOS::OnAppTerminating() {
  ash::Shell::GetInstance()->RemoveShellObserver(this);
  observing_ = false;
}
<|endoftext|>


<commit_before><commit_msg>Remove a strange use of ignore_result().<commit_after><|endoftext|>


<commit_before>#include <act-common/common.h>

// ========================================================================
// ===================== Critical Macrodefinitions ========================
// ========================================================================

/*
 * FOSC = 1MHz is confugured via Flash Fuses CKSEL=1 and SUT=2.
 */

#define IBUF_SIZE    16
#define OBUF_SIZE    32
#define BAUD_RATE    4800    // 4.8kbps
#define FOSC         1000000 // 1MHz
#define TC2_PRESCALE 1       // no Timer/Counter2 prescale

#include <act-common/usart.h>
#include <act-photo/pwm.h>

#define REMUX(c) ADMUX = (3<<REFS0)|(1<<ADLAR)|(c<<MUX0)

// ========================================================================
// ===================== Initialization ===================================
// ========================================================================

inline __monitor void init()
{
    /* USART initialization */
    usart_init();
    /* Timer/Counter2 initialization */
    init_tc2_wg();
    /* ADC initialization (1/8 prescaling,
       1st channel, internal Vref, left alignment) */
    REMUX(0);
    ADCSR = (1<<ADEN)|(3<<ADPS0);

    /* Pin configuration */
    PORTB = (1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2)|
            (0<<PORTB3)|(1<<PORTB4)|(1<<PORTB5)|
            (1<<PORTB6)|(1<<PORTB7);
    DDRB = (0<<DDB0)|(0<<DDB1)|(0<<DDB2)|
           (1<<DDB3)|(0<<DDB4)|(0<<DDB5)|
           (0<<DDB6)|(0<<DDB7);
    PORTC = (0<<PORTC0)|(0<<PORTC1)|(1<<PORTC2)|
            (1<<PORTC3)|(1<<PORTC4)|(1<<PORTC5)|
            (1<<PORTC6);
    DDRC = (0<<DDC0)|(0<<DDC1)|(0<<DDC2)|
           (0<<DDC3)|(0<<DDC4)|(0<<DDC5)|
           (0<<DDC6);
    PORTD = (0<<PORTD0)|(0<<PORTD1)|(1<<PORTD2)|
            (1<<PORTD3)|(1<<PORTD4)|(1<<PORTD5)|
            (1<<PORTD6)|(1<<PORTD7);
    DDRD = (0<<DDD0)|(1<<DDD1)|(0<<DDD2)|
           (0<<DDD3)|(0<<DDD4)|(0<<DDD5)|
           (0<<DDD6)|(0<<DDD7);
}

// ========================================================================
// ===================== Common Variables =================================
// ========================================================================

/*
 * #40 <bug> <enchancement>
 *
 *     Modify coefficient format.
 *
 * Let C = one of { KP KI KS }.
 *
 *     C * x = (M * x) >> d
 *
 * Since ATMega8 does not support floating point operations,
 * and just (x << d) or (x >> d) cannot provide precise regulation.
 */

unsigned int kp_m = 0; byte kp_d = 0;   /* proportional factor */
unsigned int ki_m = 0; byte ki_d = 0;   /* integral factor     */
unsigned int ks_m = 0; byte ks_d = 0;   /* scale factor        */

#define COEF(c, x) (((long) x) * (c##_m)) >> (c##_d)

bool  pause  = false; /* paused state                          */
bool  sync   = false; /* USART async/sync transmit operation   */

byte  adc1, adc2;     /* ADC 1st and 2nd channel               */

int   int_err = 0;    /* integral error                        */
int   cur_err = 0;    /* current error                         */
int   pwm     = 0;    /* PWM                                   */

// ========================================================================
// ===================== Message Loop Variables ===========================
// ========================================================================

/*
 * #33 <enchancement>
 *
 *     Simplify IO protocol.
 *
 * The new protocol is user-friendly and quite simple.
 * The typical command starts with `METHOD`
 * (1 byte: `SET`, `GET`, `ECHO`, etc.). The next byte is
 * method-dependent. E.g. `VARIABLE` for `SET` and `GET`.
 * `SET` method also requires the variable value (1..any bytes)
 * to be set.
 *
 * COMMAND = METHOD [CONTENT]
 * METHOD = `SET` | `GET` | `ECHO` | BYTE
 * CONTENT = {
 *     METHOD=`SET`  => VARIABLE [DATA],
 *     METHOD=`GET`  => VARIABLE,
 *     METHOD=`ECHO` => BYTE,
 *     METHOD=BYTE   => any
 * }
 * VARIABLE = BYTE
 * DATA = any
 *
 * BYTE = 1 any byte
 * any = any bytes
 */

byte command_method;
bool command_present = false;
byte command_variable;
bool command_variable_present = false;

/*
 * #47 <enchancement>
 *
 *     Add `COEFS` variable and `PACKET` output format
 *     to support controller-gui tool.
 *
 * Data packet GET format is:
 *     <0 ADC1 ADC2 CUR_ERR INT_ERR PWM OCR2>
 * Coefficient packet SET format is:
 *     <KP KI KS>
 */

#define METHOD_GET     (byte) 0
#define METHOD_SET     (byte) 1
#define METHOD_ECHO    (byte) 2

// SET
#define VAR_PAUSE      (byte) 0
#define VAR_SYNC_USART (byte) 1
#define VAR_KP         (byte) 2
#define VAR_KI         (byte) 3
#define VAR_KS         (byte) 4
#define VAR_COEFS      (byte) 5

// GET
#define NO_OUTPUT      (byte) 0
#define VAR_ADC1       (byte) 1
#define VAR_ADC2       (byte) 2
#define VAR_INT_ERR    (byte) 3
#define VAR_CUR_ERR    (byte) 4
#define VAR_PWM        (byte) 5
#define VAR_OCR2       (byte) 6
#define VAR_PACKET     (byte) 7

byte output_mode = NO_OUTPUT;

// ========================================================================
// ===================== Utility Functions ================================
// ========================================================================

void send_all(byte *data, byte size)
{
    if (!sync)
    {
        transmit_all(data, size);
    }
    else
    {
        while (!transmit_all(data, size)) // wait in sync mode
            ;
    }
}

void send_byte(byte data)
{
    send_all(&data, 1);
}

void send_int(unsigned int data)
{
    byte hi_lo[2] = { (byte) (data >> 8), (byte) (data & 0xff) };
    send_all(hi_lo, 2);
}

void send_packet(/* byte adc1, byte adc2, int cur_err, int int_err, int pwm, byte OCR2 */)
{
    /*
     * #53 <enchancement>
     *     Review the packet format.
     *
     * The new packet format is < delimiter | data | checksum >,
     * where checksum is calculated as delimiter ^ adc1 ^ (cerr & 0xff) ^ ocr2.
     */

    byte packet[11] = {
       103,
       adc1,
       adc2,
       ((unsigned int) cur_err) >> 8, cur_err & 0xff,
       ((unsigned int) int_err) >> 8, int_err & 0xff,
       ((unsigned int) pwm) >> 8,     pwm     & 0xff,
       OCR2
    };
    packet[10] = packet[0] ^ packet[1] ^ packet[4] ^ packet[9];
    send_all(packet, 11);
}

// ========================================================================
// ===================== Command Processors ===============================
// ========================================================================

bool process_set_variable()
{
    byte packet[9];
    switch(command_variable)
    {
    case VAR_PAUSE:
        if (!receive(&packet[0])) return false; // wait for the value
        pause = packet[0];
        break;
    case VAR_SYNC_USART:
        if (!receive(packet)) return false; // wait for the value
        sync = packet[0];
        break;
    case VAR_KP:
        if (!receive_all(packet, 3)) return false; // wait for the value
        kp_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        kp_d = packet[2];
        break;
    case VAR_KI:
        if (!receive_all(packet, 3)) return false; // wait for the value
        ki_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        ki_d = packet[2];
        break;
    case VAR_KS:
        if (!receive_all(packet, 3)) return false; // wait for the value
        ks_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        ks_d = packet[2];
        break;
    case VAR_COEFS:
        if (!receive_all(packet, 9)) return false; // wait for the value
        kp_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        kp_d = packet[2];
        ki_m = (((unsigned int) packet[3]) << 8) | (packet[4]);
        ki_d = packet[5];
        ks_m = (((unsigned int) packet[6]) << 8) | (packet[7]);
        ks_d = packet[8];
        break;
    }
    return true;
}

void process_command()
{
    byte echo_value;
    switch(command_method)
    {
    case METHOD_ECHO:
        if (!receive(&echo_value)) return; // wait for the value to send back
        send_byte(echo_value);
        break;
    case METHOD_GET:
        if (!receive(&output_mode)) return; // wait for the variable
        break;
    case METHOD_SET:
        // wait for the variable
        if (!command_variable_present)
        {
            if (!(command_variable_present = receive(&command_variable))) return;
        }
        if (!process_set_variable()) return; // wait for the argument(s)
        break;
    }
    command_present = false;
    command_variable_present = false;
}

// ========================================================================
// ===================== Program Logic ====================================
// ========================================================================

void do_computations()
{
    /* Read ADC input */

    REMUX(0);                       /* 1st ADC channel                    */
    ADCSR |= (1 << ADSC);           /* Initialize single-ended conversion */
    while(!(ADCSR & (1 << ADIF)))   /* Wait for the end of the conversion */
      ;
    adc1 = ADCH;                    /* Read the 8-bit conversion result   */
    ADCSR &= ~(1 << ADIF);          /* Clear Conversion Complete flag     */

    REMUX(1);                       /* 2nd ADC channel                    */
    ADCSR |= (1 << ADSC);           /* Initialize single-ended conversion */
    while(!(ADCSR & (1 << ADIF)))   /* Wait for the end of the conversion */
      ;
    adc2 = ADCH;                    /* Read the 8-bit conversion result   */
    ADCSR &= ~(1 << ADIF);          /* Clear Conversion Complete flag     */

    /* Calculate PWM width */

    /*
     * pwm = KP * e + KI * err
     */

    cur_err = ((int)(adc2) - (int)(adc1));

    long p = COEF(kp, cur_err), q = COEF(ki, int_err);
    if (ABS(p) > INT_MAX) p = ((p < 0) ? (INT_MIN) : (INT_MAX));
    if (ABS(q) > INT_MAX) q = ((q < 0) ? (INT_MIN) : (INT_MAX));
    pwm = safe_add((int) p, (int) q);

    int_err = safe_add(int_err, cur_err);

    /* Setup PWM width */

    long t = COEF(ks, pwm) + INT_MAX;
    if (ABS(t) > INT_MAX) t = ((p < 0) ? (INT_MIN) : (INT_MAX));

    OCR2 = (((unsigned int) t) + INT_MAX) >> 8;

    /* Report back */

    switch(output_mode)
    {
    case NO_OUTPUT:
        break;
    case VAR_ADC1:
        send_byte(adc1);
        break;
    case VAR_ADC2:
        send_byte(adc2);
        break;
    case VAR_INT_ERR:
        send_int(int_err);
        break;
    case VAR_CUR_ERR:
        send_int(cur_err);
        break;
    case VAR_PWM:
        send_int(pwm);
        break;
    case VAR_OCR2:
        send_byte(OCR2);
        break;
    case VAR_PACKET:
        send_packet();
        break;
    }
}

// ========================================================================
// ===================== Program Entry Point ==============================
// ========================================================================

int main()
{

    /* Initialization here */

    init();

    __enable_interrupt();

    /* Program Loop */

    for(;;)
    {

        /* Message Loop */

        if (command_present)
        {
            process_command();
        }
        else
        {
            command_present = receive(&command_method);
        }

        /* Program Body */

        if (!pause)
        {
            do_computations();
        }

    }

    return 0;
}
<commit_msg>/mc/controller common usart.h replaced with local<commit_after>#include <act-common/common.h>

// ========================================================================
// ===================== Critical Macrodefinitions ========================
// ========================================================================

/*
 * FOSC = 1MHz is confugured via Flash Fuses CKSEL=1 and SUT=2.
 */

#define BAUD_RATE    4800    // 4.8kbps
#define FOSC         1000000 // 1MHz
#define TC2_PRESCALE 1       // no Timer/Counter2 prescale

#include <act-photo/usart.h>
#include <act-photo/pwm.h>

#define REMUX(c) ADMUX = (3<<REFS0)|(1<<ADLAR)|(c<<MUX0)

// ========================================================================
// ===================== Initialization ===================================
// ========================================================================

inline __monitor void init()
{
    /* USART initialization */
    usart_init();
    /* Timer/Counter2 initialization */
    init_tc2_wg();
    /* ADC initialization (1/8 prescaling,
       1st channel, internal Vref, left alignment) */
    REMUX(0);
    ADCSR = (1<<ADEN)|(3<<ADPS0);

    /* Pin configuration */
    PORTB = (1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2)|
            (0<<PORTB3)|(1<<PORTB4)|(1<<PORTB5)|
            (1<<PORTB6)|(1<<PORTB7);
    DDRB = (0<<DDB0)|(0<<DDB1)|(0<<DDB2)|
           (1<<DDB3)|(0<<DDB4)|(0<<DDB5)|
           (0<<DDB6)|(0<<DDB7);
    PORTC = (0<<PORTC0)|(0<<PORTC1)|(1<<PORTC2)|
            (1<<PORTC3)|(1<<PORTC4)|(1<<PORTC5)|
            (1<<PORTC6);
    DDRC = (0<<DDC0)|(0<<DDC1)|(0<<DDC2)|
           (0<<DDC3)|(0<<DDC4)|(0<<DDC5)|
           (0<<DDC6);
    PORTD = (0<<PORTD0)|(0<<PORTD1)|(1<<PORTD2)|
            (1<<PORTD3)|(1<<PORTD4)|(1<<PORTD5)|
            (1<<PORTD6)|(1<<PORTD7);
    DDRD = (0<<DDD0)|(1<<DDD1)|(0<<DDD2)|
           (0<<DDD3)|(0<<DDD4)|(0<<DDD5)|
           (0<<DDD6)|(0<<DDD7);
}

// ========================================================================
// ===================== Common Variables =================================
// ========================================================================

/*
 * #40 <bug> <enchancement>
 *
 *     Modify coefficient format.
 *
 * Let C = one of { KP KI KS }.
 *
 *     C * x = (M * x) >> d
 *
 * Since ATMega8 does not support floating point operations,
 * and just (x << d) or (x >> d) cannot provide precise regulation.
 */

unsigned int kp_m = 0; byte kp_d = 0;   /* proportional factor */
unsigned int ki_m = 0; byte ki_d = 0;   /* integral factor     */
unsigned int ks_m = 0; byte ks_d = 0;   /* scale factor        */

#define COEF(c, x) (((long) x) * (c##_m)) >> (c##_d)

bool  pause  = false; /* paused state                          */
bool  sync   = false; /* USART async/sync transmit operation   */

byte  adc1, adc2;     /* ADC 1st and 2nd channel               */

int   int_err = 0;    /* integral error                        */
int   cur_err = 0;    /* current error                         */
int   pwm     = 0;    /* PWM                                   */

// ========================================================================
// ===================== Message Loop Variables ===========================
// ========================================================================

/*
 * #33 <enchancement>
 *
 *     Simplify IO protocol.
 *
 * The new protocol is user-friendly and quite simple.
 * The typical command starts with `METHOD`
 * (1 byte: `SET`, `GET`, `ECHO`, etc.). The next byte is
 * method-dependent. E.g. `VARIABLE` for `SET` and `GET`.
 * `SET` method also requires the variable value (1..any bytes)
 * to be set.
 *
 * COMMAND = METHOD [CONTENT]
 * METHOD = `SET` | `GET` | `ECHO` | BYTE
 * CONTENT = {
 *     METHOD=`SET`  => VARIABLE [DATA],
 *     METHOD=`GET`  => VARIABLE,
 *     METHOD=`ECHO` => BYTE,
 *     METHOD=BYTE   => any
 * }
 * VARIABLE = BYTE
 * DATA = any
 *
 * BYTE = 1 any byte
 * any = any bytes
 */

byte command_method;
bool command_present = false;
byte command_variable;
bool command_variable_present = false;

/*
 * #47 <enchancement>
 *
 *     Add `COEFS` variable and `PACKET` output format
 *     to support controller-gui tool.
 *
 * Data packet GET format is:
 *     <0 ADC1 ADC2 CUR_ERR INT_ERR PWM OCR2>
 * Coefficient packet SET format is:
 *     <KP KI KS>
 */

#define METHOD_GET     (byte) 0
#define METHOD_SET     (byte) 1
#define METHOD_ECHO    (byte) 2

// SET
#define VAR_PAUSE      (byte) 0
#define VAR_SYNC_USART (byte) 1
#define VAR_KP         (byte) 2
#define VAR_KI         (byte) 3
#define VAR_KS         (byte) 4
#define VAR_COEFS      (byte) 5

// GET
#define NO_OUTPUT      (byte) 0
#define VAR_ADC1       (byte) 1
#define VAR_ADC2       (byte) 2
#define VAR_INT_ERR    (byte) 3
#define VAR_CUR_ERR    (byte) 4
#define VAR_PWM        (byte) 5
#define VAR_OCR2       (byte) 6
#define VAR_PACKET     (byte) 7

byte output_mode = NO_OUTPUT;

// ========================================================================
// ===================== Utility Functions ================================
// ========================================================================

void send_all(byte *data, byte size)
{
    if (!sync)
    {
        transmit_all(data, size);
    }
    else
    {
        while (!transmit_all(data, size)) // wait in sync mode
            ;
    }
}

void send_byte(byte data)
{
    send_all(&data, 1);
}

void send_int(unsigned int data)
{
    byte hi_lo[2] = { (byte) (data >> 8), (byte) (data & 0xff) };
    send_all(hi_lo, 2);
}

void send_packet(/* byte adc1, byte adc2, int cur_err, int int_err, int pwm, byte OCR2 */)
{
    /*
     * #53 <enchancement>
     *     Review the packet format.
     *
     * The new packet format is < delimiter | data | checksum >,
     * where checksum is calculated as delimiter ^ adc1 ^ (cerr & 0xff) ^ ocr2.
     */

    byte packet[11] = {
       103,
       adc1,
       adc2,
       ((unsigned int) cur_err) >> 8, cur_err & 0xff,
       ((unsigned int) int_err) >> 8, int_err & 0xff,
       ((unsigned int) pwm) >> 8,     pwm     & 0xff,
       OCR2
    };
    packet[10] = packet[0] ^ packet[1] ^ packet[4] ^ packet[9];
    send_all(packet, 11);
}

// ========================================================================
// ===================== Command Processors ===============================
// ========================================================================

bool process_set_variable()
{
    byte packet[9];
    switch(command_variable)
    {
    case VAR_PAUSE:
        if (!receive(&packet[0])) return false; // wait for the value
        pause = packet[0];
        break;
    case VAR_SYNC_USART:
        if (!receive(packet)) return false; // wait for the value
        sync = packet[0];
        break;
    case VAR_KP:
        if (!receive_all(packet, 3)) return false; // wait for the value
        kp_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        kp_d = packet[2];
        break;
    case VAR_KI:
        if (!receive_all(packet, 3)) return false; // wait for the value
        ki_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        ki_d = packet[2];
        break;
    case VAR_KS:
        if (!receive_all(packet, 3)) return false; // wait for the value
        ks_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        ks_d = packet[2];
        break;
    case VAR_COEFS:
        if (!receive_all(packet, 9)) return false; // wait for the value
        kp_m = (((unsigned int) packet[0]) << 8) | (packet[1]);
        kp_d = packet[2];
        ki_m = (((unsigned int) packet[3]) << 8) | (packet[4]);
        ki_d = packet[5];
        ks_m = (((unsigned int) packet[6]) << 8) | (packet[7]);
        ks_d = packet[8];
        break;
    }
    return true;
}

void process_command()
{
    byte echo_value;
    switch(command_method)
    {
    case METHOD_ECHO:
        if (!receive(&echo_value)) return; // wait for the value to send back
        send_byte(echo_value);
        break;
    case METHOD_GET:
        if (!receive(&output_mode)) return; // wait for the variable
        break;
    case METHOD_SET:
        // wait for the variable
        if (!command_variable_present)
        {
            if (!(command_variable_present = receive(&command_variable))) return;
        }
        if (!process_set_variable()) return; // wait for the argument(s)
        break;
    }
    command_present = false;
    command_variable_present = false;
}

// ========================================================================
// ===================== Program Logic ====================================
// ========================================================================

void do_computations()
{
    /* Read ADC input */

    REMUX(0);                       /* 1st ADC channel                    */
    ADCSR |= (1 << ADSC);           /* Initialize single-ended conversion */
    while(!(ADCSR & (1 << ADIF)))   /* Wait for the end of the conversion */
      ;
    adc1 = ADCH;                    /* Read the 8-bit conversion result   */
    ADCSR &= ~(1 << ADIF);          /* Clear Conversion Complete flag     */

    REMUX(1);                       /* 2nd ADC channel                    */
    ADCSR |= (1 << ADSC);           /* Initialize single-ended conversion */
    while(!(ADCSR & (1 << ADIF)))   /* Wait for the end of the conversion */
      ;
    adc2 = ADCH;                    /* Read the 8-bit conversion result   */
    ADCSR &= ~(1 << ADIF);          /* Clear Conversion Complete flag     */

    /* Calculate PWM width */

    /*
     * pwm = KP * e + KI * err
     */

    cur_err = ((int)(adc2) - (int)(adc1));

    long p = COEF(kp, cur_err), q = COEF(ki, int_err);
    if (ABS(p) > INT_MAX) p = ((p < 0) ? (INT_MIN) : (INT_MAX));
    if (ABS(q) > INT_MAX) q = ((q < 0) ? (INT_MIN) : (INT_MAX));
    pwm = safe_add((int) p, (int) q);

    int_err = safe_add(int_err, cur_err);

    /* Setup PWM width */

    long t = COEF(ks, pwm) + INT_MAX;
    if (ABS(t) > INT_MAX) t = ((p < 0) ? (INT_MIN) : (INT_MAX));

    OCR2 = (((unsigned int) t) + INT_MAX) >> 8;

    /* Report back */

    switch(output_mode)
    {
    case NO_OUTPUT:
        break;
    case VAR_ADC1:
        send_byte(adc1);
        break;
    case VAR_ADC2:
        send_byte(adc2);
        break;
    case VAR_INT_ERR:
        send_int(int_err);
        break;
    case VAR_CUR_ERR:
        send_int(cur_err);
        break;
    case VAR_PWM:
        send_int(pwm);
        break;
    case VAR_OCR2:
        send_byte(OCR2);
        break;
    case VAR_PACKET:
        send_packet();
        break;
    }
}

// ========================================================================
// ===================== Program Entry Point ==============================
// ========================================================================

int main()
{

    /* Initialization here */

    init();

    __enable_interrupt();

    /* Program Loop */

    for(;;)
    {

        /* Message Loop */

        if (command_present)
        {
            process_command();
        }
        else
        {
            command_present = receive(&command_method);
        }

        /* Program Body */

        if (!pause)
        {
            do_computations();
        }

    }

    return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2018-2019 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "quorums.h"
#include "quorums_blockprocessor.h"
#include "quorums_commitment.h"
#include "quorums_dkgsession.h"
#include "quorums_dkgsessionmgr.h"
#include "quorums_init.h"
#include "quorums_utils.h"

#include "evo/specialtx.h"

#include "activemasternode.h"
#include "chainparams.h"
#include "init.h"
#include "masternode-sync.h"
#include "univalue.h"
#include "validation.h"

#include "cxxtimer.hpp"

namespace llmq
{

static const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
static const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";

CQuorumManager* quorumManager;

static uint256 MakeQuorumKey(const CQuorum& q)
{
    CHashWriter hw(SER_NETWORK, 0);
    hw << (uint8_t)q.params.type;
    hw << q.qc.quorumHash;
    for (const auto& dmn : q.members) {
        hw << dmn->proTxHash;
    }
    return hw.GetHash();
}

CQuorum::~CQuorum()
{
    // most likely the thread is already done
    stopCachePopulatorThread = true;
    if (cachePopulatorThread.joinable()) {
        cachePopulatorThread.join();
    }
}

void CQuorum::Init(const CFinalCommitment& _qc, int _height, const uint256& _minedBlockHash, const std::vector<CDeterministicMNCPtr>& _members)
{
    qc = _qc;
    height = _height;
    members = _members;
    minedBlockHash = _minedBlockHash;
}

bool CQuorum::IsMember(const uint256& proTxHash) const
{
    for (auto& dmn : members) {
        if (dmn->proTxHash == proTxHash) {
            return true;
        }
    }
    return false;
}

bool CQuorum::IsValidMember(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return qc.validMembers[i];
        }
    }
    return false;
}

CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
{
    if (quorumVvec == nullptr || memberIdx >= members.size() || !qc.validMembers[memberIdx]) {
        return CBLSPublicKey();
    }
    auto& m = members[memberIdx];
    return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId::FromHash(m->proTxHash));
}

CBLSSecretKey CQuorum::GetSkShare() const
{
    return skShare;
}

int CQuorum::GetMemberIndex(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return (int)i;
        }
    }
    return -1;
}

void CQuorum::WriteContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    if (quorumVvec != nullptr) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
    }
    if (skShare.IsValid()) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
    }
}

bool CQuorum::ReadContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    BLSVerificationVector qv;
    if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
        quorumVvec = std::make_shared<BLSVerificationVector>(std::move(qv));
    } else {
        return false;
    }

    // We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
    // member of the quorum but observed the whole DKG process to have the quorum verification vector.
    evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);

    return true;
}

void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
{
    if (_this->quorumVvec == nullptr) {
        return;
    }

    cxxtimer::Timer t(true);
    LogPrint("llmq", "CQuorum::StartCachePopulatorThread -- start\n");

    // this thread will exit after some time
    // when then later some other thread tries to get keys, it will be much faster
    _this->cachePopulatorThread = std::thread([_this, t]() {
        RenameThread("dash-q-cachepop");
        for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
            if (_this->qc.validMembers[i]) {
                _this->GetPubKeyShare(i);
            }
        }
        LogPrint("llmq", "CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
    });
}

CQuorumManager::CQuorumManager(CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) :
    evoDb(_evoDb),
    blsWorker(_blsWorker),
    dkgManager(_dkgManager)
{
}

void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
{
    if (!masternodeSync.IsBlockchainSynced()) {
        return;
    }

    for (auto& p : Params().GetConsensus().llmqs) {
        EnsureQuorumConnections(p.first, pindexNew);
    }
}

void CQuorumManager::EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexNew)
{
    const auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto myProTxHash = activeMasternodeInfo.proTxHash;
    auto lastQuorums = ScanQuorums(llmqType, pindexNew, (size_t)params.keepOldConnections);

    auto connmanQuorumsToDelete = g_connman->GetMasternodeQuorums(llmqType);

    // don't remove connections for the currently in-progress DKG round
    int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
    auto curDkgBlock = pindexNew->GetAncestor(curDkgHeight)->GetBlockHash();
    connmanQuorumsToDelete.erase(curDkgBlock);

    for (auto& quorum : lastQuorums) {
        if (!quorum->IsMember(myProTxHash) && !GetBoolArg("-watchquorums", DEFAULT_WATCH_QUORUMS)) {
            continue;
        }

        if (!g_connman->HasMasternodeQuorumNodes(llmqType, quorum->qc.quorumHash)) {
            std::set<uint256> connections;
            if (quorum->IsMember(myProTxHash)) {
                connections = CLLMQUtils::GetQuorumConnections(llmqType, quorum->qc.quorumHash, myProTxHash);
            } else {
                auto cindexes = CLLMQUtils::CalcDeterministicWatchConnections(llmqType, quorum->qc.quorumHash, quorum->members.size(), 1);
                for (auto idx : cindexes) {
                    connections.emplace(quorum->members[idx]->proTxHash);
                }
            }
            if (!connections.empty()) {
                if (LogAcceptCategory("llmq")) {
                    auto mnList = deterministicMNManager->GetListAtChainTip();
                    std::string debugMsg = strprintf("CQuorumManager::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, quorum->qc.quorumHash.ToString());
                    for (auto& c : connections) {
                        auto dmn = mnList.GetValidMN(c);
                        if (!dmn) {
                            debugMsg += strprintf("  %s (not in valid MN set anymore)\n", c.ToString());
                        } else {
                            debugMsg += strprintf("  %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString(false));
                        }
                    }
                    LogPrint("llmq", debugMsg);
                }
                g_connman->AddMasternodeQuorumNodes(llmqType, quorum->qc.quorumHash, connections);
            }
        }
        connmanQuorumsToDelete.erase(quorum->qc.quorumHash);
    }

    for (auto& qh : connmanQuorumsToDelete) {
        LogPrint("llmq", "CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
        g_connman->RemoveMasternodeQuorumNodes(llmqType, qh);
    }
}

bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, const CBlockIndex* pindexQuorum, const uint256& minedBlockHash, std::shared_ptr<CQuorum>& quorum) const
{
    assert(pindexQuorum);
    assert(qc.quorumHash == pindexQuorum->GetBlockHash());

    auto members = CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, qc.quorumHash);

    quorum->Init(qc, pindexQuorum->nHeight, minedBlockHash, members);

    bool hasValidVvec = false;
    if (quorum->ReadContributions(evoDb)) {
        hasValidVvec = true;
    } else {
        if (BuildQuorumContributions(qc, quorum)) {
            quorum->WriteContributions(evoDb);
            hasValidVvec = true;
        } else {
            LogPrint("llmq", "CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed\n", __func__, qc.quorumHash.ToString());
        }
    }

    if (hasValidVvec) {
        // pre-populate caches in the background
        // recovering public key shares is quite expensive and would result in serious lags for the first few signing
        // sessions if the shares would be calculated on-demand
        CQuorum::StartCachePopulatorThread(quorum);
    }

    return true;
}

bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
{
    std::vector<uint16_t> memberIndexes;
    std::vector<BLSVerificationVectorPtr> vvecs;
    BLSSecretKeyVector skContributions;
    if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, fqc.quorumHash, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
        return false;
    }

    BLSVerificationVectorPtr quorumVvec;
    CBLSSecretKey skShare;

    cxxtimer::Timer t2(true);
    quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
    if (quorumVvec == nullptr) {
        LogPrint("llmq", "CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
        // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
        // allows to use the quorum as a non-member (verification through the quorum pub key)
        return false;
    }
    skShare = blsWorker.AggregateSecretKeys(skContributions);
    if (!skShare.IsValid()) {
        LogPrint("llmq", "CQuorumManager::%s -- failed to build skShare\n", __func__);
        // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
        // have a valid quorum vvec at this point)
    }
    t2.stop();

    LogPrint("llmq", "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());

    quorum->quorumVvec = quorumVvec;
    quorum->skShare = skShare;

    return true;
}

bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
{
    const CBlockIndex* pindex;
    {
        LOCK(cs_main);
        pindex = chainActive.Tip();
    }
    return ScanQuorums(llmqType, pindex, maxCount);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const CBlockIndex* pindexStart, size_t maxCount)
{
    auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto cacheKey = std::make_pair(llmqType, pindexStart->GetBlockHash());
    const size_t cacheMaxSize = 25; // largest active set + 1

    std::vector<CQuorumCPtr> result;

    if (maxCount <= cacheMaxSize) {
        LOCK(quorumsCacheCs);
        if (scanQuorumsCache.get(cacheKey, result)) {
            if (result.size() > maxCount) {
                result.resize(maxCount);
            }
            return result;
        }
    }

    bool storeCache = false;
    size_t maxCount2 = maxCount;
    if (maxCount2 <= cacheMaxSize) {
        maxCount2 = cacheMaxSize;
        storeCache = true;
    }

    auto quorumIndexes = quorumBlockProcessor->GetMinedCommitmentsUntilBlock(params.type, pindexStart, maxCount2);
    result.reserve(quorumIndexes.size());

    for (auto& quorumIndex : quorumIndexes) {
        assert(quorumIndex);
        auto quorum = GetQuorum(params.type, quorumIndex);
        assert(quorum != nullptr);
        result.emplace_back(quorum);
    }

    if (storeCache) {
        LOCK(quorumsCacheCs);
        scanQuorumsCache.insert(cacheKey, result);
    }

    if (result.size() > maxCount) {
        result.resize(maxCount);
    }

    return result;
}

CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    CBlockIndex* pindexQuorum;
    {
        LOCK(cs_main);
        auto quorumIt = mapBlockIndex.find(quorumHash);

        if (quorumIt == mapBlockIndex.end()) {
            LogPrint("llmq", "CQuorumManager::%s -- block %s not found", __func__, quorumHash.ToString());
            return nullptr;
        }
        pindexQuorum = quorumIt->second;
    }
    return GetQuorum(llmqType, pindexQuorum);
}

CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
{
    assert(pindexQuorum);

    auto quorumHash = pindexQuorum->GetBlockHash();

    // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
    // cached quorums which are not in the active chain anymore
    if (!HasQuorum(llmqType, quorumHash)) {
        return nullptr;
    }

    LOCK(quorumsCacheCs);

    auto it = quorumsCache.find(std::make_pair(llmqType, quorumHash));
    if (it != quorumsCache.end()) {
        return it->second;
    }

    CFinalCommitment qc;
    uint256 minedBlockHash;
    if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc, minedBlockHash)) {
        return nullptr;
    }

    auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto quorum = std::make_shared<CQuorum>(params, blsWorker);

    if (!BuildQuorumFromCommitment(qc, pindexQuorum, minedBlockHash, quorum)) {
        return nullptr;
    }

    quorumsCache.emplace(std::make_pair(llmqType, quorumHash), quorum);

    return quorum;
}

CQuorumCPtr CQuorumManager::GetNewestQuorum(Consensus::LLMQType llmqType)
{
    auto quorums = ScanQuorums(llmqType, 1);
    if (quorums.empty()) {
        return nullptr;
    }
    return quorums.front();
}

}
<commit_msg>Only load signingActiveQuorumCount + 1 quorums into cache (#3002)<commit_after>// Copyright (c) 2018-2019 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "quorums.h"
#include "quorums_blockprocessor.h"
#include "quorums_commitment.h"
#include "quorums_dkgsession.h"
#include "quorums_dkgsessionmgr.h"
#include "quorums_init.h"
#include "quorums_utils.h"

#include "evo/specialtx.h"

#include "activemasternode.h"
#include "chainparams.h"
#include "init.h"
#include "masternode-sync.h"
#include "univalue.h"
#include "validation.h"

#include "cxxtimer.hpp"

namespace llmq
{

static const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
static const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";

CQuorumManager* quorumManager;

static uint256 MakeQuorumKey(const CQuorum& q)
{
    CHashWriter hw(SER_NETWORK, 0);
    hw << (uint8_t)q.params.type;
    hw << q.qc.quorumHash;
    for (const auto& dmn : q.members) {
        hw << dmn->proTxHash;
    }
    return hw.GetHash();
}

CQuorum::~CQuorum()
{
    // most likely the thread is already done
    stopCachePopulatorThread = true;
    if (cachePopulatorThread.joinable()) {
        cachePopulatorThread.join();
    }
}

void CQuorum::Init(const CFinalCommitment& _qc, int _height, const uint256& _minedBlockHash, const std::vector<CDeterministicMNCPtr>& _members)
{
    qc = _qc;
    height = _height;
    members = _members;
    minedBlockHash = _minedBlockHash;
}

bool CQuorum::IsMember(const uint256& proTxHash) const
{
    for (auto& dmn : members) {
        if (dmn->proTxHash == proTxHash) {
            return true;
        }
    }
    return false;
}

bool CQuorum::IsValidMember(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return qc.validMembers[i];
        }
    }
    return false;
}

CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
{
    if (quorumVvec == nullptr || memberIdx >= members.size() || !qc.validMembers[memberIdx]) {
        return CBLSPublicKey();
    }
    auto& m = members[memberIdx];
    return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId::FromHash(m->proTxHash));
}

CBLSSecretKey CQuorum::GetSkShare() const
{
    return skShare;
}

int CQuorum::GetMemberIndex(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return (int)i;
        }
    }
    return -1;
}

void CQuorum::WriteContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    if (quorumVvec != nullptr) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
    }
    if (skShare.IsValid()) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
    }
}

bool CQuorum::ReadContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    BLSVerificationVector qv;
    if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
        quorumVvec = std::make_shared<BLSVerificationVector>(std::move(qv));
    } else {
        return false;
    }

    // We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
    // member of the quorum but observed the whole DKG process to have the quorum verification vector.
    evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);

    return true;
}

void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
{
    if (_this->quorumVvec == nullptr) {
        return;
    }

    cxxtimer::Timer t(true);
    LogPrint("llmq", "CQuorum::StartCachePopulatorThread -- start\n");

    // this thread will exit after some time
    // when then later some other thread tries to get keys, it will be much faster
    _this->cachePopulatorThread = std::thread([_this, t]() {
        RenameThread("dash-q-cachepop");
        for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
            if (_this->qc.validMembers[i]) {
                _this->GetPubKeyShare(i);
            }
        }
        LogPrint("llmq", "CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
    });
}

CQuorumManager::CQuorumManager(CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) :
    evoDb(_evoDb),
    blsWorker(_blsWorker),
    dkgManager(_dkgManager)
{
}

void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
{
    if (!masternodeSync.IsBlockchainSynced()) {
        return;
    }

    for (auto& p : Params().GetConsensus().llmqs) {
        EnsureQuorumConnections(p.first, pindexNew);
    }
}

void CQuorumManager::EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexNew)
{
    const auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto myProTxHash = activeMasternodeInfo.proTxHash;
    auto lastQuorums = ScanQuorums(llmqType, pindexNew, (size_t)params.keepOldConnections);

    auto connmanQuorumsToDelete = g_connman->GetMasternodeQuorums(llmqType);

    // don't remove connections for the currently in-progress DKG round
    int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
    auto curDkgBlock = pindexNew->GetAncestor(curDkgHeight)->GetBlockHash();
    connmanQuorumsToDelete.erase(curDkgBlock);

    for (auto& quorum : lastQuorums) {
        if (!quorum->IsMember(myProTxHash) && !GetBoolArg("-watchquorums", DEFAULT_WATCH_QUORUMS)) {
            continue;
        }

        if (!g_connman->HasMasternodeQuorumNodes(llmqType, quorum->qc.quorumHash)) {
            std::set<uint256> connections;
            if (quorum->IsMember(myProTxHash)) {
                connections = CLLMQUtils::GetQuorumConnections(llmqType, quorum->qc.quorumHash, myProTxHash);
            } else {
                auto cindexes = CLLMQUtils::CalcDeterministicWatchConnections(llmqType, quorum->qc.quorumHash, quorum->members.size(), 1);
                for (auto idx : cindexes) {
                    connections.emplace(quorum->members[idx]->proTxHash);
                }
            }
            if (!connections.empty()) {
                if (LogAcceptCategory("llmq")) {
                    auto mnList = deterministicMNManager->GetListAtChainTip();
                    std::string debugMsg = strprintf("CQuorumManager::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, quorum->qc.quorumHash.ToString());
                    for (auto& c : connections) {
                        auto dmn = mnList.GetValidMN(c);
                        if (!dmn) {
                            debugMsg += strprintf("  %s (not in valid MN set anymore)\n", c.ToString());
                        } else {
                            debugMsg += strprintf("  %s (%s)\n", c.ToString(), dmn->pdmnState->addr.ToString(false));
                        }
                    }
                    LogPrint("llmq", debugMsg);
                }
                g_connman->AddMasternodeQuorumNodes(llmqType, quorum->qc.quorumHash, connections);
            }
        }
        connmanQuorumsToDelete.erase(quorum->qc.quorumHash);
    }

    for (auto& qh : connmanQuorumsToDelete) {
        LogPrint("llmq", "CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
        g_connman->RemoveMasternodeQuorumNodes(llmqType, qh);
    }
}

bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, const CBlockIndex* pindexQuorum, const uint256& minedBlockHash, std::shared_ptr<CQuorum>& quorum) const
{
    assert(pindexQuorum);
    assert(qc.quorumHash == pindexQuorum->GetBlockHash());

    auto members = CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, qc.quorumHash);

    quorum->Init(qc, pindexQuorum->nHeight, minedBlockHash, members);

    bool hasValidVvec = false;
    if (quorum->ReadContributions(evoDb)) {
        hasValidVvec = true;
    } else {
        if (BuildQuorumContributions(qc, quorum)) {
            quorum->WriteContributions(evoDb);
            hasValidVvec = true;
        } else {
            LogPrint("llmq", "CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed\n", __func__, qc.quorumHash.ToString());
        }
    }

    if (hasValidVvec) {
        // pre-populate caches in the background
        // recovering public key shares is quite expensive and would result in serious lags for the first few signing
        // sessions if the shares would be calculated on-demand
        CQuorum::StartCachePopulatorThread(quorum);
    }

    return true;
}

bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
{
    std::vector<uint16_t> memberIndexes;
    std::vector<BLSVerificationVectorPtr> vvecs;
    BLSSecretKeyVector skContributions;
    if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, fqc.quorumHash, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
        return false;
    }

    BLSVerificationVectorPtr quorumVvec;
    CBLSSecretKey skShare;

    cxxtimer::Timer t2(true);
    quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
    if (quorumVvec == nullptr) {
        LogPrint("llmq", "CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
        // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
        // allows to use the quorum as a non-member (verification through the quorum pub key)
        return false;
    }
    skShare = blsWorker.AggregateSecretKeys(skContributions);
    if (!skShare.IsValid()) {
        LogPrint("llmq", "CQuorumManager::%s -- failed to build skShare\n", __func__);
        // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
        // have a valid quorum vvec at this point)
    }
    t2.stop();

    LogPrint("llmq", "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());

    quorum->quorumVvec = quorumVvec;
    quorum->skShare = skShare;

    return true;
}

bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
{
    const CBlockIndex* pindex;
    {
        LOCK(cs_main);
        pindex = chainActive.Tip();
    }
    return ScanQuorums(llmqType, pindex, maxCount);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const CBlockIndex* pindexStart, size_t maxCount)
{
    auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto cacheKey = std::make_pair(llmqType, pindexStart->GetBlockHash());
    const size_t cacheMaxSize = params.signingActiveQuorumCount + 1;

    std::vector<CQuorumCPtr> result;

    if (maxCount <= cacheMaxSize) {
        LOCK(quorumsCacheCs);
        if (scanQuorumsCache.get(cacheKey, result)) {
            if (result.size() > maxCount) {
                result.resize(maxCount);
            }
            return result;
        }
    }

    bool storeCache = false;
    size_t maxCount2 = maxCount;
    if (maxCount2 <= cacheMaxSize) {
        maxCount2 = cacheMaxSize;
        storeCache = true;
    }

    auto quorumIndexes = quorumBlockProcessor->GetMinedCommitmentsUntilBlock(params.type, pindexStart, maxCount2);
    result.reserve(quorumIndexes.size());

    for (auto& quorumIndex : quorumIndexes) {
        assert(quorumIndex);
        auto quorum = GetQuorum(params.type, quorumIndex);
        assert(quorum != nullptr);
        result.emplace_back(quorum);
    }

    if (storeCache) {
        LOCK(quorumsCacheCs);
        scanQuorumsCache.insert(cacheKey, result);
    }

    if (result.size() > maxCount) {
        result.resize(maxCount);
    }

    return result;
}

CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    CBlockIndex* pindexQuorum;
    {
        LOCK(cs_main);
        auto quorumIt = mapBlockIndex.find(quorumHash);

        if (quorumIt == mapBlockIndex.end()) {
            LogPrint("llmq", "CQuorumManager::%s -- block %s not found", __func__, quorumHash.ToString());
            return nullptr;
        }
        pindexQuorum = quorumIt->second;
    }
    return GetQuorum(llmqType, pindexQuorum);
}

CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
{
    assert(pindexQuorum);

    auto quorumHash = pindexQuorum->GetBlockHash();

    // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
    // cached quorums which are not in the active chain anymore
    if (!HasQuorum(llmqType, quorumHash)) {
        return nullptr;
    }

    LOCK(quorumsCacheCs);

    auto it = quorumsCache.find(std::make_pair(llmqType, quorumHash));
    if (it != quorumsCache.end()) {
        return it->second;
    }

    CFinalCommitment qc;
    uint256 minedBlockHash;
    if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc, minedBlockHash)) {
        return nullptr;
    }

    auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto quorum = std::make_shared<CQuorum>(params, blsWorker);

    if (!BuildQuorumFromCommitment(qc, pindexQuorum, minedBlockHash, quorum)) {
        return nullptr;
    }

    quorumsCache.emplace(std::make_pair(llmqType, quorumHash), quorum);

    return quorum;
}

CQuorumCPtr CQuorumManager::GetNewestQuorum(Consensus::LLMQType llmqType)
{
    auto quorums = ScanQuorums(llmqType, 1);
    if (quorums.empty()) {
        return nullptr;
    }
    return quorums.front();
}

}
<|endoftext|>