<commit_before>#include "crange_arch.hh"
#include "radix.hh"

enum { radix_debug = 0 };

#define dprintf(...) do { if (radix_debug) cprintf(__VA_ARGS__); } while(0)

static_assert(key_bits == bits_per_level * radix_levels,
              "for now, we only support exact multiples of bits_per_level");

// Returns the index needed to reach |level| from |level+1|.
static u32
index(u64 key, u32 level)
{
  u64 idx = key >> (bits_per_level * level);
  idx &= (1 << bits_per_level) - 1;
  return idx;
}

// Returns the size of a subtree for a node at |level|.
static u64
level_size(u32 level)
{
  return 1L << (bits_per_level * level);
}

static radix_entry
push_down(radix_entry cur, radix_ptr *ptr)
{
  while (cur.state() != entry_node) {
    // If we're locked, just spin and try again.
    if (cur.state() == entry_locked) {
      cur = ptr->load();
      continue;
    }

    // Make a new node.
    assert(cur.state() == entry_unlocked);
    radix_elem *elem = cur.elem();
    radix_node *new_rn = new radix_node();
    if (elem != nullptr) {
      for (int i = 0; i < (1<<bits_per_level); i++) {
        new_rn->child[i].store(radix_entry(elem));
      }
      elem->incref(1<<bits_per_level);
    }
    if (ptr->compare_exchange_weak(cur, radix_entry(new_rn))) {
      // Release the ref from the pointer we replaced. FIXME: Bouncing
      // on the reference count here is annoying. Maybe the reference
      // count should be dependent on the high of the leaf?
      if (elem != nullptr)
        elem->decref();
    } else {
      // Someone else beat us to it. Back out. FIXME: docs say
      // compare_exchange_weak can return spuriously. Should avoid
      // reallocating new_rn if elem doesn't change.

      // Avoid bouncing on the refcount 1<<bits_per_level times.
      if (elem != nullptr) {
        for (int i = 0; i < (1<<bits_per_level); i++) {
          new_rn->child[i].store(radix_entry(nullptr));
        }
        elem->decref(1<<bits_per_level);
      }

      delete new_rn;
    }
  }
  return cur;
}

// Runs CB of a set of leaves whose disjoint union is the range
// [start, end)
template <class CB>
void
update_range(radix_entry cur, radix_ptr *ptr, CB cb,
             u64 cur_start, u64 cur_end,
             u64 start, u64 end, u32 level = radix_levels)
{
  assert(level_size(level) == cur_end - cur_start);
  // Assert that our ranges intersect; if this fails, we got the loop
  // below wrong.
  assert(cur_start < end && start < cur_end)

  // If our range is not strictly contained in the target, ensure we
  // are at a node.
  if (start > cur_start || end < cur_end) {
    cur = push_down(cur, ptr);
  }

  if (cur.is_node()) {
    // Find the place to start.
    if (start < cur_start)
      start = cur_start;
    assert(level > 0);
    int i = index(start, level - 1);
    u64 child_size = (cur_end - cur_start) >> bits_per_level;
    u64 child_start = cur_start + i * child_size;
    for (; (i < (1<<bits_per_level)) && (child_start < end);
         i++, child_start += child_size) {
      radix_ptr *child = &cur.node()->child[i];
      update_range(child->load(), child, cb,
                   child_start, child_start + child_size,
                   start, end, level - 1);
    }
  } else {
    // If we're here, the target range must completely contain this
    // element.
    assert(start <= cur_start && cur_end <= end);
    cb(cur, ptr, cur_start, cur_end, level);
  }
}

void
radix_entry::release()
{
  if (is_null()) return;
  if (is_node()) {
    delete node();
  } else {
    elem()->decref();
  }
}

radix_node::~radix_node()
{
  for (int i = 0; i < (1<<bits_per_level); i++) {
    child[i].load().release();
  }
}

radix::~radix()
{
  root_.load().release();
}

radix_elem*
radix::search(u64 key)
{
  scoped_gc_epoch gc;
  radix_entry cur = root_.load();
  for (u32 level = radix_levels-1; level >= 0 && !cur.is_elem(); level--) {
    cur = cur.node()->child[index(key >> shift_, level)].load();
  }
  dprintf("%p: search(%lx) -> %p\n", this, key >> shift_, cur.elem());
  return cur.elem();
}

radix_range
radix::search_lock(u64 start, u64 size)
{
  return radix_range(this, start >> shift_, size >> shift_);
}

// This should be a lambda, but it's awkward for a lambda to call
// itself.
struct entry_locker {
  u64 start_;
  u64 end_;

  entry_locker(u64 start, u64 end) : start_(start), end_(end) { }
  void operator()(radix_entry cur, radix_ptr *ptr, u64 cur_start, u64 cur_end, u32 level) const {
    while (cur.state() != entry_node) {
      // Locked -> spin and try again.
      if (cur.state() == entry_locked) {
        cur = ptr->load();
        continue;
      }
      // Otherwise it's unlocked. Try to load it.
      if (ptr->compare_exchange_weak(cur, cur.with_state(entry_locked))) {
        // Success. Remember the current value and break out.
        cur = cur.with_state(entry_locked);
        break;
      }
    }
    // Someone pushed down. Recurse some more.
    if (cur.state() == entry_node) {
      update_range(cur, ptr, *this, cur_start, cur_end, start_, end_, level-1);
    } else {
      // We managed to lock!
      assert(cur.state() == entry_locked);
    }
  }
};

radix_range::radix_range(radix *r, u64 start, u64 size)
  : r_(r), start_(start), size_(size)
{
  u64 end = start_ + size_;
  // Lock the range from left to right.
  const entry_locker& cb = entry_locker(start_, end);
  update_range(r_->root_.load(), &r_->root_, cb,
               0, 1L << key_bits, start_, end);
}

radix_range::~radix_range()
{
  if (!r_)
    return;

  update_range(r_->root_.load(), &r_->root_, [](radix_entry cur, radix_ptr *ptr, u64 cur_start, u64 cur_end, u32 level) {
      do {
        // It had better still be locked.
        assert(cur.state() == entry_locked);
      } while (!ptr->compare_exchange_weak(cur, cur.with_state(entry_unlocked)));
    }, 0, 1L << key_bits, start_, start_ + size_);
}

void
radix_range::replace(u64 start, u64 size, radix_elem *val)
{
  start = start >> r_->shift_;
  size = size >> r_->shift_;
  dprintf("%p: replace: [%lx, %lx) with %p\n", r_, start, start + size, val);

  assert(start >= start_);
  assert(start + size <= start_ + size_);

  update_range(r_->root_.load(), &r_->root_, [val](radix_entry cur, radix_ptr *ptr, u64 cur_start, u64 cur_end, u32 level) {
      dprintf(" -> [%lx, %lx); size = %lx\n", cur_start, cur_end, cur_end - cur_start);
      do {
        assert(cur.state() == entry_locked);
      } while (!ptr->compare_exchange_weak(cur, radix_entry(val, entry_locked)));
      if (val)
        val->incref();
      if (!cur.is_null())
        cur.elem()->decref();
    }, 0, 1L << key_bits, start, start + size);
}

radix_iterator::radix_iterator(const radix* r, u64 k)
  : r_(r), k_(k) {
  dprintf("%p: Made iterator with k = %lx\n", r_, k_);
  if (k_ != ~0ULL) {
    path_[radix_levels] = r_->root_.load();
    if (path_[radix_levels].is_elem())
      leaf_ = radix_levels; // Maybe best to not do this...
    else if (!find_first_leaf(radix_levels - 1))
      k_ = ~0ULL;
  }
  dprintf("%p: Adjusted: k = %lx\n", r_, k_);
}

bool
radix_iterator::advance(u32 level)
{
  // First, see if we can advance a lower level.
  if (level > leaf_ && advance(level-1)) {
    // Nothing more to do.
    return true;
  }

  // Try to advance this level, if we can.
  u32 start_idx = index(k_, level)+1;
  if (start_idx < (1<<bits_per_level)) {
    // Find the first leaf starting at our sibling node.
    k_ &= ~((1ULL<<((level+1) * bits_per_level)) - 1);
    k_ |= (u64(start_idx) << (level * bits_per_level));
    return find_first_leaf(level);
  } else {
    return false;
  }
}

bool
radix_iterator::find_first_leaf(u32 level)
{
  // Find the first non-empty node after k_ on this level.
  for (u32 idx = index(k_, level); idx < (1<<bits_per_level); idx++) {
    radix_entry next = node(level+1)->child[idx].load();
    if (!next.is_null()) {
      if (index(k_, level) != idx) {
        // We had to advance; clear everything this level and under
        // and set this one.
        k_ &= ~((1ULL<<((level+1) * bits_per_level)) - 1);
        k_ |= (u64(idx) << (level * bits_per_level));
      }
      path_[level] = next;

      if (next.is_elem()) {
        // Found a leaf. Stop now.
        leaf_ = level;
        return true;
      } else if (find_first_leaf(level-1))
        // Keep looking.
        return true;
    }
  }

  // Failed to find a leaf. Abort.
  return false;
}
<commit_msg>When leaves turn into nodes, drive the recursion in update_range<commit_after>#include "crange_arch.hh"
#include "radix.hh"

enum { radix_debug = 0 };

#define dprintf(...) do { if (radix_debug) cprintf(__VA_ARGS__); } while(0)

static_assert(key_bits == bits_per_level * radix_levels,
              "for now, we only support exact multiples of bits_per_level");

// Returns the index needed to reach |level| from |level+1|.
static u32
index(u64 key, u32 level)
{
  u64 idx = key >> (bits_per_level * level);
  idx &= (1 << bits_per_level) - 1;
  return idx;
}

// Returns the size of a subtree for a node at |level|.
static u64
level_size(u32 level)
{
  return 1L << (bits_per_level * level);
}

static radix_entry
push_down(radix_entry cur, radix_ptr *ptr)
{
  while (cur.state() != entry_node) {
    // If we're locked, just spin and try again.
    if (cur.state() == entry_locked) {
      cur = ptr->load();
      continue;
    }

    // Make a new node.
    assert(cur.state() == entry_unlocked);
    radix_elem *elem = cur.elem();
    radix_node *new_rn = new radix_node();
    if (elem != nullptr) {
      for (int i = 0; i < (1<<bits_per_level); i++) {
        new_rn->child[i].store(radix_entry(elem));
      }
      elem->incref(1<<bits_per_level);
    }
    if (ptr->compare_exchange_weak(cur, radix_entry(new_rn))) {
      // Release the ref from the pointer we replaced. FIXME: Bouncing
      // on the reference count here is annoying. Maybe the reference
      // count should be dependent on the high of the leaf?
      if (elem != nullptr)
        elem->decref();
    } else {
      // Someone else beat us to it. Back out. FIXME: docs say
      // compare_exchange_weak can return spuriously. Should avoid
      // reallocating new_rn if elem doesn't change.

      // Avoid bouncing on the refcount 1<<bits_per_level times.
      if (elem != nullptr) {
        for (int i = 0; i < (1<<bits_per_level); i++) {
          new_rn->child[i].store(radix_entry(nullptr));
        }
        elem->decref(1<<bits_per_level);
      }

      delete new_rn;
    }
  }
  return cur;
}

