<commit_before>#include "logger.hpp"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "utils.hpp"
#include <boost/bind.hpp>

#include "arch/arch.hpp"
#include "concurrency/rwi_lock.hpp"
#include "concurrency/pmap.hpp"

#include "thread_local.hpp"

FILE *log_file = stderr;

/* As an optimization, during the main phase of the server's running we route all log messages
to one thread to write them. */

TLS_with_init(log_controller_t *, log_controller, NULL);
TLS_with_init(rwi_lock_t *, log_controller_lock, NULL);

static void install_log_controller(log_controller_t *lc, int thread) {
    on_thread_t thread_switcher(thread);
    rassert(!TLS_get_log_controller());
    TLS_set_log_controller(lc);
    TLS_set_log_controller_lock(new rwi_lock_t);
}

log_controller_t::log_controller_t() : home_thread_(get_thread_id()) {
    pmap(get_num_threads(), boost::bind(&install_log_controller, this, _1));
}

static void uninstall_log_controller(log_controller_t *lc, int thread) {
    on_thread_t thread_switcher(thread);
    rassert(TLS_get_log_controller() == lc);
    TLS_set_log_controller(NULL);
    TLS_get_log_controller_lock()->co_lock(rwi_write);
    delete TLS_get_log_controller_lock();
}

log_controller_t::~log_controller_t() {
    pmap(get_num_threads(), boost::bind(&uninstall_log_controller, this, _1));
}

/* Log message class. */

struct log_message_t
{
    std::vector<char> contents;   // Not NUL-terminated

    void vprintf(const char *format, va_list arg) {

        /* We don't know how much space it will take to format the message. So we initially guess
        a reasonable size, and if that's insufficient then we try again with exactly as much space
        as is needed. */
        static const int chunk_length_guess = 200;

        /* vsnprintf mutates its 'arg' parameter, so we need to duplicate it in case we will be
        calling it a second time. */
        va_list arg2;
        va_copy(arg2, arg);

        int old_length = contents.size();
        contents.resize(old_length + chunk_length_guess + 1);   // +1 for NUL-terminator
        int chunk_length = vsnprintf(
            contents.data() + old_length,
            chunk_length_guess + 1,   // +1 for NUL-terminator
            format, arg);

        if (chunk_length == -1) {
            /* Abort; something went wrong somehow */
            contents.resize(old_length);

        } else if (chunk_length <= chunk_length_guess) {
            /* Our guess was sufficient; the entire message was printed. Now shrink the
            array again to remove any unnecessary space and to cut off the NUL-terminator. */
            contents.resize(old_length + chunk_length);

        } else {
            /* Our guess was insufficient; grow the buffer and print it again. Fortunately,
            'chunk_length' is now exactly as much space as we need. */
            contents.resize(old_length + chunk_length + 1);   // +1 for NUL-terminator
            int chunk_length_2 __attribute__((unused)) = vsnprintf(
                contents.data() + old_length,
                chunk_length + 1,   // +1 for NUL-terminator
                format, arg2);
            rassert(chunk_length_2 == chunk_length);   /* snprintf should be deterministic... */

            /* Cut off NUL-terminator */
            contents.resize(old_length + chunk_length);
        }
    }
};

/* Functions to actually do the logging */

static __thread log_message_t *current_message;

void _logf(const char *src_file, int src_line, log_level_t level, const char *format, ...) {

    log_message_t *old_message = current_message;
    current_message = NULL;

    _mlog_start(src_file, src_line, level);
    va_list arg;
    va_start(arg, format);
    current_message->vprintf(format, arg);
    va_end(arg);
    mlog_end();

    current_message = old_message;
}

void _mlog_start(const char *src_file, int src_line, log_level_t level) {

    rassert(!current_message);
    current_message = new log_message_t;

    const char *level_str;
    switch (level) {
        case DBG: level_str = "debug"; break;
        case INF: level_str = "info"; break;
        case WRN: level_str = "warn"; break;
        case ERR: level_str = "error"; break;
        default:  unreachable();
    };

    precise_time_t t = get_time_now();
    char formatted_time[formatted_precise_time_length+1];
    format_precise_time(t, formatted_time, sizeof(formatted_time));

    /* If the log controller hasn't been started yet, then assume the thread pool hasn't been
    started either, so don't write which core the message came from. */
    if (TLS_get_log_controller()) {
        mlogf("%s %s (Q%d, %s:%d): ", formatted_time, level_str, get_thread_id(), src_file, src_line);
    } else {
        mlogf("%s %s (%s:%d): ", formatted_time, level_str, src_file, src_line);
    }
}

void mlogf(const char *format, ...) {
    rassert(current_message);
    va_list arg;
    va_start(arg, format);
    current_message->vprintf(format, arg);
    va_end(arg);
}

static void do_write_message(log_message_t *msg) {
    size_t written = fwrite(msg->contents.data(), 1, msg->contents.size(), log_file);
    if (written != msg->contents.size() || ferror(log_file)) {
        // An error occurred. Not a lot we can do here.
        BREAKPOINT;
    }
}

void write_log_message(log_message_t *msg, log_controller_t *lc) {

    {
        on_thread_t thread_switcher(lc->home_thread_);
        do_write_message(msg);
    }

    delete msg;
    TLS_get_log_controller_lock()->unlock();
}

void mlog_end() {
    rassert(current_message);
    if (!TLS_get_log_controller()) {
        do_write_message(current_message);
        delete current_message;
    } else {
        TLS_get_log_controller_lock()->co_lock(rwi_read);
        coro_t::spawn(boost::bind(&write_log_message, current_message, TLS_get_log_controller()));
    }
    current_message = NULL;
}

<commit_msg>Adds an UNUSED to make things work in release mode.<commit_after>#include "logger.hpp"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "utils.hpp"
#include <boost/bind.hpp>

#include "arch/arch.hpp"
#include "concurrency/rwi_lock.hpp"
#include "concurrency/pmap.hpp"

#include "thread_local.hpp"

FILE *log_file = stderr;

/* As an optimization, during the main phase of the server's running we route all log messages
to one thread to write them. */

TLS_with_init(log_controller_t *, log_controller, NULL);
TLS_with_init(rwi_lock_t *, log_controller_lock, NULL);

static void install_log_controller(UNUSED log_controller_t *lc, int thread) {
    on_thread_t thread_switcher(thread);
    rassert(!TLS_get_log_controller());
    TLS_set_log_controller(lc);
    TLS_set_log_controller_lock(new rwi_lock_t);
}

log_controller_t::log_controller_t() : home_thread_(get_thread_id()) {
    pmap(get_num_threads(), boost::bind(&install_log_controller, this, _1));
}

static void uninstall_log_controller(UNUSED log_controller_t *lc, int thread) {
    on_thread_t thread_switcher(thread);
    rassert(TLS_get_log_controller() == lc);
    TLS_set_log_controller(NULL);
    TLS_get_log_controller_lock()->co_lock(rwi_write);
    delete TLS_get_log_controller_lock();
}

log_controller_t::~log_controller_t() {
    pmap(get_num_threads(), boost::bind(&uninstall_log_controller, this, _1));
}

/* Log message class. */

struct log_message_t
{
    std::vector<char> contents;   // Not NUL-terminated

    void vprintf(const char *format, va_list arg) {

        /* We don't know how much space it will take to format the message. So we initially guess
        a reasonable size, and if that's insufficient then we try again with exactly as much space
        as is needed. */
        static const int chunk_length_guess = 200;

        /* vsnprintf mutates its 'arg' parameter, so we need to duplicate it in case we will be
        calling it a second time. */
        va_list arg2;
        va_copy(arg2, arg);

        int old_length = contents.size();
        contents.resize(old_length + chunk_length_guess + 1);   // +1 for NUL-terminator
        int chunk_length = vsnprintf(
            contents.data() + old_length,
            chunk_length_guess + 1,   // +1 for NUL-terminator
            format, arg);

        if (chunk_length == -1) {
            /* Abort; something went wrong somehow */
            contents.resize(old_length);

        } else if (chunk_length <= chunk_length_guess) {
            /* Our guess was sufficient; the entire message was printed. Now shrink the
            array again to remove any unnecessary space and to cut off the NUL-terminator. */
            contents.resize(old_length + chunk_length);

        } else {
            /* Our guess was insufficient; grow the buffer and print it again. Fortunately,
            'chunk_length' is now exactly as much space as we need. */
            contents.resize(old_length + chunk_length + 1);   // +1 for NUL-terminator
            int chunk_length_2 __attribute__((unused)) = vsnprintf(
                contents.data() + old_length,
                chunk_length + 1,   // +1 for NUL-terminator
                format, arg2);
            rassert(chunk_length_2 == chunk_length);   /* snprintf should be deterministic... */

            /* Cut off NUL-terminator */
            contents.resize(old_length + chunk_length);
        }
    }
};

/* Functions to actually do the logging */

static __thread log_message_t *current_message;

void _logf(const char *src_file, int src_line, log_level_t level, const char *format, ...) {

    log_message_t *old_message = current_message;
    current_message = NULL;

    _mlog_start(src_file, src_line, level);
    va_list arg;
    va_start(arg, format);
    current_message->vprintf(format, arg);
    va_end(arg);
    mlog_end();

    current_message = old_message;
}

