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

#include "ospray/common/Material.h"
#include "Glass_ispc.h"

namespace ospray {
  namespace pathtracer {
    struct Glass : public ospray::Material {
      //! \brief common function to help printf-debugging
      /*! Every derived class should overrride this! */
      virtual std::string toString() const { return "ospray::pathtracer::Glass"; }

      //! \brief commit the material's parameters
      virtual void commit() {
        if (getIE() != NULL) return;

        const vec3f& transmissionInside
          = getParam3f("transmission",getParam3f("color",vec3f(1.f)));
        const vec3f& transmissionOutside
          = getParam3f("transmissionOutside",vec3f(1.f));

          std::cout << "transmission = " << transmissionInside.x << " " << transmissionInside.y << " " << transmissionInside.z << "\n";

        const float etaInside = getParamf("etaInside", getParamf("eta", 1.5f));
        const float etaOutside = getParamf("etaOutside", 1.f);

        //const vec3f& absorptionColor = getParam3f("absorptionColor",vec3f(1.0f));
        const float absorptionDistance = getParamf("absorptionDistance", 0.0f);

        // Why is "absorptionColor" not found, while "color" is found???
        const vec3f& absorptionColor = getParam3f("transmission",getParam3f("color",vec3f(1.f)));

        std::cout << "absCol = " << absorptionColor.x << " " << absorptionColor.y << " " << absorptionColor.z << ", absDist = " << absorptionDistance << "\n\n";

        ispcEquivalent = ispc::PathTracer_Glass_create
          (etaInside, (const ispc::vec3f&)transmissionInside,
           etaOutside, (const ispc::vec3f&)transmissionOutside,
           absorptionDistance, (const ispc::vec3f&)absorptionColor);
      }
    };

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

#include "ospray/common/Material.h"
#include "Glass_ispc.h"

namespace ospray {
  namespace pathtracer {
    struct Glass : public ospray::Material {
      //! \brief common function to help printf-debugging
      /*! Every derived class should overrride this! */
      virtual std::string toString() const { return "ospray::pathtracer::Glass"; }

      //! \brief commit the material's parameters
      virtual void commit() {
        if (getIE() != NULL) return;

        const vec3f& transmissionInside
          = getParam3f("transmissionInside",vec3f(1.f));
        const vec3f& transmissionOutside
          = getParam3f("transmissionOutside",vec3f(1.f));

         // std::cout << "transmission = " << transmissionInside.x << " " << transmissionInside.y << " " << transmissionInside.z << "\n";

        const float etaInside = getParamf("etaInside", getParamf("eta", 1.5f));
        const float etaOutside = getParamf("etaOutside", 1.f);

        //const vec3f& absorptionColor = getParam3f("absorptionColor",vec3f(1.0f));
        const float absorptionDistance = getParamf("absorptionDistance", 0.0f);

        // Why is "absorptionColor" not found, while "color" is found???
        const vec3f& absorptionColor = getParam3f("color",vec3f(1.f));

        //std::cout << "absCol = " << absorptionColor.x << " " << absorptionColor.y << " " << absorptionColor.z << ", absDist = " << absorptionDistance << "\n\n";

        ispcEquivalent = ispc::PathTracer_Glass_create
          (etaInside, (const ispc::vec3f&)transmissionInside,
           etaOutside, (const ispc::vec3f&)transmissionOutside,
           absorptionDistance, (const ispc::vec3f&)absorptionColor);
      }
    };

    OSP_REGISTER_MATERIAL(Glass,PathTracer_Glass);
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 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/autofill/autofill_download.h"

#include <algorithm>

#include "base/logging.h"
#include "base/rand_util.h"
#include "base/stl_util-inl.h"
#include "chrome/browser/autofill/autofill_xml_parser.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"
#include "net/http/http_response_headers.h"

#define DISABLED_REQUEST_URL "http://disabled"

#if defined(GOOGLE_CHROME_BUILD)
#include "chrome/browser/autofill/internal/autofill_download_internal.h"
#else
#define AUTO_FILL_QUERY_SERVER_REQUEST_URL DISABLED_REQUEST_URL
#define AUTO_FILL_UPLOAD_SERVER_REQUEST_URL DISABLED_REQUEST_URL
#define AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER "SOMESERVER/"
#endif

struct AutoFillDownloadManager::FormRequestData {
  std::vector<std::string> form_signatures;
  AutoFillRequestType request_type;
};

AutoFillDownloadManager::AutoFillDownloadManager(Profile* profile)
    : profile_(profile),
      observer_(NULL),
      next_query_request_(base::Time::Now()),
      next_upload_request_(base::Time::Now()),
      positive_upload_rate_(0),
      negative_upload_rate_(0),
      fetcher_id_for_unittest_(0),
      is_testing_(false) {
  // |profile_| could be NULL in some unit-tests.
  if (profile_) {
    PrefService* preferences = profile_->GetPrefs();
    positive_upload_rate_ =
        preferences->GetReal(prefs::kAutoFillPositiveUploadRate);
    negative_upload_rate_ =
        preferences->GetReal(prefs::kAutoFillNegativeUploadRate);
  }
}

AutoFillDownloadManager::~AutoFillDownloadManager() {
  STLDeleteContainerPairFirstPointers(url_fetchers_.begin(),
                                      url_fetchers_.end());
}

void AutoFillDownloadManager::SetObserver(
    AutoFillDownloadManager::Observer *observer) {
  if (observer) {
    DCHECK(!observer_);
    observer_ = observer;
  } else {
    observer_ = NULL;
  }
}

bool AutoFillDownloadManager::StartQueryRequest(
    const ScopedVector<FormStructure>& forms) {
  if (next_query_request_ > base::Time::Now()) {
    // We are in back-off mode: do not do the request.
    return false;
  }
  std::string form_xml;
  FormRequestData request_data;
  if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures,
                                         &form_xml))
    return false;

  request_data.request_type = AutoFillDownloadManager::REQUEST_QUERY;

  return StartRequest(form_xml, request_data);
}

bool AutoFillDownloadManager::StartUploadRequest(
    const FormStructure& form, bool form_was_matched) {
  if (next_upload_request_ > base::Time::Now()) {
    // We are in back-off mode: do not do the request.
    return false;
  }

  // Check if we need to upload form.
  double upload_rate = form_was_matched ? GetPositiveUploadRate() :
                                          GetNegativeUploadRate();
  if (base::RandDouble() > upload_rate) {
    VLOG(1) << "AutoFillDownloadManager: Upload request is ignored";
    // If we ever need notification that upload was skipped, add it here.
    return false;
  }
  std::string form_xml;
  if (!form.EncodeUploadRequest(form_was_matched, &form_xml))
    return false;

  FormRequestData request_data;
  request_data.form_signatures.push_back(form.FormSignature());
  request_data.request_type = AutoFillDownloadManager::REQUEST_UPLOAD;

  return StartRequest(form_xml, request_data);
}

bool AutoFillDownloadManager::CancelRequest(
    const std::string& form_signature,
    AutoFillDownloadManager::AutoFillRequestType request_type) {
  for (std::map<URLFetcher *, FormRequestData>::iterator it =
       url_fetchers_.begin();
       it != url_fetchers_.end();
       ++it) {
    if (std::find(it->second.form_signatures.begin(),
        it->second.form_signatures.end(), form_signature) !=
        it->second.form_signatures.end() &&
        it->second.request_type == request_type) {
      delete it->first;
      url_fetchers_.erase(it);
      return true;
    }
  }
  return false;
}

double AutoFillDownloadManager::GetPositiveUploadRate() const {
  return positive_upload_rate_;
}

double AutoFillDownloadManager::GetNegativeUploadRate() const {
  return negative_upload_rate_;
}

void AutoFillDownloadManager::SetPositiveUploadRate(double rate) {
  if (rate == positive_upload_rate_)
    return;
  positive_upload_rate_ = rate;
  DCHECK_GE(rate, 0.0);
  DCHECK_LE(rate, 1.0);
  DCHECK(profile_);
  PrefService* preferences = profile_->GetPrefs();
  preferences->SetReal(prefs::kAutoFillPositiveUploadRate, rate);
}

void AutoFillDownloadManager::SetNegativeUploadRate(double rate) {
  if (rate == negative_upload_rate_)
    return;
  negative_upload_rate_ = rate;
  DCHECK_GE(rate, 0.0);
  DCHECK_LE(rate, 1.0);
  DCHECK(profile_);
  PrefService* preferences = profile_->GetPrefs();
  preferences->SetReal(prefs::kAutoFillNegativeUploadRate, rate);
}

bool AutoFillDownloadManager::StartRequest(
    const std::string& form_xml,
    const FormRequestData& request_data) {
  std::string request_url;
  if (request_data.request_type == AutoFillDownloadManager::REQUEST_QUERY)
    request_url = AUTO_FILL_QUERY_SERVER_REQUEST_URL;
  else
    request_url = AUTO_FILL_UPLOAD_SERVER_REQUEST_URL;

  if (!request_url.compare(DISABLED_REQUEST_URL) && !is_testing_) {
    // We have it disabled - return true as if it succeeded, but do nothing.
    return true;
  }

  // Id is ignored for regular chrome, in unit test id's for fake fetcher
  // factory will be 0, 1, 2, ...
  URLFetcher *fetcher = URLFetcher::Create(fetcher_id_for_unittest_++,
                                           GURL(request_url),
                                           URLFetcher::POST,
                                           this);
  url_fetchers_[fetcher] = request_data;
  fetcher->set_automatically_retry_on_5xx(false);
  fetcher->set_request_context(Profile::GetDefaultRequestContext());
  fetcher->set_upload_data("text/plain", form_xml);
  fetcher->Start();
  return true;
}

void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
                                                 const GURL& url,
                                                 const URLRequestStatus& status,
                                                 int response_code,
                                                 const ResponseCookies& cookies,
                                                 const std::string& data) {
  std::map<URLFetcher *, FormRequestData>::iterator it =
      url_fetchers_.find(const_cast<URLFetcher*>(source));
  if (it == url_fetchers_.end()) {
    // Looks like crash on Mac is possibly caused with callback entering here
    // with unknown fetcher when network is refreshed.
    return;
  }
  std::string type_of_request(
      it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY ?
          "query" : "upload");
  const int kHttpResponseOk = 200;
  const int kHttpInternalServerError = 500;
  const int kHttpBadGateway = 502;
  const int kHttpServiceUnavailable = 503;

  CHECK(it->second.form_signatures.size());
  if (response_code != kHttpResponseOk) {
    bool back_off = false;
    std::string server_header;
    switch (response_code) {
      case kHttpBadGateway:
        if (!source->response_headers()->EnumerateHeader(NULL, "server",
                                                         &server_header) ||
            StartsWithASCII(server_header.c_str(),
                            AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER,
                            false) != 0)
          break;
        // Bad getaway was received from AutoFill servers. Fall through to back
        // off.
      case kHttpInternalServerError:
      case kHttpServiceUnavailable:
        back_off = true;
        break;
    }

    if (back_off) {
      base::Time back_off_time(base::Time::Now() + source->backoff_delay());
      if (it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY) {
        next_query_request_ = back_off_time;
      } else {
        next_upload_request_ = back_off_time;
      }
    }

    LOG(WARNING) << "AutoFillDownloadManager: " << type_of_request
                 << " request has failed with response" << response_code;
    if (observer_) {
      observer_->OnHeuristicsRequestError(it->second.form_signatures[0],
                                          it->second.request_type,
                                          response_code);
    }
  } else {
    VLOG(1) << "AutoFillDownloadManager: " << type_of_request
            << " request has succeeded";
    if (it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY) {
      if (observer_)
        observer_->OnLoadedAutoFillHeuristics(data);
    } else {
      double new_positive_upload_rate = 0;
      double new_negative_upload_rate = 0;
      AutoFillUploadXmlParser parse_handler(&new_positive_upload_rate,
                                            &new_negative_upload_rate);
      buzz::XmlParser parser(&parse_handler);
      parser.Parse(data.data(), data.length(), true);
      if (parse_handler.succeeded()) {
        SetPositiveUploadRate(new_positive_upload_rate);
        SetNegativeUploadRate(new_negative_upload_rate);
      }

      if (observer_)
        observer_->OnUploadedAutoFillHeuristics(it->second.form_signatures[0]);
    }
  }
  delete it->first;
  url_fetchers_.erase(it);
}

<commit_msg>Fix a small typo in AutoFillDownloadManager::OnURLFetchComplete.<commit_after>// Copyright (c) 2010 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/autofill/autofill_download.h"

#include <algorithm>
#include <vector>

#include "base/logging.h"
#include "base/rand_util.h"
#include "base/stl_util-inl.h"
#include "chrome/browser/autofill/autofill_xml_parser.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"
#include "net/http/http_response_headers.h"

#define DISABLED_REQUEST_URL "http://disabled"

#if defined(GOOGLE_CHROME_BUILD)
#include "chrome/browser/autofill/internal/autofill_download_internal.h"
#else
#define AUTO_FILL_QUERY_SERVER_REQUEST_URL DISABLED_REQUEST_URL
#define AUTO_FILL_UPLOAD_SERVER_REQUEST_URL DISABLED_REQUEST_URL
#define AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER "SOMESERVER/"
#endif

struct AutoFillDownloadManager::FormRequestData {
  std::vector<std::string> form_signatures;
  AutoFillRequestType request_type;
};

AutoFillDownloadManager::AutoFillDownloadManager(Profile* profile)
    : profile_(profile),
      observer_(NULL),
      next_query_request_(base::Time::Now()),
      next_upload_request_(base::Time::Now()),
      positive_upload_rate_(0),
      negative_upload_rate_(0),
      fetcher_id_for_unittest_(0),
      is_testing_(false) {
  // |profile_| could be NULL in some unit-tests.
  if (profile_) {
    PrefService* preferences = profile_->GetPrefs();
    positive_upload_rate_ =
        preferences->GetReal(prefs::kAutoFillPositiveUploadRate);
    negative_upload_rate_ =
        preferences->GetReal(prefs::kAutoFillNegativeUploadRate);
  }
}

AutoFillDownloadManager::~AutoFillDownloadManager() {
  STLDeleteContainerPairFirstPointers(url_fetchers_.begin(),
                                      url_fetchers_.end());
}

void AutoFillDownloadManager::SetObserver(
    AutoFillDownloadManager::Observer *observer) {
  if (observer) {
    DCHECK(!observer_);
    observer_ = observer;
  } else {
    observer_ = NULL;
  }
}

bool AutoFillDownloadManager::StartQueryRequest(
    const ScopedVector<FormStructure>& forms) {
  if (next_query_request_ > base::Time::Now()) {
    // We are in back-off mode: do not do the request.
    return false;
  }
  std::string form_xml;
  FormRequestData request_data;
  if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures,
                                         &form_xml))
    return false;

  request_data.request_type = AutoFillDownloadManager::REQUEST_QUERY;

  return StartRequest(form_xml, request_data);
}