// Runs |cb| of a set of leaves whose disjoint union is the range
// [start, end). Callback returns the last known state of the
// radix_ptr. It is assumed |cb| does not convert the leaf into a
// node. If |cb| returns an entry_node, we recurse into the node and
// call |cb| on the new subtree.
template <class CB>
void
update_range(radix_entry cur, radix_ptr *ptr, CB cb,
             u64 cur_start, u64 cur_end,
             u64 start, u64 end, u32 level = radix_levels)
{
  assert(level_size(level) == cur_end - cur_start);
  // Assert that our ranges intersect; if this fails, we got the loop
  // below wrong.
  assert(cur_start < end && start < cur_end)

  // If our range is not strictly contained in the target, ensure we
  // are at a node.
  if (start > cur_start || end < cur_end) {
    cur = push_down(cur, ptr);
  }

  if (cur.is_elem()) {
    // If we're here, the target range must completely contain this
    // element.
    assert(start <= cur_start && cur_end <= end);
    dprintf(" -> [%lx, %lx); size = %lx\n", cur_start, cur_end, cur_end - cur_start);
    cur = cb(cur, ptr);
  }

  // Recurse if we became a node or we already one.
  if (cur.is_node()) {
    // Find the place to start.
    if (start < cur_start)
      start = cur_start;
    assert(level > 0);
    int i = index(start, level - 1);
    u64 child_size = (cur_end - cur_start) >> bits_per_level;
    u64 child_start = cur_start + i * child_size;
    for (; (i < (1<<bits_per_level)) && (child_start < end);
         i++, child_start += child_size) {
      radix_ptr *child = &cur.node()->child[i];
      update_range(child->load(), child, cb,
                   child_start, child_start + child_size,
                   start, end, level - 1);
    }
  }
}

void
radix_entry::release()
{
  if (is_null()) return;
  if (is_node()) {
    delete node();
  } else {
    elem()->decref();
  }
}

radix_node::~radix_node()
{
  for (int i = 0; i < (1<<bits_per_level); i++) {
    child[i].load().release();
  }
}

radix::~radix()
{
  root_.load().release();
}

radix_elem*
radix::search(u64 key)
{
  scoped_gc_epoch gc;
  radix_entry cur = root_.load();
  for (u32 level = radix_levels-1; level >= 0 && !cur.is_elem(); level--) {
    cur = cur.node()->child[index(key >> shift_, level)].load();
  }
  dprintf("%p: search(%lx) -> %p\n", this, key >> shift_, cur.elem());
  return cur.elem();
}

radix_range
radix::search_lock(u64 start, u64 size)
{
  return radix_range(this, start >> shift_, size >> shift_);
}

radix_range::radix_range(radix *r, u64 start, u64 size)
  : r_(r), start_(start), size_(size)
{
  u64 end = start_ + size_;
  // Lock the range from left to right.
  dprintf("%p: lock [%lx, %lx)\n", r_, start, start + size);
  update_range(r_->root_.load(), &r_->root_, [](radix_entry cur, radix_ptr *ptr) -> radix_entry {
      while (cur.state() != entry_node) {
        // Locked -> spin and try again.
        if (cur.state() == entry_locked) {
          cur = ptr->load();
          continue;
        }
        // Otherwise it's unlocked. Try to load it.
        if (ptr->compare_exchange_weak(cur, cur.with_state(entry_locked))) {
          // Success. Remember the current value and break out.
          cur = cur.with_state(entry_locked);
          break;
        }
      }
      // We either managed a lock or someone turned us into a node.
      assert(cur.state() == entry_node || cur.state() == entry_locked);
      return cur;
    }, 0, 1L << key_bits, start_, end);
}

radix_range::~radix_range()
{
  if (!r_)
    return;

  dprintf("%p: unlock [%lx, %lx)\n", r_, start_, start_ + size_);
  update_range(r_->root_.load(), &r_->root_, [](radix_entry cur, radix_ptr *ptr) -> radix_entry {
      do {
        // It had better still be locked.
        assert(cur.state() == entry_locked);
      } while (!ptr->compare_exchange_weak(cur, cur.with_state(entry_unlocked)));
      // Not a node, but let's return what it wants anyway.
      return cur.with_state(entry_unlocked);
    }, 0, 1L << key_bits, start_, start_ + size_);
}

void
radix_range::replace(u64 start, u64 size, radix_elem *val)
{
  start = start >> r_->shift_;
  size = size >> r_->shift_;

  assert(start >= start_);
  assert(start + size <= start_ + size_);

  dprintf("%p: replace: [%lx, %lx) with %p\n", r_, start, start + size, val);
  update_range(r_->root_.load(), &r_->root_, [val](radix_entry cur, radix_ptr *ptr) -> radix_entry {
      do {
        assert(cur.state() == entry_locked);
      } while (!ptr->compare_exchange_weak(cur, radix_entry(val, entry_locked)));
      if (val)
        val->incref();
      // cur is now the old value.
      if (!cur.is_null())
        cur.elem()->decref();
      // Not a node, but let's return what it wants anyway.
      return radix_entry(val, entry_locked);
    }, 0, 1L << key_bits, start, start + size);
}

radix_iterator::radix_iterator(const radix* r, u64 k)
  : r_(r), k_(k) {
  dprintf("%p: Made iterator with k = %lx\n", r_, k_);
  if (k_ != ~0ULL) {
    path_[radix_levels] = r_->root_.load();
    if (path_[radix_levels].is_elem())
      leaf_ = radix_levels; // Maybe best to not do this...
    else if (!find_first_leaf(radix_levels - 1))
      k_ = ~0ULL;
  }
  dprintf("%p: Adjusted: k = %lx\n", r_, k_);
}

bool
radix_iterator::advance(u32 level)
{
  // First, see if we can advance a lower level.
  if (level > leaf_ && advance(level-1)) {
    // Nothing more to do.
    return true;
  }

  // Try to advance this level, if we can.
  u32 start_idx = index(k_, level)+1;
  if (start_idx < (1<<bits_per_level)) {
    // Find the first leaf starting at our sibling node.
    k_ &= ~((1ULL<<((level+1) * bits_per_level)) - 1);
    k_ |= (u64(start_idx) << (level * bits_per_level));
    return find_first_leaf(level);
  } else {
    return false;
  }
}

bool
radix_iterator::find_first_leaf(u32 level)
{
  // Find the first non-empty node after k_ on this level.
  for (u32 idx = index(k_, level); idx < (1<<bits_per_level); idx++) {
    radix_entry next = node(level+1)->child[idx].load();
    if (!next.is_null()) {
      if (index(k_, level) != idx) {
        // We had to advance; clear everything this level and under
        // and set this one.
        k_ &= ~((1ULL<<((level+1) * bits_per_level)) - 1);
        k_ |= (u64(idx) << (level * bits_per_level));
      }
      path_[level] = next;

      if (next.is_elem()) {
        // Found a leaf. Stop now.
        leaf_ = level;
        return true;
      } else if (find_first_leaf(level-1))
        // Keep looking.
        return true;
    }
  }

  // Failed to find a leaf. Abort.
  return false;
}
<|endoftext|>


<commit_before>#include "logging.h"
#include <iostream>
#include <QString>

void ERROR(QString mesg){
    QString string;
    string ="ERROR: "+mesg;
    std::cerr << string.toStdString() << std::endl;
}
void WARNING(QString mesg){
    QString string;
    string="WARNING: "+mesg;
    std::cerr << string.toStdString() << std::endl;
}
<commit_msg>Update logging.cpp<commit_after>#include "logging.h"
#include <iostream>
#include <QString>

void ERROR(QString mesg)
{
    QString string;
    string ="ERROR: "+mesg;
    std::cerr << string.toStdString() << std::endl;
}

void WARNING(QString mesg)
{
    QString string;
    string="WARNING: "+mesg;
    std::cerr << string.toStdString() << std::endl;
}

void ERROR(std::string mesg)
{
    std::string string ="ERROR: "+mesg;
    std::cerr << string << std::endl;
}

void WARNING(std::string mesg)
{
    std::string string="WARNING: "+mesg;
    std::cerr << string << std::endl;
}
<|endoftext|>


<commit_before>/*
    Kopete , The KDE Instant Messenger
    Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>

    Viva Chile Mierda!
    Started at Wed Dec 26 03:12:10 CLST 2001, Santiago de Chile

    Kopete    (c) 2002-2003 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include "kopete.h"

#include <dcopclient.h>
#include "kopeteiface.h"

#define KOPETE_VERSION "0.6.90cvs >= 20030620"

static const char description[] =
	I18N_NOOP("Kopete, the KDE Instant Messenger");

static KCmdLineOptions options[] =
{
	{ "noplugins", I18N_NOOP("Do not load plugins"), 0 },
	{ "noconnect" , I18N_NOOP("Disable auto-connection") , 0 },
//	{ "connect <account>" , I18N_NOOP("auto-connect specified account") , 0 }, //TODO
	{ "disable <plugin>", I18N_NOOP("Do not load specified plugin"), 0 },
	{ "!+[plugin]", I18N_NOOP("Load specified plugins"), 0 },
	KCmdLineLastOption
};

int main(int argc, char *argv[])
{
	KAboutData aboutData( "kopete", I18N_NOOP("Kopete"),
		KOPETE_VERSION, description, KAboutData::License_GPL,
		I18N_NOOP("(c) 2001,2003, Duncan Mac-Vicar Prett\n(c) 2002,2003, The Kopete Development Team"), "kopete-devel@kde.org", "http://kopete.kde.org");

	aboutData.addAuthor ( "Duncan Mac-Vicar Prett", I18N_NOOP("Original author, core developer"), "duncan@kde.org", "http://www.mac-vicar.org/~duncan" );
	aboutData.addAuthor ( "Nick Betcher", I18N_NOOP("Core developer, fastest plugin developer on earth."), "nbetcher@kde.org");
	aboutData.addAuthor ( "Martijn Klingens", I18N_NOOP("Core developer"), "klingens@kde.org" );
	aboutData.addAuthor ( "Till Gerken", I18N_NOOP("Core developer, Jabber plugin"), "till@tantalo.net");
	aboutData.addAuthor ( "Olivier Goffart", I18N_NOOP("Core developer, MSN Plugin"), "ogoffart@tiscalinet.be");
	aboutData.addAuthor ( "Stefan Gehn", I18N_NOOP("Developer"), "metz@gehn.net", "http://metz.gehn.net" );
	aboutData.addAuthor ( "Gav Wood", I18N_NOOP("Winpopup plugin"), "gjw102@york.ac.uk" );
	aboutData.addAuthor ( "Grzegorz Jaskiewicz", I18N_NOOP("Gadu plugin developer"), "gj@pointblue.com.pl" );
	aboutData.addAuthor ( "Zack Rusin", I18N_NOOP("Core developer, Gadu plugin"), "zack@kde.org" );
	aboutData.addAuthor ( "Chris TenHarmsel", I18N_NOOP("Developer"), "tenharmsel@users.sourceforge.net", "http://bemis.kicks-ass.net");
	aboutData.addAuthor ( "Chris Howells", I18N_NOOP("Connection status plugin author"), "howells@kde.org", "http://chrishowells.co.uk");
	aboutData.addAuthor ( "Jason Keirstead", I18N_NOOP("Core developer"), "jason@keirstead.org", "http://www.keirstead.org");
	aboutData.addAuthor ( "Andy Goossens", I18N_NOOP("Developer"), "andygoossens@pandora.be" );
	aboutData.addAuthor ( "Will Stephenson", I18N_NOOP("Developer, Icons, Plugins"), "lists@stevello.free-online.co.uk" );
	aboutData.addAuthor ( "Matt Rogers", I18N_NOOP("Yahoo Plugin Maintainer"), "mattrogers@sbcglobal.net" );

	aboutData.addCredit ( "Luciash d' Being", I18N_NOOP("Icon Author") );
	aboutData.addCredit ( "Vladimir Shutoff", I18N_NOOP("SIM icq library") );
	aboutData.addCredit ( "Herwin Jan Steehouwer", I18N_NOOP("KxEngine icq code") );
	aboutData.addCredit ( "Olaf Lueg", I18N_NOOP("Kmerlin MSN code") );
	//aboutData.addCredit ( "Neil Stevens", I18N_NOOP("TAim engine AIM code") );
	aboutData.addCredit ( "Justin Karneges", I18N_NOOP("Psi Jabber code") );
	aboutData.addCredit ( "Steve Cable", I18N_NOOP("Sounds") );

	aboutData.addAuthor ( "Daniel Stone", I18N_NOOP("Former developer, Jabber plugin"), "dstone@kde.org", "http://raging.dropbear.id.au/daniel/");
	aboutData.addCredit ( "Ryan Cumming", I18N_NOOP("Former developer"), "ryan@kde.org" );
	aboutData.addCredit ( "Richard Stellingwerff", I18N_NOOP("Former Developer"), "remenic@linuxfromscratch.org");
	aboutData.addCredit ( "Hendrik vom Lehn", I18N_NOOP("Former Developer"), "hennevl@hennevl.de", "http://www.hennevl.de");
	aboutData.addCredit ( "Andres Krapf", I18N_NOOP("Former Developer"), "dae@chez.com" );
	aboutData.addCredit ( "Carsten Pfeiffer", I18N_NOOP("Misc Bugfixes and Enhancelets"), "pfeiffer@kde.org" );

	KCmdLineArgs::init( argc, argv, &aboutData );
	KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
	KUniqueApplication::addCmdLineOptions();

	Kopete kopete;
	kapp->dcopClient()->setDefaultObject( (new KopeteIface())->objId() ); // Has to be called before exec

	kopete.exec();
}
/*
 * Local variables:
 * c-indentation-style: k&r
 * c-basic-offset: 8
 * indent-tabs-mode: t
 * End:
 */