void _mlog_start(const char *src_file, int src_line, log_level_t level) {

    rassert(!current_message);
    current_message = new log_message_t;

    const char *level_str;
    switch (level) {
        case DBG: level_str = "debug"; break;
        case INF: level_str = "info"; break;
        case WRN: level_str = "warn"; break;
        case ERR: level_str = "error"; break;
        default:  unreachable();
    };

    precise_time_t t = get_time_now();
    char formatted_time[formatted_precise_time_length+1];
    format_precise_time(t, formatted_time, sizeof(formatted_time));

    /* If the log controller hasn't been started yet, then assume the thread pool hasn't been
    started either, so don't write which core the message came from. */
    if (TLS_get_log_controller()) {
        mlogf("%s %s (Q%d, %s:%d): ", formatted_time, level_str, get_thread_id(), src_file, src_line);
    } else {
        mlogf("%s %s (%s:%d): ", formatted_time, level_str, src_file, src_line);
    }
}

void mlogf(const char *format, ...) {
    rassert(current_message);
    va_list arg;
    va_start(arg, format);
    current_message->vprintf(format, arg);
    va_end(arg);
}

static void do_write_message(log_message_t *msg) {
    size_t written = fwrite(msg->contents.data(), 1, msg->contents.size(), log_file);
    if (written != msg->contents.size() || ferror(log_file)) {
        // An error occurred. Not a lot we can do here.
        BREAKPOINT;
    }
}

void write_log_message(log_message_t *msg, log_controller_t *lc) {

    {
        on_thread_t thread_switcher(lc->home_thread_);
        do_write_message(msg);
    }

    delete msg;
    TLS_get_log_controller_lock()->unlock();
}

void mlog_end() {
    rassert(current_message);
    if (!TLS_get_log_controller()) {
        do_write_message(current_message);
        delete current_message;
    } else {
        TLS_get_log_controller_lock()->co_lock(rwi_read);
        coro_t::spawn(boost::bind(&write_log_message, current_message, TLS_get_log_controller()));
    }
    current_message = NULL;
}

<|endoftext|>


<commit_before>// Copyright 2014 the V8 project 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 "src/v8.h"

#include "src/bootstrapper.h"
#include "src/deoptimizer.h"
#include "src/lookup.h"
#include "src/lookup-inl.h"

namespace v8 {
namespace internal {

void LookupIterator::Next() {
  DCHECK_NE(JSPROXY, state_);
  DCHECK_NE(TRANSITION, state_);
  DisallowHeapAllocation no_gc;
  has_property_ = false;

  JSReceiver* holder = *holder_;
  Map* map = *holder_map_;

  // Perform lookup on current holder.
  state_ = LookupInHolder(map, holder);
  if (IsFound()) return;

  // Continue lookup if lookup on current holder failed.
  do {
    JSReceiver* maybe_holder = NextHolder(map);
    if (maybe_holder == nullptr) {
      if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
        RestartLookupForNonMaskingInterceptors();
        return;
      }
      break;
    }
    holder = maybe_holder;
    map = holder->map();
    state_ = LookupInHolder(map, holder);
  } while (!IsFound());

  if (holder != *holder_) {
    holder_ = handle(holder, isolate_);
    holder_map_ = handle(map, isolate_);
  }
}

void LookupIterator::RestartLookupForNonMaskingInterceptors() {
  interceptor_state_ = InterceptorState::kProcessNonMasking;
  state_ = NOT_FOUND;
  property_details_ = PropertyDetails::Empty();
  number_ = DescriptorArray::kNotFound;
  holder_ = initial_holder_;
  holder_map_ = handle(holder_->map(), isolate_);
  Next();
}

Handle<JSReceiver> LookupIterator::GetRoot(Handle<Object> receiver,
                                           Isolate* isolate) {
  if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
  auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
  CHECK(!root->IsNull());
  return Handle<JSReceiver>::cast(root);
}

Handle<Map> LookupIterator::GetReceiverMap() const {
  if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
  return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
}

Handle<JSObject> LookupIterator::GetStoreTarget() const {
  if (receiver_->IsJSGlobalProxy()) {
    PrototypeIterator iter(isolate(), receiver_);
    if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
    return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
  }
  return Handle<JSObject>::cast(receiver_);
}

bool LookupIterator::IsBootstrapping() const {
  return isolate_->bootstrapper()->IsActive();
}

bool LookupIterator::HasAccess() const {
  DCHECK_EQ(ACCESS_CHECK, state_);
  return isolate_->MayAccess(GetHolder<JSObject>());
}

void LookupIterator::ReloadPropertyInformation() {
  state_ = BEFORE_PROPERTY;
  interceptor_state_ = InterceptorState::kUninitialized;
  state_ = LookupInHolder(*holder_map_, *holder_);
  DCHECK(IsFound() || holder_map_->is_dictionary_map());
}

void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
  DCHECK(state_ == DATA || state_ == ACCESSOR);
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  if (holder_map_->is_dictionary_map()) return;
  holder_map_ =
      Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
  JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
  ReloadPropertyInformation();
}

void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                             PropertyAttributes attributes) {
  DCHECK(state_ == DATA || state_ == ACCESSOR);
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  Handle<JSObject> holder = GetHolder<JSObject>();
  if (holder_map_->is_dictionary_map()) {
    PropertyDetails details(attributes, v8::internal::DATA, 0,
                            PropertyCellType::kMutable);
    JSObject::SetNormalizedProperty(holder, name(), value, details);
  } else {
    holder_map_ = Map::ReconfigureExistingProperty(
        holder_map_, descriptor_number(), i::kData, attributes);
    holder_map_ =
        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
    JSObject::MigrateToMap(holder, holder_map_);
  }

  ReloadPropertyInformation();
}

void LookupIterator::PrepareTransitionToDataProperty(
    Handle<Object> value, PropertyAttributes attributes,
    Object::StoreFromKeyed store_mode) {
  if (state_ == TRANSITION) return;
  DCHECK_NE(LookupIterator::ACCESSOR, state_);
  DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, state_);
  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
  // Can only be called when the receiver is a JSObject. JSProxy has to be
  // handled via a trap. Adding properties to primitive values is not
  // observable.
  Handle<JSObject> receiver = GetStoreTarget();

  if (!isolate()->IsInternallyUsedPropertyName(name()) &&
      !receiver->map()->is_extensible()) {
    return;
  }

  auto transition = Map::TransitionToDataProperty(
      handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
  state_ = TRANSITION;
  transition_ = transition;

  if (receiver->IsGlobalObject()) {
    // Install a property cell.
    InternalizeName();
    auto cell = GlobalObject::EnsurePropertyCell(
        Handle<GlobalObject>::cast(receiver), name());
    DCHECK(cell->value()->IsTheHole());
    transition_ = cell;
  } else if (transition->GetBackPointer()->IsMap()) {
    property_details_ = transition->GetLastDescriptorDetails();
    has_property_ = true;
  }
}

void LookupIterator::ApplyTransitionToDataProperty() {
  DCHECK_EQ(TRANSITION, state_);

  Handle<JSObject> receiver = GetStoreTarget();
  if (receiver->IsGlobalObject()) return;
  holder_ = receiver;
  holder_map_ = transition_map();
  JSObject::MigrateToMap(receiver, holder_map_);
  ReloadPropertyInformation();
}

void LookupIterator::TransitionToAccessorProperty(
    AccessorComponent component, Handle<Object> accessor,
    PropertyAttributes attributes) {
  DCHECK(!accessor->IsNull());
  // Can only be called when the receiver is a JSObject. JSProxy has to be
  // handled via a trap. Adding properties to primitive values is not
  // observable.
  Handle<JSObject> receiver = GetStoreTarget();
  holder_ = receiver;
  holder_map_ =
      Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
                                        name_, component, accessor, attributes);
  JSObject::MigrateToMap(receiver, holder_map_);

  ReloadPropertyInformation();

  if (!holder_map_->is_dictionary_map()) return;

  // Install the accessor into the dictionary-mode object.
  PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
                          PropertyCellType::kMutable);
  Handle<AccessorPair> pair;
  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
    pair = Handle<AccessorPair>::cast(GetAccessors());
    // If the component and attributes are identical, nothing has to be done.
    if (pair->get(component) == *accessor) {
      if (property_details().attributes() == attributes) return;
    } else {
      pair = AccessorPair::Copy(pair);
      pair->set(component, *accessor);
    }
  } else {
    pair = isolate()->factory()->NewAccessorPair();
    pair->set(component, *accessor);
  }
  JSObject::SetNormalizedProperty(receiver, name_, pair, details);

  JSObject::ReoptimizeIfPrototype(receiver);
  holder_map_ = handle(receiver->map(), isolate_);
  ReloadPropertyInformation();
}

bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
  // Optimization that only works if configuration_ is not mutable.
  if (!check_prototype_chain()) return true;
  DisallowHeapAllocation no_gc;
  if (!receiver_->IsJSReceiver()) return false;
  Object* current = *receiver_;
  JSReceiver* holder = *holder_;
  // JSProxy do not occur as hidden prototypes.
  if (current->IsJSProxy()) {
    return JSReceiver::cast(current) == holder;
  }
  PrototypeIterator iter(isolate(), current,
                         PrototypeIterator::START_AT_RECEIVER);
  do {
    if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
    DCHECK(!current->IsJSProxy());
    iter.Advance();
  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
  return false;
}

Handle<Object> LookupIterator::FetchValue() const {
  Object* result = NULL;
  Handle<JSObject> holder = GetHolder<JSObject>();
  if (holder_map_->is_dictionary_map()) {
    result = holder->property_dictionary()->ValueAt(number_);
    if (holder_map_->IsGlobalObjectMap()) {
      DCHECK(result->IsPropertyCell());
      result = PropertyCell::cast(result)->value();
    }
  } else if (property_details_.type() == v8::internal::DATA) {
    FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
    return JSObject::FastPropertyAt(holder, property_details_.representation(),
                                    field_index);
  } else {
    result = holder_map_->instance_descriptors()->GetValue(number_);
  }
  return handle(result, isolate_);
}