bool AutoFillDownloadManager::StartUploadRequest(
    const FormStructure& form, bool form_was_matched) {
  if (next_upload_request_ > base::Time::Now()) {
    // We are in back-off mode: do not do the request.
    return false;
  }

  // Check if we need to upload form.
  double upload_rate = form_was_matched ? GetPositiveUploadRate() :
                                          GetNegativeUploadRate();
  if (base::RandDouble() > upload_rate) {
    VLOG(1) << "AutoFillDownloadManager: Upload request is ignored";
    // If we ever need notification that upload was skipped, add it here.
    return false;
  }
  std::string form_xml;
  if (!form.EncodeUploadRequest(form_was_matched, &form_xml))
    return false;

  FormRequestData request_data;
  request_data.form_signatures.push_back(form.FormSignature());
  request_data.request_type = AutoFillDownloadManager::REQUEST_UPLOAD;

  return StartRequest(form_xml, request_data);
}

bool AutoFillDownloadManager::CancelRequest(
    const std::string& form_signature,
    AutoFillDownloadManager::AutoFillRequestType request_type) {
  for (std::map<URLFetcher *, FormRequestData>::iterator it =
       url_fetchers_.begin();
       it != url_fetchers_.end();
       ++it) {
    if (std::find(it->second.form_signatures.begin(),
        it->second.form_signatures.end(), form_signature) !=
        it->second.form_signatures.end() &&
        it->second.request_type == request_type) {
      delete it->first;
      url_fetchers_.erase(it);
      return true;
    }
  }
  return false;
}

double AutoFillDownloadManager::GetPositiveUploadRate() const {
  return positive_upload_rate_;
}

double AutoFillDownloadManager::GetNegativeUploadRate() const {
  return negative_upload_rate_;
}

void AutoFillDownloadManager::SetPositiveUploadRate(double rate) {
  if (rate == positive_upload_rate_)
    return;
  positive_upload_rate_ = rate;
  DCHECK_GE(rate, 0.0);
  DCHECK_LE(rate, 1.0);
  DCHECK(profile_);
  PrefService* preferences = profile_->GetPrefs();
  preferences->SetReal(prefs::kAutoFillPositiveUploadRate, rate);
}

void AutoFillDownloadManager::SetNegativeUploadRate(double rate) {
  if (rate == negative_upload_rate_)
    return;
  negative_upload_rate_ = rate;
  DCHECK_GE(rate, 0.0);
  DCHECK_LE(rate, 1.0);
  DCHECK(profile_);
  PrefService* preferences = profile_->GetPrefs();
  preferences->SetReal(prefs::kAutoFillNegativeUploadRate, rate);
}

bool AutoFillDownloadManager::StartRequest(
    const std::string& form_xml,
    const FormRequestData& request_data) {
  std::string request_url;
  if (request_data.request_type == AutoFillDownloadManager::REQUEST_QUERY)
    request_url = AUTO_FILL_QUERY_SERVER_REQUEST_URL;
  else
    request_url = AUTO_FILL_UPLOAD_SERVER_REQUEST_URL;

  if (!request_url.compare(DISABLED_REQUEST_URL) && !is_testing_) {
    // We have it disabled - return true as if it succeeded, but do nothing.
    return true;
  }

  // Id is ignored for regular chrome, in unit test id's for fake fetcher
  // factory will be 0, 1, 2, ...
  URLFetcher *fetcher = URLFetcher::Create(fetcher_id_for_unittest_++,
                                           GURL(request_url),
                                           URLFetcher::POST,
                                           this);
  url_fetchers_[fetcher] = request_data;
  fetcher->set_automatically_retry_on_5xx(false);
  fetcher->set_request_context(Profile::GetDefaultRequestContext());
  fetcher->set_upload_data("text/plain", form_xml);
  fetcher->Start();
  return true;
}