// vim: set noet ts=4 sts=4 sw=4:

<commit_msg>version bump<commit_after>/*
    Kopete , The KDE Instant Messenger
    Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>

    Viva Chile Mierda!
    Started at Wed Dec 26 03:12:10 CLST 2001, Santiago de Chile

    Kopete    (c) 2002-2003 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include "kopete.h"

#include <dcopclient.h>
#include "kopeteiface.h"

#define KOPETE_VERSION "0.6.90cvs >= 20030713"

static const char description[] =
	I18N_NOOP("Kopete, the KDE Instant Messenger");

static KCmdLineOptions options[] =
{
	{ "noplugins", I18N_NOOP("Do not load plugins"), 0 },
	{ "noconnect" , I18N_NOOP("Disable auto-connection") , 0 },
//	{ "connect <account>" , I18N_NOOP("auto-connect specified account") , 0 }, //TODO
	{ "disable <plugin>", I18N_NOOP("Do not load specified plugin"), 0 },
	{ "!+[plugin]", I18N_NOOP("Load specified plugins"), 0 },
	KCmdLineLastOption
};

int main(int argc, char *argv[])
{
	KAboutData aboutData( "kopete", I18N_NOOP("Kopete"),
		KOPETE_VERSION, description, KAboutData::License_GPL,
		I18N_NOOP("(c) 2001,2003, Duncan Mac-Vicar Prett\n(c) 2002,2003, The Kopete Development Team"), "kopete-devel@kde.org", "http://kopete.kde.org");

	aboutData.addAuthor ( "Duncan Mac-Vicar Prett", I18N_NOOP("Original author, core developer"), "duncan@kde.org", "http://www.mac-vicar.org/~duncan" );
	aboutData.addAuthor ( "Nick Betcher", I18N_NOOP("Core developer, fastest plugin developer on earth."), "nbetcher@kde.org");
	aboutData.addAuthor ( "Martijn Klingens", I18N_NOOP("Core developer"), "klingens@kde.org" );
	aboutData.addAuthor ( "Till Gerken", I18N_NOOP("Core developer, Jabber plugin"), "till@tantalo.net");
	aboutData.addAuthor ( "Olivier Goffart", I18N_NOOP("Core developer, MSN Plugin"), "ogoffart@tiscalinet.be");
	aboutData.addAuthor ( "Stefan Gehn", I18N_NOOP("Developer"), "metz@gehn.net", "http://metz.gehn.net" );
	aboutData.addAuthor ( "Gav Wood", I18N_NOOP("Winpopup plugin"), "gjw102@york.ac.uk" );
	aboutData.addAuthor ( "Grzegorz Jaskiewicz", I18N_NOOP("Gadu plugin developer"), "gj@pointblue.com.pl" );
	aboutData.addAuthor ( "Zack Rusin", I18N_NOOP("Core developer, Gadu plugin"), "zack@kde.org" );
	aboutData.addAuthor ( "Chris TenHarmsel", I18N_NOOP("Developer"), "tenharmsel@users.sourceforge.net", "http://bemis.kicks-ass.net");
	aboutData.addAuthor ( "Chris Howells", I18N_NOOP("Connection status plugin author"), "howells@kde.org", "http://chrishowells.co.uk");
	aboutData.addAuthor ( "Jason Keirstead", I18N_NOOP("Core developer"), "jason@keirstead.org", "http://www.keirstead.org");
	aboutData.addAuthor ( "Andy Goossens", I18N_NOOP("Developer"), "andygoossens@pandora.be" );
	aboutData.addAuthor ( "Will Stephenson", I18N_NOOP("Developer, Icons, Plugins"), "lists@stevello.free-online.co.uk" );
	aboutData.addAuthor ( "Matt Rogers", I18N_NOOP("Yahoo Plugin Maintainer"), "mattrogers@sbcglobal.net" );

	aboutData.addCredit ( "Luciash d' Being", I18N_NOOP("Icon Author") );
	aboutData.addCredit ( "Vladimir Shutoff", I18N_NOOP("SIM icq library") );
	aboutData.addCredit ( "Herwin Jan Steehouwer", I18N_NOOP("KxEngine icq code") );
	aboutData.addCredit ( "Olaf Lueg", I18N_NOOP("Kmerlin MSN code") );
	//aboutData.addCredit ( "Neil Stevens", I18N_NOOP("TAim engine AIM code") );
	aboutData.addCredit ( "Justin Karneges", I18N_NOOP("Psi Jabber code") );
	aboutData.addCredit ( "Steve Cable", I18N_NOOP("Sounds") );

	aboutData.addAuthor ( "Daniel Stone", I18N_NOOP("Former developer, Jabber plugin"), "dstone@kde.org", "http://raging.dropbear.id.au/daniel/");
	aboutData.addCredit ( "Ryan Cumming", I18N_NOOP("Former developer"), "ryan@kde.org" );
	aboutData.addCredit ( "Richard Stellingwerff", I18N_NOOP("Former Developer"), "remenic@linuxfromscratch.org");
	aboutData.addCredit ( "Hendrik vom Lehn", I18N_NOOP("Former Developer"), "hennevl@hennevl.de", "http://www.hennevl.de");
	aboutData.addCredit ( "Andres Krapf", I18N_NOOP("Former Developer"), "dae@chez.com" );
	aboutData.addCredit ( "Carsten Pfeiffer", I18N_NOOP("Misc Bugfixes and Enhancelets"), "pfeiffer@kde.org" );

	KCmdLineArgs::init( argc, argv, &aboutData );
	KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
	KUniqueApplication::addCmdLineOptions();

	Kopete kopete;
	kapp->dcopClient()->setDefaultObject( (new KopeteIface())->objId() ); // Has to be called before exec

	kopete.exec();
}
/*
 * Local variables:
 * c-indentation-style: k&r
 * c-basic-offset: 8
 * indent-tabs-mode: t
 * End:
 */
// vim: set noet ts=4 sts=4 sw=4:

<|endoftext|>