int LookupIterator::GetAccessorIndex() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
  return descriptor_number();
}

int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
  return descriptor_number();
}

FieldIndex LookupIterator::GetFieldIndex() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
  int index =
      holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
  bool is_double = representation().IsDouble();
  return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
}

Handle<HeapType> LookupIterator::GetFieldType() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
  return handle(
      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
      isolate_);
}

Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
  Handle<JSObject> holder = GetHolder<JSObject>();
  Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
  Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
  DCHECK(value->IsPropertyCell());
  return handle(PropertyCell::cast(value));
}

Handle<Object> LookupIterator::GetAccessors() const {
  DCHECK_EQ(ACCESSOR, state_);
  return FetchValue();
}

Handle<Object> LookupIterator::GetDataValue() const {
  DCHECK_EQ(DATA, state_);
  Handle<Object> value = FetchValue();
  return value;
}

Handle<Object> LookupIterator::WriteDataValue(Handle<Object> value) {
  DCHECK_EQ(DATA, state_);
  Handle<JSObject> holder = GetHolder<JSObject>();
  if (holder_map_->is_dictionary_map()) {
    Handle<NameDictionary> property_dictionary =
        handle(holder->property_dictionary());
    if (holder->IsGlobalObject()) {
      value = PropertyCell::UpdateCell(property_dictionary, dictionary_entry(),
                                       value, property_details_);
    } else {
      property_dictionary->ValueAtPut(dictionary_entry(), *value);
    }
  } else if (property_details_.type() == v8::internal::DATA) {
    holder->WriteToField(descriptor_number(), *value);
  } else {
    DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
  }
  return value;
}

bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
  DCHECK(exotic_index_state_ != ExoticIndexState::kNoIndex);
  // Currently typed arrays are the only such objects.
  if (!holder->IsJSTypedArray()) return false;
  if (exotic_index_state_ == ExoticIndexState::kIndex) return true;
  DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
  bool result = false;
  // Compute and cache result.
  if (name()->IsString()) {
    Handle<String> name_string = Handle<String>::cast(name());
    if (name_string->length() != 0) {
      result = IsNonArrayIndexInteger(*name_string);
    }
  }
  exotic_index_state_ =
      result ? ExoticIndexState::kIndex : ExoticIndexState::kNoIndex;
  return result;
}

void LookupIterator::InternalizeName() {
  if (name_->IsUniqueName()) return;
  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}

bool LookupIterator::SkipInterceptor(JSObject* holder) {
  auto info = holder->GetNamedInterceptor();
  // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
  if (info->non_masking()) {
    switch (interceptor_state_) {
      case InterceptorState::kUninitialized:
        interceptor_state_ = InterceptorState::kSkipNonMasking;
      // Fall through.
      case InterceptorState::kSkipNonMasking:
        return true;
      case InterceptorState::kProcessNonMasking:
        return false;
    }
  }
  return interceptor_state_ == InterceptorState::kProcessNonMasking;
}
} }  // namespace v8::internal
<commit_msg>Restore PushStackTraceAndDie for the case where we lookup starting with null<commit_after>// Copyright 2014 the V8 project 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 "src/v8.h"

#include "src/bootstrapper.h"
#include "src/deoptimizer.h"
#include "src/lookup.h"
#include "src/lookup-inl.h"

namespace v8 {
namespace internal {

void LookupIterator::Next() {
  DCHECK_NE(JSPROXY, state_);
  DCHECK_NE(TRANSITION, state_);
  DisallowHeapAllocation no_gc;
  has_property_ = false;

  JSReceiver* holder = *holder_;
  Map* map = *holder_map_;

  // Perform lookup on current holder.
  state_ = LookupInHolder(map, holder);
  if (IsFound()) return;

  // Continue lookup if lookup on current holder failed.
  do {
    JSReceiver* maybe_holder = NextHolder(map);
    if (maybe_holder == nullptr) {
      if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
        RestartLookupForNonMaskingInterceptors();
        return;
      }
      break;
    }
    holder = maybe_holder;
    map = holder->map();
    state_ = LookupInHolder(map, holder);
  } while (!IsFound());

  if (holder != *holder_) {
    holder_ = handle(holder, isolate_);
    holder_map_ = handle(map, isolate_);
  }
}

void LookupIterator::RestartLookupForNonMaskingInterceptors() {
  interceptor_state_ = InterceptorState::kProcessNonMasking;
  state_ = NOT_FOUND;
  property_details_ = PropertyDetails::Empty();
  number_ = DescriptorArray::kNotFound;
  holder_ = initial_holder_;
  holder_map_ = handle(holder_->map(), isolate_);
  Next();
}

Handle<JSReceiver> LookupIterator::GetRoot(Handle<Object> receiver,
                                           Isolate* isolate) {
  if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
  auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
  if (root->IsNull()) {
    unsigned int magic = 0xbbbbbbbb;
    isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
  }
  return Handle<JSReceiver>::cast(root);
}

Handle<Map> LookupIterator::GetReceiverMap() const {
  if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
  return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
}

Handle<JSObject> LookupIterator::GetStoreTarget() const {
  if (receiver_->IsJSGlobalProxy()) {
    PrototypeIterator iter(isolate(), receiver_);
    if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
    return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
  }
  return Handle<JSObject>::cast(receiver_);
}

bool LookupIterator::IsBootstrapping() const {
  return isolate_->bootstrapper()->IsActive();
}

bool LookupIterator::HasAccess() const {
  DCHECK_EQ(ACCESS_CHECK, state_);
  return isolate_->MayAccess(GetHolder<JSObject>());
}

void LookupIterator::ReloadPropertyInformation() {
  state_ = BEFORE_PROPERTY;
  interceptor_state_ = InterceptorState::kUninitialized;
  state_ = LookupInHolder(*holder_map_, *holder_);
  DCHECK(IsFound() || holder_map_->is_dictionary_map());
}

void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
  DCHECK(state_ == DATA || state_ == ACCESSOR);
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  if (holder_map_->is_dictionary_map()) return;
  holder_map_ =
      Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
  JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
  ReloadPropertyInformation();
}

void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                             PropertyAttributes attributes) {
  DCHECK(state_ == DATA || state_ == ACCESSOR);
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  Handle<JSObject> holder = GetHolder<JSObject>();
  if (holder_map_->is_dictionary_map()) {
    PropertyDetails details(attributes, v8::internal::DATA, 0,
                            PropertyCellType::kMutable);
    JSObject::SetNormalizedProperty(holder, name(), value, details);
  } else {
    holder_map_ = Map::ReconfigureExistingProperty(
        holder_map_, descriptor_number(), i::kData, attributes);
    holder_map_ =
        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
    JSObject::MigrateToMap(holder, holder_map_);
  }

  ReloadPropertyInformation();
}

void LookupIterator::PrepareTransitionToDataProperty(
    Handle<Object> value, PropertyAttributes attributes,
    Object::StoreFromKeyed store_mode) {
  if (state_ == TRANSITION) return;
  DCHECK_NE(LookupIterator::ACCESSOR, state_);
  DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, state_);
  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
  // Can only be called when the receiver is a JSObject. JSProxy has to be
  // handled via a trap. Adding properties to primitive values is not
  // observable.
  Handle<JSObject> receiver = GetStoreTarget();

  if (!isolate()->IsInternallyUsedPropertyName(name()) &&
      !receiver->map()->is_extensible()) {
    return;
  }

  auto transition = Map::TransitionToDataProperty(
      handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
  state_ = TRANSITION;
  transition_ = transition;

  if (receiver->IsGlobalObject()) {
    // Install a property cell.
    InternalizeName();
    auto cell = GlobalObject::EnsurePropertyCell(
        Handle<GlobalObject>::cast(receiver), name());
    DCHECK(cell->value()->IsTheHole());
    transition_ = cell;
  } else if (transition->GetBackPointer()->IsMap()) {
    property_details_ = transition->GetLastDescriptorDetails();
    has_property_ = true;
  }
}

void LookupIterator::ApplyTransitionToDataProperty() {
  DCHECK_EQ(TRANSITION, state_);

  Handle<JSObject> receiver = GetStoreTarget();
  if (receiver->IsGlobalObject()) return;
  holder_ = receiver;
  holder_map_ = transition_map();
  JSObject::MigrateToMap(receiver, holder_map_);
  ReloadPropertyInformation();
}

void LookupIterator::TransitionToAccessorProperty(
    AccessorComponent component, Handle<Object> accessor,
    PropertyAttributes attributes) {
  DCHECK(!accessor->IsNull());
  // Can only be called when the receiver is a JSObject. JSProxy has to be
  // handled via a trap. Adding properties to primitive values is not
  // observable.
  Handle<JSObject> receiver = GetStoreTarget();
  holder_ = receiver;
  holder_map_ =
      Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
                                        name_, component, accessor, attributes);
  JSObject::MigrateToMap(receiver, holder_map_);

  ReloadPropertyInformation();

  if (!holder_map_->is_dictionary_map()) return;

  // Install the accessor into the dictionary-mode object.
  PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
                          PropertyCellType::kMutable);
  Handle<AccessorPair> pair;
  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
    pair = Handle<AccessorPair>::cast(GetAccessors());
    // If the component and attributes are identical, nothing has to be done.
    if (pair->get(component) == *accessor) {
      if (property_details().attributes() == attributes) return;
    } else {
      pair = AccessorPair::Copy(pair);
      pair->set(component, *accessor);
    }
  } else {
    pair = isolate()->factory()->NewAccessorPair();
    pair->set(component, *accessor);
  }
  JSObject::SetNormalizedProperty(receiver, name_, pair, details);

  JSObject::ReoptimizeIfPrototype(receiver);
  holder_map_ = handle(receiver->map(), isolate_);
  ReloadPropertyInformation();
}

bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
  // Optimization that only works if configuration_ is not mutable.
  if (!check_prototype_chain()) return true;
  DisallowHeapAllocation no_gc;
  if (!receiver_->IsJSReceiver()) return false;
  Object* current = *receiver_;
  JSReceiver* holder = *holder_;
  // JSProxy do not occur as hidden prototypes.
  if (current->IsJSProxy()) {
    return JSReceiver::cast(current) == holder;
  }
  PrototypeIterator iter(isolate(), current,
                         PrototypeIterator::START_AT_RECEIVER);
  do {
    if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
    DCHECK(!current->IsJSProxy());
    iter.Advance();
  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
  return false;
}

Handle<Object> LookupIterator::FetchValue() const {
  Object* result = NULL;
  Handle<JSObject> holder = GetHolder<JSObject>();
  if (holder_map_->is_dictionary_map()) {
    result = holder->property_dictionary()->ValueAt(number_);
    if (holder_map_->IsGlobalObjectMap()) {
      DCHECK(result->IsPropertyCell());
      result = PropertyCell::cast(result)->value();
    }
  } else if (property_details_.type() == v8::internal::DATA) {
    FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
    return JSObject::FastPropertyAt(holder, property_details_.representation(),
                                    field_index);
  } else {
    result = holder_map_->instance_descriptors()->GetValue(number_);
  }
  return handle(result, isolate_);
}

int LookupIterator::GetAccessorIndex() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
  return descriptor_number();
}

int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
  return descriptor_number();
}

FieldIndex LookupIterator::GetFieldIndex() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
  int index =
      holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
  bool is_double = representation().IsDouble();
  return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
}

Handle<HeapType> LookupIterator::GetFieldType() const {
  DCHECK(has_property_);
  DCHECK(!holder_map_->is_dictionary_map());
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
  return handle(
      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
      isolate_);
}

Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
  Handle<JSObject> holder = GetHolder<JSObject>();
  Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
  Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
  DCHECK(value->IsPropertyCell());
  return handle(PropertyCell::cast(value));
}

Handle<Object> LookupIterator::GetAccessors() const {
  DCHECK_EQ(ACCESSOR, state_);
  return FetchValue();
}

Handle<Object> LookupIterator::GetDataValue() const {
  DCHECK_EQ(DATA, state_);
  Handle<Object> value = FetchValue();
  return value;
}

Handle<Object> LookupIterator::WriteDataValue(Handle<Object> value) {
  DCHECK_EQ(DATA, state_);
  Handle<JSObject> holder = GetHolder<JSObject>();
  if (holder_map_->is_dictionary_map()) {
    Handle<NameDictionary> property_dictionary =
        handle(holder->property_dictionary());
    if (holder->IsGlobalObject()) {
      value = PropertyCell::UpdateCell(property_dictionary, dictionary_entry(),
                                       value, property_details_);
    } else {
      property_dictionary->ValueAtPut(dictionary_entry(), *value);
    }
  } else if (property_details_.type() == v8::internal::DATA) {
    holder->WriteToField(descriptor_number(), *value);
  } else {
    DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
  }
  return value;
}

bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
  DCHECK(exotic_index_state_ != ExoticIndexState::kNoIndex);
  // Currently typed arrays are the only such objects.
  if (!holder->IsJSTypedArray()) return false;
  if (exotic_index_state_ == ExoticIndexState::kIndex) return true;
  DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
  bool result = false;
  // Compute and cache result.
  if (name()->IsString()) {
    Handle<String> name_string = Handle<String>::cast(name());
    if (name_string->length() != 0) {
      result = IsNonArrayIndexInteger(*name_string);
    }
  }
  exotic_index_state_ =
      result ? ExoticIndexState::kIndex : ExoticIndexState::kNoIndex;
  return result;
}

void LookupIterator::InternalizeName() {
  if (name_->IsUniqueName()) return;
  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}

bool LookupIterator::SkipInterceptor(JSObject* holder) {
  auto info = holder->GetNamedInterceptor();
  // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
  if (info->non_masking()) {
    switch (interceptor_state_) {
      case InterceptorState::kUninitialized:
        interceptor_state_ = InterceptorState::kSkipNonMasking;
      // Fall through.
      case InterceptorState::kSkipNonMasking:
        return true;
      case InterceptorState::kProcessNonMasking:
        return false;
    }
  }
  return interceptor_state_ == InterceptorState::kProcessNonMasking;
}
} }  // namespace v8::internal
<|endoftext|>