void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
                                                 const GURL& url,
                                                 const URLRequestStatus& status,
                                                 int response_code,
                                                 const ResponseCookies& cookies,
                                                 const std::string& data) {
  std::map<URLFetcher *, FormRequestData>::iterator it =
      url_fetchers_.find(const_cast<URLFetcher*>(source));
  if (it == url_fetchers_.end()) {
    // Looks like crash on Mac is possibly caused with callback entering here
    // with unknown fetcher when network is refreshed.
    return;
  }
  std::string type_of_request(
      it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY ?
          "query" : "upload");
  const int kHttpResponseOk = 200;
  const int kHttpInternalServerError = 500;
  const int kHttpBadGateway = 502;
  const int kHttpServiceUnavailable = 503;

  CHECK(it->second.form_signatures.size());
  if (response_code != kHttpResponseOk) {
    bool back_off = false;
    std::string server_header;
    switch (response_code) {
      case kHttpBadGateway:
        if (!source->response_headers()->EnumerateHeader(NULL, "server",
                                                         &server_header) ||
            StartsWithASCII(server_header.c_str(),
                            AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER,
                            false) != 0)
          break;
        // Bad getaway was received from AutoFill servers. Fall through to back
        // off.
      case kHttpInternalServerError:
      case kHttpServiceUnavailable:
        back_off = true;
        break;
    }

    if (back_off) {
      base::Time back_off_time(base::Time::Now() + source->backoff_delay());
      if (it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY) {
        next_query_request_ = back_off_time;
      } else {
        next_upload_request_ = back_off_time;
      }
    }

    LOG(WARNING) << "AutoFillDownloadManager: " << type_of_request
                 << " request has failed with response " << response_code;
    if (observer_) {
      observer_->OnHeuristicsRequestError(it->second.form_signatures[0],
                                          it->second.request_type,
                                          response_code);
    }
  } else {
    VLOG(1) << "AutoFillDownloadManager: " << type_of_request
            << " request has succeeded";
    if (it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY) {
      if (observer_)
        observer_->OnLoadedAutoFillHeuristics(data);
    } else {
      double new_positive_upload_rate = 0;
      double new_negative_upload_rate = 0;
      AutoFillUploadXmlParser parse_handler(&new_positive_upload_rate,
                                            &new_negative_upload_rate);
      buzz::XmlParser parser(&parse_handler);
      parser.Parse(data.data(), data.length(), true);
      if (parse_handler.succeeded()) {
        SetPositiveUploadRate(new_positive_upload_rate);
        SetNegativeUploadRate(new_negative_upload_rate);
      }

      if (observer_)
        observer_->OnUploadedAutoFillHeuristics(it->second.form_signatures[0]);
    }
  }
  delete it->first;
  url_fetchers_.erase(it);
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 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/chromeos/audio_mixer_pulse.h"

#include <pulse/pulseaudio.h>

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_thread.h"

namespace chromeos {

// Using asynchronous versions of the threaded PulseAudio API, as well
// as a worker thread so gets, sets, and the init sequence do not block the
// calling thread.  GetVolume() and IsMute() can still be called synchronously
// if needed, but take a bit longer (~2ms vs ~0.3ms).
//
// Set calls just return without waiting.  If you must guarantee the value has
// been set before continuing, immediately call the blocking Get version to
// synchronously get the value back.
//
// TODO(davej): Serialize volume/mute to preserve settings when restarting?

namespace {

const int kInvalidDeviceId = -1;

const double kMinVolumeDb = -90.0;
// Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some
// in case sounds or their setup is too quiet for them.
const double kMaxVolumeDb = 6.0;

// Used for passing custom data to the PulseAudio callbacks.
struct CallbackWrapper {
  AudioMixerPulse* instance;
  bool done;
  void* userdata;
};

}  // namespace

// AudioInfo contains all the values we care about when getting info for a
// Sink (output device) used by GetAudioInfo().
struct AudioMixerPulse::AudioInfo {
  pa_cvolume cvolume;
  bool muted;
};

AudioMixerPulse::AudioMixerPulse()
    : device_id_(kInvalidDeviceId),
      last_channels_(0),
      mainloop_lock_count_(0),
      mixer_state_(UNINITIALIZED),
      pa_context_(NULL),
      pa_mainloop_(NULL) {
}

AudioMixerPulse::~AudioMixerPulse() {
  PulseAudioFree();
  if (thread_ != NULL) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    // A ScopedAllowIO object is required to join the thread when calling Stop.
    // The worker thread should be idle at this time.
    // See http://crosbug.com/11110 for discussion.
    base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
    thread_->message_loop()->AssertIdle();

    thread_->Stop();
    thread_.reset();
  }
}

void AudioMixerPulse::Init(InitDoneCallback* callback) {
  DCHECK(callback);
  if (!InitThread()) {
    callback->Run(false);
    delete callback;
    return;
  }

  // Post the task of starting up, which can block for 200-500ms,
  // so best not to do it on the caller's thread.
  thread_->message_loop()->PostTask(FROM_HERE,
      NewRunnableMethod(this, &AudioMixerPulse::DoInit, callback));
}

bool AudioMixerPulse::InitSync() {
  if (!InitThread())
    return false;
  return PulseAudioInit();
}

double AudioMixerPulse::GetVolumeDb() const {
  if (!MainloopLockIfReady())
    return AudioMixer::kSilenceDb;
  AudioInfo data;
  GetAudioInfo(&data);
  MainloopUnlock();
  return pa_sw_volume_to_dB(data.cvolume.values[0]);
}

bool AudioMixerPulse::GetVolumeLimits(double* vol_min, double* vol_max) {
  if (vol_min)
    *vol_min = kMinVolumeDb;
  if (vol_max)
    *vol_max = kMaxVolumeDb;
  return true;
}

void AudioMixerPulse::SetVolumeDb(double vol_db) {
  if (!MainloopLockIfReady())
    return;

  // last_channels_ determines the number of channels on the main output device,
  // and is used later to set the volume on all channels at once.
  if (!last_channels_) {
    AudioInfo data;
    GetAudioInfo(&data);
    last_channels_ = data.cvolume.channels;
  }

  pa_operation* pa_op;
  pa_cvolume cvolume;
  pa_cvolume_set(&cvolume, last_channels_, pa_sw_volume_from_dB(vol_db));
  pa_op = pa_context_set_sink_volume_by_index(pa_context_, device_id_,
                                              &cvolume, NULL, NULL);
  pa_operation_unref(pa_op);
  MainloopUnlock();
  VLOG(1) << "Set volume to " << vol_db << " dB";
}

bool AudioMixerPulse::IsMute() const {
  if (!MainloopLockIfReady())
    return false;
  AudioInfo data;
  GetAudioInfo(&data);
  MainloopUnlock();
  return data.muted;
}

void AudioMixerPulse::SetMute(bool mute) {
  if (!MainloopLockIfReady())
    return;
  pa_operation* pa_op;
  pa_op = pa_context_set_sink_mute_by_index(pa_context_, device_id_,
                                            mute ? 1 : 0, NULL, NULL);
  pa_operation_unref(pa_op);
  MainloopUnlock();
  VLOG(1) << "Set mute to " << mute;
}

AudioMixer::State AudioMixerPulse::GetState() const {
  base::AutoLock lock(mixer_state_lock_);
  // If we think it's ready, verify it is actually so.
  if ((mixer_state_ == READY) &&
      (pa_context_get_state(pa_context_) != PA_CONTEXT_READY))
    mixer_state_ = IN_ERROR;
  return mixer_state_;
}

////////////////////////////////////////////////////////////////////////////////
// Private functions follow

void AudioMixerPulse::DoInit(InitDoneCallback* callback) {
  bool success = PulseAudioInit();
  callback->Run(success);
  delete callback;
}

bool AudioMixerPulse::InitThread() {
  base::AutoLock lock(mixer_state_lock_);

  if (mixer_state_ != UNINITIALIZED)
    return false;

  if (thread_ == NULL) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    thread_.reset(new base::Thread("AudioMixerPulse"));
    if (!thread_->Start()) {
      thread_.reset();
      return false;
    }
  }
  mixer_state_ = INITIALIZING;
  return true;
}

// static
void AudioMixerPulse::ConnectToPulseCallbackThunk(
    pa_context* context, void* userdata) {
  CallbackWrapper* data =
      static_cast<CallbackWrapper*>(userdata);
  data->instance->OnConnectToPulseCallback(context, &data->done);
}

void AudioMixerPulse::OnConnectToPulseCallback(
    pa_context* context, bool* connect_done) {
  pa_context_state_t state = pa_context_get_state(context);
  if (state == PA_CONTEXT_READY ||
      state == PA_CONTEXT_FAILED ||
      state == PA_CONTEXT_TERMINATED) {
    // Connection process has reached a terminal state. Wake PulseAudioInit().
    *connect_done = true;
    MainloopSignal();
  }
}

bool AudioMixerPulse::PulseAudioInit() {
  pa_context_state_t state = PA_CONTEXT_FAILED;

  {
    base::AutoLock lock(mixer_state_lock_);
    if (mixer_state_ != INITIALIZING)
      return false;

    pa_mainloop_ = pa_threaded_mainloop_new();
    if (!pa_mainloop_) {
      LOG(ERROR) << "Can't create PulseAudio mainloop";
      mixer_state_ = UNINITIALIZED;
      return false;
    }

    if (pa_threaded_mainloop_start(pa_mainloop_) != 0) {
      LOG(ERROR) << "Can't start PulseAudio mainloop";
      pa_threaded_mainloop_free(pa_mainloop_);
      mixer_state_ = UNINITIALIZED;
      return false;
    }
  }

  while (true) {
    // Create connection to default server.
    if (!MainloopSafeLock())
      return false;

    while (true) {
      pa_mainloop_api* pa_mlapi = pa_threaded_mainloop_get_api(pa_mainloop_);
      if (!pa_mlapi) {
        LOG(ERROR) << "Can't get PulseAudio mainloop api";
        break;
      }
      // This one takes the most time if run at app startup.
      pa_context_ = pa_context_new(pa_mlapi, "ChromeAudio");
      if (!pa_context_) {
        LOG(ERROR) << "Can't create new PulseAudio context";
        break;
      }

      MainloopUnlock();
      if (!MainloopSafeLock())
        return false;

      CallbackWrapper data = {this, false, NULL};
      pa_context_set_state_callback(pa_context_,
                                    &ConnectToPulseCallbackThunk,
                                    &data);

      if (pa_context_connect(pa_context_, NULL,
                             PA_CONTEXT_NOAUTOSPAWN, NULL) != 0) {
        LOG(ERROR) << "Can't start connection to PulseAudio sound server";
      } else {
        // Wait until we have a completed connection or fail.
        do {
          MainloopWait();
        } while (!data.done);

        state = pa_context_get_state(pa_context_);

        if (state == PA_CONTEXT_FAILED) {
          LOG(ERROR) << "PulseAudio connection failed (daemon not running?)";
        } else if (state == PA_CONTEXT_TERMINATED) {
          LOG(ERROR) << "PulseAudio connection terminated early";
        } else if (state != PA_CONTEXT_READY) {
          LOG(ERROR) << "Unknown problem connecting to PulseAudio";
        }
      }

      pa_context_set_state_callback(pa_context_, NULL, NULL);
      break;
    }

    MainloopUnlock();

    if (state != PA_CONTEXT_READY)
      break;

    if (!MainloopSafeLock())
      return false;
    GetDefaultPlaybackDevice();
    MainloopUnlock();

    if (device_id_ == kInvalidDeviceId)
      break;

    {
      base::AutoLock lock(mixer_state_lock_);
      if (mixer_state_ == SHUTTING_DOWN)
        return false;
      mixer_state_ = READY;
    }

    return true;
  }

  // Failed startup sequence, clean up now.
  PulseAudioFree();
  return false;
}

void AudioMixerPulse::PulseAudioFree() {
  {
    base::AutoLock lock(mixer_state_lock_);
    if (!pa_mainloop_)
      mixer_state_ = UNINITIALIZED;
    if ((mixer_state_ == UNINITIALIZED) || (mixer_state_ == SHUTTING_DOWN))
      return;

    // If still initializing on another thread, this will cause it to exit.
    mixer_state_ = SHUTTING_DOWN;
  }

  DCHECK(pa_mainloop_);

  MainloopLock();
  if (pa_context_) {
    pa_context_disconnect(pa_context_);
    pa_context_unref(pa_context_);
    pa_context_ = NULL;
  }
  MainloopUnlock();

  pa_threaded_mainloop_stop(pa_mainloop_);
  pa_threaded_mainloop_free(pa_mainloop_);
  pa_mainloop_ = NULL;

  {
    base::AutoLock lock(mixer_state_lock_);
    mixer_state_ = UNINITIALIZED;
  }
}