<commit_before>/*
    Kopete , The KDE Instant Messenger
    Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>

    Viva Chile Mierda!
    Started at Wed Dec 26 03:12:10 CLST 2001, Santiago de Chile

    Kopete    (c) 2002-2005 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include "kopeteapplication.h"
#include <klocale.h>
#include <dcopclient.h>
#include "kopeteiface.h"
#include "kimifaceimpl.h"
#include "kopeteversion.h"

static const char description[] =
	I18N_NOOP( "Kopete, the KDE Instant Messenger" );

static KCmdLineOptions options[] =
{
	{ "noplugins",              I18N_NOOP( "Do not load plugins. This option overrides all other options." ), 0 },
	{ "noconnect",              I18N_NOOP( "Disable auto-connection" ), 0 },
	{ "autoconnect <accounts>", I18N_NOOP( "Auto-connect the specified accounts. Use a comma-separated list\n"
		"to auto-connect multiple accounts." ), 0 },
	{ "disable <plugins>",      I18N_NOOP( "Do not load the specified plugin. Use a comma-separated list\n"
		"to disable multiple plugins." ), 0 },
	{ "load-plugins <plugins>", I18N_NOOP( "Load only the specified plugins. Use a comma-separated list\n"
		"to load multiple plugins. This option has no effect when\n"
		"--noplugins is set and overrides all other plugin related\n"
		"command line options." ), 0 },
//	{ "url <url>",              I18N_NOOP( "Load the given Kopete URL" ), 0 },
//	{ "!+[plugin]",            I18N_NOOP( "Load specified plugins" ), 0 },
	{ "!+[URL]",                 I18N_NOOP("URLs to pass to kopete / emoticon themes to install"), 0},
	KCmdLineLastOption
};

int main( int argc, char *argv[] )
{
	KAboutData aboutData( "kopete", I18N_NOOP("Kopete"),
		KOPETE_VERSION_STRING, description, KAboutData::License_GPL,
		I18N_NOOP("(c) 2001-2004, Duncan Mac-Vicar Prett\n(c) 2002-2005, Kopete Development Team"), "kopete-devel@kde.org", "http://kopete.kde.org");

	aboutData.addAuthor ( "Duncan Mac-Vicar Prett", I18N_NOOP("Developer and Project founder"), "duncan@kde.org", "http://www.mac-vicar.org/~duncan" );
	aboutData.addAuthor ( "Andre Duffeck", I18N_NOOP("Developer, Yahoo plugin maintainer"), "andre@duffeck.de" );
	aboutData.addAuthor ( "Till Gerken", I18N_NOOP("Developer, Jabber plugin maintainer"), "till@tantalo.net");
	aboutData.addAuthor ( "Olivier Goffart", I18N_NOOP("Lead Developer, MSN plugin maintainer"), "ogoffart @ kde.org");
	aboutData.addAuthor ( "Andy Goossens", I18N_NOOP("Developer"), "andygoossens@telenet.be" );
	aboutData.addAuthor ( "Michel Hermier", I18N_NOOP("IRC plugin maintainer"), "michel.hermier@wanadoo.fr" );
	aboutData.addAuthor ( "Chris Howells", I18N_NOOP("Developer, Connection status plugin author"), "howells@kde.org", "http://chrishowells.co.uk");
	aboutData.addAuthor ( "Grzegorz Jaskiewicz", I18N_NOOP("Developer, Gadu plugin maintainer"), "gj@pointblue.com.pl" );
	aboutData.addAuthor ( "Jason Keirstead", I18N_NOOP("Developer"), "jason@keirstead.org", "http://www.keirstead.org");
	aboutData.addAuthor ( "Chetan Reddy", I18N_NOOP("Developer, Yahoo"), "chetan13@gmail.com" );
	aboutData.addAuthor ( "Matt Rogers", I18N_NOOP("Lead Developer, AIM and ICQ plugin maintainer"), "mattr@kde.org" );
	aboutData.addAuthor ( "Richard Smith", I18N_NOOP("Developer, UI maintainer"), "kde@metafoo.co.uk" );
	aboutData.addAuthor ( "Will Stephenson", I18N_NOOP("Developer, GroupWise maintainer"), "lists@stevello.free-online.co.uk" );
	aboutData.addAuthor ( "Michaël Larouche", I18N_NOOP("Developer, MSN, Chatwindow"), "michael.larouche@kdemail.net", "http://mlarouche.blogspot.com" );

	aboutData.addCredit ( "Luciash d' Being", I18N_NOOP("Kopete's icon author") );
	aboutData.addCredit ( "Steve Cable", I18N_NOOP("Sounds") );
	aboutData.addCredit ( "Jessica Hall", I18N_NOOP("Kopete Docugoddess, Bug and Patch Testing.") );
	aboutData.addCredit ( "Justin Karneges", I18N_NOOP("Iris Jabber Backend Library") );
	aboutData.addCredit ( "Tom Linsky", I18N_NOOP("OscarSocket author"), "twl6@po.cwru.edu" );
	aboutData.addCredit ( "Olaf Lueg", I18N_NOOP("Kmerlin MSN code") );

	aboutData.addCredit ( "Nick Betcher", I18N_NOOP("Former developer, project co-founder"), "nbetcher@kde.org");
	aboutData.addCredit ( "Ryan Cumming", I18N_NOOP("Former developer"), "ryan@kde.org" );
	aboutData.addCredit ( "Stefan Gehn", I18N_NOOP("Former developer"), "metz@gehn.net", "http://metz.gehn.net" );
	aboutData.addCredit ( "Martijn Klingens", I18N_NOOP("Former developer"), "klingens@kde.org" );
	aboutData.addCredit ( "Andres Krapf", I18N_NOOP("Former developer"), "dae@chez.com" );
	aboutData.addCredit ( "Carsten Pfeiffer", I18N_NOOP("Misc bugfixes and enhancements"), "pfeiffer@kde.org" );
	aboutData.addCredit ( "Zack Rusin", I18N_NOOP("Former developer, original Gadu plugin author"), "zack@kde.org" );
	aboutData.addCredit ( "Richard Stellingwerff", I18N_NOOP("Former developer"), "remenic@linuxfromscratch.org");
	aboutData.addCredit ( "Daniel Stone", I18N_NOOP("Former developer, Jabber plugin author"), "daniel@fooishbar.org", "http://fooishbar.org");
	aboutData.addCredit ( "Chris TenHarmsel", I18N_NOOP("Former developer, Oscar plugin"), "tenharmsel@users.sourceforge.net");
	aboutData.addCredit ( "Hendrik vom Lehn", I18N_NOOP("Former developer"), "hennevl@hennevl.de", "http://www.hennevl.de");
	aboutData.addCredit ( "Gav Wood", I18N_NOOP("Former developer and WinPopup maintainer"), "gav@indigoarchive.net" );

	aboutData.setTranslator( I18N_NOOP("_: NAME OF TRANSLATORS\nYour names"),
		I18N_NOOP("_: EMAIL OF TRANSLATORS\nYour emails") );

	KCmdLineArgs::init( argc, argv, &aboutData );
	KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
	KUniqueApplication::addCmdLineOptions();

	KopeteApplication kopete;
	new KIMIfaceImpl();
	kapp->dcopClient()->registerAs( "kopete", false );
	kapp->dcopClient()->setDefaultObject( (new KopeteIface())->objId() ); // Has to be called before exec

	kopete.exec();
}
// vim: set noet ts=4 sts=4 sw=4:
<commit_msg>Now that xgettext 0.15-pre is used, the placeholders for the translators must be declared correctly. (goutte)<commit_after>/*
    Kopete , The KDE Instant Messenger
    Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>

    Viva Chile Mierda!
    Started at Wed Dec 26 03:12:10 CLST 2001, Santiago de Chile

    Kopete    (c) 2002-2005 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include "kopeteapplication.h"
#include <klocale.h>
#include <dcopclient.h>
#include "kopeteiface.h"
#include "kimifaceimpl.h"
#include "kopeteversion.h"

static const char description[] =
	I18N_NOOP( "Kopete, the KDE Instant Messenger" );

static KCmdLineOptions options[] =
{
	{ "noplugins",              I18N_NOOP( "Do not load plugins. This option overrides all other options." ), 0 },
	{ "noconnect",              I18N_NOOP( "Disable auto-connection" ), 0 },
	{ "autoconnect <accounts>", I18N_NOOP( "Auto-connect the specified accounts. Use a comma-separated list\n"
		"to auto-connect multiple accounts." ), 0 },
	{ "disable <plugins>",      I18N_NOOP( "Do not load the specified plugin. Use a comma-separated list\n"
		"to disable multiple plugins." ), 0 },
	{ "load-plugins <plugins>", I18N_NOOP( "Load only the specified plugins. Use a comma-separated list\n"
		"to load multiple plugins. This option has no effect when\n"
		"--noplugins is set and overrides all other plugin related\n"
		"command line options." ), 0 },
//	{ "url <url>",              I18N_NOOP( "Load the given Kopete URL" ), 0 },
//	{ "!+[plugin]",            I18N_NOOP( "Load specified plugins" ), 0 },
	{ "!+[URL]",                 I18N_NOOP("URLs to pass to kopete / emoticon themes to install"), 0},
	KCmdLineLastOption
};

int main( int argc, char *argv[] )
{
	KAboutData aboutData( "kopete", I18N_NOOP("Kopete"),
		KOPETE_VERSION_STRING, description, KAboutData::License_GPL,
		I18N_NOOP("(c) 2001-2004, Duncan Mac-Vicar Prett\n(c) 2002-2005, Kopete Development Team"), "kopete-devel@kde.org", "http://kopete.kde.org");

	aboutData.addAuthor ( "Duncan Mac-Vicar Prett", I18N_NOOP("Developer and Project founder"), "duncan@kde.org", "http://www.mac-vicar.org/~duncan" );
	aboutData.addAuthor ( "Andre Duffeck", I18N_NOOP("Developer, Yahoo plugin maintainer"), "andre@duffeck.de" );
	aboutData.addAuthor ( "Till Gerken", I18N_NOOP("Developer, Jabber plugin maintainer"), "till@tantalo.net");
	aboutData.addAuthor ( "Olivier Goffart", I18N_NOOP("Lead Developer, MSN plugin maintainer"), "ogoffart @ kde.org");
	aboutData.addAuthor ( "Andy Goossens", I18N_NOOP("Developer"), "andygoossens@telenet.be" );
	aboutData.addAuthor ( "Michel Hermier", I18N_NOOP("IRC plugin maintainer"), "michel.hermier@wanadoo.fr" );
	aboutData.addAuthor ( "Chris Howells", I18N_NOOP("Developer, Connection status plugin author"), "howells@kde.org", "http://chrishowells.co.uk");
	aboutData.addAuthor ( "Grzegorz Jaskiewicz", I18N_NOOP("Developer, Gadu plugin maintainer"), "gj@pointblue.com.pl" );
	aboutData.addAuthor ( "Jason Keirstead", I18N_NOOP("Developer"), "jason@keirstead.org", "http://www.keirstead.org");
	aboutData.addAuthor ( "Chetan Reddy", I18N_NOOP("Developer, Yahoo"), "chetan13@gmail.com" );
	aboutData.addAuthor ( "Matt Rogers", I18N_NOOP("Lead Developer, AIM and ICQ plugin maintainer"), "mattr@kde.org" );
	aboutData.addAuthor ( "Richard Smith", I18N_NOOP("Developer, UI maintainer"), "kde@metafoo.co.uk" );
	aboutData.addAuthor ( "Will Stephenson", I18N_NOOP("Developer, GroupWise maintainer"), "lists@stevello.free-online.co.uk" );
	aboutData.addAuthor ( "Michaël Larouche", I18N_NOOP("Developer, MSN, Chatwindow"), "michael.larouche@kdemail.net", "http://mlarouche.blogspot.com" );

	aboutData.addCredit ( "Luciash d' Being", I18N_NOOP("Kopete's icon author") );
	aboutData.addCredit ( "Steve Cable", I18N_NOOP("Sounds") );
	aboutData.addCredit ( "Jessica Hall", I18N_NOOP("Kopete Docugoddess, Bug and Patch Testing.") );
	aboutData.addCredit ( "Justin Karneges", I18N_NOOP("Iris Jabber Backend Library") );
	aboutData.addCredit ( "Tom Linsky", I18N_NOOP("OscarSocket author"), "twl6@po.cwru.edu" );
	aboutData.addCredit ( "Olaf Lueg", I18N_NOOP("Kmerlin MSN code") );

	aboutData.addCredit ( "Nick Betcher", I18N_NOOP("Former developer, project co-founder"), "nbetcher@kde.org");
	aboutData.addCredit ( "Ryan Cumming", I18N_NOOP("Former developer"), "ryan@kde.org" );
	aboutData.addCredit ( "Stefan Gehn", I18N_NOOP("Former developer"), "metz@gehn.net", "http://metz.gehn.net" );
	aboutData.addCredit ( "Martijn Klingens", I18N_NOOP("Former developer"), "klingens@kde.org" );
	aboutData.addCredit ( "Andres Krapf", I18N_NOOP("Former developer"), "dae@chez.com" );
	aboutData.addCredit ( "Carsten Pfeiffer", I18N_NOOP("Misc bugfixes and enhancements"), "pfeiffer@kde.org" );
	aboutData.addCredit ( "Zack Rusin", I18N_NOOP("Former developer, original Gadu plugin author"), "zack@kde.org" );
	aboutData.addCredit ( "Richard Stellingwerff", I18N_NOOP("Former developer"), "remenic@linuxfromscratch.org");
	aboutData.addCredit ( "Daniel Stone", I18N_NOOP("Former developer, Jabber plugin author"), "daniel@fooishbar.org", "http://fooishbar.org");
	aboutData.addCredit ( "Chris TenHarmsel", I18N_NOOP("Former developer, Oscar plugin"), "tenharmsel@users.sourceforge.net");
	aboutData.addCredit ( "Hendrik vom Lehn", I18N_NOOP("Former developer"), "hennevl@hennevl.de", "http://www.hennevl.de");
	aboutData.addCredit ( "Gav Wood", I18N_NOOP("Former developer and WinPopup maintainer"), "gav@indigoarchive.net" );

	aboutData.setTranslator( I18N_NOOP2("NAME OF TRANSLATORS","Your names"),
		I18N_NOOP2("EMAIL OF TRANSLATORS","Your emails") );

	KCmdLineArgs::init( argc, argv, &aboutData );
	KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
	KUniqueApplication::addCmdLineOptions();

	KopeteApplication kopete;
	new KIMIfaceImpl();
	kapp->dcopClient()->registerAs( "kopete", false );
	kapp->dcopClient()->setDefaultObject( (new KopeteIface())->objId() ); // Has to be called before exec

	kopete.exec();
}
// vim: set noet ts=4 sts=4 sw=4:
<|endoftext|>


<commit_before>// Copyright 2021 The TCMalloc Authors
//
// 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
//
//     https://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 "tcmalloc/lifetime_based_allocator.h"

#include <stddef.h>
#include <stdlib.h>

#include <string>
#include <thread>  // NOLINT(build/c++11)

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "absl/synchronization/barrier.h"
#include "absl/time/time.h"
#include "tcmalloc/common.h"
#include "tcmalloc/huge_page_aware_allocator.h"
#include "tcmalloc/huge_pages.h"
#include "tcmalloc/testing/testutil.h"

namespace tcmalloc {
namespace tcmalloc_internal {
namespace {

using ::testing::StrEq;

class LifetimeBasedAllocatorTest : public ::testing::Test {
 public:
  // Represents an allocation returned by the lifetime-aware allocator,
  // including any trackers (if applicable).
  struct Allocation {
    LifetimeTracker::Tracker tracker;
    PageId page;
  };

  // Represents memory (and refcount) allocated by the lifetime-aware allocator.
  struct BackingMemory {
    void* ptr;
    int refcount;
  };

  static constexpr absl::Duration kLifetimeThreshold = absl::Milliseconds(100);
  static const int kAllocationSize = kPagesPerHugePage.raw_num() - 2;

  LifetimeBasedAllocatorTest()
      : LifetimeBasedAllocatorTest(LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kEnabled,
            LifetimePredictionOptions::Strategy::kPredictedLifetimeRegions,
            absl::Milliseconds(500))) {}

  explicit LifetimeBasedAllocatorTest(LifetimePredictionOptions opts)
      : lifetime_allocator_(opts, &region_alloc_, kFakeClock) {}

 protected:
  const Clock kFakeClock =
      Clock{.now = FakeClock, .freq = GetFakeClockFrequency};

  void Advance(absl::Duration d) {
    clock_ += absl::ToDoubleSeconds(d) * GetFakeClockFrequency();
  }

  class FakeRegionAlloc : public LifetimeBasedAllocator::RegionAlloc {
   public:
    explicit FakeRegionAlloc() {
      region_metadata_ =
          BackingMemory{.ptr = calloc(1, sizeof(HugeRegion)), .refcount = 0};
      region_backing_memory_ = BackingMemory{
          .ptr = aligned_alloc(kHugePageSize, HugeRegion::size().in_bytes()),
          .refcount = 0};
    }
    ~FakeRegionAlloc() override {
      free(region_metadata_.ptr);
      free(region_backing_memory_.ptr);
    }

    HugeRegion* AllocRegion(HugeLength n, HugeRange* range) override {
      if (!range->valid()) {
        CHECK_CONDITION(n.in_bytes() == HugeRegion::size().in_bytes());
        CHECK_CONDITION(region_backing_memory_.refcount == 0);
        ++region_backing_memory_.refcount;
        *range =
            HugeRange::Make(HugePageContaining(region_backing_memory_.ptr), n);
      }

      CHECK_CONDITION(region_metadata_.refcount == 0);
      ++region_metadata_.refcount;
      new (region_metadata_.ptr)
          HugeRegion(*range, MemoryModifyFunction(NopUnbackFn));
      return static_cast<HugeRegion*>(region_metadata_.ptr);
    }

   private:
    BackingMemory region_backing_memory_;
    BackingMemory region_metadata_;
  };

  static bool NopUnbackFn(void* p, size_t len) {
    // TODO(b/122551676): Return non-trivial success results.
    return true;
  }

  ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL void Allocate(
      Allocation* out) {
    Length n = Length(kAllocationSize);
    LifetimeStats* context = lifetime_allocator_.CollectLifetimeContext(n);
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    bool from_released;
    auto res = lifetime_allocator_.MaybeGet(n, &from_released, context);
    if (!res.TryGetAllocation(&out->page)) {
      // If not allocated in the short-lived region, do not actually back the
      // memory and return the nullptr span.
      res.InitTracker(&out->tracker);
      lifetime_allocator_.MaybeAddTracker(res, &out->tracker);
      out->page = PageIdContaining(nullptr);
    }
  }

  void Delete(Allocation* alloc) {
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    lifetime_allocator_.MaybePut(alloc->page, Length(kAllocationSize));
    lifetime_allocator_.MaybePutTracker(&alloc->tracker,
                                        Length(kAllocationSize));
  }

  void GenerateInterestingAllocs() {
    Allocation long_lived_allocs[10];

    for (int i = 0; i < 10; ++i) {
      Allocation short_lived_allocs[10];

      Allocate(&long_lived_allocs[i]);  // will be classified as long-lived

      PRAGMA_NO_UNROLL
      for (int j = 0; j < 10; ++j) {
        Allocate(&short_lived_allocs[j]);  // will be classified as short-lived
      }

      Advance(absl::Microseconds(10));

      for (int j = 0; j < 10; ++j) {
        Delete(&short_lived_allocs[j]);
      }
    }

    Advance(absl::Seconds(10));
    for (int i = 0; i < 10; ++i) {
      Delete(&long_lived_allocs[i]);
    }
  }

  FakeRegionAlloc region_alloc_;
  LifetimeBasedAllocator lifetime_allocator_;

 private:
  static int64_t FakeClock() { return clock_; }

  static double GetFakeClockFrequency() {
    return absl::ToDoubleNanoseconds(absl::Seconds(2));
  }

  static int64_t clock_;
};

int64_t LifetimeBasedAllocatorTest::clock_{0};

class ParameterizedLifetimeBasedAllocatorTest
    : public LifetimeBasedAllocatorTest,
      public ::testing::WithParamInterface<LifetimePredictionOptions> {
 public:
  ParameterizedLifetimeBasedAllocatorTest()
      : LifetimeBasedAllocatorTest(GetParam()) {}
  ~ParameterizedLifetimeBasedAllocatorTest() override {}
};

// Test lifetime predictions.
TEST_P(ParameterizedLifetimeBasedAllocatorTest, Basic) {
  Allocation long_lived_allocs[10];

  for (int i = 0; i < 10; ++i) {
    Allocation short_lived_allocs[10];
    Allocation medium_lived_allocs[10];

    // This pragma is needed since otherwise the compiler may unroll the loop
    // and create more than one allocation site.
    PRAGMA_NO_UNROLL
    for (int j = 0; j < 10; ++j) {
      Allocate(&medium_lived_allocs[j]);  // will be classified as short-lived
    }

    Allocate(&long_lived_allocs[i]);  // will be classified as long-lived

    Advance(absl::Milliseconds(100));

    PRAGMA_NO_UNROLL
    for (int j = 0; j < 10; ++j) {
      Allocate(&short_lived_allocs[j]);  // will be classified as short-lived
    }

    Advance(absl::Microseconds(10));

    for (int j = 0; j < 10; ++j) {
      Delete(&short_lived_allocs[j]);
      Delete(&medium_lived_allocs[j]);
    }
  }

  Advance(absl::Seconds(1));

  LifetimeBasedAllocator::Stats stats = lifetime_allocator_.GetStats();

  EXPECT_EQ(3, stats.database_size);
  EXPECT_EQ(0, stats.database_evictions);

  if (GetParam().always_predict_short_lived()) {
    EXPECT_EQ(210, stats.tracker.short_lived_predictions);
    EXPECT_EQ(0, stats.tracker.long_lived_predictions);
    EXPECT_EQ(110, stats.tracker.expired_lifetimes);
    EXPECT_EQ(0, stats.tracker.overestimated_lifetimes);
    EXPECT_EQ(210, stats.region.allocations);
    EXPECT_EQ(53340, stats.region.allocated_pages);
    EXPECT_EQ(200, stats.region.deallocations);
    EXPECT_EQ(50800, stats.region.deallocated_pages);
  } else {
    EXPECT_EQ(80, stats.tracker.short_lived_predictions);
    EXPECT_EQ(130, stats.tracker.long_lived_predictions);
    EXPECT_EQ(0, stats.tracker.expired_lifetimes);
    EXPECT_EQ(20, stats.tracker.overestimated_lifetimes);
    EXPECT_EQ(80, stats.region.allocations);
    EXPECT_EQ(20320, stats.region.allocated_pages);
    EXPECT_EQ(80, stats.region.deallocations);
    EXPECT_EQ(20320, stats.region.deallocated_pages);
  }

  EXPECT_NE(LifetimePredictionOptions::Mode::kDisabled, stats.opts.mode());
  EXPECT_EQ(absl::Milliseconds(100), stats.opts.threshold());
  EXPECT_EQ(GetParam().counterfactual(), stats.opts.counterfactual());
}

// During allocation, the lifetime-based allocator drops the page heap lock,
// which can lead to complex race conditions. This is a stress-test to uncover
// this behavior.
TEST_P(ParameterizedLifetimeBasedAllocatorTest, MultithreadingStressTest) {
  const int kThreads = 100;
  const int kIterations = 4;
  const int kAllocations = 10;

  for (int i = 0; i < kIterations; ++i) {
    std::vector<std::thread> threads;
    std::vector<Allocation> allocs;
    allocs.resize(kThreads);

    absl::Barrier* b1 = new absl::Barrier(kThreads);
    absl::Barrier* b2 = new absl::Barrier(kThreads);
    for (int j = 0; j < kThreads; ++j) {
      threads.push_back(std::thread([this, j, &b1, &b2, &allocs]() {
        PRAGMA_NO_UNROLL
        for (int k = 0; k < kAllocations; ++k) {
          absl::SleepFor(absl::Microseconds(1));
          Allocate(&allocs[j]);
          // Synchronize the first iteration to ensure that all subsequent
          // iterations get predicted short-lived.
          if (k == 0 && b1->Block()) {
            delete b1;
          }
          absl::SleepFor(absl::Microseconds(1));
          Delete(&allocs[j]);
          if (k == 0 && b2->Block()) {
            delete b2;
          }
        }
      }));
    }

    for (auto& t : threads) {
      t.join();
    }
  }

  // Check that the lifetime prediction works as expected.
  LifetimeBasedAllocator::Stats stats = lifetime_allocator_.GetStats();
  if (GetParam().always_predict_short_lived()) {
    EXPECT_EQ(4000, stats.tracker.short_lived_predictions);
    EXPECT_EQ(0, stats.tracker.long_lived_predictions);
  } else {
    EXPECT_EQ(3900, stats.tracker.short_lived_predictions);
    EXPECT_EQ(100, stats.tracker.long_lived_predictions);
  }
  EXPECT_EQ(1, stats.database_size);
}

TEST_F(LifetimeBasedAllocatorTest, Print) {
  GenerateInterestingAllocs();
  std::string buffer(1024 * 1024, '\0');
  {
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    Printer printer(&*buffer.begin(), buffer.size());
    lifetime_allocator_.Print(&printer);
    buffer.erase(printer.SpaceRequired());
  }

  EXPECT_THAT(buffer, StrEq(R"(HugePageAware: *** Lifetime-based regions: ***
HugePageAware: Predictions: 80 short / 30 long lived (0 expired, 20 overestimated)
HugePageAware: Lifetime-based allocations (enabled / short-lived regions): Threshold = 0.50s, 2 stack traces (0 evicted)
LifetimeBasedRegion: 80 allocated (20320 pages), 80 freed (20320 pages) in lifetime region.

)"));
}

TEST_F(LifetimeBasedAllocatorTest, PrintInPbTxt) {
  GenerateInterestingAllocs();
  std::string buffer(1024 * 1024, '\0');
  Printer printer(&*buffer.begin(), buffer.size());
  {
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    PbtxtRegion region(&printer, kTop);
    lifetime_allocator_.PrintInPbtxt(&region);
  }
  buffer.erase(printer.SpaceRequired());

  auto canonicalize = [](std::string* s) {
    *s = absl::StrReplaceAll(*s, {{"\n", " "}});
    absl::RemoveExtraAsciiWhitespace(s);
  };
  canonicalize(&buffer);
  std::string expected(R"(
  lifetime_based_allocator_stats {
    enabled: true
    counterfactual: false
    threshold_ms: 500
    num_predicted_short_lived: 80
    num_predicted_long_lived: 30
    num_expired: 0
    num_overestimated: 20
    database_size: 2
    database_evicted_count: 0
    lifetime_region_allocated: 80
    lifetime_region_allocated_pages: 20320
    lifetime_region_freed: 80
    lifetime_region_freed_pages: 20320})");
  canonicalize(&expected);
  EXPECT_THAT(buffer, StrEq(expected));
}

INSTANTIATE_TEST_SUITE_P(
    LifetimeTests, ParameterizedLifetimeBasedAllocatorTest,
    testing::Values(
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kEnabled,
            LifetimePredictionOptions::Strategy::kPredictedLifetimeRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold),
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kCounterfactual,
            LifetimePredictionOptions::Strategy::kPredictedLifetimeRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold),
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kEnabled,
            LifetimePredictionOptions::Strategy::kAlwaysShortLivedRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold),
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kCounterfactual,
            LifetimePredictionOptions::Strategy::kAlwaysShortLivedRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold)),
    [](const testing::TestParamInfo<LifetimePredictionOptions>& info) {
      return absl::StrFormat(
          "%s_%s", ((info.index >= 2) ? "always_regions" : "lifetime_regions"),
          ((info.index % 2 == 0) ? "enabled" : "counterfactual"));
    });

}  // namespace
}  // namespace tcmalloc_internal
}  // namespace tcmalloc
<commit_msg>Update bug number reference.<commit_after>// Copyright 2021 The TCMalloc Authors
//
// 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
//
//     https://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 "tcmalloc/lifetime_based_allocator.h"

#include <stddef.h>
#include <stdlib.h>

#include <string>
#include <thread>  // NOLINT(build/c++11)

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "absl/synchronization/barrier.h"
#include "absl/time/time.h"
#include "tcmalloc/common.h"
#include "tcmalloc/huge_page_aware_allocator.h"
#include "tcmalloc/huge_pages.h"
#include "tcmalloc/testing/testutil.h"

namespace tcmalloc {
namespace tcmalloc_internal {
namespace {

using ::testing::StrEq;

class LifetimeBasedAllocatorTest : public ::testing::Test {
 public:
  // Represents an allocation returned by the lifetime-aware allocator,
  // including any trackers (if applicable).
  struct Allocation {
    LifetimeTracker::Tracker tracker;
    PageId page;
  };

  // Represents memory (and refcount) allocated by the lifetime-aware allocator.
  struct BackingMemory {
    void* ptr;
    int refcount;
  };

  static constexpr absl::Duration kLifetimeThreshold = absl::Milliseconds(100);
  static const int kAllocationSize = kPagesPerHugePage.raw_num() - 2;

  LifetimeBasedAllocatorTest()
      : LifetimeBasedAllocatorTest(LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kEnabled,
            LifetimePredictionOptions::Strategy::kPredictedLifetimeRegions,
            absl::Milliseconds(500))) {}

  explicit LifetimeBasedAllocatorTest(LifetimePredictionOptions opts)
      : lifetime_allocator_(opts, &region_alloc_, kFakeClock) {}

 protected:
  const Clock kFakeClock =
      Clock{.now = FakeClock, .freq = GetFakeClockFrequency};

  void Advance(absl::Duration d) {
    clock_ += absl::ToDoubleSeconds(d) * GetFakeClockFrequency();
  }

  class FakeRegionAlloc : public LifetimeBasedAllocator::RegionAlloc {
   public:
    explicit FakeRegionAlloc() {
      region_metadata_ =
          BackingMemory{.ptr = calloc(1, sizeof(HugeRegion)), .refcount = 0};
      region_backing_memory_ = BackingMemory{
          .ptr = aligned_alloc(kHugePageSize, HugeRegion::size().in_bytes()),
          .refcount = 0};
    }
    ~FakeRegionAlloc() override {
      free(region_metadata_.ptr);
      free(region_backing_memory_.ptr);
    }

    HugeRegion* AllocRegion(HugeLength n, HugeRange* range) override {
      if (!range->valid()) {
        CHECK_CONDITION(n.in_bytes() == HugeRegion::size().in_bytes());
        CHECK_CONDITION(region_backing_memory_.refcount == 0);
        ++region_backing_memory_.refcount;
        *range =
            HugeRange::Make(HugePageContaining(region_backing_memory_.ptr), n);
      }

      CHECK_CONDITION(region_metadata_.refcount == 0);
      ++region_metadata_.refcount;
      new (region_metadata_.ptr)
          HugeRegion(*range, MemoryModifyFunction(NopUnbackFn));
      return static_cast<HugeRegion*>(region_metadata_.ptr);
    }

   private:
    BackingMemory region_backing_memory_;
    BackingMemory region_metadata_;
  };

  static bool NopUnbackFn(void* p, size_t len) {
    // TODO(b/258278604): Return non-trivial success results.
    return true;
  }

  ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL void Allocate(
      Allocation* out) {
    Length n = Length(kAllocationSize);
    LifetimeStats* context = lifetime_allocator_.CollectLifetimeContext(n);
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    bool from_released;
    auto res = lifetime_allocator_.MaybeGet(n, &from_released, context);
    if (!res.TryGetAllocation(&out->page)) {
      // If not allocated in the short-lived region, do not actually back the
      // memory and return the nullptr span.
      res.InitTracker(&out->tracker);
      lifetime_allocator_.MaybeAddTracker(res, &out->tracker);
      out->page = PageIdContaining(nullptr);
    }
  }

  void Delete(Allocation* alloc) {
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    lifetime_allocator_.MaybePut(alloc->page, Length(kAllocationSize));
    lifetime_allocator_.MaybePutTracker(&alloc->tracker,
                                        Length(kAllocationSize));
  }

  void GenerateInterestingAllocs() {
    Allocation long_lived_allocs[10];

    for (int i = 0; i < 10; ++i) {
      Allocation short_lived_allocs[10];

      Allocate(&long_lived_allocs[i]);  // will be classified as long-lived

      PRAGMA_NO_UNROLL
      for (int j = 0; j < 10; ++j) {
        Allocate(&short_lived_allocs[j]);  // will be classified as short-lived
      }

      Advance(absl::Microseconds(10));

      for (int j = 0; j < 10; ++j) {
        Delete(&short_lived_allocs[j]);
      }
    }

    Advance(absl::Seconds(10));
    for (int i = 0; i < 10; ++i) {
      Delete(&long_lived_allocs[i]);
    }
  }

  FakeRegionAlloc region_alloc_;
  LifetimeBasedAllocator lifetime_allocator_;

 private:
  static int64_t FakeClock() { return clock_; }

  static double GetFakeClockFrequency() {
    return absl::ToDoubleNanoseconds(absl::Seconds(2));
  }

  static int64_t clock_;
};

int64_t LifetimeBasedAllocatorTest::clock_{0};

class ParameterizedLifetimeBasedAllocatorTest
    : public LifetimeBasedAllocatorTest,
      public ::testing::WithParamInterface<LifetimePredictionOptions> {
 public:
  ParameterizedLifetimeBasedAllocatorTest()
      : LifetimeBasedAllocatorTest(GetParam()) {}
  ~ParameterizedLifetimeBasedAllocatorTest() override {}
};

// Test lifetime predictions.
TEST_P(ParameterizedLifetimeBasedAllocatorTest, Basic) {
  Allocation long_lived_allocs[10];

  for (int i = 0; i < 10; ++i) {
    Allocation short_lived_allocs[10];
    Allocation medium_lived_allocs[10];

    // This pragma is needed since otherwise the compiler may unroll the loop
    // and create more than one allocation site.
    PRAGMA_NO_UNROLL
    for (int j = 0; j < 10; ++j) {
      Allocate(&medium_lived_allocs[j]);  // will be classified as short-lived
    }

    Allocate(&long_lived_allocs[i]);  // will be classified as long-lived

    Advance(absl::Milliseconds(100));

    PRAGMA_NO_UNROLL
    for (int j = 0; j < 10; ++j) {
      Allocate(&short_lived_allocs[j]);  // will be classified as short-lived
    }

    Advance(absl::Microseconds(10));

    for (int j = 0; j < 10; ++j) {
      Delete(&short_lived_allocs[j]);
      Delete(&medium_lived_allocs[j]);
    }
  }

  Advance(absl::Seconds(1));

  LifetimeBasedAllocator::Stats stats = lifetime_allocator_.GetStats();

  EXPECT_EQ(3, stats.database_size);
  EXPECT_EQ(0, stats.database_evictions);

  if (GetParam().always_predict_short_lived()) {
    EXPECT_EQ(210, stats.tracker.short_lived_predictions);
    EXPECT_EQ(0, stats.tracker.long_lived_predictions);
    EXPECT_EQ(110, stats.tracker.expired_lifetimes);
    EXPECT_EQ(0, stats.tracker.overestimated_lifetimes);
    EXPECT_EQ(210, stats.region.allocations);
    EXPECT_EQ(53340, stats.region.allocated_pages);
    EXPECT_EQ(200, stats.region.deallocations);
    EXPECT_EQ(50800, stats.region.deallocated_pages);
  } else {
    EXPECT_EQ(80, stats.tracker.short_lived_predictions);
    EXPECT_EQ(130, stats.tracker.long_lived_predictions);
    EXPECT_EQ(0, stats.tracker.expired_lifetimes);
    EXPECT_EQ(20, stats.tracker.overestimated_lifetimes);
    EXPECT_EQ(80, stats.region.allocations);
    EXPECT_EQ(20320, stats.region.allocated_pages);
    EXPECT_EQ(80, stats.region.deallocations);
    EXPECT_EQ(20320, stats.region.deallocated_pages);
  }

  EXPECT_NE(LifetimePredictionOptions::Mode::kDisabled, stats.opts.mode());
  EXPECT_EQ(absl::Milliseconds(100), stats.opts.threshold());
  EXPECT_EQ(GetParam().counterfactual(), stats.opts.counterfactual());
}

// During allocation, the lifetime-based allocator drops the page heap lock,
// which can lead to complex race conditions. This is a stress-test to uncover
// this behavior.
TEST_P(ParameterizedLifetimeBasedAllocatorTest, MultithreadingStressTest) {
  const int kThreads = 100;
  const int kIterations = 4;
  const int kAllocations = 10;

  for (int i = 0; i < kIterations; ++i) {
    std::vector<std::thread> threads;
    std::vector<Allocation> allocs;
    allocs.resize(kThreads);

    absl::Barrier* b1 = new absl::Barrier(kThreads);
    absl::Barrier* b2 = new absl::Barrier(kThreads);
    for (int j = 0; j < kThreads; ++j) {
      threads.push_back(std::thread([this, j, &b1, &b2, &allocs]() {
        PRAGMA_NO_UNROLL
        for (int k = 0; k < kAllocations; ++k) {
          absl::SleepFor(absl::Microseconds(1));
          Allocate(&allocs[j]);
          // Synchronize the first iteration to ensure that all subsequent
          // iterations get predicted short-lived.
          if (k == 0 && b1->Block()) {
            delete b1;
          }
          absl::SleepFor(absl::Microseconds(1));
          Delete(&allocs[j]);
          if (k == 0 && b2->Block()) {
            delete b2;
          }
        }
      }));
    }

    for (auto& t : threads) {
      t.join();
    }
  }

  // Check that the lifetime prediction works as expected.
  LifetimeBasedAllocator::Stats stats = lifetime_allocator_.GetStats();
  if (GetParam().always_predict_short_lived()) {
    EXPECT_EQ(4000, stats.tracker.short_lived_predictions);
    EXPECT_EQ(0, stats.tracker.long_lived_predictions);
  } else {
    EXPECT_EQ(3900, stats.tracker.short_lived_predictions);
    EXPECT_EQ(100, stats.tracker.long_lived_predictions);
  }
  EXPECT_EQ(1, stats.database_size);
}

TEST_F(LifetimeBasedAllocatorTest, Print) {
  GenerateInterestingAllocs();
  std::string buffer(1024 * 1024, '\0');
  {
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    Printer printer(&*buffer.begin(), buffer.size());
    lifetime_allocator_.Print(&printer);
    buffer.erase(printer.SpaceRequired());
  }

  EXPECT_THAT(buffer, StrEq(R"(HugePageAware: *** Lifetime-based regions: ***
HugePageAware: Predictions: 80 short / 30 long lived (0 expired, 20 overestimated)
HugePageAware: Lifetime-based allocations (enabled / short-lived regions): Threshold = 0.50s, 2 stack traces (0 evicted)
LifetimeBasedRegion: 80 allocated (20320 pages), 80 freed (20320 pages) in lifetime region.

)"));
}

TEST_F(LifetimeBasedAllocatorTest, PrintInPbTxt) {
  GenerateInterestingAllocs();
  std::string buffer(1024 * 1024, '\0');
  Printer printer(&*buffer.begin(), buffer.size());
  {
    absl::base_internal::SpinLockHolder h(&pageheap_lock);
    PbtxtRegion region(&printer, kTop);
    lifetime_allocator_.PrintInPbtxt(&region);
  }
  buffer.erase(printer.SpaceRequired());

  auto canonicalize = [](std::string* s) {
    *s = absl::StrReplaceAll(*s, {{"\n", " "}});
    absl::RemoveExtraAsciiWhitespace(s);
  };
  canonicalize(&buffer);
  std::string expected(R"(
  lifetime_based_allocator_stats {
    enabled: true
    counterfactual: false
    threshold_ms: 500
    num_predicted_short_lived: 80
    num_predicted_long_lived: 30
    num_expired: 0
    num_overestimated: 20
    database_size: 2
    database_evicted_count: 0
    lifetime_region_allocated: 80
    lifetime_region_allocated_pages: 20320
    lifetime_region_freed: 80
    lifetime_region_freed_pages: 20320})");
  canonicalize(&expected);
  EXPECT_THAT(buffer, StrEq(expected));
}

INSTANTIATE_TEST_SUITE_P(
    LifetimeTests, ParameterizedLifetimeBasedAllocatorTest,
    testing::Values(
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kEnabled,
            LifetimePredictionOptions::Strategy::kPredictedLifetimeRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold),
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kCounterfactual,
            LifetimePredictionOptions::Strategy::kPredictedLifetimeRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold),
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kEnabled,
            LifetimePredictionOptions::Strategy::kAlwaysShortLivedRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold),
        LifetimePredictionOptions(
            LifetimePredictionOptions::Mode::kCounterfactual,
            LifetimePredictionOptions::Strategy::kAlwaysShortLivedRegions,
            LifetimeBasedAllocatorTest::kLifetimeThreshold)),
    [](const testing::TestParamInfo<LifetimePredictionOptions>& info) {
      return absl::StrFormat(
          "%s_%s", ((info.index >= 2) ? "always_regions" : "lifetime_regions"),
          ((info.index % 2 == 0) ? "enabled" : "counterfactual"));
    });

}  // namespace
}  // namespace tcmalloc_internal
}  // namespace tcmalloc
<|endoftext|>


<commit_before>
#include <vector>

#include "geometry/permutation.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/r3_element.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"

using principia::si::Metre;
using principia::testing_utilities::AlmostEquals;
using testing::Eq;

namespace principia {
namespace geometry {

class PermutationTest : public testing::Test {
 protected:
  struct World;
  typedef OrthogonalMap<World, World> Orth;
  typedef Permutation<World, World> Perm;
  typedef R3Element<quantities::Length> R3;

  void SetUp() override {
    vector_ = Vector<quantities::Length, World>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    bivector_ = Bivector<quantities::Length, World>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    trivector_ = Trivector<quantities::Length, World>(4.0 * Metre);
  }

  Vector<quantities::Length, World> vector_;
  Bivector<quantities::Length, World> bivector_;
  Trivector<quantities::Length, World> trivector_;
};

TEST_F(PermutationTest, Identity) {
  EXPECT_THAT(Perm::Identity()(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, XYZ) {
  EXPECT_THAT(Perm(Perm::XYZ)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, YZX) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, ZXY) {
  EXPECT_THAT(Perm(Perm::ZXY)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, XZY) {
  EXPECT_THAT(Perm(Perm::XZY)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, ZYX) {
  EXPECT_THAT(Perm(Perm::ZYX)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, YXZ) {
  EXPECT_THAT(Perm(Perm::YXZ)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, Determinant) {
  Perm xyz(Perm::XYZ);
  Perm yzx(Perm::YZX);
  Perm zxy(Perm::ZXY);
  Perm xzy(Perm::XZY);
  Perm zyx(Perm::ZYX);
  Perm yxz(Perm::YXZ);
  EXPECT_TRUE(xyz.Determinant().Positive());
  EXPECT_TRUE(yzx.Determinant().Positive());
  EXPECT_TRUE(zxy.Determinant().Positive());
  EXPECT_TRUE(xzy.Determinant().Negative());
  EXPECT_TRUE(zyx.Determinant().Negative());
  EXPECT_TRUE(yxz.Determinant().Negative());
}

TEST_F(PermutationTest, AppliedToVector) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToBivector) {
  EXPECT_THAT(Perm(Perm::ZXY)(bivector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZYX)(bivector_).coordinates(),
              Eq<R3>({-3.0 * Metre, -2.0 * Metre, -1.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToTrivector) {
  EXPECT_THAT(Perm(Perm::XYZ)(trivector_).coordinates(),
              Eq(4.0 * Metre));
  EXPECT_THAT(Perm(Perm::XZY)(trivector_).coordinates(),
              Eq(-4.0 * Metre));
}

TEST_F(PermutationTest, Inverse) {
  EXPECT_THAT(Perm(Perm::YZX).Inverse()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ).Inverse()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, Forget) {
  EXPECT_THAT(Perm(Perm::XYZ).Forget()(vector_).coordinates(),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YZX).Forget()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZXY).Forget()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::XZY).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}, 2));
  EXPECT_THAT(Perm(Perm::ZYX).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}, 4));
  EXPECT_THAT(Perm(Perm::YXZ).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}, 2));
}

TEST_F(PermutationTest, Compose) {
  std::vector<Perm> all = {Perm(Perm::XYZ),Perm(Perm::YZX),Perm(Perm::ZXY),
                           Perm(Perm::XZY),Perm(Perm::ZYX),Perm(Perm::YXZ)};
  for (const Perm& p1 : all) {
    Orth const o1 = p1.Forget();
    for (const Perm& p2 : all) {
      Orth const o2 = p2.Forget();
      Perm const p12 = p1 * p2;
      Orth const o12 = o1 * o2;
      EXPECT_THAT(p12(vector_), AlmostEquals(o12(vector_), 20));
    }
  }
}

}  // namespace geometry
}  // namespace principia
<commit_msg>Moar tests.<commit_after>
#include <vector>

#include "geometry/permutation.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/r3_element.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"

using principia::si::Metre;
using principia::testing_utilities::AlmostEquals;
using testing::Eq;

namespace principia {
namespace geometry {

class PermutationTest : public testing::Test {
 protected:
  struct World;
  typedef OrthogonalMap<World, World> Orth;
  typedef Permutation<World, World> Perm;
  typedef R3Element<quantities::Length> R3;

  void SetUp() override {
    vector_ = Vector<quantities::Length, World>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    bivector_ = Bivector<quantities::Length, World>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    trivector_ = Trivector<quantities::Length, World>(4.0 * Metre);
  }

  Vector<quantities::Length, World> vector_;
  Bivector<quantities::Length, World> bivector_;
  Trivector<quantities::Length, World> trivector_;
};

TEST_F(PermutationTest, Identity) {
  EXPECT_THAT(Perm::Identity()(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, XYZ) {
  EXPECT_THAT(Perm(Perm::XYZ)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, YZX) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, ZXY) {
  EXPECT_THAT(Perm(Perm::ZXY)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, XZY) {
  EXPECT_THAT(Perm(Perm::XZY)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, ZYX) {
  EXPECT_THAT(Perm(Perm::ZYX)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, YXZ) {
  EXPECT_THAT(Perm(Perm::YXZ)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, Determinant) {
  Perm xyz(Perm::XYZ);
  Perm yzx(Perm::YZX);
  Perm zxy(Perm::ZXY);
  Perm xzy(Perm::XZY);
  Perm zyx(Perm::ZYX);
  Perm yxz(Perm::YXZ);
  EXPECT_TRUE(xyz.Determinant().Positive());
  EXPECT_TRUE(yzx.Determinant().Positive());
  EXPECT_TRUE(zxy.Determinant().Positive());
  EXPECT_TRUE(xzy.Determinant().Negative());
  EXPECT_TRUE(zyx.Determinant().Negative());
  EXPECT_TRUE(yxz.Determinant().Negative());
}

TEST_F(PermutationTest, AppliedToVector) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToBivector) {
  EXPECT_THAT(Perm(Perm::ZXY)(bivector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZYX)(bivector_).coordinates(),
              Eq<R3>({-3.0 * Metre, -2.0 * Metre, -1.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToTrivector) {
  EXPECT_THAT(Perm(Perm::XYZ)(trivector_).coordinates(),
              Eq(4.0 * Metre));
  EXPECT_THAT(Perm(Perm::XZY)(trivector_).coordinates(),
              Eq(-4.0 * Metre));
}

TEST_F(PermutationTest, Inverse) {
  EXPECT_THAT(Perm(Perm::YZX).Inverse()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ).Inverse()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));

  std::vector<Perm> all = {Perm(Perm::XYZ),Perm(Perm::YZX),Perm(Perm::ZXY),
                           Perm(Perm::XZY),Perm(Perm::ZYX),Perm(Perm::YXZ)};
  for (const Perm& p : all) {
    Perm const identity = p * p.Inverse();
    EXPECT_THAT(identity(vector_), Eq(vector_));
  }
}

TEST_F(PermutationTest, Forget) {
  EXPECT_THAT(Perm(Perm::XYZ).Forget()(vector_).coordinates(),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YZX).Forget()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZXY).Forget()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::XZY).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}, 2));
  EXPECT_THAT(Perm(Perm::ZYX).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}, 4));
  EXPECT_THAT(Perm(Perm::YXZ).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}, 2));
}

TEST_F(PermutationTest, Compose) {
  std::vector<Perm> all = {Perm(Perm::XYZ),Perm(Perm::YZX),Perm(Perm::ZXY),
                           Perm(Perm::XZY),Perm(Perm::ZYX),Perm(Perm::YXZ)};
  for (const Perm& p1 : all) {
    Orth const o1 = p1.Forget();
    for (const Perm& p2 : all) {
      Orth const o2 = p2.Forget();
      Perm const p12 = p1 * p2;
      Orth const o12 = o1 * o2;
      EXPECT_THAT(p12(vector_), AlmostEquals(o12(vector_), 20));
    }
  }
}

}  // namespace geometry
}  // namespace principia
<|endoftext|>


<commit_before>#include "LibraryPanel.h"
#include "LibraryPage.h"

#include <wx/notebook.h>

#include <easyanim.h>
#include <easymesh.h>
#include <easyscale9.h>
#include <easyparticle3d.h>
#include <easyparticle2d.h>
#include <easyejoy2d.h>
#include <easyterrain2d.h>
#include <easytexture.h>
#include <easyicon.h>
#include <easyshadow.h>
#include <easysketch.h>
#include <easytext.h>

namespace ecomplex
{

LibraryPanel::LibraryPanel(wxWindow* parent)
	: d2d::LibraryPanel(parent)
{
	wxWindow* nb = GetNotebook();
	AddPage(new d2d::LibraryImagePage(nb));
	AddPage(new LibraryPage(nb));
	AddPage(new libanim::LibraryPage(nb));
	AddPage(new escale9::LibraryPage(nb));
	AddPage(new etext::LibraryPage(nb));
	AddPage(new d2d::LibraryFontBlankPage(nb));
	AddPage(new emesh::LibraryPage(nb));
	AddPage(new d2d::LibraryScriptsPage(nb));
	AddPage(new d2d::LibraryFontPage(nb));
	AddPage(new eparticle3d::LibraryPage(nb));
	AddPage(new eparticle2d::LibraryPage(nb));
	AddPage(new eejoy2d::LibraryPage(nb));
	AddPage(new eterrain2d::LibraryPage(nb));
	AddPage(new etexture::LibraryPage(nb));
	AddPage(new eicon::LibraryPage(nb));
	AddPage(new eshadow::LibraryPage(nb));
	AddPage(new libsketch::LibraryPage(nb));
}

} // ecomplex<commit_msg>[REMOVED] ecomplex library里不用的<commit_after>#include "LibraryPanel.h"
#include "LibraryPage.h"

#include <wx/notebook.h>

#include <easyanim.h>
#include <easymesh.h>
#include <easyscale9.h>
#include <easyparticle3d.h>
#include <easyparticle2d.h>
#include <easyejoy2d.h>
#include <easyterrain2d.h>
#include <easytexture.h>
#include <easyicon.h>
#include <easyshadow.h>
#include <easysketch.h>
#include <easytext.h>

namespace ecomplex
{

LibraryPanel::LibraryPanel(wxWindow* parent)
	: d2d::LibraryPanel(parent)
{
	wxWindow* nb = GetNotebook();
	AddPage(new d2d::LibraryImagePage(nb));
	AddPage(new LibraryPage(nb));
	AddPage(new libanim::LibraryPage(nb));
	AddPage(new escale9::LibraryPage(nb));
	AddPage(new etext::LibraryPage(nb));
//	AddPage(new d2d::LibraryFontBlankPage(nb));
	AddPage(new emesh::LibraryPage(nb));
	AddPage(new d2d::LibraryScriptsPage(nb));
//	AddPage(new d2d::LibraryFontPage(nb));
	AddPage(new eparticle3d::LibraryPage(nb));
	AddPage(new eparticle2d::LibraryPage(nb));
	AddPage(new eejoy2d::LibraryPage(nb));
	AddPage(new eterrain2d::LibraryPage(nb));
	AddPage(new etexture::LibraryPage(nb));
	AddPage(new eicon::LibraryPage(nb));
	AddPage(new eshadow::LibraryPage(nb));
	AddPage(new libsketch::LibraryPage(nb));
}

} // ecomplex<|endoftext|>


<commit_before><commit_msg>Fix how we calculate workarea size (_NET_WORKAREA gives width/height).<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2009 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/common/process_watcher.h"

#include <errno.h>
#include <signal.h>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "base/time.h"

namespace {

// Reap |child| process.
// This call blocks until completion.
void BlockingReap(pid_t child) {
  const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
  if (result == -1) {
    PLOG(ERROR) << "waitpid(" << child << ")";
    NOTREACHED();
  }
}

// Waits for |timeout| seconds for the given |child| to exit and reap it.
// If the child doesn't exit within a couple of seconds, kill it.
void WaitForChildToDie(pid_t child, unsigned timeout) {
  int kq = HANDLE_EINTR(kqueue());
  file_util::ScopedFD auto_close(&kq);
  if (kq == -1) {
    PLOG(ERROR) << "Failed to create kqueue";
    return;
  }

  struct kevent event_to_add = {0};
  EV_SET(&event_to_add, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
  // Register interest with kqueue.
  int result = HANDLE_EINTR(kevent(kq, &event_to_add, 1, NULL, 0, NULL));
  if (result == -1 && errno == ESRCH) {
    // A "No Such Process" error is fine, the process may have died already
    // and been reaped by someone else. But make sure that it was/is reaped.
    // Don't report an error in case it was already reaped.
    HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
    return;
  }

  if (result == -1) {
    PLOG(ERROR) << "Failed to register event to listen for death of pid "
                << child;
    return;
  }

  struct kevent event = {0};

  DCHECK(timeout != 0);

  int num_processes_that_died = -1;
  using base::Time;
  using base::TimeDelta;
  // We need to keep track of the elapsed time - if kevent() returns
  // EINTR in the middle of blocking call we want to make up what's left
  // of the timeout.
  TimeDelta time_left = TimeDelta::FromSeconds(timeout);
  Time wakeup = Time::Now() + time_left;
  while(time_left.InMilliseconds() > 0) {
    const struct timespec timeout = time_left.ToTimeSpec();
    num_processes_that_died = kevent(kq, NULL, 0, &event, 1, &timeout);
    if (num_processes_that_died >= 0)
      break;
    if (num_processes_that_died == -1 && errno == EINTR) {
      time_left = wakeup - Time::Now();
      continue;
    }

    // If we got here, kevent() must have returned -1.
    PLOG(ERROR) << "kevent() failed";
    break;
  }

  if (num_processes_that_died == -1) {
    PLOG(ERROR) << "kevent failed";
    return;
  }
  if (num_processes_that_died == 1) {
    if (event.fflags & NOTE_EXIT &&
        event.ident == static_cast<uintptr_t>(child)) {
      // Process died, it's safe to make a blocking call here since the
      // kqueue() notification occurs when the process is already zombified.
      BlockingReap(child);
      return;
    } else {
      PLOG(ERROR) << "kevent() returned unexpected result - ke.fflags ="
                  << event.fflags
                  << " ke.ident ="
                  << event.ident
                  << " while listening for pid="
                  << child;
    }
  }

  // If we got here the child is still alive so kill it...
  if (kill(child, SIGKILL) == 0) {
    // SIGKILL is uncatchable. Since the signal was delivered, we can
    // just wait for the process to die now in a blocking manner.
    BlockingReap(child);
  } else {
    PLOG(ERROR) << "While waiting for " << child << " to terminate we"
               << " failed to deliver a SIGKILL signal";
  }
}

}  // namespace

void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
  WaitForChildToDie(process, 2);
}
<commit_msg>Kernel zombie death race!<commit_after>// Copyright (c) 2009 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/common/process_watcher.h"

#include <errno.h>
#include <signal.h>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "base/time.h"

namespace {

const int kWaitBeforeKillSeconds = 2;

// Reap |child| process. This call blocks until completion.
void BlockingReap(pid_t child) {
  const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
  if (result == -1) {
    PLOG(ERROR) << "waitpid(" << child << ", NULL, 0)";
  }
}

// Waits for |timeout| seconds for the given |child| to exit and reap it. If
// the child doesn't exit within the time specified, kills it.
//
// This function takes two approaches: first, it tries to use kqueue to
// observe when the process exits. kevent can monitor a kqueue with a
// timeout, so this method is preferred to wait for a specified period of
// time. Once the kqueue indicates the process has exited, waitpid will reap
// the exited child. If the kqueue doesn't provide an exit event notification,
// before the timeout expires, or if the kqueue fails or misbehaves, the
// process will be mercilessly killed and reaped.
//
// A child process passed to this function may be in one of several states:
// running, terminated and not yet reaped, and (apparently, and unfortunately)
// terminated and already reaped. Normally, a process will at least have been
// asked to exit before this function is called, but this is not required.
// If a process is terminating and unreaped, there may be a window between the
// time that kqueue will no longer recognize it and when it becomes an actual
// zombie that a non-blocking (WNOHANG) waitpid can reap. This condition is
// detected when kqueue indicates that the process is not running and a
// non-blocking waitpid fails to reap the process but indicates that it is
// still running. In this event, a blocking attempt to reap the process
// collects the known-dying child, preventing zombies from congregating.
//
// In the event that the kqueue misbehaves entirely, as it might under a
// EMFILE condition ("too many open files", or out of file descriptors), this
// function will forcibly kill and reap the child without delay. This
// eliminates another potential zombie vector. (If you're out of file
// descriptors, you're probably deep into something else, but that doesn't
// mean that zombies be allowed to kick you while you're down.)
//
// The fact that this function seemingly can be called to wait on a child
// that's not only already terminated but already reaped is a bit of a
// problem: a reaped child's pid can be reclaimed and may refer to a distinct
// process in that case. The fact that this function can seemingly be called
// to wait on a process that's not even a child is also a problem: kqueue will
// work in that case, but waitpid won't, and killing a non-child might not be
// the best approach.
void WaitForChildToDie(pid_t child, int timeout) {
  DCHECK(child > 0);
  DCHECK(timeout > 0);

  // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that
  // |child| has been reaped. Specifically, even if a kqueue, kevent, or other
  // call fails, this function should fall back to the last resort of trying
  // to kill and reap the process. Not observing this rule will resurrect
  // zombies.

  int result;

  int kq = HANDLE_EINTR(kqueue());
  if (kq == -1) {
    PLOG(ERROR) << "kqueue()";
  } else {
    file_util::ScopedFD auto_close_kq(&kq);

    struct kevent change = {0};
    EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
    result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));

    if (result == -1) {
      if (errno != ESRCH) {
        PLOG(ERROR) << "kevent (setup " << child << ")";
      } else {
        // At this point, one of the following has occurred:
        // 1. The process has died but has not yet been reaped.
        // 2. The process has died and has already been reaped.
        // 3. The process is in the process of dying. It's no longer
        //    kqueueable, but it may not be waitable yet either. Mark calls
        //    this case the "zombie death race".

        result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));

        if (result != 0) {
          // A positive result indicates case 1. waitpid succeeded and reaped
          // the child. A result of -1 indicates case 2. The child has already
          // been reaped. In both of these cases, no further action is
          // necessary.
          return;
        }

        // |result| is 0, indicating case 3. The process will be waitable in
        // short order. Fall back out of the kqueue code to kill it (for good
        // measure) and reap it.
      }
    } else {
      // Keep track of the elapsed time to be able to restart kevent if it's
      // interrupted.
      base::TimeDelta remaining_delta = base::TimeDelta::FromSeconds(timeout);
      base::Time deadline = base::Time::Now() + remaining_delta;
      result = -1;
      struct kevent event = {0};
      while (remaining_delta.InMilliseconds() > 0) {
        const struct timespec remaining_timespec = remaining_delta.ToTimeSpec();
        result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec);
        if (result == -1 && errno == EINTR) {
          remaining_delta = deadline - base::Time::Now();
          result = 0;
        } else {
          break;
        }
      }

      if (result == -1) {
        PLOG(ERROR) << "kevent (wait " << child << ")";
      } else if (result > 1) {
        LOG(ERROR) << "kevent (wait " << child << "): unexpected result "
                   << result;
      } else if (result == 1) {
        if ((event.fflags & NOTE_EXIT) &&
            (event.ident == static_cast<uintptr_t>(child))) {
          // The process is dead or dying. This won't block for long, if at
          // all.
          BlockingReap(child);
          return;
        } else {
          LOG(ERROR) << "kevent (wait " << child
                     << "): unexpected event: fflags=" << event.fflags
                     << ", ident=" << event.ident;
        }
      }
    }
  }

  // The child is still alive, or is very freshly dead. Be sure by sending it
  // a signal. This is safe even if it's freshly dead, because it will be a
  // zombie (or on the way to zombiedom) and kill will return 0 even if the
  // signal is not delivered to a live process.
  result = kill(child, SIGKILL);
  if (result == -1) {
    PLOG(ERROR) << "kill(" << child << ", SIGKILL)";
  } else {
    // The child is definitely on the way out now. BlockingReap won't need to
    // wait for long, if at all.
    BlockingReap(child);
  }
}

}  // namespace

void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
  WaitForChildToDie(process, kWaitBeforeKillSeconds);
}
<|endoftext|>