<commit_before>/*
 * libjingle
 * Copyright 2012 Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "talk/media/base/capturemanager.h"

#include <algorithm>

#include "talk/media/base/videocapturer.h"
#include "talk/media/base/videoprocessor.h"
#include "talk/media/base/videorenderer.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"

namespace cricket {

// CaptureManager helper class.
class VideoCapturerState {
 public:
  static const VideoFormatPod kDefaultCaptureFormat;

  static VideoCapturerState* Create(VideoCapturer* video_capturer);
  ~VideoCapturerState() {}

  void AddCaptureResolution(const VideoFormat& desired_format);
  bool RemoveCaptureResolution(const VideoFormat& format);
  VideoFormat GetHighestFormat(VideoCapturer* video_capturer) const;

  int IncCaptureStartRef();
  int DecCaptureStartRef();
  CaptureRenderAdapter* adapter() {
    DCHECK(thread_checker_.CalledOnValidThread());
    return adapter_.get();
  }
  VideoCapturer* GetVideoCapturer() {
    DCHECK(thread_checker_.CalledOnValidThread());
    return adapter()->video_capturer();
  }

  int start_count() const {
    DCHECK(thread_checker_.CalledOnValidThread());
    return start_count_;
  }

 private:
  struct CaptureResolutionInfo {
    VideoFormat video_format;
    int format_ref_count;
  };
  typedef std::vector<CaptureResolutionInfo> CaptureFormats;

  explicit VideoCapturerState(CaptureRenderAdapter* adapter);

  rtc::ThreadChecker thread_checker_;
  rtc::scoped_ptr<CaptureRenderAdapter> adapter_;

  int start_count_;
  CaptureFormats capture_formats_;
};

const VideoFormatPod VideoCapturerState::kDefaultCaptureFormat = {
  640, 360, FPS_TO_INTERVAL(30), FOURCC_ANY
};

VideoCapturerState::VideoCapturerState(CaptureRenderAdapter* adapter)
    : adapter_(adapter), start_count_(1) {}

// static
VideoCapturerState* VideoCapturerState::Create(VideoCapturer* video_capturer) {
  CaptureRenderAdapter* adapter = CaptureRenderAdapter::Create(video_capturer);
  if (!adapter) {
    return NULL;
  }
  return new VideoCapturerState(adapter);
}

void VideoCapturerState::AddCaptureResolution(
    const VideoFormat& desired_format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  for (CaptureFormats::iterator iter = capture_formats_.begin();
       iter != capture_formats_.end(); ++iter) {
    if (desired_format == iter->video_format) {
      ++(iter->format_ref_count);
      return;
    }
  }
  CaptureResolutionInfo capture_resolution = { desired_format, 1 };
  capture_formats_.push_back(capture_resolution);
}

bool VideoCapturerState::RemoveCaptureResolution(const VideoFormat& format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  for (CaptureFormats::iterator iter = capture_formats_.begin();
       iter != capture_formats_.end(); ++iter) {
    if (format == iter->video_format) {
      --(iter->format_ref_count);
      if (iter->format_ref_count == 0) {
        capture_formats_.erase(iter);
      }
      return true;
    }
  }
  return false;
}

VideoFormat VideoCapturerState::GetHighestFormat(
    VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoFormat highest_format(0, 0, VideoFormat::FpsToInterval(1), FOURCC_ANY);
  if (capture_formats_.empty()) {
    VideoFormat default_format(kDefaultCaptureFormat);
    return default_format;
  }
  for (CaptureFormats::const_iterator iter = capture_formats_.begin();
       iter != capture_formats_.end(); ++iter) {
    if (iter->video_format.width > highest_format.width) {
      highest_format.width = iter->video_format.width;
    }
    if (iter->video_format.height > highest_format.height) {
      highest_format.height = iter->video_format.height;
    }
    if (iter->video_format.interval < highest_format.interval) {
      highest_format.interval = iter->video_format.interval;
    }
  }
  return highest_format;
}

int VideoCapturerState::IncCaptureStartRef() {
  DCHECK(thread_checker_.CalledOnValidThread());
  return ++start_count_;
}

int VideoCapturerState::DecCaptureStartRef() {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (start_count_ > 0) {
    // Start count may be 0 if a capturer was added but never started.
    --start_count_;
  }
  return start_count_;
}

CaptureManager::CaptureManager() {
  // Allowing construction of manager in any thread as long as subsequent calls
  // are all from the same thread.
  thread_checker_.DetachFromThread();
}

CaptureManager::~CaptureManager() {
  DCHECK(thread_checker_.CalledOnValidThread());

  // Since we don't own any of the capturers, all capturers should have been
  // cleaned up before we get here. In fact, in the normal shutdown sequence,
  // all capturers *will* be shut down by now, so trying to stop them here
  // will crash. If we're still tracking any, it's a dangling pointer.
  // TODO(hbos): DCHECK instead of CHECK until we figure out why capture_states_
  // is not always empty here.
  DCHECK(capture_states_.empty());
}

bool CaptureManager::StartVideoCapture(VideoCapturer* video_capturer,
                                       const VideoFormat& desired_format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (desired_format.width == 0 || desired_format.height == 0) {
    return false;
  }
  if (!video_capturer) {
    return false;
  }
  VideoCapturerState* capture_state = GetCaptureState(video_capturer);
  if (capture_state) {
    const int ref_count = capture_state->IncCaptureStartRef();
    if (ref_count < 1) {
      ASSERT(false);
    }
    // VideoCapturer has already been started. Don't start listening to
    // callbacks since that has already been done.
    capture_state->AddCaptureResolution(desired_format);
    return true;
  }
  if (!RegisterVideoCapturer(video_capturer)) {
    return false;
  }
  capture_state = GetCaptureState(video_capturer);
  ASSERT(capture_state != NULL);
  capture_state->AddCaptureResolution(desired_format);
  if (!StartWithBestCaptureFormat(capture_state, video_capturer)) {
    UnregisterVideoCapturer(capture_state);
    return false;
  }
  return true;
}

bool CaptureManager::StopVideoCapture(VideoCapturer* video_capturer,
                                      const VideoFormat& format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturerState* capture_state = GetCaptureState(video_capturer);
  if (!capture_state) {
    return false;
  }
  if (!capture_state->RemoveCaptureResolution(format)) {
    return false;
  }

  if (capture_state->DecCaptureStartRef() == 0) {
    // Unregistering cannot fail as capture_state is not NULL.
    UnregisterVideoCapturer(capture_state);
  }
  return true;
}

bool CaptureManager::RestartVideoCapture(
    VideoCapturer* video_capturer,
    const VideoFormat& previous_format,
    const VideoFormat& desired_format,
    CaptureManager::RestartOptions options) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!IsCapturerRegistered(video_capturer)) {
    LOG(LS_ERROR) << "RestartVideoCapture: video_capturer is not registered.";
    return false;
  }
  // Start the new format first. This keeps the capturer running.
  if (!StartVideoCapture(video_capturer, desired_format)) {
    LOG(LS_ERROR) << "RestartVideoCapture: unable to start video capture with "
        "desired_format=" << desired_format.ToString();
    return false;
  }
  // Stop the old format.
  if (!StopVideoCapture(video_capturer, previous_format)) {
    LOG(LS_ERROR) << "RestartVideoCapture: unable to stop video capture with "
        "previous_format=" << previous_format.ToString();
    // Undo the start request we just performed.
    StopVideoCapture(video_capturer, desired_format);
    return false;
  }

  switch (options) {
    case kForceRestart: {
      VideoCapturerState* capture_state = GetCaptureState(video_capturer);
      ASSERT(capture_state && capture_state->start_count() > 0);
      // Try a restart using the new best resolution.
      VideoFormat highest_asked_format =
          capture_state->GetHighestFormat(video_capturer);
      VideoFormat capture_format;
      if (video_capturer->GetBestCaptureFormat(highest_asked_format,
                                               &capture_format)) {
        if (!video_capturer->Restart(capture_format)) {
          LOG(LS_ERROR) << "RestartVideoCapture: Restart failed.";
        }
      } else {
        LOG(LS_WARNING)
            << "RestartVideoCapture: Couldn't find a best capture format for "
            << highest_asked_format.ToString();
      }
      break;
    }
    case kRequestRestart:
      // TODO(ryanpetrie): Support restart requests. Should this
      // to-be-implemented logic be used for {Start,Stop}VideoCapture as well?
      break;
    default:
      LOG(LS_ERROR) << "Unknown/unimplemented RestartOption";
      break;
  }
  return true;
}

bool CaptureManager::AddVideoRenderer(VideoCapturer* video_capturer,
                                      VideoRenderer* video_renderer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_renderer) {
    return false;
  }
  CaptureRenderAdapter* adapter = GetAdapter(video_capturer);
  if (!adapter) {
    return false;
  }
  return adapter->AddRenderer(video_renderer);
}

bool CaptureManager::RemoveVideoRenderer(VideoCapturer* video_capturer,
                                         VideoRenderer* video_renderer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_renderer) {
    return false;
  }
  CaptureRenderAdapter* adapter = GetAdapter(video_capturer);
  if (!adapter) {
    return false;
  }
  return adapter->RemoveRenderer(video_renderer);
}

bool CaptureManager::AddVideoProcessor(VideoCapturer* video_capturer,
                                       VideoProcessor* video_processor) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_processor) {
    return false;
  }
  if (!IsCapturerRegistered(video_capturer)) {
    return false;
  }
  video_capturer->AddVideoProcessor(video_processor);
  return true;
}

bool CaptureManager::RemoveVideoProcessor(VideoCapturer* video_capturer,
                                          VideoProcessor* video_processor) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_processor) {
    return false;
  }
  if (!IsCapturerRegistered(video_capturer)) {
    return false;
  }
  return video_capturer->RemoveVideoProcessor(video_processor);
}

bool CaptureManager::IsCapturerRegistered(VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  return GetCaptureState(video_capturer) != NULL;
}

bool CaptureManager::RegisterVideoCapturer(VideoCapturer* video_capturer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturerState* capture_state =
      VideoCapturerState::Create(video_capturer);
  if (!capture_state) {
    return false;
  }
  capture_states_[video_capturer] = capture_state;
  SignalCapturerStateChange.repeat(video_capturer->SignalStateChange);
  return true;
}

void CaptureManager::UnregisterVideoCapturer(
    VideoCapturerState* capture_state) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturer* video_capturer = capture_state->GetVideoCapturer();
  capture_states_.erase(video_capturer);
  delete capture_state;

  // When unregistering a VideoCapturer, the CaptureManager needs to unregister
  // from all state change callbacks from the VideoCapturer. E.g. to avoid
  // problems with multiple callbacks if registering the same VideoCapturer
  // multiple times. The VideoCapturer will update the capturer state. However,
  // this is done through Post-calls which means it may happen at any time. If
  // the CaptureManager no longer is listening to the VideoCapturer it will not
  // receive those callbacks. Here it is made sure that the the callback is
  // indeed sent by letting the ChannelManager do the signaling. The downside is
  // that the callback may happen before the VideoCapturer is stopped. However,
  // for the CaptureManager it doesn't matter as it will no longer receive any
  // frames from the VideoCapturer.
  SignalCapturerStateChange.stop(video_capturer->SignalStateChange);
  video_capturer->Stop();
  SignalCapturerStateChange(video_capturer, CS_STOPPED);
}

bool CaptureManager::StartWithBestCaptureFormat(
    VideoCapturerState* capture_state, VideoCapturer* video_capturer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoFormat highest_asked_format =
      capture_state->GetHighestFormat(video_capturer);
  VideoFormat capture_format;
  if (!video_capturer->GetBestCaptureFormat(highest_asked_format,
                                            &capture_format)) {
    LOG(LS_WARNING) << "Unsupported format:"
                    << " width=" << highest_asked_format.width
                    << " height=" << highest_asked_format.height
                    << ". Supported formats are:";
    const std::vector<VideoFormat>* formats =
        video_capturer->GetSupportedFormats();
    ASSERT(formats != NULL);
    for (std::vector<VideoFormat>::const_iterator i = formats->begin();
         i != formats->end(); ++i) {
      const VideoFormat& format = *i;
      LOG(LS_WARNING) << "  " << GetFourccName(format.fourcc)
                      << ":" << format.width << "x" << format.height << "x"
                      << format.framerate();
    }
    return false;
  }
  return video_capturer->StartCapturing(capture_format);
}

VideoCapturerState* CaptureManager::GetCaptureState(
    VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  CaptureStates::const_iterator iter = capture_states_.find(video_capturer);
  if (iter == capture_states_.end()) {
    return NULL;
  }
  return iter->second;
}

CaptureRenderAdapter* CaptureManager::GetAdapter(
    VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturerState* capture_state = GetCaptureState(video_capturer);
  if (!capture_state) {
    return NULL;
  }
  return capture_state->adapter();
}

}  // namespace cricket
<commit_msg>CaptureManager: Don't stop a capturer at UnregisterVideoCapturer if it did not start in the first place.<commit_after>/*
 * libjingle
 * Copyright 2012 Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "talk/media/base/capturemanager.h"

#include <algorithm>

#include "talk/media/base/videocapturer.h"
#include "talk/media/base/videoprocessor.h"
#include "talk/media/base/videorenderer.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"

namespace cricket {

// CaptureManager helper class.
class VideoCapturerState {
 public:
  static const VideoFormatPod kDefaultCaptureFormat;

  static VideoCapturerState* Create(VideoCapturer* video_capturer);
  ~VideoCapturerState() {}

  void AddCaptureResolution(const VideoFormat& desired_format);
  bool RemoveCaptureResolution(const VideoFormat& format);
  VideoFormat GetHighestFormat(VideoCapturer* video_capturer) const;

  int IncCaptureStartRef();
  int DecCaptureStartRef();
  CaptureRenderAdapter* adapter() {
    DCHECK(thread_checker_.CalledOnValidThread());
    return adapter_.get();
  }
  VideoCapturer* GetVideoCapturer() {
    DCHECK(thread_checker_.CalledOnValidThread());
    return adapter()->video_capturer();
  }

  int start_count() const {
    DCHECK(thread_checker_.CalledOnValidThread());
    return start_count_;
  }

 private:
  struct CaptureResolutionInfo {
    VideoFormat video_format;
    int format_ref_count;
  };
  typedef std::vector<CaptureResolutionInfo> CaptureFormats;

  explicit VideoCapturerState(CaptureRenderAdapter* adapter);

  rtc::ThreadChecker thread_checker_;
  rtc::scoped_ptr<CaptureRenderAdapter> adapter_;

  int start_count_;
  CaptureFormats capture_formats_;
};

const VideoFormatPod VideoCapturerState::kDefaultCaptureFormat = {
  640, 360, FPS_TO_INTERVAL(30), FOURCC_ANY
};

VideoCapturerState::VideoCapturerState(CaptureRenderAdapter* adapter)
    : adapter_(adapter), start_count_(1) {}

// static
VideoCapturerState* VideoCapturerState::Create(VideoCapturer* video_capturer) {
  CaptureRenderAdapter* adapter = CaptureRenderAdapter::Create(video_capturer);
  if (!adapter) {
    return NULL;
  }
  return new VideoCapturerState(adapter);
}

void VideoCapturerState::AddCaptureResolution(
    const VideoFormat& desired_format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  for (CaptureFormats::iterator iter = capture_formats_.begin();
       iter != capture_formats_.end(); ++iter) {
    if (desired_format == iter->video_format) {
      ++(iter->format_ref_count);
      return;
    }
  }
  CaptureResolutionInfo capture_resolution = { desired_format, 1 };
  capture_formats_.push_back(capture_resolution);
}

bool VideoCapturerState::RemoveCaptureResolution(const VideoFormat& format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  for (CaptureFormats::iterator iter = capture_formats_.begin();
       iter != capture_formats_.end(); ++iter) {
    if (format == iter->video_format) {
      --(iter->format_ref_count);
      if (iter->format_ref_count == 0) {
        capture_formats_.erase(iter);
      }
      return true;
    }
  }
  return false;
}

VideoFormat VideoCapturerState::GetHighestFormat(
    VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoFormat highest_format(0, 0, VideoFormat::FpsToInterval(1), FOURCC_ANY);
  if (capture_formats_.empty()) {
    VideoFormat default_format(kDefaultCaptureFormat);
    return default_format;
  }
  for (CaptureFormats::const_iterator iter = capture_formats_.begin();
       iter != capture_formats_.end(); ++iter) {
    if (iter->video_format.width > highest_format.width) {
      highest_format.width = iter->video_format.width;
    }
    if (iter->video_format.height > highest_format.height) {
      highest_format.height = iter->video_format.height;
    }
    if (iter->video_format.interval < highest_format.interval) {
      highest_format.interval = iter->video_format.interval;
    }
  }
  return highest_format;
}

int VideoCapturerState::IncCaptureStartRef() {
  DCHECK(thread_checker_.CalledOnValidThread());
  return ++start_count_;
}

int VideoCapturerState::DecCaptureStartRef() {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (start_count_ > 0) {
    // Start count may be 0 if a capturer was added but never started.
    --start_count_;
  }
  return start_count_;
}

CaptureManager::CaptureManager() {
  // Allowing construction of manager in any thread as long as subsequent calls
  // are all from the same thread.
  thread_checker_.DetachFromThread();
}

CaptureManager::~CaptureManager() {
  DCHECK(thread_checker_.CalledOnValidThread());

  // Since we don't own any of the capturers, all capturers should have been
  // cleaned up before we get here. In fact, in the normal shutdown sequence,
  // all capturers *will* be shut down by now, so trying to stop them here
  // will crash. If we're still tracking any, it's a dangling pointer.
  // TODO(hbos): DCHECK instead of CHECK until we figure out why capture_states_
  // is not always empty here.
  DCHECK(capture_states_.empty());
}

bool CaptureManager::StartVideoCapture(VideoCapturer* video_capturer,
                                       const VideoFormat& desired_format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (desired_format.width == 0 || desired_format.height == 0) {
    return false;
  }
  if (!video_capturer) {
    return false;
  }
  VideoCapturerState* capture_state = GetCaptureState(video_capturer);
  if (capture_state) {
    const int ref_count = capture_state->IncCaptureStartRef();
    if (ref_count < 1) {
      ASSERT(false);
    }
    // VideoCapturer has already been started. Don't start listening to
    // callbacks since that has already been done.
    capture_state->AddCaptureResolution(desired_format);
    return true;
  }
  if (!RegisterVideoCapturer(video_capturer)) {
    return false;
  }
  capture_state = GetCaptureState(video_capturer);
  ASSERT(capture_state != NULL);
  capture_state->AddCaptureResolution(desired_format);
  if (!StartWithBestCaptureFormat(capture_state, video_capturer)) {
    UnregisterVideoCapturer(capture_state);
    return false;
  }
  return true;
}

bool CaptureManager::StopVideoCapture(VideoCapturer* video_capturer,
                                      const VideoFormat& format) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturerState* capture_state = GetCaptureState(video_capturer);
  if (!capture_state) {
    return false;
  }
  if (!capture_state->RemoveCaptureResolution(format)) {
    return false;
  }

  if (capture_state->DecCaptureStartRef() == 0) {
    // Unregistering cannot fail as capture_state is not NULL.
    UnregisterVideoCapturer(capture_state);
  }
  return true;
}

bool CaptureManager::RestartVideoCapture(
    VideoCapturer* video_capturer,
    const VideoFormat& previous_format,
    const VideoFormat& desired_format,
    CaptureManager::RestartOptions options) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!IsCapturerRegistered(video_capturer)) {
    LOG(LS_ERROR) << "RestartVideoCapture: video_capturer is not registered.";
    return false;
  }
  // Start the new format first. This keeps the capturer running.
  if (!StartVideoCapture(video_capturer, desired_format)) {
    LOG(LS_ERROR) << "RestartVideoCapture: unable to start video capture with "
        "desired_format=" << desired_format.ToString();
    return false;
  }
  // Stop the old format.
  if (!StopVideoCapture(video_capturer, previous_format)) {
    LOG(LS_ERROR) << "RestartVideoCapture: unable to stop video capture with "
        "previous_format=" << previous_format.ToString();
    // Undo the start request we just performed.
    StopVideoCapture(video_capturer, desired_format);
    return false;
  }

  switch (options) {
    case kForceRestart: {
      VideoCapturerState* capture_state = GetCaptureState(video_capturer);
      ASSERT(capture_state && capture_state->start_count() > 0);
      // Try a restart using the new best resolution.
      VideoFormat highest_asked_format =
          capture_state->GetHighestFormat(video_capturer);
      VideoFormat capture_format;
      if (video_capturer->GetBestCaptureFormat(highest_asked_format,
                                               &capture_format)) {
        if (!video_capturer->Restart(capture_format)) {
          LOG(LS_ERROR) << "RestartVideoCapture: Restart failed.";
        }
      } else {
        LOG(LS_WARNING)
            << "RestartVideoCapture: Couldn't find a best capture format for "
            << highest_asked_format.ToString();
      }
      break;
    }
    case kRequestRestart:
      // TODO(ryanpetrie): Support restart requests. Should this
      // to-be-implemented logic be used for {Start,Stop}VideoCapture as well?
      break;
    default:
      LOG(LS_ERROR) << "Unknown/unimplemented RestartOption";
      break;
  }
  return true;
}

bool CaptureManager::AddVideoRenderer(VideoCapturer* video_capturer,
                                      VideoRenderer* video_renderer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_renderer) {
    return false;
  }
  CaptureRenderAdapter* adapter = GetAdapter(video_capturer);
  if (!adapter) {
    return false;
  }
  return adapter->AddRenderer(video_renderer);
}

bool CaptureManager::RemoveVideoRenderer(VideoCapturer* video_capturer,
                                         VideoRenderer* video_renderer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_renderer) {
    return false;
  }
  CaptureRenderAdapter* adapter = GetAdapter(video_capturer);
  if (!adapter) {
    return false;
  }
  return adapter->RemoveRenderer(video_renderer);
}

bool CaptureManager::AddVideoProcessor(VideoCapturer* video_capturer,
                                       VideoProcessor* video_processor) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_processor) {
    return false;
  }
  if (!IsCapturerRegistered(video_capturer)) {
    return false;
  }
  video_capturer->AddVideoProcessor(video_processor);
  return true;
}

bool CaptureManager::RemoveVideoProcessor(VideoCapturer* video_capturer,
                                          VideoProcessor* video_processor) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!video_capturer || !video_processor) {
    return false;
  }
  if (!IsCapturerRegistered(video_capturer)) {
    return false;
  }
  return video_capturer->RemoveVideoProcessor(video_processor);
}

bool CaptureManager::IsCapturerRegistered(VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  return GetCaptureState(video_capturer) != NULL;
}

bool CaptureManager::RegisterVideoCapturer(VideoCapturer* video_capturer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturerState* capture_state =
      VideoCapturerState::Create(video_capturer);
  if (!capture_state) {
    return false;
  }
  capture_states_[video_capturer] = capture_state;
  SignalCapturerStateChange.repeat(video_capturer->SignalStateChange);
  return true;
}

void CaptureManager::UnregisterVideoCapturer(
    VideoCapturerState* capture_state) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturer* video_capturer = capture_state->GetVideoCapturer();
  capture_states_.erase(video_capturer);
  delete capture_state;

  // When unregistering a VideoCapturer, the CaptureManager needs to unregister
  // from all state change callbacks from the VideoCapturer. E.g. to avoid
  // problems with multiple callbacks if registering the same VideoCapturer
  // multiple times. The VideoCapturer will update the capturer state. However,
  // this is done through Post-calls which means it may happen at any time. If
  // the CaptureManager no longer is listening to the VideoCapturer it will not
  // receive those callbacks. Here it is made sure that the the callback is
  // indeed sent by letting the ChannelManager do the signaling. The downside is
  // that the callback may happen before the VideoCapturer is stopped. However,
  // for the CaptureManager it doesn't matter as it will no longer receive any
  // frames from the VideoCapturer.
  SignalCapturerStateChange.stop(video_capturer->SignalStateChange);
  if (video_capturer->IsRunning()) {
    video_capturer->Stop();
    SignalCapturerStateChange(video_capturer, CS_STOPPED);
  }
}

bool CaptureManager::StartWithBestCaptureFormat(
    VideoCapturerState* capture_state, VideoCapturer* video_capturer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoFormat highest_asked_format =
      capture_state->GetHighestFormat(video_capturer);
  VideoFormat capture_format;
  if (!video_capturer->GetBestCaptureFormat(highest_asked_format,
                                            &capture_format)) {
    LOG(LS_WARNING) << "Unsupported format:"
                    << " width=" << highest_asked_format.width
                    << " height=" << highest_asked_format.height
                    << ". Supported formats are:";
    const std::vector<VideoFormat>* formats =
        video_capturer->GetSupportedFormats();
    ASSERT(formats != NULL);
    for (std::vector<VideoFormat>::const_iterator i = formats->begin();
         i != formats->end(); ++i) {
      const VideoFormat& format = *i;
      LOG(LS_WARNING) << "  " << GetFourccName(format.fourcc)
                      << ":" << format.width << "x" << format.height << "x"
                      << format.framerate();
    }
    return false;
  }
  return video_capturer->StartCapturing(capture_format);
}

VideoCapturerState* CaptureManager::GetCaptureState(
    VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  CaptureStates::const_iterator iter = capture_states_.find(video_capturer);
  if (iter == capture_states_.end()) {
    return NULL;
  }
  return iter->second;
}

CaptureRenderAdapter* CaptureManager::GetAdapter(
    VideoCapturer* video_capturer) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  VideoCapturerState* capture_state = GetCaptureState(video_capturer);
  if (!capture_state) {
    return NULL;
  }
  return capture_state->adapter();
}

}  // namespace cricket
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: MasterPageObserver.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 05:08:05 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef SD_VIEW_MASTER_PAGE_OBSERVER_HXX
#define SD_VIEW_MASTER_PAGE_OBSERVER_HXX

#include "tools/SdGlobalResourceContainer.hxx"
#include <osl/mutex.hxx>
#include <tools/link.hxx>
#include <memory>
#include <set>

class SdDrawDocument;
class String;

namespace sd {

/** This singleton observes all registered documents for changes in the used
    master pages and in turn informs its listeners about it.  One such
    listener is the master page selector control in the tool panel that
    shows the recently used master pages.
*/
class MasterPageObserver
    : public SdGlobalResource
{
public:
    typedef ::std::set<String> MasterPageNameSet;

    /** Return the single instance of this class.
    */
    static MasterPageObserver& Instance (void);

    /** The master page observer will listen to events of this document and
        detect changes of the use of master pages.
    */
    void RegisterDocument (SdDrawDocument& rDocument);

    /** The master page observer will stop to listen to events of this
        document.
    */
    void UnregisterDocument (SdDrawDocument& rDocument);

    /** Add a listener that is informed of master pages that are newly
        assigned to slides or become unassigned.
        @param rEventListener
            The event listener to call for future events.  Call
            RemoveEventListener() before the listener is destroyed.
    */
    void AddEventListener (const Link& rEventListener);

    /** Remove the given listener from the list of listeners.
        @param rEventListener
            After this method returns the given listener is not called back
            from this object.  Passing a listener that has not
            been registered before is safe and is silently ignored.
    */
    void RemoveEventListener (const Link& rEventListener);

    /** Return a set of the names of master pages for the given document.
        This convenience method exists because this set is part of the
        internal data structure and thus takes no time to create.
    */
    MasterPageNameSet GetMasterPageNames (SdDrawDocument& rDocument);

private:
    static ::osl::Mutex maMutex;

    class Implementation;
    ::std::auto_ptr<Implementation> mpImpl;

    MasterPageObserver (void);
    virtual ~MasterPageObserver (void);

    /// The copy constructor is not implemented.  Do not use!
    MasterPageObserver (const MasterPageObserver&);

    /// The assignment operator is not implemented.  Do not use!
    MasterPageObserver& operator= (const MasterPageObserver&);
};