void AudioMixerPulse::CompleteOperation(pa_operation* pa_op,
                                        bool* done) const {
  // After starting any operation, this helper checks if it started OK, then
  // waits for it to complete by iterating through the mainloop until the
  // operation is not running anymore.
  CHECK(pa_op);

  while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
    // If operation still running, but we got what we needed, cancel it now.
    if (*done) {
      pa_operation_cancel(pa_op);
      break;
    }
    MainloopWait();
  }
  pa_operation_unref(pa_op);
}

// Must be called with mainloop lock held
void AudioMixerPulse::GetDefaultPlaybackDevice() {
  DCHECK_GT(mainloop_lock_count_, 0);
  DCHECK(pa_context_);
  DCHECK(pa_context_get_state(pa_context_) == PA_CONTEXT_READY);

  CallbackWrapper data = {this, false, NULL};

  pa_operation* pa_op = pa_context_get_sink_info_list(pa_context_,
                                        EnumerateDevicesCallback,
                                        &data);
  CompleteOperation(pa_op, &data.done);
  return;
}

void AudioMixerPulse::OnEnumerateDevices(const pa_sink_info* sink_info,
                                         int eol, bool* done) {
  if (device_id_ != kInvalidDeviceId)
    return;

  // TODO(davej): Should we handle cases of more than one output sink device?

  // eol is < 0 for error, > 0 for end of list, ==0 while listing.
  if (eol == 0) {
    device_id_ = sink_info->index;
  }
  *done = true;
  MainloopSignal();
}

// static
void AudioMixerPulse::EnumerateDevicesCallback(pa_context* unused,
                                               const pa_sink_info* sink_info,
                                               int eol,
                                               void* userdata) {
  CallbackWrapper* data =
      static_cast<CallbackWrapper*>(userdata);
  data->instance->OnEnumerateDevices(sink_info, eol, &data->done);
}

// Must be called with lock held
void AudioMixerPulse::GetAudioInfo(AudioInfo* info) const {
  DCHECK_GT(mainloop_lock_count_, 0);
  CallbackWrapper data = {const_cast<AudioMixerPulse*>(this), false, info};
  pa_operation* pa_op = pa_context_get_sink_info_by_index(pa_context_,
                                                          device_id_,
                                                          GetAudioInfoCallback,
                                                          &data);
  CompleteOperation(pa_op, &data.done);
}

// static
void AudioMixerPulse::GetAudioInfoCallback(pa_context* unused,
                                           const pa_sink_info* sink_info,
                                           int eol,
                                           void* userdata) {
  CallbackWrapper* data = static_cast<CallbackWrapper*>(userdata);
  AudioInfo* info = static_cast<AudioInfo*>(data->userdata);

  // Copy just the information we care about.
  if (eol == 0) {
    info->cvolume = sink_info->volume;
    info->muted = sink_info->mute ? true : false;
    data->done = true;
  }
  data->instance->MainloopSignal();
}

inline void AudioMixerPulse::MainloopLock() const {
  pa_threaded_mainloop_lock(pa_mainloop_);
  ++mainloop_lock_count_;
}

inline void AudioMixerPulse::MainloopUnlock() const {
  --mainloop_lock_count_;
  pa_threaded_mainloop_unlock(pa_mainloop_);
}

// Must be called with the lock held.
inline void AudioMixerPulse::MainloopWait() const {
  DCHECK_GT(mainloop_lock_count_, 0);
  pa_threaded_mainloop_wait(pa_mainloop_);
}

// Must be called with the lock held.
inline void AudioMixerPulse::MainloopSignal() const {
  DCHECK_GT(mainloop_lock_count_, 0);
  pa_threaded_mainloop_signal(pa_mainloop_, 0);
}

inline bool AudioMixerPulse::MainloopSafeLock() const {
  base::AutoLock lock(mixer_state_lock_);
  if ((mixer_state_ == SHUTTING_DOWN) || (!pa_mainloop_))
    return false;

  pa_threaded_mainloop_lock(pa_mainloop_);
  ++mainloop_lock_count_;
  return true;
}

inline bool AudioMixerPulse::MainloopLockIfReady() const {
  base::AutoLock lock(mixer_state_lock_);
  if (mixer_state_ != READY)
    return false;
  if (!pa_mainloop_)
    return false;
  pa_threaded_mainloop_lock(pa_mainloop_);
  ++mainloop_lock_count_;
  return true;
}

}  // namespace chromeos
<commit_msg>Fix flaky browser_tests incoming_queue_.empty() failure<commit_after>// Copyright (c) 2010 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/chromeos/audio_mixer_pulse.h"

#include <pulse/pulseaudio.h>

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_thread.h"

namespace chromeos {

// Using asynchronous versions of the threaded PulseAudio API, as well
// as a worker thread so gets, sets, and the init sequence do not block the
// calling thread.  GetVolume() and IsMute() can still be called synchronously
// if needed, but take a bit longer (~2ms vs ~0.3ms).
//
// Set calls just return without waiting.  If you must guarantee the value has
// been set before continuing, immediately call the blocking Get version to
// synchronously get the value back.
//
// TODO(davej): Serialize volume/mute to preserve settings when restarting?

namespace {

const int kInvalidDeviceId = -1;

const double kMinVolumeDb = -90.0;
// Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some
// in case sounds or their setup is too quiet for them.
const double kMaxVolumeDb = 6.0;

// Used for passing custom data to the PulseAudio callbacks.
struct CallbackWrapper {
  AudioMixerPulse* instance;
  bool done;
  void* userdata;
};

}  // namespace

// AudioInfo contains all the values we care about when getting info for a
// Sink (output device) used by GetAudioInfo().
struct AudioMixerPulse::AudioInfo {
  pa_cvolume cvolume;
  bool muted;
};

AudioMixerPulse::AudioMixerPulse()
    : device_id_(kInvalidDeviceId),
      last_channels_(0),
      mainloop_lock_count_(0),
      mixer_state_(UNINITIALIZED),
      pa_context_(NULL),
      pa_mainloop_(NULL) {
}

AudioMixerPulse::~AudioMixerPulse() {
  bool run_all_pending = false;
  {
    base::AutoLock lock(mixer_state_lock_);
    // DoInit() has not even been called yet, but is still in the thread message
    // queue.  Force it out of the queue and exit DoInit() right away.
    if ((!pa_mainloop_) && (mixer_state_ == INITIALIZING)) {
      mixer_state_ = UNINITIALIZED;
      run_all_pending = true;
    }
  }
  if (run_all_pending)
    thread_->message_loop()->RunAllPending();

  PulseAudioFree();
  if (thread_ != NULL) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    // A ScopedAllowIO object is required to join the thread when calling Stop.
    // The worker thread should be idle at this time.
    // See http://crosbug.com/11110 for discussion.
    base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
    thread_->message_loop()->AssertIdle();

    thread_->Stop();
    thread_.reset();
  }
}

void AudioMixerPulse::Init(InitDoneCallback* callback) {
  DCHECK(callback);
  if (!InitThread()) {
    callback->Run(false);
    delete callback;
    return;
  }

  // Post the task of starting up, which can block for 200-500ms,
  // so best not to do it on the caller's thread.
  thread_->message_loop()->PostTask(FROM_HERE,
      NewRunnableMethod(this, &AudioMixerPulse::DoInit, callback));
}

bool AudioMixerPulse::InitSync() {
  if (!InitThread())
    return false;
  return PulseAudioInit();
}

double AudioMixerPulse::GetVolumeDb() const {
  if (!MainloopLockIfReady())
    return AudioMixer::kSilenceDb;
  AudioInfo data;
  GetAudioInfo(&data);
  MainloopUnlock();
  return pa_sw_volume_to_dB(data.cvolume.values[0]);
}

bool AudioMixerPulse::GetVolumeLimits(double* vol_min, double* vol_max) {
  if (vol_min)
    *vol_min = kMinVolumeDb;
  if (vol_max)
    *vol_max = kMaxVolumeDb;
  return true;
}

void AudioMixerPulse::SetVolumeDb(double vol_db) {
  if (!MainloopLockIfReady())
    return;

  // last_channels_ determines the number of channels on the main output device,
  // and is used later to set the volume on all channels at once.
  if (!last_channels_) {
    AudioInfo data;
    GetAudioInfo(&data);
    last_channels_ = data.cvolume.channels;
  }

  pa_operation* pa_op;
  pa_cvolume cvolume;
  pa_cvolume_set(&cvolume, last_channels_, pa_sw_volume_from_dB(vol_db));
  pa_op = pa_context_set_sink_volume_by_index(pa_context_, device_id_,
                                              &cvolume, NULL, NULL);
  pa_operation_unref(pa_op);
  MainloopUnlock();
  VLOG(1) << "Set volume to " << vol_db << " dB";
}

bool AudioMixerPulse::IsMute() const {
  if (!MainloopLockIfReady())
    return false;
  AudioInfo data;
  GetAudioInfo(&data);
  MainloopUnlock();
  return data.muted;
}

void AudioMixerPulse::SetMute(bool mute) {
  if (!MainloopLockIfReady())
    return;
  pa_operation* pa_op;
  pa_op = pa_context_set_sink_mute_by_index(pa_context_, device_id_,
                                            mute ? 1 : 0, NULL, NULL);
  pa_operation_unref(pa_op);
  MainloopUnlock();
  VLOG(1) << "Set mute to " << mute;
}

AudioMixer::State AudioMixerPulse::GetState() const {
  base::AutoLock lock(mixer_state_lock_);
  // If we think it's ready, verify it is actually so.
  if ((mixer_state_ == READY) &&
      (pa_context_get_state(pa_context_) != PA_CONTEXT_READY))
    mixer_state_ = IN_ERROR;
  return mixer_state_;
}

////////////////////////////////////////////////////////////////////////////////
// Private functions follow

void AudioMixerPulse::DoInit(InitDoneCallback* callback) {
  bool success = PulseAudioInit();
  callback->Run(success);
  delete callback;
}

bool AudioMixerPulse::InitThread() {
  base::AutoLock lock(mixer_state_lock_);

  if (mixer_state_ != UNINITIALIZED)
    return false;

  if (thread_ == NULL) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    thread_.reset(new base::Thread("AudioMixerPulse"));
    if (!thread_->Start()) {
      thread_.reset();
      return false;
    }
  }
  mixer_state_ = INITIALIZING;
  return true;
}

// static
void AudioMixerPulse::ConnectToPulseCallbackThunk(
    pa_context* context, void* userdata) {
  CallbackWrapper* data =
      static_cast<CallbackWrapper*>(userdata);
  data->instance->OnConnectToPulseCallback(context, &data->done);
}

void AudioMixerPulse::OnConnectToPulseCallback(
    pa_context* context, bool* connect_done) {
  pa_context_state_t state = pa_context_get_state(context);
  if (state == PA_CONTEXT_READY ||
      state == PA_CONTEXT_FAILED ||
      state == PA_CONTEXT_TERMINATED) {
    // Connection process has reached a terminal state. Wake PulseAudioInit().
    *connect_done = true;
    MainloopSignal();
  }
}

bool AudioMixerPulse::PulseAudioInit() {
  pa_context_state_t state = PA_CONTEXT_FAILED;

  {
    base::AutoLock lock(mixer_state_lock_);
    if (mixer_state_ != INITIALIZING)
      return false;

    pa_mainloop_ = pa_threaded_mainloop_new();
    if (!pa_mainloop_) {
      LOG(ERROR) << "Can't create PulseAudio mainloop";
      mixer_state_ = UNINITIALIZED;
      return false;
    }

    if (pa_threaded_mainloop_start(pa_mainloop_) != 0) {
      LOG(ERROR) << "Can't start PulseAudio mainloop";
      pa_threaded_mainloop_free(pa_mainloop_);
      mixer_state_ = UNINITIALIZED;
      return false;
    }
  }

  while (true) {
    // Create connection to default server.
    if (!MainloopSafeLock())
      return false;

    while (true) {
      pa_mainloop_api* pa_mlapi = pa_threaded_mainloop_get_api(pa_mainloop_);
      if (!pa_mlapi) {
        LOG(ERROR) << "Can't get PulseAudio mainloop api";
        break;
      }
      // This one takes the most time if run at app startup.
      pa_context_ = pa_context_new(pa_mlapi, "ChromeAudio");
      if (!pa_context_) {
        LOG(ERROR) << "Can't create new PulseAudio context";
        break;
      }

      MainloopUnlock();
      if (!MainloopSafeLock())
        return false;

      CallbackWrapper data = {this, false, NULL};
      pa_context_set_state_callback(pa_context_,
                                    &ConnectToPulseCallbackThunk,
                                    &data);

      if (pa_context_connect(pa_context_, NULL,
                             PA_CONTEXT_NOAUTOSPAWN, NULL) != 0) {
        LOG(ERROR) << "Can't start connection to PulseAudio sound server";
      } else {
        // Wait until we have a completed connection or fail.
        do {
          MainloopWait();
        } while (!data.done);

        state = pa_context_get_state(pa_context_);

        if (state == PA_CONTEXT_FAILED) {
          LOG(ERROR) << "PulseAudio connection failed (daemon not running?)";
        } else if (state == PA_CONTEXT_TERMINATED) {
          LOG(ERROR) << "PulseAudio connection terminated early";
        } else if (state != PA_CONTEXT_READY) {
          LOG(ERROR) << "Unknown problem connecting to PulseAudio";
        }
      }

      pa_context_set_state_callback(pa_context_, NULL, NULL);
      break;
    }

    MainloopUnlock();

    if (state != PA_CONTEXT_READY)
      break;

    if (!MainloopSafeLock())
      return false;
    GetDefaultPlaybackDevice();
    MainloopUnlock();

    if (device_id_ == kInvalidDeviceId)
      break;

    {
      base::AutoLock lock(mixer_state_lock_);
      if (mixer_state_ == SHUTTING_DOWN)
        return false;
      mixer_state_ = READY;
    }

    return true;
  }

  // Failed startup sequence, clean up now.
  PulseAudioFree();
  return false;
}

void AudioMixerPulse::PulseAudioFree() {
  {
    base::AutoLock lock(mixer_state_lock_);
    if (!pa_mainloop_)
      mixer_state_ = UNINITIALIZED;
    if ((mixer_state_ == UNINITIALIZED) || (mixer_state_ == SHUTTING_DOWN))
      return;

    // If still initializing on another thread, this will cause it to exit.
    mixer_state_ = SHUTTING_DOWN;
  }

  DCHECK(pa_mainloop_);

  MainloopLock();
  if (pa_context_) {
    pa_context_disconnect(pa_context_);
    pa_context_unref(pa_context_);
    pa_context_ = NULL;
  }
  MainloopUnlock();

  pa_threaded_mainloop_stop(pa_mainloop_);
  pa_threaded_mainloop_free(pa_mainloop_);
  pa_mainloop_ = NULL;

  {
    base::AutoLock lock(mixer_state_lock_);
    mixer_state_ = UNINITIALIZED;
  }
}

void AudioMixerPulse::CompleteOperation(pa_operation* pa_op,
                                        bool* done) const {
  // After starting any operation, this helper checks if it started OK, then
  // waits for it to complete by iterating through the mainloop until the
  // operation is not running anymore.
  CHECK(pa_op);

  while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
    // If operation still running, but we got what we needed, cancel it now.
    if (*done) {
      pa_operation_cancel(pa_op);
      break;
    }
    MainloopWait();
  }
  pa_operation_unref(pa_op);
}

// Must be called with mainloop lock held
void AudioMixerPulse::GetDefaultPlaybackDevice() {
  DCHECK_GT(mainloop_lock_count_, 0);
  DCHECK(pa_context_);
  DCHECK(pa_context_get_state(pa_context_) == PA_CONTEXT_READY);

  CallbackWrapper data = {this, false, NULL};

  pa_operation* pa_op = pa_context_get_sink_info_list(pa_context_,
                                        EnumerateDevicesCallback,
                                        &data);
  CompleteOperation(pa_op, &data.done);
  return;
}

void AudioMixerPulse::OnEnumerateDevices(const pa_sink_info* sink_info,
                                         int eol, bool* done) {
  if (device_id_ != kInvalidDeviceId)
    return;

  // TODO(davej): Should we handle cases of more than one output sink device?

  // eol is < 0 for error, > 0 for end of list, ==0 while listing.
  if (eol == 0) {
    device_id_ = sink_info->index;
  }
  *done = true;
  MainloopSignal();
}

// static
void AudioMixerPulse::EnumerateDevicesCallback(pa_context* unused,
                                               const pa_sink_info* sink_info,
                                               int eol,
                                               void* userdata) {
  CallbackWrapper* data =
      static_cast<CallbackWrapper*>(userdata);
  data->instance->OnEnumerateDevices(sink_info, eol, &data->done);
}

// Must be called with lock held
void AudioMixerPulse::GetAudioInfo(AudioInfo* info) const {
  DCHECK_GT(mainloop_lock_count_, 0);
  CallbackWrapper data = {const_cast<AudioMixerPulse*>(this), false, info};
  pa_operation* pa_op = pa_context_get_sink_info_by_index(pa_context_,
                                                          device_id_,
                                                          GetAudioInfoCallback,
                                                          &data);
  CompleteOperation(pa_op, &data.done);
}

// static
void AudioMixerPulse::GetAudioInfoCallback(pa_context* unused,
                                           const pa_sink_info* sink_info,
                                           int eol,
                                           void* userdata) {
  CallbackWrapper* data = static_cast<CallbackWrapper*>(userdata);
  AudioInfo* info = static_cast<AudioInfo*>(data->userdata);

  // Copy just the information we care about.
  if (eol == 0) {
    info->cvolume = sink_info->volume;
    info->muted = sink_info->mute ? true : false;
    data->done = true;
  }
  data->instance->MainloopSignal();
}

inline void AudioMixerPulse::MainloopLock() const {
  pa_threaded_mainloop_lock(pa_mainloop_);
  ++mainloop_lock_count_;
}

inline void AudioMixerPulse::MainloopUnlock() const {
  --mainloop_lock_count_;
  pa_threaded_mainloop_unlock(pa_mainloop_);
}

// Must be called with the lock held.
inline void AudioMixerPulse::MainloopWait() const {
  DCHECK_GT(mainloop_lock_count_, 0);
  pa_threaded_mainloop_wait(pa_mainloop_);
}

// Must be called with the lock held.
inline void AudioMixerPulse::MainloopSignal() const {
  DCHECK_GT(mainloop_lock_count_, 0);
  pa_threaded_mainloop_signal(pa_mainloop_, 0);
}

inline bool AudioMixerPulse::MainloopSafeLock() const {
  base::AutoLock lock(mixer_state_lock_);
  if ((mixer_state_ == SHUTTING_DOWN) || (!pa_mainloop_))
    return false;

  pa_threaded_mainloop_lock(pa_mainloop_);
  ++mainloop_lock_count_;
  return true;
}

inline bool AudioMixerPulse::MainloopLockIfReady() const {
  base::AutoLock lock(mixer_state_lock_);
  if (mixer_state_ != READY)
    return false;
  if (!pa_mainloop_)
    return false;
  pa_threaded_mainloop_lock(pa_mainloop_);
  ++mainloop_lock_count_;
  return true;
}

}  // namespace chromeos
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/testapp.h>

#include <tests/proton/common/dummydbowner.h>
#include <vespa/config-attributes.h>
#include <vespa/config-bucketspaces.h>
#include <vespa/config-imported-fields.h>
#include <vespa/config-indexschema.h>
#include <vespa/config-rank-profiles.h>
#include <vespa/config-summary.h>
#include <vespa/config-summarymap.h>
#include <vespa/document/base/testdocman.h>
#include <vespa/document/config/documenttypes_config_fwd.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/document/test/make_bucket_space.h>
#include <vespa/fastos/file.h>
#include <vespa/persistence/conformancetest/conformancetest.h>
#include <vespa/persistence/dummyimpl/dummy_bucket_executor.h>
#include <vespa/searchcommon/common/schemaconfigurer.h>
#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/proton/matching/querylimiter.h>
#include <vespa/searchcore/proton/metrics/metricswireservice.h>
#include <vespa/searchcore/proton/persistenceengine/ipersistenceengineowner.h>
#include <vespa/searchcore/proton/persistenceengine/persistenceengine.h>
#include <vespa/searchcore/proton/server/bootstrapconfig.h>
#include <vespa/searchcore/proton/server/document_db_maintenance_config.h>
#include <vespa/searchcore/proton/server/documentdb.h>
#include <vespa/searchcore/proton/server/documentdbconfigmanager.h>
#include <vespa/searchcore/proton/server/fileconfigmanager.h>
#include <vespa/searchcore/proton/server/memoryconfigstore.h>
#include <vespa/searchcore/proton/server/persistencehandlerproxy.h>
#include <vespa/searchcore/proton/server/threading_service_config.h>
#include <vespa/searchcore/proton/test/disk_mem_usage_notifier.h>
#include <vespa/searchcore/proton/test/mock_shared_threading_service.h>
#include <vespa/searchlib/index/dummyfileheadercontext.h>
#include <vespa/searchlib/transactionlog/translogserver.h>
#include <vespa/searchsummary/config/config-juniperrc.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/size_literals.h>