/** Objects of this class are sent to listeners of the MasterPageObserver
    singleton when the list of master pages of one document has changed.
*/
class MasterPageObserverEvent
{
public:
    enum EventType {
        /// Master page already exists when document is registered.
        ET_MASTER_PAGE_EXISTS,
        /// Master page has been added to a document.
        ET_MASTER_PAGE_ADDED,
        /// Master page has been removed from to a document.
        ET_MASTER_PAGE_REMOVED
    };

    EventType meType;
    SdDrawDocument& mrDocument;
    const String& mrMasterPageName;

    MasterPageObserverEvent (
        EventType eType,
        SdDrawDocument& rDocument,
        const String& rMasterPageName)
        : meType(eType),
          mrDocument(rDocument),
          mrMasterPageName(rMasterPageName)
    {}

};

} // end of namespace sd

#endif
<commit_msg>INTEGRATION: CWS changefileheader (1.4.624); FILE MERGED 2008/03/31 13:58:10 rt 1.4.624.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: MasterPageObserver.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef SD_VIEW_MASTER_PAGE_OBSERVER_HXX
#define SD_VIEW_MASTER_PAGE_OBSERVER_HXX

#include "tools/SdGlobalResourceContainer.hxx"
#include <osl/mutex.hxx>
#include <tools/link.hxx>
#include <memory>
#include <set>