#include <vespa/log/log.h>
LOG_SETUP("persistenceconformance_test");

using namespace config;
using namespace proton;
using namespace cloud::config::filedistribution;
using namespace vespa::config::search::core;
using namespace vespa::config::search::summary;
using namespace vespa::config::search;
using namespace std::chrono_literals;
using vespa::config::content::core::BucketspacesConfig;

using std::shared_ptr;
using document::BucketSpace;
using document::DocumentType;
using document::DocumentTypeRepo;
using document::TestDocMan;
using document::test::makeBucketSpace;
using search::TuneFileDocumentDB;
using search::index::DummyFileHeaderContext;
using search::index::Schema;
using search::index::SchemaBuilder;
using search::transactionlog::TransLogServer;
using storage::spi::ConformanceTest;
using storage::spi::PersistenceProvider;

typedef ConformanceTest::PersistenceFactory         PersistenceFactory;
typedef DocumentDBConfig::DocumenttypesConfigSP     DocumenttypesConfigSP;
typedef std::map<DocTypeName, DocumentDB::SP>       DocumentDBMap;
typedef std::vector<DocTypeName>                    DocTypeVector;

void
storeDocType(DocTypeVector *types, const DocumentType &type)
{
    types->push_back(DocTypeName(type.getName()));
}

struct SchemaConfigFactory {
    typedef DocumentDBConfig CS;
    typedef std::shared_ptr<SchemaConfigFactory> SP;
    virtual ~SchemaConfigFactory() = default;
    static SchemaConfigFactory::SP get() { return std::make_shared<SchemaConfigFactory>(); }
    virtual CS::IndexschemaConfigSP createIndexSchema(const DocumentType &docType) {
        (void) docType;
        return std::make_shared<IndexschemaConfig>();
    }
    virtual CS::AttributesConfigSP createAttributes(const DocumentType &docType) {
        (void) docType;
        return std::make_shared<AttributesConfig>();
    }
    virtual CS::SummaryConfigSP createSummary(const DocumentType &docType) {
        (void) docType;
        return std::make_shared<SummaryConfig>();
    }
};

class ConfigFactory {
private:
    std::shared_ptr<const DocumentTypeRepo>    _repo;
    DocumenttypesConfigSP   _typeCfg;
    SchemaConfigFactory::SP _schemaFactory;

public:
    ConfigFactory(std::shared_ptr<const DocumentTypeRepo> repo,
                  DocumenttypesConfigSP typeCfg,
                  SchemaConfigFactory::SP schemaFactory);
    ~ConfigFactory();
    std::shared_ptr<const DocumentTypeRepo> getTypeRepo() const { return _repo; }
    DocumenttypesConfigSP getTypeCfg() const { return _typeCfg; }
    DocTypeVector getDocTypes() const {
        DocTypeVector types;
        _repo->forEachDocumentType(*DocumentTypeRepo::makeLambda([&types](const DocumentType &type) {
            types.push_back(DocTypeName(type.getName()));
        }));
        return types;
    }
    DocumentDBConfig::SP create(const DocTypeName &docTypeName) const {
        const DocumentType *docType = _repo->getDocumentType(docTypeName.getName());
        if (docType == nullptr) {
            return DocumentDBConfig::SP();
        }
        typedef DocumentDBConfig CS;
        CS::IndexschemaConfigSP indexschema = _schemaFactory->createIndexSchema(*docType);
        CS::AttributesConfigSP attributes = _schemaFactory->createAttributes(*docType);
        CS::SummaryConfigSP summary = _schemaFactory->createSummary(*docType);
        Schema::SP schema(new Schema());
        SchemaBuilder::build(*indexschema, *schema);
        SchemaBuilder::build(*attributes, *schema);
        SchemaBuilder::build(*summary, *schema);
        return std::make_shared<DocumentDBConfig>(
                        1,
                        std::make_shared<RankProfilesConfig>(),
                        std::make_shared<matching::RankingConstants>(),
                        std::make_shared<matching::RankingExpressions>(),
                        std::make_shared<matching::OnnxModels>(),
                        indexschema,
                        attributes,
                        summary,
                        std::make_shared<SummarymapConfig>(),
                        std::make_shared<JuniperrcConfig>(),
                        _typeCfg,
                        _repo,
                        std::make_shared<ImportedFieldsConfig>(),
                        std::make_shared<TuneFileDocumentDB>(),
                        schema,
                        std::make_shared<DocumentDBMaintenanceConfig>(),
                        search::LogDocumentStore::Config(),
                        std::make_shared<const ThreadingServiceConfig>(ThreadingServiceConfig::make(1)),
                        std::make_shared<const AllocConfig>(),
                        "client",
                        docTypeName.getName());
    }
};

ConfigFactory::ConfigFactory(std::shared_ptr<const DocumentTypeRepo> repo, DocumenttypesConfigSP typeCfg,
                             SchemaConfigFactory::SP schemaFactory)
    : _repo(std::move(repo)),
      _typeCfg(std::move(typeCfg)),
      _schemaFactory(std::move(schemaFactory))
{}
ConfigFactory::~ConfigFactory() = default;

class DocumentDBFactory : public DummyDBOwner {
private:
    vespalib::string          _baseDir;
    DummyFileHeaderContext    _fileHeaderContext;
    TransLogServer            _tls;
    vespalib::string          _tlsSpec;
    matching::QueryLimiter    _queryLimiter;
    vespalib::Clock           _clock;
    mutable DummyWireService  _metricsWireService;
    mutable MemoryConfigStores _config_stores;
    vespalib::ThreadStackExecutor _summaryExecutor;
    MockSharedThreadingService _shared_service;
    storage::spi::dummy::DummyBucketExecutor _bucketExecutor;

public:
    DocumentDBFactory(const vespalib::string &baseDir, int tlsListenPort);
    ~DocumentDBFactory() override;
    DocumentDB::SP create(BucketSpace bucketSpace,
                          const DocTypeName &docType,
                          const ConfigFactory &factory) {
        DocumentDBConfig::SP snapshot = factory.create(docType);
        vespalib::mkdir(_baseDir, false);
        vespalib::mkdir(_baseDir + "/" + docType.toString(), false);
        vespalib::string inputCfg = _baseDir + "/" + docType.toString() + "/baseconfig";
        {
            FileConfigManager fileCfg(inputCfg, "", docType.getName());
            fileCfg.saveConfig(*snapshot, 1);
        }
        config::DirSpec spec(inputCfg + "/config-1");
        TuneFileDocumentDB::SP tuneFileDocDB(new TuneFileDocumentDB());
        DocumentDBConfigHelper mgr(spec, docType.getName());
        auto b = std::make_shared<BootstrapConfig>(1, factory.getTypeCfg(), factory.getTypeRepo(),
                                                  std::make_shared<ProtonConfig>(),
                                                  std::make_shared<FiledistributorrpcConfig>(),
                                                  std::make_shared<BucketspacesConfig>(),
                                                  tuneFileDocDB, HwInfo());
        mgr.forwardConfig(b);
        mgr.nextGeneration(0ms);
        return DocumentDB::create(_baseDir, mgr.getConfig(), _tlsSpec, _queryLimiter, _clock, docType, bucketSpace,
                                  *b->getProtonConfigSP(), const_cast<DocumentDBFactory &>(*this),
                                  _shared_service, _bucketExecutor, _tls, _metricsWireService,
                                  _fileHeaderContext, _config_stores.getConfigStore(docType.toString()),
                                  std::make_shared<vespalib::ThreadStackExecutor>(16, 128_Ki), HwInfo());
    }
};

DocumentDBFactory::DocumentDBFactory(const vespalib::string &baseDir, int tlsListenPort)
    : _baseDir(baseDir),
      _fileHeaderContext(),
      _tls("tls", tlsListenPort, baseDir, _fileHeaderContext),
      _tlsSpec(vespalib::make_string("tcp/localhost:%d", tlsListenPort)),
      _queryLimiter(),
      _clock(),
      _metricsWireService(),
      _summaryExecutor(8, 128_Ki),
      _shared_service(_summaryExecutor, _summaryExecutor),
      _bucketExecutor(2)
{}
DocumentDBFactory::~DocumentDBFactory()  = default;

class DocumentDBRepo {
private:
    DocumentDBMap _docDbs;
public:
    typedef std::unique_ptr<DocumentDBRepo> UP;
    DocumentDBRepo(const ConfigFactory &cfgFactory, DocumentDBFactory &docDbFactory)
        : _docDbs()
    {
        DocTypeVector types = cfgFactory.getDocTypes();
        for (const auto & type : types) {
            BucketSpace bucketSpace(makeBucketSpace(type.getName()));
            DocumentDB::SP docDb = docDbFactory.create(bucketSpace, type, cfgFactory);
            docDb->start();
            docDb->waitForOnlineState();
            _docDbs[type] = docDb;
        }
    }

    void close() {
        for (auto & dbEntry : _docDbs) {
            dbEntry.second->close();
        }
    }

    ~DocumentDBRepo() {
        close();
    }
    const DocumentDBMap &getDocDbs() const { return _docDbs; }
};

class DocDBRepoHolder
{
protected:
    DocumentDBRepo::UP      _docDbRepo;

    explicit DocDBRepoHolder(DocumentDBRepo::UP docDbRepo)
        : _docDbRepo(std::move(docDbRepo))
    {
    }

    virtual ~DocDBRepoHolder() = default;

    void close() {
        if (_docDbRepo)
            _docDbRepo->close();
    }
};

class MyPersistenceEngineOwner : public IPersistenceEngineOwner
{
    void setClusterState(BucketSpace, const storage::spi::ClusterState &) override { }
};

struct MyResourceWriteFilter : public IResourceWriteFilter
{
    bool acceptWriteOperation() const override { return true; }
    State getAcceptState() const override { return IResourceWriteFilter::State(); }
};

class MyPersistenceEngine : public DocDBRepoHolder,
                            public PersistenceEngine
{
public:
    MyPersistenceEngine(MyPersistenceEngineOwner &owner,
                        MyResourceWriteFilter &writeFilter,
                        IDiskMemUsageNotifier& disk_mem_usage_notifier,
                        DocumentDBRepo::UP docDbRepo,
                        const vespalib::string &docType = "")
        : DocDBRepoHolder(std::move(docDbRepo)),
          PersistenceEngine(owner, writeFilter, disk_mem_usage_notifier, -1, false)
    {
        addHandlers(docType);
    }

    void
    addHandlers(const vespalib::string &docType)
    {
        if (!_docDbRepo)
            return;
        const DocumentDBMap &docDbs = _docDbRepo->getDocDbs();
        for (const auto & dbEntry : docDbs) {
            if (!docType.empty() && docType != dbEntry.first.getName()) {
                continue;
            }
            LOG(info, "putHandler(%s)", dbEntry.first.toString().c_str());
            auto proxy = std::make_shared<PersistenceHandlerProxy>(dbEntry.second);
            putHandler(getWLock(), dbEntry.second->getBucketSpace(), dbEntry.first, proxy);
        }
    }

    void
    removeHandlers()
    {
        if ( ! _docDbRepo)
            return;
        const DocumentDBMap &docDbs = _docDbRepo->getDocDbs();
        for (const auto & dbEntry : docDbs) {
            IPersistenceHandler::SP proxy(removeHandler(getWLock(), dbEntry.second->getBucketSpace(), dbEntry.first));
            (void) proxy;
        }
    }

    ~MyPersistenceEngine() override
    {
        destroyIterators();
        removeHandlers(); // Block calls to document db from engine
        close();      // Block upcalls to engine from document db
    }
};

class MyPersistenceFactory : public PersistenceFactory {
private:
    vespalib::string          _baseDir;
    DocumentDBFactory       _docDbFactory;
    SchemaConfigFactory::SP _schemaFactory;
    DocumentDBRepo::UP      _docDbRepo;
    vespalib::string        _docType;
    MyPersistenceEngineOwner _engineOwner;
    MyResourceWriteFilter    _writeFilter;
    test::DiskMemUsageNotifier   _disk_mem_usage_notifier;
public:
    MyPersistenceFactory(const vespalib::string &baseDir, int tlsListenPort,
                         SchemaConfigFactory::SP schemaFactory,
                         const vespalib::string & docType = "")
        : _baseDir(baseDir),
          _docDbFactory(baseDir, tlsListenPort),
          _schemaFactory(std::move(schemaFactory)),
          _docDbRepo(),
          _docType(docType),
          _engineOwner(),
          _writeFilter(),
          _disk_mem_usage_notifier(DiskMemUsageState({ 0.8, 0.5 }, { 0.8, 0.4 }))
    {
        clear();
    }
    ~MyPersistenceFactory() override {
        clear();
    }
    std::unique_ptr<PersistenceProvider> getPersistenceImplementation(const std::shared_ptr<const DocumentTypeRepo> &repo,
                                                         const DocumenttypesConfig &typesCfg) override {
        ConfigFactory cfgFactory(repo, std::make_shared<DocumenttypesConfig>(typesCfg), _schemaFactory);
        _docDbRepo = std::make_unique<DocumentDBRepo>(cfgFactory, _docDbFactory);
        auto engine = std::make_unique<MyPersistenceEngine>(_engineOwner,_writeFilter, _disk_mem_usage_notifier, std::move(_docDbRepo), _docType);
        assert( ! _docDbRepo); // Repo should be handed over
        return engine;
    }

    void clear() override {
        FastOS_FileInterface::EmptyAndRemoveDirectory(_baseDir.c_str());
    }

    bool hasPersistence() const override { return true; }
    bool supportsActiveState() const override { return true; }
    bool supportsBucketSpaces() const override { return true; }
};

std::unique_ptr<PersistenceFactory>
makeMyPersistenceFactory(const std::string &docType)
{
    return std::make_unique<MyPersistenceFactory>("testdb", 9017, SchemaConfigFactory::get(), docType);
}

int
main(int argc, char* argv[])
{
    ::testing::InitGoogleTest(&argc, argv);
    DummyFileHeaderContext::setCreator("persistenceconformance_test");
    ConformanceTest::_factoryFactory = &makeMyPersistenceFactory;
    return RUN_ALL_TESTS();
}
<commit_msg>Speed up persistence conformance test.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/testapp.h>

#include <tests/proton/common/dummydbowner.h>
#include <vespa/config-attributes.h>
#include <vespa/config-bucketspaces.h>
#include <vespa/config-imported-fields.h>
#include <vespa/config-indexschema.h>
#include <vespa/config-rank-profiles.h>
#include <vespa/config-summary.h>
#include <vespa/config-summarymap.h>
#include <vespa/document/base/testdocman.h>
#include <vespa/document/config/documenttypes_config_fwd.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/document/test/make_bucket_space.h>
#include <vespa/fastos/file.h>
#include <vespa/persistence/conformancetest/conformancetest.h>
#include <vespa/persistence/dummyimpl/dummy_bucket_executor.h>
#include <vespa/searchcommon/common/schemaconfigurer.h>
#include <vespa/searchcore/proton/common/alloc_config.h>
#include <vespa/searchcore/proton/common/hw_info.h>
#include <vespa/searchcore/proton/matching/querylimiter.h>
#include <vespa/searchcore/proton/metrics/metricswireservice.h>
#include <vespa/searchcore/proton/persistenceengine/ipersistenceengineowner.h>
#include <vespa/searchcore/proton/persistenceengine/persistenceengine.h>
#include <vespa/searchcore/proton/server/bootstrapconfig.h>
#include <vespa/searchcore/proton/server/document_db_maintenance_config.h>
#include <vespa/searchcore/proton/server/documentdb.h>
#include <vespa/searchcore/proton/server/documentdbconfigmanager.h>
#include <vespa/searchcore/proton/server/fileconfigmanager.h>
#include <vespa/searchcore/proton/server/memoryconfigstore.h>
#include <vespa/searchcore/proton/server/persistencehandlerproxy.h>
#include <vespa/searchcore/proton/server/threading_service_config.h>
#include <vespa/searchcore/proton/test/disk_mem_usage_notifier.h>
#include <vespa/searchcore/proton/test/mock_shared_threading_service.h>
#include <vespa/searchlib/index/dummyfileheadercontext.h>
#include <vespa/searchlib/transactionlog/translogserver.h>
#include <vespa/searchsummary/config/config-juniperrc.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/size_literals.h>

#include <vespa/log/log.h>
LOG_SETUP("persistenceconformance_test");

using namespace config;
using namespace proton;
using namespace cloud::config::filedistribution;
using namespace vespa::config::search::core;
using namespace vespa::config::search::summary;
using namespace vespa::config::search;
using namespace std::chrono_literals;
using vespa::config::content::core::BucketspacesConfig;

using std::shared_ptr;
using document::BucketSpace;
using document::DocumentType;
using document::DocumentTypeRepo;
using document::TestDocMan;
using document::test::makeBucketSpace;
using search::TuneFileDocumentDB;
using search::index::DummyFileHeaderContext;
using search::index::Schema;
using search::index::SchemaBuilder;
using search::transactionlog::TransLogServer;
using storage::spi::ConformanceTest;
using storage::spi::PersistenceProvider;

typedef ConformanceTest::PersistenceFactory         PersistenceFactory;
typedef DocumentDBConfig::DocumenttypesConfigSP     DocumenttypesConfigSP;
typedef std::map<DocTypeName, DocumentDB::SP>       DocumentDBMap;
typedef std::vector<DocTypeName>                    DocTypeVector;

void
storeDocType(DocTypeVector *types, const DocumentType &type)
{
    types->push_back(DocTypeName(type.getName()));
}

struct SchemaConfigFactory {
    typedef DocumentDBConfig CS;
    typedef std::shared_ptr<SchemaConfigFactory> SP;
    virtual ~SchemaConfigFactory() = default;
    static SchemaConfigFactory::SP get() { return std::make_shared<SchemaConfigFactory>(); }
    virtual CS::IndexschemaConfigSP createIndexSchema(const DocumentType &docType) {
        (void) docType;
        return std::make_shared<IndexschemaConfig>();
    }
    virtual CS::AttributesConfigSP createAttributes(const DocumentType &docType) {
        (void) docType;
        return std::make_shared<AttributesConfig>();
    }
    virtual CS::SummaryConfigSP createSummary(const DocumentType &docType) {
        (void) docType;
        return std::make_shared<SummaryConfig>();
    }
};

class ConfigFactory {
private:
    std::shared_ptr<const DocumentTypeRepo>    _repo;
    DocumenttypesConfigSP   _typeCfg;
    SchemaConfigFactory::SP _schemaFactory;

public:
    ConfigFactory(std::shared_ptr<const DocumentTypeRepo> repo,
                  DocumenttypesConfigSP typeCfg,
                  SchemaConfigFactory::SP schemaFactory);
    ~ConfigFactory();
    std::shared_ptr<const DocumentTypeRepo> getTypeRepo() const { return _repo; }
    DocumenttypesConfigSP getTypeCfg() const { return _typeCfg; }
    DocTypeVector getDocTypes() const {
        DocTypeVector types;
        _repo->forEachDocumentType(*DocumentTypeRepo::makeLambda([&types](const DocumentType &type) {
            types.push_back(DocTypeName(type.getName()));
        }));
        return types;
    }
    DocumentDBConfig::SP create(const DocTypeName &docTypeName) const {
        const DocumentType *docType = _repo->getDocumentType(docTypeName.getName());
        if (docType == nullptr) {
            return DocumentDBConfig::SP();
        }
        typedef DocumentDBConfig CS;
        CS::IndexschemaConfigSP indexschema = _schemaFactory->createIndexSchema(*docType);
        CS::AttributesConfigSP attributes = _schemaFactory->createAttributes(*docType);
        CS::SummaryConfigSP summary = _schemaFactory->createSummary(*docType);
        Schema::SP schema(new Schema());
        SchemaBuilder::build(*indexschema, *schema);
        SchemaBuilder::build(*attributes, *schema);
        SchemaBuilder::build(*summary, *schema);
        return std::make_shared<DocumentDBConfig>(
                        1,
                        std::make_shared<RankProfilesConfig>(),
                        std::make_shared<matching::RankingConstants>(),
                        std::make_shared<matching::RankingExpressions>(),
                        std::make_shared<matching::OnnxModels>(),
                        indexschema,
                        attributes,
                        summary,
                        std::make_shared<SummarymapConfig>(),
                        std::make_shared<JuniperrcConfig>(),
                        _typeCfg,
                        _repo,
                        std::make_shared<ImportedFieldsConfig>(),
                        std::make_shared<TuneFileDocumentDB>(),
                        schema,
                        std::make_shared<DocumentDBMaintenanceConfig>(),
                        search::LogDocumentStore::Config(),
                        std::make_shared<const ThreadingServiceConfig>(ThreadingServiceConfig::make(1)),
                        std::make_shared<const AllocConfig>(),
                        "client",
                        docTypeName.getName());
    }
};