class SdDrawDocument;
class String;

namespace sd {

/** This singleton observes all registered documents for changes in the used
    master pages and in turn informs its listeners about it.  One such
    listener is the master page selector control in the tool panel that
    shows the recently used master pages.
*/
class MasterPageObserver
    : public SdGlobalResource
{
public:
    typedef ::std::set<String> MasterPageNameSet;

    /** Return the single instance of this class.
    */
    static MasterPageObserver& Instance (void);

    /** The master page observer will listen to events of this document and
        detect changes of the use of master pages.
    */
    void RegisterDocument (SdDrawDocument& rDocument);

    /** The master page observer will stop to listen to events of this
        document.
    */
    void UnregisterDocument (SdDrawDocument& rDocument);

    /** Add a listener that is informed of master pages that are newly
        assigned to slides or become unassigned.
        @param rEventListener
            The event listener to call for future events.  Call
            RemoveEventListener() before the listener is destroyed.
    */
    void AddEventListener (const Link& rEventListener);

    /** Remove the given listener from the list of listeners.
        @param rEventListener
            After this method returns the given listener is not called back
            from this object.  Passing a listener that has not
            been registered before is safe and is silently ignored.
    */
    void RemoveEventListener (const Link& rEventListener);

    /** Return a set of the names of master pages for the given document.
        This convenience method exists because this set is part of the
        internal data structure and thus takes no time to create.
    */
    MasterPageNameSet GetMasterPageNames (SdDrawDocument& rDocument);

private:
    static ::osl::Mutex maMutex;

    class Implementation;
    ::std::auto_ptr<Implementation> mpImpl;

    MasterPageObserver (void);
    virtual ~MasterPageObserver (void);

    /// The copy constructor is not implemented.  Do not use!
    MasterPageObserver (const MasterPageObserver&);

    /// The assignment operator is not implemented.  Do not use!
    MasterPageObserver& operator= (const MasterPageObserver&);
};

/** Objects of this class are sent to listeners of the MasterPageObserver
    singleton when the list of master pages of one document has changed.
*/
class MasterPageObserverEvent
{
public:
    enum EventType {
        /// Master page already exists when document is registered.
        ET_MASTER_PAGE_EXISTS,
        /// Master page has been added to a document.
        ET_MASTER_PAGE_ADDED,
        /// Master page has been removed from to a document.
        ET_MASTER_PAGE_REMOVED
    };

    EventType meType;
    SdDrawDocument& mrDocument;
    const String& mrMasterPageName;

    MasterPageObserverEvent (
        EventType eType,
        SdDrawDocument& rDocument,
        const String& rMasterPageName)
        : meType(eType),
          mrDocument(rDocument),
          mrMasterPageName(rMasterPageName)
    {}

};

} // end of namespace sd

#endif
<|endoftext|>