ConfigFactory::ConfigFactory(std::shared_ptr<const DocumentTypeRepo> repo, DocumenttypesConfigSP typeCfg,
                             SchemaConfigFactory::SP schemaFactory)
    : _repo(std::move(repo)),
      _typeCfg(std::move(typeCfg)),
      _schemaFactory(std::move(schemaFactory))
{}
ConfigFactory::~ConfigFactory() = default;

class DocumentDBFactory : public DummyDBOwner {
private:
    vespalib::string          _baseDir;
    DummyFileHeaderContext    _fileHeaderContext;
    TransLogServer            _tls;
    vespalib::string          _tlsSpec;
    matching::QueryLimiter    _queryLimiter;
    vespalib::Clock           _clock;
    mutable DummyWireService  _metricsWireService;
    mutable MemoryConfigStores _config_stores;
    vespalib::ThreadStackExecutor _summaryExecutor;
    MockSharedThreadingService _shared_service;
    storage::spi::dummy::DummyBucketExecutor _bucketExecutor;

    static std::shared_ptr<ProtonConfig> make_proton_config() {
        ProtonConfigBuilder proton_config;
        proton_config.indexing.optimize = ProtonConfigBuilder::Indexing::Optimize::LATENCY;
        return std::make_shared<ProtonConfig>(proton_config);
    }

public:
    DocumentDBFactory(const vespalib::string &baseDir, int tlsListenPort);
    ~DocumentDBFactory() override;
    DocumentDB::SP create(BucketSpace bucketSpace,
                          const DocTypeName &docType,
                          const ConfigFactory &factory) {
        DocumentDBConfig::SP snapshot = factory.create(docType);
        vespalib::mkdir(_baseDir, false);
        vespalib::mkdir(_baseDir + "/" + docType.toString(), false);
        vespalib::string inputCfg = _baseDir + "/" + docType.toString() + "/baseconfig";
        {
            FileConfigManager fileCfg(inputCfg, "", docType.getName());
            fileCfg.saveConfig(*snapshot, 1);
        }
        config::DirSpec spec(inputCfg + "/config-1");
        TuneFileDocumentDB::SP tuneFileDocDB(new TuneFileDocumentDB());
        DocumentDBConfigHelper mgr(spec, docType.getName());
        auto b = std::make_shared<BootstrapConfig>(1, factory.getTypeCfg(), factory.getTypeRepo(),
                                                  make_proton_config(),
                                                  std::make_shared<FiledistributorrpcConfig>(),
                                                  std::make_shared<BucketspacesConfig>(),
                                                  tuneFileDocDB, HwInfo());
        mgr.forwardConfig(b);
        mgr.nextGeneration(0ms);
        return DocumentDB::create(_baseDir, mgr.getConfig(), _tlsSpec, _queryLimiter, _clock, docType, bucketSpace,
                                  *b->getProtonConfigSP(), const_cast<DocumentDBFactory &>(*this),
                                  _shared_service, _bucketExecutor, _tls, _metricsWireService,
                                  _fileHeaderContext, _config_stores.getConfigStore(docType.toString()),
                                  std::make_shared<vespalib::ThreadStackExecutor>(16, 128_Ki), HwInfo());
    }
};

DocumentDBFactory::DocumentDBFactory(const vespalib::string &baseDir, int tlsListenPort)
    : _baseDir(baseDir),
      _fileHeaderContext(),
      _tls("tls", tlsListenPort, baseDir, _fileHeaderContext),
      _tlsSpec(vespalib::make_string("tcp/localhost:%d", tlsListenPort)),
      _queryLimiter(),
      _clock(),
      _metricsWireService(),
      _summaryExecutor(8, 128_Ki),
      _shared_service(_summaryExecutor, _summaryExecutor),
      _bucketExecutor(2)
{}
DocumentDBFactory::~DocumentDBFactory()  = default;

class DocumentDBRepo {
private:
    DocumentDBMap _docDbs;
public:
    typedef std::unique_ptr<DocumentDBRepo> UP;
    DocumentDBRepo(const ConfigFactory &cfgFactory, DocumentDBFactory &docDbFactory)
        : _docDbs()
    {
        DocTypeVector types = cfgFactory.getDocTypes();
        for (const auto & type : types) {
            BucketSpace bucketSpace(makeBucketSpace(type.getName()));
            DocumentDB::SP docDb = docDbFactory.create(bucketSpace, type, cfgFactory);
            docDb->start();
            docDb->waitForOnlineState();
            _docDbs[type] = docDb;
        }
    }

    void close() {
        for (auto & dbEntry : _docDbs) {
            dbEntry.second->close();
        }
    }

    ~DocumentDBRepo() {
        close();
    }
    const DocumentDBMap &getDocDbs() const { return _docDbs; }
};

class DocDBRepoHolder
{
protected:
    DocumentDBRepo::UP      _docDbRepo;

    explicit DocDBRepoHolder(DocumentDBRepo::UP docDbRepo)
        : _docDbRepo(std::move(docDbRepo))
    {
    }

    virtual ~DocDBRepoHolder() = default;

    void close() {
        if (_docDbRepo)
            _docDbRepo->close();
    }
};

class MyPersistenceEngineOwner : public IPersistenceEngineOwner
{
    void setClusterState(BucketSpace, const storage::spi::ClusterState &) override { }
};

struct MyResourceWriteFilter : public IResourceWriteFilter
{
    bool acceptWriteOperation() const override { return true; }
    State getAcceptState() const override { return IResourceWriteFilter::State(); }
};

class MyPersistenceEngine : public DocDBRepoHolder,
                            public PersistenceEngine
{
public:
    MyPersistenceEngine(MyPersistenceEngineOwner &owner,
                        MyResourceWriteFilter &writeFilter,
                        IDiskMemUsageNotifier& disk_mem_usage_notifier,
                        DocumentDBRepo::UP docDbRepo,
                        const vespalib::string &docType = "")
        : DocDBRepoHolder(std::move(docDbRepo)),
          PersistenceEngine(owner, writeFilter, disk_mem_usage_notifier, -1, false)
    {
        addHandlers(docType);
    }

    void
    addHandlers(const vespalib::string &docType)
    {
        if (!_docDbRepo)
            return;
        const DocumentDBMap &docDbs = _docDbRepo->getDocDbs();
        for (const auto & dbEntry : docDbs) {
            if (!docType.empty() && docType != dbEntry.first.getName()) {
                continue;
            }
            LOG(info, "putHandler(%s)", dbEntry.first.toString().c_str());
            auto proxy = std::make_shared<PersistenceHandlerProxy>(dbEntry.second);
            putHandler(getWLock(), dbEntry.second->getBucketSpace(), dbEntry.first, proxy);
        }
    }

    void
    removeHandlers()
    {
        if ( ! _docDbRepo)
            return;
        const DocumentDBMap &docDbs = _docDbRepo->getDocDbs();
        for (const auto & dbEntry : docDbs) {
            IPersistenceHandler::SP proxy(removeHandler(getWLock(), dbEntry.second->getBucketSpace(), dbEntry.first));
            (void) proxy;
        }
    }

    ~MyPersistenceEngine() override
    {
        destroyIterators();
        removeHandlers(); // Block calls to document db from engine
        close();      // Block upcalls to engine from document db
    }
};

class MyPersistenceFactory : public PersistenceFactory {
private:
    vespalib::string          _baseDir;
    DocumentDBFactory       _docDbFactory;
    SchemaConfigFactory::SP _schemaFactory;
    DocumentDBRepo::UP      _docDbRepo;
    vespalib::string        _docType;
    MyPersistenceEngineOwner _engineOwner;
    MyResourceWriteFilter    _writeFilter;
    test::DiskMemUsageNotifier   _disk_mem_usage_notifier;
public:
    MyPersistenceFactory(const vespalib::string &baseDir, int tlsListenPort,
                         SchemaConfigFactory::SP schemaFactory,
                         const vespalib::string & docType = "")
        : _baseDir(baseDir),
          _docDbFactory(baseDir, tlsListenPort),
          _schemaFactory(std::move(schemaFactory)),
          _docDbRepo(),
          _docType(docType),
          _engineOwner(),
          _writeFilter(),
          _disk_mem_usage_notifier(DiskMemUsageState({ 0.8, 0.5 }, { 0.8, 0.4 }))
    {
        clear();
    }
    ~MyPersistenceFactory() override {
        clear();
    }
    std::unique_ptr<PersistenceProvider> getPersistenceImplementation(const std::shared_ptr<const DocumentTypeRepo> &repo,
                                                         const DocumenttypesConfig &typesCfg) override {
        ConfigFactory cfgFactory(repo, std::make_shared<DocumenttypesConfig>(typesCfg), _schemaFactory);
        _docDbRepo = std::make_unique<DocumentDBRepo>(cfgFactory, _docDbFactory);
        auto engine = std::make_unique<MyPersistenceEngine>(_engineOwner,_writeFilter, _disk_mem_usage_notifier, std::move(_docDbRepo), _docType);
        assert( ! _docDbRepo); // Repo should be handed over
        return engine;
    }

    void clear() override {
        FastOS_FileInterface::EmptyAndRemoveDirectory(_baseDir.c_str());
    }

    bool hasPersistence() const override { return true; }
    bool supportsActiveState() const override { return true; }
    bool supportsBucketSpaces() const override { return true; }
};

std::unique_ptr<PersistenceFactory>
makeMyPersistenceFactory(const std::string &docType)
{
    return std::make_unique<MyPersistenceFactory>("testdb", 9017, SchemaConfigFactory::get(), docType);
}

int
main(int argc, char* argv[])
{
    ::testing::InitGoogleTest(&argc, argv);
    DummyFileHeaderContext::setCreator("persistenceconformance_test");
    ConformanceTest::_factoryFactory = &makeMyPersistenceFactory;
    return RUN_ALL_TESTS();
}
<|endoftext|>