<commit_before>/*
* Copyright (C) 2011 Daniele E. Domenichelli <daniele.domenichelli@gmail.com>
* Copyright (C) 1999 Preston Brown <pbrown@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "telepathy-handler-application.h"

#include <QTimer>
#include <KCmdLineArgs>
#include <KDebug>

#include <TelepathyQt4/Types>
#include <TelepathyQt4/Debug>

extern bool kde_kdebug_enable_dbus_interface;

namespace KTelepathy {

class TelepathyHandlerApplication::Private
{
public:
    Private(TelepathyHandlerApplication *q);
    ~Private();

    void _k_onInitialTimeout();
    void _k_onTimeout();

    static KComponentData initHack();
    void init(int initialTimeout, int timeout);

    TelepathyHandlerApplication *q;

    static bool s_persist;
    static bool s_debug;

    int initialTimeout;
    int timeout;
    QTimer *timer;
    bool firstJobStarted;
    QAtomicInt jobCount;
};

TelepathyHandlerApplication::Private::Private(TelepathyHandlerApplication *q)
    : q(q),
      firstJobStarted(false),
      jobCount(0)
{
}

TelepathyHandlerApplication::Private::~Private()
{
}

void TelepathyHandlerApplication::Private::_k_onInitialTimeout()
{
    if (jobCount == 0 && jobCount.fetchAndAddOrdered(-1) == 0) {
        // m_jobCount is now -1
        kDebug() << "No job received. Exiting";
        QCoreApplication::quit();
    }
}

void TelepathyHandlerApplication::Private::_k_onTimeout()
{
    if (jobCount == 0 && jobCount.fetchAndAddOrdered(-1) == 0) {
        // m_jobCount is now -1
        kDebug() << "Timeout. Exiting";
        QCoreApplication::quit();
    }
}

// this gets called before even entering QApplication::QApplication()
KComponentData TelepathyHandlerApplication::Private::initHack()
{
    KComponentData cData(KCmdLineArgs::aboutData());
    KCmdLineOptions handler_options;
    handler_options.add("persist", ki18n("Persistent mode (do not exit on timeout)"));
    handler_options.add("debug", ki18n("Show Telepathy debugging information"));
    KCmdLineArgs::addCmdLineOptions(handler_options, ki18n("KDE Telepathy"), "kde-telepathy", "kde");
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde-telepathy");
    Private::s_persist = args->isSet("persist");
    Private::s_debug = args->isSet("debug");
    return cData;
}

void TelepathyHandlerApplication::Private::init(int initialTimeout, int timeout)
{
    this->initialTimeout = timeout;
    this->timeout = timeout;

    // If timeout < 0 we let the application exit when the last window is closed,
    // Otherwise we handle it with the timeout
    if (timeout >= 0) {
        q->setQuitOnLastWindowClosed(false);
    }

    Tp::registerTypes();
    //Enable telepathy-Qt4 debug
    Tp::enableDebug(s_debug);
    Tp::enableWarnings(true);

    if (s_debug) {
        kde_kdebug_enable_dbus_interface = true;
    }

    if (!Private::s_persist) {
        timer = new QTimer(q);
        if (initialTimeout >= 0) {
            q->connect(timer, SIGNAL(timeout()), q, SLOT(_k_onInitialTimeout()));
            timer->start(initialTimeout);
        }
    }
}

bool TelepathyHandlerApplication::Private::s_persist = false;
bool TelepathyHandlerApplication::Private::s_debug = false;

TelepathyHandlerApplication::TelepathyHandlerApplication(bool GUIenabled,
                                                         int initialTimeout,
                                                         int timeout)
    : KApplication(GUIenabled, Private::initHack()),
      d(new Private(this))
{
    d->init(initialTimeout, timeout);
}

TelepathyHandlerApplication::TelepathyHandlerApplication(Display *display,
                                                         Qt::HANDLE visual,
                                                         Qt::HANDLE colormap,
                                                         int initialTimeout,
                                                         int timeout)
    : KApplication(display, visual, colormap, Private::initHack()),
      d(new Private(this))
{
    d->init(initialTimeout, timeout);
}

TelepathyHandlerApplication::~TelepathyHandlerApplication()
{
    delete d;
}

int TelepathyHandlerApplication::newJob()
{
    TelepathyHandlerApplication *app = qobject_cast<TelepathyHandlerApplication*>(KApplication::kApplication());
    TelepathyHandlerApplication::Private *d = app->d;

    int ret = d->jobCount.fetchAndAddOrdered(1);
    if (!Private::s_persist) {
        if (d->timer->isActive()) {
            d->timer->stop();
        }
        if (!d->firstJobStarted) {
            if (d->initialTimeout) {
                disconnect(d->timer, SIGNAL(timeout()), app, SLOT(_k_onInitialTimeout()));
            }
            if (d->timeout >= 0) {
                connect(d->timer, SIGNAL(timeout()), app, SLOT(_k_onTimeout()));
            }
            d->firstJobStarted = true;
        }
    }
    return ret;
}

void TelepathyHandlerApplication::jobFinished()
{
    TelepathyHandlerApplication *app = qobject_cast<TelepathyHandlerApplication*>(KApplication::kApplication());
    TelepathyHandlerApplication::Private *d = app->d;

    if (d->jobCount.fetchAndAddOrdered(-1) <= 1) {
        kDebug() << "No other jobs at the moment. Starting timer.";
        if (!Private::s_persist && d->timeout >= 0) {
            d->timer->start(d->timeout);
        }
    }
}

} // namespace KTelepathy

#include "telepathy-handler-application.moc"
<commit_msg>Print debug at the right moment<commit_after>/*
* Copyright (C) 2011 Daniele E. Domenichelli <daniele.domenichelli@gmail.com>
* Copyright (C) 1999 Preston Brown <pbrown@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "telepathy-handler-application.h"

#include <QTimer>
#include <KCmdLineArgs>
#include <KDebug>

#include <TelepathyQt4/Types>
#include <TelepathyQt4/Debug>

extern bool kde_kdebug_enable_dbus_interface;

namespace KTelepathy {

class TelepathyHandlerApplication::Private
{
public:
    Private(TelepathyHandlerApplication *q);
    ~Private();

    void _k_onInitialTimeout();
    void _k_onTimeout();

    static KComponentData initHack();
    void init(int initialTimeout, int timeout);

    TelepathyHandlerApplication *q;

    static bool s_persist;
    static bool s_debug;

    int initialTimeout;
    int timeout;
    QTimer *timer;
    bool firstJobStarted;
    QAtomicInt jobCount;
};

TelepathyHandlerApplication::Private::Private(TelepathyHandlerApplication *q)
    : q(q),
      firstJobStarted(false),
      jobCount(0)
{
}

TelepathyHandlerApplication::Private::~Private()
{
}

void TelepathyHandlerApplication::Private::_k_onInitialTimeout()
{
    if (jobCount == 0 && jobCount.fetchAndAddOrdered(-1) == 0) {
        // m_jobCount is now -1
        kDebug() << "No job received. Exiting";
        QCoreApplication::quit();
    }
}

void TelepathyHandlerApplication::Private::_k_onTimeout()
{
    if (jobCount == 0 && jobCount.fetchAndAddOrdered(-1) == 0) {
        // m_jobCount is now -1
        kDebug() << "Timeout. Exiting";
        QCoreApplication::quit();
    }
}

// this gets called before even entering QApplication::QApplication()
KComponentData TelepathyHandlerApplication::Private::initHack()
{
    KComponentData cData(KCmdLineArgs::aboutData());
    KCmdLineOptions handler_options;
    handler_options.add("persist", ki18n("Persistent mode (do not exit on timeout)"));
    handler_options.add("debug", ki18n("Show Telepathy debugging information"));
    KCmdLineArgs::addCmdLineOptions(handler_options, ki18n("KDE Telepathy"), "kde-telepathy", "kde");
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde-telepathy");
    Private::s_persist = args->isSet("persist");
    Private::s_debug = args->isSet("debug");
    return cData;
}

void TelepathyHandlerApplication::Private::init(int initialTimeout, int timeout)
{
    this->initialTimeout = timeout;
    this->timeout = timeout;

    // If timeout < 0 we let the application exit when the last window is closed,
    // Otherwise we handle it with the timeout
    if (timeout >= 0) {
        q->setQuitOnLastWindowClosed(false);
    }

    Tp::registerTypes();
    //Enable telepathy-Qt4 debug
    Tp::enableDebug(s_debug);
    Tp::enableWarnings(true);

    if (s_debug) {
        kde_kdebug_enable_dbus_interface = true;
    }

    if (!Private::s_persist) {
        timer = new QTimer(q);
        if (initialTimeout >= 0) {
            q->connect(timer, SIGNAL(timeout()), q, SLOT(_k_onInitialTimeout()));
            timer->start(initialTimeout);
        }
    }
}

bool TelepathyHandlerApplication::Private::s_persist = false;
bool TelepathyHandlerApplication::Private::s_debug = false;

TelepathyHandlerApplication::TelepathyHandlerApplication(bool GUIenabled,
                                                         int initialTimeout,
                                                         int timeout)
    : KApplication(GUIenabled, Private::initHack()),
      d(new Private(this))
{
    d->init(initialTimeout, timeout);
}

TelepathyHandlerApplication::TelepathyHandlerApplication(Display *display,
                                                         Qt::HANDLE visual,
                                                         Qt::HANDLE colormap,
                                                         int initialTimeout,
                                                         int timeout)
    : KApplication(display, visual, colormap, Private::initHack()),
      d(new Private(this))
{
    d->init(initialTimeout, timeout);
}

TelepathyHandlerApplication::~TelepathyHandlerApplication()
{
    delete d;
}

int TelepathyHandlerApplication::newJob()
{
    TelepathyHandlerApplication *app = qobject_cast<TelepathyHandlerApplication*>(KApplication::kApplication());
    TelepathyHandlerApplication::Private *d = app->d;

    int ret = d->jobCount.fetchAndAddOrdered(1);
    if (!Private::s_persist) {
        if (d->timer->isActive()) {
            d->timer->stop();
        }
        if (!d->firstJobStarted) {
            if (d->initialTimeout) {
                disconnect(d->timer, SIGNAL(timeout()), app, SLOT(_k_onInitialTimeout()));
            }
            if (d->timeout >= 0) {
                connect(d->timer, SIGNAL(timeout()), app, SLOT(_k_onTimeout()));
            }
            d->firstJobStarted = true;
        }
    }
    return ret;
}

void TelepathyHandlerApplication::jobFinished()
{
    TelepathyHandlerApplication *app = qobject_cast<TelepathyHandlerApplication*>(KApplication::kApplication());
    TelepathyHandlerApplication::Private *d = app->d;

    if (d->jobCount.fetchAndAddOrdered(-1) <= 1) {
        if (!Private::s_persist && d->timeout >= 0) {
            kDebug() << "No other jobs at the moment. Starting timer.";
            d->timer->start(d->timeout);
        }
    }
}

} // namespace KTelepathy

#include "telepathy-handler-application.moc"
<|endoftext|>