<commit_before>#include "types.h"
#include "amd64.h"
#include "kernel.hh"
#include "cpu.hh"
#include "gc.hh"
#include "percpu.hh"
#include "spinlock.h"
#include "condvar.h"
#include "proc.hh"
#include "uwq.hh"
#include "vm.hh"
#include "kalloc.hh"
#include "bits.hh"
extern "C" {
#include "kern_c.h"
}

bool
uwq_trywork(void)
{
  // Returning true means uwq added a thread to the run queue

  u64 i, k;

  // A "random" victim CPU
  k = rdtsc();
  for (i = 0; i < NCPU; i++) {
    u64 j = (i+k) % NCPU;

    if (j == mycpuid())
      continue;
    struct cpu *c = &cpus[j];

    // The gc_epoch is for p and uwq
    scoped_gc_epoch xgc();
    barrier();

    struct proc *p = c->proc;
    if (p == nullptr || p->uwq == nullptr)
      continue;
    uwq* uwq = p->uwq;

    if (uwq->haswork()) {
      if (uwq->tryworker())
        return true;
      break;
    }
  }

  return false;
}

long
sys_wqwait(void)
{
  uwq_worker* w = myproc()->worker;
  if (w == nullptr)
    return -1;

  return w->wait();
}

//
// uwq_worker
//
uwq_worker::uwq_worker(uwq* u, proc* p)
  : uwq_(u), proc_(p), running_(false)
{
  initlock(&lock_, "worker_lock", 0);
  initcondvar(&cv_, "worker_cv");
}

void
uwq_worker::exit(void)
{
  if (--uwq_->uref_ == 0)
    gc_delayed(uwq_);
  release(&lock_);
  delete this;
  ::exit();
}

long
uwq_worker::wait(void)
{
  acquire(&lock_);
  if (!uwq_->valid())
    this->exit();

  running_ = false;
  cv_sleep(&cv_, &lock_);

  if (!uwq_->valid())
    this->exit();
  release(&lock_);
  return 0;
}

//
// uwq
//
uwq*
uwq::alloc(vmap* vmap, filetable *ftable)
{
  padded_length* len;
  uwq* u;

  len = (padded_length*) ksalloc(slab_userwq);
  if (len == nullptr)
    return nullptr;

  ftable->incref();
  vmap->incref();

  u = new uwq(vmap, ftable, len);
  if (u == nullptr) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    return nullptr;
  }

  if (mapkva(vmap->pml4, (char*)len, USERWQ, USERWQSIZE)) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    u->dec();
    return nullptr;
  }

  return u;
}

uwq::uwq(vmap* vmap, filetable *ftable, padded_length *len)
  : rcu_freed("uwq"),
    vmap_(vmap), ftable_(ftable), len_(len),
    uentry_(0), ustack_(UWQSTACK), uref_(0)
{
  for (int i = 0; i < NCPU; i++)
    len_[i].v_ = 0;

  initlock(&lock_, "uwq_lock", 0);
  memset(worker_, 0, sizeof(worker_));
}

uwq::~uwq(void)
{
  if (len_ != nullptr)
    ksfree(slab_userwq, len_);
  vmap_->decref();
  ftable_->decref();
}

bool
uwq::haswork(void) const
{
  if (len_ == nullptr)
    return false;

  for (int i = 0; i < NCPU; i++) {
    if (len_[i].v_ > 0) {
      return true;
    }
  }
  return false;
}

bool
uwq::tryworker(void)
{
  // Try to start a worker thread
  scoped_acquire lock0(&lock_);

  if (!valid())
    return false;

  int slot = -1;
  for (int i = 0; i < NWORKERS; i++) {
    if (worker_[i] == nullptr) {
      if (slot == -1)
        slot = i;
      continue;
    }

    uwq_worker *w = worker_[i];
    if (w->running_)
      continue;
    else {
      scoped_acquire lock1(&w->lock_);
      proc* p = w->proc_;

      acquire(&p->lock);
      p->cpuid = mycpuid();
      release(&p->lock);

      w->running_ = true;
      cv_wakeup(&w->cv_);
      return true;
    }
  }

  if (slot != -1) {
    proc* p = allocworker();
    if (p != nullptr) {
      uwq_worker* w = new uwq_worker(this, p);
      assert(w != nullptr);

      ++uref_;
      p->worker = w;
      w->running_ = true;

      acquire(&p->lock);
      p->cpuid = mycpuid();
      addrun(p);
      release(&p->lock);

      worker_[slot] = w;
      return true;
    }
  }

  return nullptr;
}

void
uwq::finish(void)
{
  bool gcnow = true;

  scoped_acquire lock0(&lock_);
  for (int i = 0; i < NWORKERS; i++) {
    if (worker_[i] != nullptr) {
      uwq_worker* w = worker_[i];
      gcnow = false;
      acquire(&w->lock_);
      cv_wakeup(&w->cv_);
      release(&w->lock_);
    }
  }

  if (gcnow)
    gc_delayed(this);
}

void
uwq::onzero() const
{
  uwq *u = (uwq*)this;
  u->finish();
}

void
uwq::setuentry(uptr uentry)
{
  uentry_ = uentry;
}

proc*
uwq::allocworker(void)
{
  uptr uentry = uentry_;

  if (uentry == 0)
    return nullptr;

  proc* p = proc::alloc();
  if (p == nullptr)
    return nullptr;
  safestrcpy(p->name, "uwq_worker", sizeof(p->name));

  // finishproc will drop these refs
  vmap_->incref();
  ftable_->incref();

  p->vmap = vmap_;
  p->ftable = ftable_;

  struct vmnode *vmn;
  if ((vmn = new vmnode(USTACKPAGES)) == nullptr) {
    finishproc(p);
    return nullptr;
  }

  uptr stacktop = ustack_ + (USTACKPAGES*PGSIZE);
  if (vmap_->insert(vmn, ustack_, 1) < 0) {
    delete vmn;
    finishproc(p);
    return nullptr;
  }
  // Include a bumper page
  ustack_ += (USTACKPAGES*PGSIZE)+PGSIZE;

  p->tf->rsp = stacktop - 8;
  p->tf->rip = uentry;
  p->tf->cs = UCSEG | 0x3;
  p->tf->ds = UDSEG | 0x3;
  p->tf->ss = p->tf->ds;
  p->tf->rflags = FL_IF;

  return p;
}
<commit_msg>uwq_trywork bug fix<commit_after>#include "types.h"
#include "amd64.h"
#include "kernel.hh"
#include "cpu.hh"
#include "gc.hh"
#include "percpu.hh"
#include "spinlock.h"
#include "condvar.h"
#include "proc.hh"
#include "uwq.hh"
#include "vm.hh"
#include "kalloc.hh"
#include "bits.hh"
extern "C" {
#include "kern_c.h"
}

bool
uwq_trywork(void)
{
  // Returning true means uwq added a thread to the run queue

  u64 i, k;

  // A "random" victim CPU
  k = rdtsc();
  for (i = 0; i < NCPU; i++) {
    u64 j = (i+k) % NCPU;

    if (j == mycpuid())
      continue;
    struct cpu *c = &cpus[j];

    // The gc_epoch is for p and uwq
    scoped_gc_epoch xgc();
    barrier();

    struct proc *p = c->proc;
    if (p == nullptr)
      continue;
    uwq* uwq = p->uwq;
    if (uwq == nullptr)
      continue;

    if (uwq->haswork()) {
      if (uwq->tryworker())
        return true;
      break;
    }
  }

  return false;
}

long
sys_wqwait(void)
{
  uwq_worker* w = myproc()->worker;
  if (w == nullptr)
    return -1;

  return w->wait();
}

//
// uwq_worker
//
uwq_worker::uwq_worker(uwq* u, proc* p)
  : uwq_(u), proc_(p), running_(false)
{
  initlock(&lock_, "worker_lock", 0);
  initcondvar(&cv_, "worker_cv");
}

void
uwq_worker::exit(void)
{
  if (--uwq_->uref_ == 0)
    gc_delayed(uwq_);
  release(&lock_);
  delete this;
  ::exit();
}

long
uwq_worker::wait(void)
{
  acquire(&lock_);
  if (!uwq_->valid())
    this->exit();

  running_ = false;
  cv_sleep(&cv_, &lock_);

  if (!uwq_->valid())
    this->exit();
  release(&lock_);
  return 0;
}

//
// uwq
//
uwq*
uwq::alloc(vmap* vmap, filetable *ftable)
{
  padded_length* len;
  uwq* u;

  len = (padded_length*) ksalloc(slab_userwq);
  if (len == nullptr)
    return nullptr;

  ftable->incref();
  vmap->incref();

  u = new uwq(vmap, ftable, len);
  if (u == nullptr) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    return nullptr;
  }

  if (mapkva(vmap->pml4, (char*)len, USERWQ, USERWQSIZE)) {
    ftable->decref();
    vmap->decref();
    ksfree(slab_userwq, len);
    u->dec();
    return nullptr;
  }

  return u;
}

uwq::uwq(vmap* vmap, filetable *ftable, padded_length *len)
  : rcu_freed("uwq"),
    vmap_(vmap), ftable_(ftable), len_(len),
    uentry_(0), ustack_(UWQSTACK), uref_(0)
{
  for (int i = 0; i < NCPU; i++)
    len_[i].v_ = 0;

  initlock(&lock_, "uwq_lock", 0);
  memset(worker_, 0, sizeof(worker_));
}

uwq::~uwq(void)
{
  if (len_ != nullptr)
    ksfree(slab_userwq, len_);
  vmap_->decref();
  ftable_->decref();
}

bool
uwq::haswork(void) const
{
  if (len_ == nullptr)
    return false;

  for (int i = 0; i < NCPU; i++) {
    if (len_[i].v_ > 0) {
      return true;
    }
  }
  return false;
}

bool
uwq::tryworker(void)
{
  // Try to start a worker thread
  scoped_acquire lock0(&lock_);

  if (!valid())
    return false;

  int slot = -1;
  for (int i = 0; i < NWORKERS; i++) {
    if (worker_[i] == nullptr) {
      if (slot == -1)
        slot = i;
      continue;
    }

    uwq_worker *w = worker_[i];
    if (w->running_)
      continue;
    else {
      scoped_acquire lock1(&w->lock_);
      proc* p = w->proc_;

      acquire(&p->lock);
      p->cpuid = mycpuid();
      release(&p->lock);

      w->running_ = true;
      cv_wakeup(&w->cv_);
      return true;
    }
  }

  if (slot != -1) {
    proc* p = allocworker();
    if (p != nullptr) {
      uwq_worker* w = new uwq_worker(this, p);
      assert(w != nullptr);

      ++uref_;
      p->worker = w;
      w->running_ = true;

      acquire(&p->lock);
      p->cpuid = mycpuid();
      addrun(p);
      release(&p->lock);

      worker_[slot] = w;
      return true;
    }
  }

  return nullptr;
}

void
uwq::finish(void)
{
  bool gcnow = true;

  scoped_acquire lock0(&lock_);
  for (int i = 0; i < NWORKERS; i++) {
    if (worker_[i] != nullptr) {
      uwq_worker* w = worker_[i];
      gcnow = false;
      acquire(&w->lock_);
      cv_wakeup(&w->cv_);
      release(&w->lock_);
    }
  }

  if (gcnow)
    gc_delayed(this);
}

void
uwq::onzero() const
{
  uwq *u = (uwq*)this;
  u->finish();
}

void
uwq::setuentry(uptr uentry)
{
  uentry_ = uentry;
}

proc*
uwq::allocworker(void)
{
  uptr uentry = uentry_;

  if (uentry == 0)
    return nullptr;

  proc* p = proc::alloc();
  if (p == nullptr)
    return nullptr;
  safestrcpy(p->name, "uwq_worker", sizeof(p->name));

  // finishproc will drop these refs
  vmap_->incref();
  ftable_->incref();

  p->vmap = vmap_;
  p->ftable = ftable_;

  struct vmnode *vmn;
  if ((vmn = new vmnode(USTACKPAGES)) == nullptr) {
    finishproc(p);
    return nullptr;
  }

  uptr stacktop = ustack_ + (USTACKPAGES*PGSIZE);
  if (vmap_->insert(vmn, ustack_, 1) < 0) {
    delete vmn;
    finishproc(p);
    return nullptr;
  }
  // Include a bumper page
  ustack_ += (USTACKPAGES*PGSIZE)+PGSIZE;

  p->tf->rsp = stacktop - 8;
  p->tf->rip = uentry;
  p->tf->cs = UCSEG | 0x3;
  p->tf->ds = UDSEG | 0x3;
  p->tf->ss = p->tf->ds;
  p->tf->rflags = FL_IF;

  return p;
}
<|endoftext|>


<commit_before>#include"all_defines.hpp"
#include"objects.hpp"
#include"heaps.hpp"
#include"types.hpp"

#include<cstdlib>
#include<stdint.h>

/*-----------------------------------------------------------------------------
Semispaces
-----------------------------------------------------------------------------*/

Semispace::Semispace(size_t nsz)
	: max(nsz) {
	mem = std::malloc(nsz);
	if(!mem) throw std::bad_alloc();

	allocpt = mem;
	// check alignment
	intptr_t tmp = reinterpret_cast<intptr_t>(allocpt);
	if(tmp & Object::tag_mask) {
		char* callocpt = (char*)allocpt;
		callocpt += Object::alignment - (tmp & Object::tag_mask);
		allocpt = callocpt;
	}
	allocstart = allocpt;

	char* cmem = (char*)mem;
	char* clifoallocpt = cmem + nsz;
	// adjust for alignment
	tmp = reinterpret_cast<intptr_t>(lifoallocpt);
	clifoallocpt -= (tmp & Object::tag_mask);

	lifoallocpt = clifoallocpt;
}

Semispace::~Semispace() {
	Generic* gp;
	size_t step;
	char* mvpt;
	char* endpt;

	/*TODO: consider refactoring*/
	/*----Delete normally-allocated memory----*/
	mvpt = (char*) allocstart;
	endpt = (char*) allocpt;

	while(mvpt < endpt) {
		gp = (Generic*)(void*) mvpt;
		step = gp->real_size();
		gp->~Generic();
		mvpt += step;
	}

	/*----Delete lifo-allocated memory----*/
	mvpt = (char*) lifoallocpt;
	endpt = (char*) lifoallocstart;

	while(mvpt < endpt) {
		gp = (Generic*)(void*) mvpt;
		step = gp->real_size();
		gp->~Generic();
		mvpt += step;
	}
}

/*
sz must be computed using the exact same
computation used in real_size() of the
object to be allocated.  This includes
alignment.
*/
void* Semispace::alloc(size_t sz) {
	prev_alloc = sz;
	void* tmp = allocpt;
	char* callocpt = (char*) allocpt;
	callocpt += sz;
	allocpt = callocpt;
	return tmp;
}

/*should be used only for most recent allocation
(i.e. should be used for freeing memory when the
constructor throws.)
*/
void Semispace::dealloc(void* pt) {
	#ifdef DEBUG
		char* callocpt = allocpt;
		callocpt -= prev_alloc;
		if(callocpt != pt) throw_DeallocError(pt);
	#endif
	allocpt = pt;
}

void* Semispace::lifo_alloc(size_t sz) {
	prev_alloc = sz;
	char* clifoallocpt = (char*) lifoallocpt;
	clifoallocpt -= sz;
	lifoallocpt = clifoallocpt;
	return lifoallocpt;
}

void Semispace::lifo_dealloc(void* pt) {
	/*if we can't deallocate, just ignore*/
	if(pt != lifoallocpt) return;
	size_t sz = ((Generic*) pt)->real_size();
	((Generic*) pt)->~Generic();
	char* clifoallocpt = (char*) pt;
	clifoallocpt += sz;
	lifoallocpt = clifoallocpt;
}

/*This function should be used only when the
constructor for the object fails.  It does
*not* properly destroy the object.
*/
void Semispace::lifo_dealloc_abort(void* pt) {
	#ifdef DEBUG
		if(pt != lifoallocpt) throw_DeallocError(pt);
	#endif
	char* clifoallocpt = (char*) lifoallocpt;
	clifoallocpt += prev_alloc;
	lifoallocpt = clifoallocpt;
}

/*TODO*/
void Semispace::clone(boost::scoped_ptr<Semispace>& sp, Generic*& g) const;

/*-----------------------------------------------------------------------------
Heaps
-----------------------------------------------------------------------------*/

/*copy and modify GC class*/
class GCTraverser : public GenericTraverser {
	Semispace* nsp;
public:
	explicit GCTraverser(Semispace* nnsp) : nsp(nnsp) { }
	void traverse(Object::ref& r) {
		if(is_a<Generic*>(r)) {
			Generic* gp = as_a<Generic*>(r);
			BrokenHeart* bp = dynamic_cast<BrokenHeart*>(gp);
			if(bp) { //broken heart
				r = Object::to_ref(bp->to);
			} else { //unbroken
				Generic* ngp = gp->clone(nsp);
				gp->break_heart(ngp);
				r = Object::to_ref(ngp);
			}
		} else return;
	}
};

static void cheney_collection(Heap& hp, Semispace* nsp) {
	GCTraverser gc(nsp);
	/*step 1: initial traverse*/
	hp.scan_root_object(gc);
	/*step 2: non-root traverse*/
	/*notice that we traverse the new semispace
	this is a two-pointer Cheney collector, with mvpt
	being one pointer and nsp->allocpt the other one
	*/
	char* mvpt = nsp->allocstart;
	while(mvpt < ((char*) nsp->allocpt)) {
		Generic* gp = (Generic*)(void*) mvpt;
		size_t obsz = gp->real_size();
		gp->traverse_references(gc);
		mvpt += obsz;
	}
}

void Heap::GC(size_t insurance) {
	/*Determine the sizes of all semispaces*/
	size_t total = main->used() + insurance +
	(other_spaces) ?		other_spaces->used_total() :
	/*otherwise*/			0 ;

	if(tight) total *= 2;

	/*get a new Semispace*/
	boost::scoped_ptr<Semispace> nsp(new Semispace(total));

	/*traverse*/
	cheney_collection(*this, &*nsp);

	/*replace*/
	main.swap(nsp);
	nsp.reset();
	other_spaces.reset();

	/*determine if resizing is appropriate*/
	if(main->used() + insurance <= total / 4) {
		/*semispace a bit large... make it smaller*/
		nsp.reset(new Semispace(total / 2));
		cheney_collection(&*nsp);
		main.swap(nsp);
		nsp.reset();
	} else if(main->used() + insurance >= (total / 4) * 3) {
		tight = 1;
	}
}

<commit_msg>src/heaps.cpp: Changed computation of total size in Heap::GC<commit_after>#include"all_defines.hpp"
#include"objects.hpp"
#include"heaps.hpp"
#include"types.hpp"

#include<cstdlib>
#include<stdint.h>

/*-----------------------------------------------------------------------------
Semispaces
-----------------------------------------------------------------------------*/

Semispace::Semispace(size_t nsz)
	: max(nsz) {
	mem = std::malloc(nsz);
	if(!mem) throw std::bad_alloc();

	allocpt = mem;
	// check alignment
	intptr_t tmp = reinterpret_cast<intptr_t>(allocpt);
	if(tmp & Object::tag_mask) {
		char* callocpt = (char*)allocpt;
		callocpt += Object::alignment - (tmp & Object::tag_mask);
		allocpt = callocpt;
	}
	allocstart = allocpt;

	char* cmem = (char*)mem;
	char* clifoallocpt = cmem + nsz;
	// adjust for alignment
	tmp = reinterpret_cast<intptr_t>(lifoallocpt);
	clifoallocpt -= (tmp & Object::tag_mask);

	lifoallocpt = clifoallocpt;
}

Semispace::~Semispace() {
	Generic* gp;
	size_t step;
	char* mvpt;
	char* endpt;

	/*TODO: consider refactoring*/
	/*----Delete normally-allocated memory----*/
	mvpt = (char*) allocstart;
	endpt = (char*) allocpt;

	while(mvpt < endpt) {
		gp = (Generic*)(void*) mvpt;
		step = gp->real_size();
		gp->~Generic();
		mvpt += step;
	}

	/*----Delete lifo-allocated memory----*/
	mvpt = (char*) lifoallocpt;
	endpt = (char*) lifoallocstart;

	while(mvpt < endpt) {
		gp = (Generic*)(void*) mvpt;
		step = gp->real_size();
		gp->~Generic();
		mvpt += step;
	}
}

/*
sz must be computed using the exact same
computation used in real_size() of the
object to be allocated.  This includes
alignment.
*/
void* Semispace::alloc(size_t sz) {
	prev_alloc = sz;
	void* tmp = allocpt;
	char* callocpt = (char*) allocpt;
	callocpt += sz;
	allocpt = callocpt;
	return tmp;
}

/*should be used only for most recent allocation
(i.e. should be used for freeing memory when the
constructor throws.)
*/
void Semispace::dealloc(void* pt) {
	#ifdef DEBUG
		char* callocpt = allocpt;
		callocpt -= prev_alloc;
		if(callocpt != pt) throw_DeallocError(pt);
	#endif
	allocpt = pt;
}

void* Semispace::lifo_alloc(size_t sz) {
	prev_alloc = sz;
	char* clifoallocpt = (char*) lifoallocpt;
	clifoallocpt -= sz;
	lifoallocpt = clifoallocpt;
	return lifoallocpt;
}

void Semispace::lifo_dealloc(void* pt) {
	/*if we can't deallocate, just ignore*/
	if(pt != lifoallocpt) return;
	size_t sz = ((Generic*) pt)->real_size();
	((Generic*) pt)->~Generic();
	char* clifoallocpt = (char*) pt;
	clifoallocpt += sz;
	lifoallocpt = clifoallocpt;
}

/*This function should be used only when the
constructor for the object fails.  It does
*not* properly destroy the object.
*/
void Semispace::lifo_dealloc_abort(void* pt) {
	#ifdef DEBUG
		if(pt != lifoallocpt) throw_DeallocError(pt);
	#endif
	char* clifoallocpt = (char*) lifoallocpt;
	clifoallocpt += prev_alloc;
	lifoallocpt = clifoallocpt;
}

/*TODO*/
void Semispace::clone(boost::scoped_ptr<Semispace>& sp, Generic*& g) const;

/*-----------------------------------------------------------------------------
Heaps
-----------------------------------------------------------------------------*/

/*copy and modify GC class*/
class GCTraverser : public GenericTraverser {
	Semispace* nsp;
public:
	explicit GCTraverser(Semispace* nnsp) : nsp(nnsp) { }
	void traverse(Object::ref& r) {
		if(is_a<Generic*>(r)) {
			Generic* gp = as_a<Generic*>(r);
			BrokenHeart* bp = dynamic_cast<BrokenHeart*>(gp);
			if(bp) { //broken heart
				r = Object::to_ref(bp->to);
			} else { //unbroken
				Generic* ngp = gp->clone(nsp);
				gp->break_heart(ngp);
				r = Object::to_ref(ngp);
			}
		} else return;
	}
};

static void cheney_collection(Heap& hp, Semispace* nsp) {
	GCTraverser gc(nsp);
	/*step 1: initial traverse*/
	hp.scan_root_object(gc);
	/*step 2: non-root traverse*/
	/*notice that we traverse the new semispace
	this is a two-pointer Cheney collector, with mvpt
	being one pointer and nsp->allocpt the other one
	*/
	char* mvpt = nsp->allocstart;
	while(mvpt < ((char*) nsp->allocpt)) {
		Generic* gp = (Generic*)(void*) mvpt;
		size_t obsz = gp->real_size();
		gp->traverse_references(gc);
		mvpt += obsz;
	}
}

void Heap::GC(size_t insurance) {
	/*Determine the sizes of all semispaces*/
	size_t total = main->used() + insurance;
	total +=
	(other_spaces) ?		other_spaces->used_total() :
	/*otherwise*/			0 ;

	if(tight) total *= 2;

	/*get a new Semispace*/
	boost::scoped_ptr<Semispace> nsp(new Semispace(total));

	/*traverse*/
	cheney_collection(*this, &*nsp);

	/*replace*/
	main.swap(nsp);
	nsp.reset();
	other_spaces.reset();

	/*determine if resizing is appropriate*/
	if(main->used() + insurance <= total / 4) {
		/*semispace a bit large... make it smaller*/
		nsp.reset(new Semispace(total / 2));
		cheney_collection(&*nsp);
		main.swap(nsp);
		nsp.reset();
	} else if(main->used() + insurance >= (total / 4) * 3) {
		tight = 1;
	}
}

<|endoftext|>


<commit_before>/**
 * Clever programming language
 * Copyright (c) 2011-2012 Clever Team
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <iostream>
#include "compiler/pkgmanager.h"
#include "compiler/value.h"
#include "compiler/cstring.h"
#include "modules/std/std_pkg.h"
#include "modules/web/web_pkg.h"

namespace clever {

/**
 * Loads native packages
 */
void PackageManager::init() {
	addPackage(CSTRING("std"), new packages::Std());
	addPackage(CSTRING("web"), new packages::Web());
}

/**
 * Load an entire package
 */
void PackageManager::loadPackage(Scope* scope, const CString* const package) {
	PackageMap::const_iterator it = m_packages.find(package);

	if (it != m_packages.end()) {
		/**
		 * Check if the package already has been loaded
		 */
		if (it->second->isLoaded()) {
			return;
		}
		/**
		 * Initializes the package
		 */
		it->second->init();

		ModuleMap& modules = it->second->getModules();
		ModuleMap::const_iterator mit = modules.begin(), end = modules.end();

		while (mit != end) {
			loadModule(scope, package, mit->second, NULL);
			++mit;
		}
		/**
		 * Sets the package state to fully loaded
		 */
		it->second->setFullyLoaded();
	} else {
		std::cerr << "package '" << *package << "' not found" << std::endl;
	}
}

/**
 * Loads an specific module
 */
void PackageManager::loadModule(Scope* scope, const CString* const package,
	Module* const module, const CString* const alias) {
	/**
	 * Checks if the module already has been loaded
	 */
	if (module->isLoaded()) {
		return;
	}
	/**
	 * Initializes the module
	 */
	module->init();

	const std::string prefix_name = alias ?
		alias->str() + "::" : package->str() + "." + module->getName() + "::";

	FunctionMap& funcs = module->getFunctions();
	FunctionMap::const_iterator it = funcs.begin(), end = funcs.end();

	/**
	 * Inserts the function into the symbol table
	 */
	while (it != end) {
		CallableValue* fvalue = new CallableValue(CSTRING(it->first));

		fvalue->setHandler(it->second);

		scope->pushValue(CSTRING(prefix_name + *fvalue->getName()), fvalue);
		scope->pushValue(fvalue->getName(), fvalue);
		fvalue->addRef();
		++it;
	}

	ClassMap& classes = module->getClassTable();
	ClassMap::iterator itc = classes.begin(), endc = classes.end();

	/**
	 * Inserts all classes into the symbol table
	 */
	while (itc != endc) {
		g_scope.pushType(CSTRING(prefix_name + *itc->first), itc->second);
		g_scope.pushType(itc->first, itc->second);
		itc->second->addRef();

		itc->second->init();
		++itc;
	}

	ConstMap& constants = module->getConstants();
	ConstMap::iterator itcs = constants.begin(), endcs = constants.end();

	/**
	 * Inserts all constants into the symbol table
	 */
	while (itcs != endcs) {
		g_scope.pushValue(CSTRING(prefix_name + itcs->first->str()), itcs->second);
		++itcs;
	}

	/**
	 * Sets the module state to loaded
	 */
	module->setLoaded();
}

/**
 * Loads an specific module package by supplying the package and module names
 */
void PackageManager::loadModule(Scope* scope, const CString* const package,
	const CString* const module, const CString* const alias) {
	PackageMap::const_iterator it = m_packages.find(package);

	if (it == m_packages.end()) {
		return;
	}

	/**
	 * Checks if the package is unloaded, in this case initialize it
	 */
	if (it->second->isUnloaded()) {
		it->second->init();
	}
	/**
	 * Check if the package already has been fully loaded
	 */
	if (!it->second->isFullyLoaded()) {
		ModuleMap& modules = it->second->getModules();
		ModuleMap::const_iterator it_mod = modules.find(module);

		/**
		 * Loads the module if it has been found
		 */
		if (it_mod != modules.end()) {
			loadModule(scope, package, it_mod->second, alias);
		}
	}
	/**
	 * Change the package state to loaded
	 */
	it->second->setLoaded();
}

/**
 * Copy user-defined function in a scope to another supplied alias
 */
void PackageManager::copyScopeToAlias(Scope* scope, const std::string& alias) {
	SymbolMap& symbols = scope->getSymbols();
	SymbolMap::const_iterator it2(symbols.begin()), end2(symbols.end());

	while (it2 != end2) {
		Symbol* sym = it2->second;

		if (sym->isValue()) {
			Value* val = sym->getValue();

			if (val->isCallable()) {
				CallableValue* fvalue = static_cast<CallableValue*>(val);

				if (fvalue->getFunction()->isUserDefined()) {
					scope->pushValue(CSTRING(alias + val->getName()->str()),
						fvalue);
					fvalue->addRef();
				}
			}
		}
		++it2;
	}
}

/**
 * Removes the packages and its modules
 */
void PackageManager::shutdown() {
	PackageMap::const_iterator it = m_packages.begin(), end = m_packages.end();

	while (it != end) {
		ModuleMap& modules = it->second->getModules();
		ModuleMap::const_iterator it_module = modules.begin(), end_module = modules.end();

		/**
		 * Deletes the module entries
		 */
		while (it_module != end_module) {
			delete it_module->second;
			++it_module;
		}
		/**
		 * Deletes the package
		 */
		delete it->second;
		++it;
	}
}

} // clever
<commit_msg>- Simplified code and added check for non-existent module<commit_after>/**
 * Clever programming language
 * Copyright (c) 2011-2012 Clever Team
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <iostream>
#include "compiler/pkgmanager.h"
#include "compiler/value.h"
#include "compiler/cstring.h"
#include "modules/std/std_pkg.h"
#include "modules/web/web_pkg.h"

namespace clever {

/**
 * Loads native packages
 */
void PackageManager::init() {
	addPackage(CSTRING("std"), new packages::Std());
	addPackage(CSTRING("web"), new packages::Web());
}

/**
 * Load an entire package
 */
void PackageManager::loadPackage(Scope* scope, const CString* const package) {
	PackageMap::const_iterator it = m_packages.find(package);

	if (it == m_packages.end()) {
		std::cerr << "package '" << *package << "' not found" << std::endl;
		return;
	}

	/**
	 * Check if the package already has been loaded
	 */
	if (it->second->isLoaded()) {
		return;
	}
	/**
	 * Initializes the package
	 */
	it->second->init();

	ModuleMap& modules = it->second->getModules();
	ModuleMap::const_iterator mit = modules.begin(), end = modules.end();

	while (mit != end) {
		loadModule(scope, package, mit->second, NULL);
		++mit;
	}
	/**
	 * Sets the package state to fully loaded
	 */
	it->second->setFullyLoaded();
}

/**
 * Loads an specific module
 */
void PackageManager::loadModule(Scope* scope, const CString* const package,
	Module* const module, const CString* const alias) {
	/**
	 * Checks if the module already has been loaded
	 */
	if (module->isLoaded()) {
		return;
	}
	/**
	 * Initializes the module
	 */
	module->init();

	const std::string prefix_name = alias ?
		alias->str() + "::" : package->str() + "." + module->getName() + "::";

	FunctionMap& funcs = module->getFunctions();
	FunctionMap::const_iterator it = funcs.begin(), end = funcs.end();

	/**
	 * Inserts the function into the symbol table
	 */
	while (it != end) {
		CallableValue* fvalue = new CallableValue(CSTRING(it->first));

		fvalue->setHandler(it->second);

		scope->pushValue(CSTRING(prefix_name + *fvalue->getName()), fvalue);
		scope->pushValue(fvalue->getName(), fvalue);
		fvalue->addRef();
		++it;
	}

	ClassMap& classes = module->getClassTable();
	ClassMap::iterator itc = classes.begin(), endc = classes.end();

	/**
	 * Inserts all classes into the symbol table
	 */
	while (itc != endc) {
		g_scope.pushType(CSTRING(prefix_name + *itc->first), itc->second);
		g_scope.pushType(itc->first, itc->second);
		itc->second->addRef();

		itc->second->init();
		++itc;
	}

	ConstMap& constants = module->getConstants();
	ConstMap::iterator itcs = constants.begin(), endcs = constants.end();

	/**
	 * Inserts all constants into the symbol table
	 */
	while (itcs != endcs) {
		g_scope.pushValue(CSTRING(prefix_name + itcs->first->str()), itcs->second);
		++itcs;
	}

	/**
	 * Sets the module state to loaded
	 */
	module->setLoaded();
}

/**
 * Loads an specific module package by supplying the package and module names
 */
void PackageManager::loadModule(Scope* scope, const CString* const package,
	const CString* const module, const CString* const alias) {
	PackageMap::const_iterator it = m_packages.find(package);

	if (it == m_packages.end()) {
		return;
	}

	/**
	 * Checks if the package is unloaded, in this case initialize it
	 */
	if (it->second->isUnloaded()) {
		it->second->init();
	}
	/**
	 * Check if the package already has been fully loaded
	 */
	if (!it->second->isFullyLoaded()) {
		ModuleMap& modules = it->second->getModules();
		ModuleMap::const_iterator it_mod = modules.find(module);

		if (it_mod == modules.end()) {
			std::cerr << "module '" << *module << "' not found" << std::endl;
			return;
		}

		/**
		 * Loads the module if it has been found
		 */
		loadModule(scope, package, it_mod->second, alias);
	}
	/**
	 * Change the package state to loaded
	 */
	it->second->setLoaded();
}

/**
 * Copy user-defined function in a scope to another supplied alias
 */
void PackageManager::copyScopeToAlias(Scope* scope, const std::string& alias) {
	SymbolMap& symbols = scope->getSymbols();
	SymbolMap::const_iterator it2(symbols.begin()), end2(symbols.end());

	while (it2 != end2) {
		Symbol* sym = it2->second;

		if (sym->isValue()) {
			Value* val = sym->getValue();

			if (val->isCallable()) {
				CallableValue* fvalue = static_cast<CallableValue*>(val);

				if (fvalue->getFunction()->isUserDefined()) {
					scope->pushValue(CSTRING(alias + val->getName()->str()),
						fvalue);
					fvalue->addRef();
				}
			}
		}
		++it2;
	}
}

/**
 * Removes the packages and its modules
 */
void PackageManager::shutdown() {
	PackageMap::const_iterator it = m_packages.begin(), end = m_packages.end();

	while (it != end) {
		ModuleMap& modules = it->second->getModules();
		ModuleMap::const_iterator it_module = modules.begin(), end_module = modules.end();

		/**
		 * Deletes the module entries
		 */
		while (it_module != end_module) {
			delete it_module->second;
			++it_module;
		}
		/**
		 * Deletes the package
		 */
		delete it->second;
		++it;
	}
}

} // clever
<|endoftext|>


<commit_before>#ifndef __NODE_OSRM_QUERY_H__
#define __NODE_OSRM_QUERY_H__

// v8
#include <v8.h>

// node
#include <node.h>
#include <node_version.h>
#include <node_object_wrap.h>

#include <osrm/OSRM.h>

using namespace v8;

namespace node_osrm {

typedef boost::shared_ptr<RouteParameters> route_parameters_ptr;

class Query: public node::ObjectWrap {
public:
    static Persistent<FunctionTemplate> constructor;
    static void Initialize(Handle<Object> target);
    static Handle<Value> New(Arguments const& args);
    Query();
    inline route_parameters_ptr get() { return this_; }
    void _ref() { Ref(); }
    void _unref() { Unref(); }
private:
    ~Query();
    route_parameters_ptr this_;
};

Persistent<FunctionTemplate> Query::constructor;

void Query::Initialize(Handle<Object> target) {
    HandleScope scope;
    constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Query::New));
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(String::NewSymbol("Query"));
    target->Set(String::NewSymbol("Query"),constructor->GetFunction());
}

Query::Query()
  : ObjectWrap(),
    this_(boost::make_shared<RouteParameters>()) { }

Query::~Query() { }

Handle<Value> Query::New(Arguments const& args)
{
    HandleScope scope;
    if (!args.IsConstructCall()) {
        return ThrowException(Exception::Error(String::New("Cannot call constructor as function, you need to use 'new' keyword")));
    }
    try {
        if (args.Length() != 1) {
            return ThrowException(Exception::TypeError(String::New("please provide an object of options for the first argument")));
        }
        if (!args[0]->IsObject()) {
            return ThrowException(Exception::TypeError(String::New("first argument must be an object")));
        }
        Local<Object> obj = args[0]->ToObject();
        if (obj->IsNull() || obj->IsUndefined()) {
            return ThrowException(Exception::TypeError(String::New("first arg must be an object")));
        }
        if (!obj->Has(String::NewSymbol("coordinates"))) {
            return ThrowException(Exception::TypeError(String::New("must provide a coordinates property")));
        }
        Local<Value> coordinates = obj->Get(String::New("coordinates"));
        if (!coordinates->IsArray()) {
            return ThrowException(Exception::TypeError(String::New("coordinates must be an array of (lat/long) pairs")));
        }
        Local<Array> coordinates_array = Local<Array>::Cast(coordinates);
        if (coordinates_array->Length() < 2) {
            return ThrowException(Exception::TypeError(String::New("at least two coordinates must be provided")));
        }

        Query* q = new Query();
        q->this_->zoomLevel = 18; //no generalization
        q->this_->printInstructions = false; //turn by turn instructions
        q->this_->alternateRoute = true; //get an alternate route, too
        q->this_->geometry = true; //retrieve geometry of route
        q->this_->compression = true; //polyline encoding
        q->this_->checkSum = UINT_MAX; //see wiki
        q->this_->service = "viaroute"; //that's routing
        q->this_->outputFormat = "json";
        q->this_->jsonpParameter = ""; //set for jsonp wrapping
        q->this_->language = ""; //unused atm

        if (obj->Has(String::NewSymbol("alternateRoute"))) {
            q->this_->alternateRoute = obj->Get(String::New("alternateRoute"))->BooleanValue();
        }

        if (obj->Has(String::NewSymbol("checksum"))) {
            q->this_->checkSum = static_cast<unsigned>(obj->Get(String::New("checksum"))->Uint32Value());
        }

        if (obj->Has(String::NewSymbol("zoomLevel"))) {
            q->this_->zoomLevel = static_cast<short>(obj->Get(String::New("zoomLevel"))->Int32Value());
        }

        if (obj->Has(String::NewSymbol("printInstructions"))) {
            q->this_->printInstructions = obj->Get(String::New("printInstructions"))->BooleanValue();
        }

        if (obj->Has(String::NewSymbol("jsonpParameter"))) {
            q->this_->jsonpParameter = *v8::String::Utf8Value(obj->Get(String::New("jsonpParameter")));
        }

        if (obj->Has(String::NewSymbol("hints"))) {
            Local<Value> hints = obj->Get(String::New("hints"));
            if (!hints->IsArray()) {
                return ThrowException(Exception::TypeError(String::New("hints must be an array of strings/null")));
            }
            Local<Array> hints_array = Local<Array>::Cast(hints);
            for (uint32_t i = 0; i < hints_array->Length(); ++i) {
                Local<Value> hint = hints_array->Get(i);
                if (hint->IsString()) {
                    q->this_->hints.push_back(*v8::String::Utf8Value(hint));
                } else if(hint->IsNull()){
                    q->this_->hints.push_back("");
                }else{
                    return ThrowException(Exception::TypeError(String::New("hint must be null or string")));
                }
            }
        }

        for (uint32_t i = 0; i < coordinates_array->Length(); ++i) {
            Local<Value> coordinate = coordinates_array->Get(i);
            if (!coordinate->IsArray()) {
                return ThrowException(Exception::TypeError(String::New("coordinates must be an array of (lat/long) pairs")));
            }
            Local<Array> coordinate_array = Local<Array>::Cast(coordinate);
            if (coordinate_array->Length() != 2) {
                return ThrowException(Exception::TypeError(String::New("coordinates must be an array of (lat/long) pairs")));
            }
            q->this_->coordinates.push_back(
                FixedPointCoordinate(coordinate_array->Get(0)->NumberValue()*COORDINATE_PRECISION,
                                     coordinate_array->Get(1)->NumberValue()*COORDINATE_PRECISION));
        }

        q->Wrap(args.This());
        return args.This();
    } catch (std::exception const& ex) {
        return ThrowException(Exception::TypeError(String::New(ex.what())));
    }
    return Undefined();
}

} // namespace node_osrm

#endif // __NODE_OSRM_QUERY_H__
<commit_msg>use String::New instead of String::NewSymbol<commit_after>#ifndef __NODE_OSRM_QUERY_H__
#define __NODE_OSRM_QUERY_H__

// v8
#include <v8.h>

// node
#include <node.h>
#include <node_version.h>
#include <node_object_wrap.h>

#include <osrm/OSRM.h>

using namespace v8;

namespace node_osrm {

typedef boost::shared_ptr<RouteParameters> route_parameters_ptr;

class Query: public node::ObjectWrap {
public:
    static Persistent<FunctionTemplate> constructor;
    static void Initialize(Handle<Object> target);
    static Handle<Value> New(Arguments const& args);
    Query();
    inline route_parameters_ptr get() { return this_; }
    void _ref() { Ref(); }
    void _unref() { Unref(); }
private:
    ~Query();
    route_parameters_ptr this_;
};

Persistent<FunctionTemplate> Query::constructor;

void Query::Initialize(Handle<Object> target) {
    HandleScope scope;
    constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Query::New));
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(String::New("Query"));
    target->Set(String::New("Query"),constructor->GetFunction());
}

Query::Query()
  : ObjectWrap(),
    this_(boost::make_shared<RouteParameters>()) { }

Query::~Query() { }

Handle<Value> Query::New(Arguments const& args)
{
    HandleScope scope;
    if (!args.IsConstructCall()) {
        return ThrowException(Exception::Error(String::New("Cannot call constructor as function, you need to use 'new' keyword")));
    }
    try {
        if (args.Length() != 1) {
            return ThrowException(Exception::TypeError(String::New("please provide an object of options for the first argument")));
        }
        if (!args[0]->IsObject()) {
            return ThrowException(Exception::TypeError(String::New("first argument must be an object")));
        }
        Local<Object> obj = args[0]->ToObject();
        if (obj->IsNull() || obj->IsUndefined()) {
            return ThrowException(Exception::TypeError(String::New("first arg must be an object")));
        }
        if (!obj->Has(String::New("coordinates"))) {
            return ThrowException(Exception::TypeError(String::New("must provide a coordinates property")));
        }
        Local<Value> coordinates = obj->Get(String::New("coordinates"));
        if (!coordinates->IsArray()) {
            return ThrowException(Exception::TypeError(String::New("coordinates must be an array of (lat/long) pairs")));
        }
        Local<Array> coordinates_array = Local<Array>::Cast(coordinates);
        if (coordinates_array->Length() < 2) {
            return ThrowException(Exception::TypeError(String::New("at least two coordinates must be provided")));
        }

        Query* q = new Query();
        q->this_->zoomLevel = 18; //no generalization
        q->this_->printInstructions = false; //turn by turn instructions
        q->this_->alternateRoute = true; //get an alternate route, too
        q->this_->geometry = true; //retrieve geometry of route
        q->this_->compression = true; //polyline encoding
        q->this_->checkSum = UINT_MAX; //see wiki
        q->this_->service = "viaroute"; //that's routing
        q->this_->outputFormat = "json";
        q->this_->jsonpParameter = ""; //set for jsonp wrapping
        q->this_->language = ""; //unused atm

        if (obj->Has(String::New("alternateRoute"))) {
            q->this_->alternateRoute = obj->Get(String::New("alternateRoute"))->BooleanValue();
        }

        if (obj->Has(String::New("checksum"))) {
            q->this_->checkSum = static_cast<unsigned>(obj->Get(String::New("checksum"))->Uint32Value());
        }

        if (obj->Has(String::New("zoomLevel"))) {
            q->this_->zoomLevel = static_cast<short>(obj->Get(String::New("zoomLevel"))->Int32Value());
        }

        if (obj->Has(String::New("printInstructions"))) {
            q->this_->printInstructions = obj->Get(String::New("printInstructions"))->BooleanValue();
        }

        if (obj->Has(String::New("jsonpParameter"))) {
            q->this_->jsonpParameter = *v8::String::Utf8Value(obj->Get(String::New("jsonpParameter")));
        }

        if (obj->Has(String::New("hints"))) {
            Local<Value> hints = obj->Get(String::New("hints"));
            if (!hints->IsArray()) {
                return ThrowException(Exception::TypeError(String::New("hints must be an array of strings/null")));
            }
            Local<Array> hints_array = Local<Array>::Cast(hints);
            for (uint32_t i = 0; i < hints_array->Length(); ++i) {
                Local<Value> hint = hints_array->Get(i);
                if (hint->IsString()) {
                    q->this_->hints.push_back(*v8::String::Utf8Value(hint));
                } else if(hint->IsNull()){
                    q->this_->hints.push_back("");
                }else{
                    return ThrowException(Exception::TypeError(String::New("hint must be null or string")));
                }
            }
        }

        for (uint32_t i = 0; i < coordinates_array->Length(); ++i) {
            Local<Value> coordinate = coordinates_array->Get(i);
            if (!coordinate->IsArray()) {
                return ThrowException(Exception::TypeError(String::New("coordinates must be an array of (lat/long) pairs")));
            }
            Local<Array> coordinate_array = Local<Array>::Cast(coordinate);
            if (coordinate_array->Length() != 2) {
                return ThrowException(Exception::TypeError(String::New("coordinates must be an array of (lat/long) pairs")));
            }
            q->this_->coordinates.push_back(
                FixedPointCoordinate(coordinate_array->Get(0)->NumberValue()*COORDINATE_PRECISION,
                                     coordinate_array->Get(1)->NumberValue()*COORDINATE_PRECISION));
        }

        q->Wrap(args.This());
        return args.This();
    } catch (std::exception const& ex) {
        return ThrowException(Exception::TypeError(String::New(ex.what())));
    }
    return Undefined();
}

} // namespace node_osrm

#endif // __NODE_OSRM_QUERY_H__
<|endoftext|>


<commit_before>// Copyright (C) 2009, 2010 by Thomas Moulard, CNRS.
//
// This file is part of the hppWalkFootPlanner.
//
// hppWalkFootPlanner 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 3 of the License, or
// (at your option) any later version.
//
// hppWalkFootPlanner 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 hppWalkFootPlanner.  If not, see <http://www.gnu.org/licenses/>.

#include <cassert>
#include <iomanip>
#include <ostream>

#include "hpp/util/indent.hh"

namespace hpp
{
  inline long int& indent (std::ostream& o)
  {
    // The slot to store the current indentation level.
    static const long int indent_index = std::ios::xalloc ();
    return o.iword (indent_index);
  }

  std::ostream& incindent (std::ostream& o)
  {
    indent (o) += 2;
    return o;
  }

  std::ostream& decindent (std::ostream& o)
  {
    assert (indent (o));
    indent (o) -= 2;
    return o;
  }

  std::ostream& resetindent (std::ostream& o)
  {
    indent (o) = 0;
    return o;
  }

  std::ostream& iendl (std::ostream& o)
  {
    o << std::endl;
    // Be sure to be able to restore the stream flags.
    char fill = o.fill (' ');
    return o << std::setw (indent (o))
	     << ""
	     << std::setfill (fill);
  }

  std::ostream& incendl (std::ostream& o)
  {
    return o << incindent << iendl;
  }

  std::ostream& decendl (std::ostream& o)
  {
    return o << decindent << iendl;
  }

} // end of namespace hpp.
<commit_msg>Fix indent return type which triggered an error on amd64.<commit_after>// Copyright (C) 2009, 2010 by Thomas Moulard, CNRS.
//
// This file is part of the hppWalkFootPlanner.
//
// hppWalkFootPlanner 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 3 of the License, or
// (at your option) any later version.
//
// hppWalkFootPlanner 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 hppWalkFootPlanner.  If not, see <http://www.gnu.org/licenses/>.

#include <cassert>
#include <iomanip>
#include <ostream>

#include "hpp/util/indent.hh"

namespace hpp
{
  inline int& indent (std::ostream& o)
  {
    // The slot to store the current indentation level.
    static const int indent_index = std::ios::xalloc ();
    return o.iword (indent_index);
  }

  std::ostream& incindent (std::ostream& o)
  {
    indent (o) += 2;
    return o;
  }

  std::ostream& decindent (std::ostream& o)
  {
    assert (indent (o));
    indent (o) -= 2;
    return o;
  }

  std::ostream& resetindent (std::ostream& o)
  {
    indent (o) = 0;
    return o;
  }

  std::ostream& iendl (std::ostream& o)
  {
    o << std::endl;
    // Be sure to be able to restore the stream flags.
    char fill = o.fill (' ');
    return o << std::setw (indent (o))
	     << ""
	     << std::setfill (fill);
  }

  std::ostream& incendl (std::ostream& o)
  {
    return o << incindent << iendl;
  }

  std::ostream& decendl (std::ostream& o)
  {
    return o << decindent << iendl;
  }

} // end of namespace hpp.
<|endoftext|>


<commit_before>#include "space.h"
#include "display.h"
#include "window.h"
#include "tree.h"
#include "border.h"
#include "keys.h"
#include "notifications.h"
#include "helpers.h"

extern kwm_mach KWMMach;
extern kwm_tiling KWMTiling;
extern kwm_screen KWMScreen;
extern kwm_focus KWMFocus;
extern kwm_toggles KWMToggles;
extern kwm_mode KWMMode;
extern kwm_thread KWMThread;
extern kwm_border FocusedBorder;
extern kwm_border MarkedBorder;

void GetTagForMonocleSpace(space_info *Space, std::string &Tag)
{
    tree_node *Node = Space->RootNode;
    bool FoundFocusedWindow = false;
    int FocusedIndex = 0;
    int NumberOfWindows = 0;

    if(Node && KWMFocus.Window)
    {
        link_node *Link = Node->List;
        while(Link)
        {
            if(!FoundFocusedWindow)
                ++FocusedIndex;

            if(Link->WindowID == KWMFocus.Window->WID)
                FoundFocusedWindow = true;

            ++NumberOfWindows;
            Link = Link->Next;
        }
    }

    if(FoundFocusedWindow)
        Tag = "[" + std::to_string(FocusedIndex) + "/" + std::to_string(NumberOfWindows) + "]";
    else
        Tag = "[" + std::to_string(NumberOfWindows) + "]";
}

void GetTagForCurrentSpace(std::string &Tag)
{
    if(IsSpaceInitializedForScreen(KWMScreen.Current))
    {
        space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        if(Space->Settings.Mode == SpaceModeBSP)
            Tag = "[bsp]";
        else if(Space->Settings.Mode == SpaceModeFloating)
            Tag = "[float]";
        else if(Space->Settings.Mode == SpaceModeMonocle)
            GetTagForMonocleSpace(Space, Tag);
    }
    else
    {
        if(KWMMode.Space == SpaceModeBSP)
            Tag = "[bsp]";
        else if(KWMMode.Space == SpaceModeFloating)
            Tag = "[float]";
        else if(KWMMode.Space == SpaceModeMonocle)
            Tag = "[monocle]";
    }
}

void MoveWindowToSpace(std::string SpaceID)
{
    if(!KWMFocus.Window)
        return;

    CGEventTapEnable(KWMMach.EventTap, false);
    DestroyApplicationNotifications();

    window_info *Window = KWMFocus.Window;
    bool WasWindowFloating = IsFocusedWindowFloating();
    if(!WasWindowFloating)
    {
        screen_info *ScreenOfWindow = GetDisplayOfWindow(Window);
        space_info *SpaceOfWindow = GetActiveSpaceOfScreen(ScreenOfWindow);
        if(SpaceOfWindow->Settings.Mode == SpaceModeBSP)
            RemoveWindowFromBSPTree(ScreenOfWindow, Window->WID, false, false);
        else if(SpaceOfWindow->Settings.Mode == SpaceModeMonocle)
            RemoveWindowFromMonocleTree(ScreenOfWindow, Window->WID, false);
    }

    CGPoint CursorPos = GetCursorPos();
    CGPoint WindowPos = GetWindowPos(Window);
    CGPoint ClickPos = CGPointMake(WindowPos.x + 75, WindowPos.y + 7);

    CGEventRef ClickEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, ClickPos, kCGMouseButtonLeft);
    CGEventSetFlags(ClickEvent, 0);
    CGEventPost(kCGHIDEventTap, ClickEvent);
    CFRelease(ClickEvent);
    usleep(150000);

    KwmEmitKeystroke(KWMHotkeys.SpacesKey, SpaceID);
    usleep(300000);

    KWMScreen.PrevSpace = KWMScreen.Current->ActiveSpace;
    KWMScreen.Current->ActiveSpace = GetActiveSpaceOfDisplay(KWMScreen.Current);
    ShouldActiveSpaceBeManaged();
    UpdateActiveWindowList(KWMScreen.Current);

    ClickEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, ClickPos, kCGMouseButtonLeft);
    CGEventSetFlags(ClickEvent, 0);
    CGEventPost(kCGHIDEventTap, ClickEvent);
    CFRelease(ClickEvent);
    usleep(150000);

    if(!WasWindowFloating)
    {
        screen_info *ScreenOfWindow = GetDisplayOfWindow(Window);
        space_info *SpaceOfWindow = GetActiveSpaceOfScreen(ScreenOfWindow);
        if(SpaceOfWindow->Settings.Mode == SpaceModeBSP)
            AddWindowToBSPTree(ScreenOfWindow, Window->WID);
        else if(SpaceOfWindow->Settings.Mode == SpaceModeMonocle)
            AddWindowToMonocleTree(ScreenOfWindow, Window->WID);
    }

    CGWarpMouseCursorPosition(CursorPos);
    CreateApplicationNotifications();
    CGEventTapEnable(KWMMach.EventTap, true);
}

bool IsActiveSpaceFloating()
{
    return IsSpaceFloating(KWMScreen.Current->ActiveSpace);
}

bool IsSpaceFloating(int SpaceID)
{
    bool Result = false;

    if(IsSpaceInitializedForScreen(KWMScreen.Current))
    {
        std::map<int, space_info>::iterator It = KWMScreen.Current->Space.find(SpaceID);
        if(It != KWMScreen.Current->Space.end())
            Result = KWMScreen.Current->Space[SpaceID].Settings.Mode == SpaceModeFloating;
    }

    return Result;
}

space_info *GetActiveSpaceOfScreen(screen_info *Screen)
{
    space_info *Space = NULL;
    std::map<int, space_info>::iterator It = Screen->Space.find(Screen->ActiveSpace);

    if(It == Screen->Space.end())
    {
        space_info Clear = {{{0}}};
        Screen->ActiveSpace = GetActiveSpaceOfDisplay(Screen);
        Screen->Space[Screen->ActiveSpace] = Clear;
        Space = &Screen->Space[Screen->ActiveSpace];
    }
    else
    {
        Space = &Screen->Space[Screen->ActiveSpace];
    }

    return Space;
}

bool IsSpaceInitializedForScreen(screen_info *Screen)
{
    if(!Screen)
        return false;

    std::map<int, space_info>::iterator It = Screen->Space.find(Screen->ActiveSpace);
    if(It == Screen->Space.end())
        return false;
    else
        return It->second.Initialized;
}

bool DoesSpaceExistInMapOfScreen(screen_info *Screen)
{
    if(!Screen)
        return false;

    std::map<int, space_info>::iterator It = Screen->Space.find(Screen->ActiveSpace);
    if(It == Screen->Space.end())
        return false;
    else
        return It->second.RootNode != NULL && It->second.Initialized;
}

bool IsSpaceTransitionInProgress()
{
    if(KWMScreen.Transitioning)
        return true;

    bool Result = false;
    std::map<unsigned int, screen_info>::iterator It;
    for(It = KWMTiling.DisplayMap.begin(); It != KWMTiling.DisplayMap.end(); ++It)
    {
        screen_info *Screen = &It->second;
        if(!Screen->Identifier)
            Screen->Identifier = GetDisplayIdentifier(Screen);

        Result = Result || CGSManagedDisplayIsAnimating(CGSDefaultConnection, Screen->Identifier);
    }

    if(Result)
    {
        DEBUG("IsSpaceTransitionInProgress() Space transition detected")
        KWMScreen.Transitioning = true;
        ClearFocusedWindow();
        ClearMarkedWindow();
    }

    return Result;
}

bool IsActiveSpaceManaged()
{
    space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
    return Space->Managed;
}

void ShouldActiveSpaceBeManaged()
{
    space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
    Space->Managed = CGSSpaceGetType(CGSDefaultConnection, KWMScreen.Current->ActiveSpace) == CGSSpaceTypeUser;
}

void FloatFocusedSpace()
{
    if(KWMToggles.EnableTilingMode &&
       !IsSpaceTransitionInProgress() &&
       IsActiveSpaceManaged())
    {
        space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        if(Space->Settings.Mode == SpaceModeFloating)
            return;

        DestroyNodeTree(Space->RootNode);
        Space->RootNode = NULL;

        Space->Settings.Mode = SpaceModeFloating;
        Space->Initialized = true;
        ClearFocusedWindow();
    }
}

void TileFocusedSpace(space_tiling_option Mode)
{
    if(KWMToggles.EnableTilingMode &&
       !IsSpaceTransitionInProgress() &&
       IsActiveSpaceManaged() &&
       FilterWindowList(KWMScreen.Current))
    {
        space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        if(Space->Settings.Mode == Mode)
            return;

        DestroyNodeTree(Space->RootNode);
        Space->RootNode = NULL;

        Space->Settings.Mode = Mode;
        std::vector<window_info*> WindowsOnDisplay = GetAllWindowsOnDisplay(KWMScreen.Current->ID);
        CreateWindowNodeTree(KWMScreen.Current, &WindowsOnDisplay);
    }
}

void UpdateActiveSpace()
{
    pthread_mutex_lock(&KWMThread.Lock);
    Assert(KWMScreen.Current)

    KWMScreen.PrevSpace = KWMScreen.Current->ActiveSpace;
    KWMScreen.Current->ActiveSpace = GetActiveSpaceOfDisplay(KWMScreen.Current);
    ShouldActiveSpaceBeManaged();

    space_info *Space = NULL;
    if(KWMScreen.PrevSpace != KWMScreen.Current->ActiveSpace)
    {
        DEBUG("UpdateActiveSpace() Space transition ended " << KWMScreen.PrevSpace << " -> " << KWMScreen.Current->ActiveSpace)

        Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        UpdateActiveWindowList(KWMScreen.Current);
        if(Space->FocusedWindowID != 0)
        {
            FocusWindowByID(Space->FocusedWindowID);
            MoveCursorToCenterOfFocusedWindow();
        }
    }
    else
    {
        std::map<unsigned int, screen_info>::iterator It;
        for(It = KWMTiling.DisplayMap.begin(); It != KWMTiling.DisplayMap.end(); ++It)
        {
            screen_info *Screen = &It->second;
            if(Screen->ID == KWMScreen.Current->ID)
                continue;

            int ScreenCurrentSpace = Screen->ActiveSpace;
            int ScreenNewSpace = GetActiveSpaceOfDisplay(Screen);
            if(ScreenCurrentSpace != ScreenNewSpace)
            {
                DEBUG("space changed on monitor: " << Screen->ID)

                Screen->ActiveSpace = ScreenNewSpace;
                KWMScreen.PrevSpace = KWMScreen.Current->ActiveSpace;
                KWMScreen.Current = Screen;
                ShouldActiveSpaceBeManaged();
                Space = GetActiveSpaceOfScreen(Screen);
                UpdateActiveWindowList(Screen);
                FilterWindowList(Screen);
                break;
            }
        }
    }

    if(!Space || !Space->Managed || Space->Settings.Mode == SpaceModeFloating)
    {
        ClearFocusedWindow();
        ClearMarkedWindow();
    }

    KWMScreen.Transitioning = false;
    pthread_mutex_unlock(&KWMThread.Lock);
}

space_settings *GetSpaceSettingsForDesktopID(int ScreenID, int DesktopID)
{
    space_identifier Lookup = { ScreenID, DesktopID };
    std::map<space_identifier, space_settings>::iterator It = KWMTiling.SpaceSettings.find(Lookup);
    if(It != KWMTiling.SpaceSettings.end())
        return &It->second;
    else
        return NULL;
}
<commit_msg>Destroy notifications for unmanaged spaces<commit_after>#include "space.h"
#include "display.h"
#include "window.h"
#include "tree.h"
#include "border.h"
#include "keys.h"
#include "notifications.h"
#include "helpers.h"

extern kwm_mach KWMMach;
extern kwm_tiling KWMTiling;
extern kwm_screen KWMScreen;
extern kwm_focus KWMFocus;
extern kwm_toggles KWMToggles;
extern kwm_mode KWMMode;
extern kwm_thread KWMThread;
extern kwm_border FocusedBorder;
extern kwm_border MarkedBorder;

void GetTagForMonocleSpace(space_info *Space, std::string &Tag)
{
    tree_node *Node = Space->RootNode;
    bool FoundFocusedWindow = false;
    int FocusedIndex = 0;
    int NumberOfWindows = 0;

    if(Node && KWMFocus.Window)
    {
        link_node *Link = Node->List;
        while(Link)
        {
            if(!FoundFocusedWindow)
                ++FocusedIndex;

            if(Link->WindowID == KWMFocus.Window->WID)
                FoundFocusedWindow = true;

            ++NumberOfWindows;
            Link = Link->Next;
        }
    }

    if(FoundFocusedWindow)
        Tag = "[" + std::to_string(FocusedIndex) + "/" + std::to_string(NumberOfWindows) + "]";
    else
        Tag = "[" + std::to_string(NumberOfWindows) + "]";
}

void GetTagForCurrentSpace(std::string &Tag)
{
    if(IsSpaceInitializedForScreen(KWMScreen.Current))
    {
        space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        if(Space->Settings.Mode == SpaceModeBSP)
            Tag = "[bsp]";
        else if(Space->Settings.Mode == SpaceModeFloating)
            Tag = "[float]";
        else if(Space->Settings.Mode == SpaceModeMonocle)
            GetTagForMonocleSpace(Space, Tag);
    }
    else
    {
        if(KWMMode.Space == SpaceModeBSP)
            Tag = "[bsp]";
        else if(KWMMode.Space == SpaceModeFloating)
            Tag = "[float]";
        else if(KWMMode.Space == SpaceModeMonocle)
            Tag = "[monocle]";
    }
}

void MoveWindowToSpace(std::string SpaceID)
{
    if(!KWMFocus.Window)
        return;

    CGEventTapEnable(KWMMach.EventTap, false);
    DestroyApplicationNotifications();

    window_info *Window = KWMFocus.Window;
    bool WasWindowFloating = IsFocusedWindowFloating();
    if(!WasWindowFloating)
    {
        screen_info *ScreenOfWindow = GetDisplayOfWindow(Window);
        space_info *SpaceOfWindow = GetActiveSpaceOfScreen(ScreenOfWindow);
        if(SpaceOfWindow->Settings.Mode == SpaceModeBSP)
            RemoveWindowFromBSPTree(ScreenOfWindow, Window->WID, false, false);
        else if(SpaceOfWindow->Settings.Mode == SpaceModeMonocle)
            RemoveWindowFromMonocleTree(ScreenOfWindow, Window->WID, false);
    }

    CGPoint CursorPos = GetCursorPos();
    CGPoint WindowPos = GetWindowPos(Window);
    CGPoint ClickPos = CGPointMake(WindowPos.x + 75, WindowPos.y + 7);

    CGEventRef ClickEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, ClickPos, kCGMouseButtonLeft);
    CGEventSetFlags(ClickEvent, 0);
    CGEventPost(kCGHIDEventTap, ClickEvent);
    CFRelease(ClickEvent);
    usleep(150000);

    KwmEmitKeystroke(KWMHotkeys.SpacesKey, SpaceID);
    usleep(300000);

    KWMScreen.PrevSpace = KWMScreen.Current->ActiveSpace;
    KWMScreen.Current->ActiveSpace = GetActiveSpaceOfDisplay(KWMScreen.Current);
    ShouldActiveSpaceBeManaged();
    UpdateActiveWindowList(KWMScreen.Current);

    ClickEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, ClickPos, kCGMouseButtonLeft);
    CGEventSetFlags(ClickEvent, 0);
    CGEventPost(kCGHIDEventTap, ClickEvent);
    CFRelease(ClickEvent);
    usleep(150000);

    if(!WasWindowFloating)
    {
        screen_info *ScreenOfWindow = GetDisplayOfWindow(Window);
        space_info *SpaceOfWindow = GetActiveSpaceOfScreen(ScreenOfWindow);
        if(SpaceOfWindow->Settings.Mode == SpaceModeBSP)
            AddWindowToBSPTree(ScreenOfWindow, Window->WID);
        else if(SpaceOfWindow->Settings.Mode == SpaceModeMonocle)
            AddWindowToMonocleTree(ScreenOfWindow, Window->WID);
    }

    CGWarpMouseCursorPosition(CursorPos);
    CreateApplicationNotifications();
    CGEventTapEnable(KWMMach.EventTap, true);
}

bool IsActiveSpaceFloating()
{
    return IsSpaceFloating(KWMScreen.Current->ActiveSpace);
}

bool IsSpaceFloating(int SpaceID)
{
    bool Result = false;

    if(IsSpaceInitializedForScreen(KWMScreen.Current))
    {
        std::map<int, space_info>::iterator It = KWMScreen.Current->Space.find(SpaceID);
        if(It != KWMScreen.Current->Space.end())
            Result = KWMScreen.Current->Space[SpaceID].Settings.Mode == SpaceModeFloating;
    }

    return Result;
}

space_info *GetActiveSpaceOfScreen(screen_info *Screen)
{
    space_info *Space = NULL;
    std::map<int, space_info>::iterator It = Screen->Space.find(Screen->ActiveSpace);

    if(It == Screen->Space.end())
    {
        space_info Clear = {{{0}}};
        Screen->ActiveSpace = GetActiveSpaceOfDisplay(Screen);
        Screen->Space[Screen->ActiveSpace] = Clear;
        Space = &Screen->Space[Screen->ActiveSpace];
    }
    else
    {
        Space = &Screen->Space[Screen->ActiveSpace];
    }

    return Space;
}

bool IsSpaceInitializedForScreen(screen_info *Screen)
{
    if(!Screen)
        return false;

    std::map<int, space_info>::iterator It = Screen->Space.find(Screen->ActiveSpace);
    if(It == Screen->Space.end())
        return false;
    else
        return It->second.Initialized;
}

bool DoesSpaceExistInMapOfScreen(screen_info *Screen)
{
    if(!Screen)
        return false;

    std::map<int, space_info>::iterator It = Screen->Space.find(Screen->ActiveSpace);
    if(It == Screen->Space.end())
        return false;
    else
        return It->second.RootNode != NULL && It->second.Initialized;
}

bool IsSpaceTransitionInProgress()
{
    if(KWMScreen.Transitioning)
        return true;

    bool Result = false;
    std::map<unsigned int, screen_info>::iterator It;
    for(It = KWMTiling.DisplayMap.begin(); It != KWMTiling.DisplayMap.end(); ++It)
    {
        screen_info *Screen = &It->second;
        if(!Screen->Identifier)
            Screen->Identifier = GetDisplayIdentifier(Screen);

        Result = Result || CGSManagedDisplayIsAnimating(CGSDefaultConnection, Screen->Identifier);
    }

    if(Result)
    {
        DEBUG("IsSpaceTransitionInProgress() Space transition detected")
        KWMScreen.Transitioning = true;
        ClearFocusedWindow();
        ClearMarkedWindow();
    }

    return Result;
}

bool IsActiveSpaceManaged()
{
    space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
    return Space->Managed;
}

void ShouldActiveSpaceBeManaged()
{
    space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
    Space->Managed = CGSSpaceGetType(CGSDefaultConnection, KWMScreen.Current->ActiveSpace) == CGSSpaceTypeUser;
}

void FloatFocusedSpace()
{
    if(KWMToggles.EnableTilingMode &&
       !IsSpaceTransitionInProgress() &&
       IsActiveSpaceManaged())
    {
        space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        if(Space->Settings.Mode == SpaceModeFloating)
            return;

        DestroyNodeTree(Space->RootNode);
        Space->RootNode = NULL;

        Space->Settings.Mode = SpaceModeFloating;
        Space->Initialized = true;
        ClearFocusedWindow();
    }
}

void TileFocusedSpace(space_tiling_option Mode)
{
    if(KWMToggles.EnableTilingMode &&
       !IsSpaceTransitionInProgress() &&
       IsActiveSpaceManaged() &&
       FilterWindowList(KWMScreen.Current))
    {
        space_info *Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        if(Space->Settings.Mode == Mode)
            return;

        DestroyNodeTree(Space->RootNode);
        Space->RootNode = NULL;

        Space->Settings.Mode = Mode;
        std::vector<window_info*> WindowsOnDisplay = GetAllWindowsOnDisplay(KWMScreen.Current->ID);
        CreateWindowNodeTree(KWMScreen.Current, &WindowsOnDisplay);
    }
}

void UpdateActiveSpace()
{
    pthread_mutex_lock(&KWMThread.Lock);
    Assert(KWMScreen.Current)

    KWMScreen.PrevSpace = KWMScreen.Current->ActiveSpace;
    KWMScreen.Current->ActiveSpace = GetActiveSpaceOfDisplay(KWMScreen.Current);
    ShouldActiveSpaceBeManaged();

    space_info *Space = NULL;
    if(KWMScreen.PrevSpace != KWMScreen.Current->ActiveSpace)
    {
        DEBUG("UpdateActiveSpace() Space transition ended " << KWMScreen.PrevSpace << " -> " << KWMScreen.Current->ActiveSpace)

        Space = GetActiveSpaceOfScreen(KWMScreen.Current);
        UpdateActiveWindowList(KWMScreen.Current);
        if(Space->FocusedWindowID != 0)
        {
            FocusWindowByID(Space->FocusedWindowID);
            MoveCursorToCenterOfFocusedWindow();
        }
    }
    else
    {
        std::map<unsigned int, screen_info>::iterator It;
        for(It = KWMTiling.DisplayMap.begin(); It != KWMTiling.DisplayMap.end(); ++It)
        {
            screen_info *Screen = &It->second;
            if(Screen->ID == KWMScreen.Current->ID)
                continue;

            int ScreenCurrentSpace = Screen->ActiveSpace;
            int ScreenNewSpace = GetActiveSpaceOfDisplay(Screen);
            if(ScreenCurrentSpace != ScreenNewSpace)
            {
                DEBUG("space changed on monitor: " << Screen->ID)

                Screen->ActiveSpace = ScreenNewSpace;
                KWMScreen.PrevSpace = KWMScreen.Current->ActiveSpace;
                KWMScreen.Current = Screen;
                ShouldActiveSpaceBeManaged();
                Space = GetActiveSpaceOfScreen(Screen);
                UpdateActiveWindowList(Screen);
                FilterWindowList(Screen);
                break;
            }
        }
    }

    if(!Space || !Space->Managed || Space->Settings.Mode == SpaceModeFloating)
    {
        ClearFocusedWindow();
        ClearMarkedWindow();
        DestroyApplicationNotifications();
    }

    KWMScreen.Transitioning = false;
    pthread_mutex_unlock(&KWMThread.Lock);
}

space_settings *GetSpaceSettingsForDesktopID(int ScreenID, int DesktopID)
{
    space_identifier Lookup = { ScreenID, DesktopID };
    std::map<space_identifier, space_settings>::iterator It = KWMTiling.SpaceSettings.find(Lookup);
    if(It != KWMTiling.SpaceSettings.end())
        return &It->second;
    else
        return NULL;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013, "Kira"
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 "precompiled_headers.hpp"

#include "redis.hpp"

#include "logging.hpp"
#include "startup_config.hpp"
#include <time.h>

pthread_mutex_t Redis::requestMutex = PTHREAD_MUTEX_INITIALIZER;
//pthread_mutex_t Redis::replyMutex = PTHREAD_MUTEX_INITIALIZER;
queue<RedisRequest*> Redis::requestQueue;
bool Redis::doRun = true;
struct ev_loop* Redis::redis_loop = 0;
ev_timer* Redis::redis_timer = 0;
redisContext* Redis::context = 0;
const string Redis::onlineUsersKey("users.online");
const string Redis::lastCheckinKey("chat.lastCheckin");

const __useconds_t Redis::REDIS_FAILURE_WAIT = 5000000; //5 seconds;

void Redis::connectToRedis() {
    DLOG(INFO) << "Connecting to redis.";
    if (context)
        redisFree(context);
    context = 0;

    static struct timeval contimeout;
    contimeout.tv_sec = 10;
    contimeout.tv_usec = 0;
    context = redisConnectWithTimeout(StartupConfig::getString("redishost").c_str(), static_cast<int> (StartupConfig::getDouble("redisport")), contimeout);
    if (context->err) {
        DLOG(INFO) << "Failed to connect to redis with error: " << context->errstr;
        redisFree(context);
        context = 0;
        return;
    }
    redisSetTimeout(context, contimeout);
    redisReply* reply = (redisReply*) redisCommand(context, "AUTH %s", StartupConfig::getString("redispass").c_str());
    if (reply == 0 || (reply->type != REDIS_REPLY_STATUS && reply->len != 2)) {
        DLOG(INFO) << "Failed to authenticate with the redis server.";
        if (reply)
            freeReplyObject(reply);
        reply = 0;
        redisFree(context);
        context = 0;
        return;
    }
    freeReplyObject(reply);
}

void Redis::processRequest(RedisRequest* req) {
    while (doRun && !context) {
        usleep(REDIS_FAILURE_WAIT);
        connectToRedis();
    }

    if (doRun && context) {
        const RedisMethod method = req->method;
        switch (method) {
            case REDIS_DEL:
            {
                redisReply* reply = (redisReply*) redisCommand(context, "DEL %s", req->key.c_str());
                freeReplyObject(reply);
                break;
            }
            case REDIS_SADD:
            case REDIS_SREM:
            case REDIS_LPUSH:
            case REDIS_LREM:
            case REDIS_SET:
            {
                const char* key = req->key.c_str();
                int size = req->values.size();
                for (int i = 0; i < size; ++i) {
                    switch (method) {
                        case REDIS_SADD:
                            redisAppendCommand(context, "SADD %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_SREM:
                            redisAppendCommand(context, "SREM %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_LPUSH:
                            redisAppendCommand(context, "LPUSH %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_LREM:
                            redisAppendCommand(context, "LREM %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_SET:
                            redisAppendCommand(context, "SET %s %s", key, req->values.front().c_str());
                            break;
                        default:
                            delete req;
                            return;
                    }
                    req->values.pop();
                }
                for (int i = 0; i < size; ++i) {
                    redisReply* reply = 0;
                    int ret = redisGetReply(context, (void**) &reply);
                    if (ret != REDIS_OK) {
                        DLOG(WARNING) << "A redis command failed. Restarting the redis system.";
                        if (reply)
                            freeReplyObject(reply);
                        redisFree(context);
                        context = 0;
                        delete req;
                        return;
                    }
                    if (reply)
                        freeReplyObject(reply);
                }
                break;
            }
            default:
                break;
        }
    }
    delete req;
}

void* Redis::runThread(void* param) {
    DLOG(INFO) << "Starting Redis thread.";
    redis_loop = ev_loop_new(EVFLAG_AUTO);
    redis_timer = new ev_timer;
    ev_timer_init(redis_timer, Redis::timeoutCallback, 0, 5.);
    ev_timer_start(redis_loop, redis_timer);

    ev_loop(redis_loop, 0);

    //Cleanup
    ev_timer_stop(redis_loop, redis_timer);
    delete redis_timer;
    redis_timer = 0;
    ev_loop_destroy(redis_loop);
    redis_loop = 0;
    DLOG(INFO) << "Redis thread exiting.";
    pthread_exit(NULL);
}

void Redis::timeoutCallback(struct ev_loop* loop, ev_timer* w, int revents) {
    if (!doRun) {
        ev_unloop(redis_loop, EVUNLOOP_ONE);
        return;
    }

    static char timebuf[32];
    time_t now = time(NULL);
    strftime(&timebuf[0], sizeof (timebuf), "%s", gmtime(&now));
    RedisRequest* checkin = new RedisRequest;
    checkin->key = lastCheckinKey;
    checkin->method = REDIS_SET;
    checkin->updateContext = RCONTEXT_ONLINE;
    checkin->values.push(&timebuf[0]);
    if (!addRequest(checkin))
        delete checkin;

    RedisRequest* req = getRequest();
    while (doRun && req) {
        processRequest(req);
        req = getRequest();
    }

    ev_timer_again(redis_loop, w);
}

RedisRequest* Redis::getRequest() {
    RedisRequest* ret = 0;
    MUT_LOCK(requestMutex);
    if (requestQueue.size() > 0) {
        ret = requestQueue.front();
        requestQueue.pop();
    }
    MUT_UNLOCK(requestMutex);
    return ret;
}

bool Redis::addRequest(RedisRequest* newRequest) {
    if (!doRun)
        return false;
    struct timespec abs_time;
    clock_gettime(CLOCK_REALTIME, &abs_time);
    abs_time.tv_nsec += REDIS_MUTEX_TIMEOUT;
    if (MUT_TIMEDLOCK(requestMutex, abs_time))
        return false;
    requestQueue.push(newRequest);
    MUT_UNLOCK(requestMutex);
    return true;
}
<commit_msg>[ISSUE #28] Crash on DEL command in Redis.<commit_after>/*
 * Copyright (c) 2011-2013, "Kira"
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 "precompiled_headers.hpp"

#include "redis.hpp"

#include "logging.hpp"
#include "startup_config.hpp"
#include <time.h>

pthread_mutex_t Redis::requestMutex = PTHREAD_MUTEX_INITIALIZER;
//pthread_mutex_t Redis::replyMutex = PTHREAD_MUTEX_INITIALIZER;
queue<RedisRequest*> Redis::requestQueue;
bool Redis::doRun = true;
struct ev_loop* Redis::redis_loop = 0;
ev_timer* Redis::redis_timer = 0;
redisContext* Redis::context = 0;
const string Redis::onlineUsersKey("users.online");
const string Redis::lastCheckinKey("chat.lastCheckin");

const __useconds_t Redis::REDIS_FAILURE_WAIT = 5000000; //5 seconds;

void Redis::connectToRedis() {
    DLOG(INFO) << "Connecting to redis.";
    if (context)
        redisFree(context);
    context = 0;

    static struct timeval contimeout;
    contimeout.tv_sec = 10;
    contimeout.tv_usec = 0;
    context = redisConnectWithTimeout(StartupConfig::getString("redishost").c_str(), static_cast<int> (StartupConfig::getDouble("redisport")), contimeout);
    if (context->err) {
        DLOG(INFO) << "Failed to connect to redis with error: " << context->errstr;
        redisFree(context);
        context = 0;
        return;
    }
    redisSetTimeout(context, contimeout);
    redisReply* reply = (redisReply*) redisCommand(context, "AUTH %s", StartupConfig::getString("redispass").c_str());
    if (reply == 0 || (reply->type != REDIS_REPLY_STATUS && reply->len != 2)) {
        DLOG(INFO) << "Failed to authenticate with the redis server.";
        if (reply)
            freeReplyObject(reply);
        reply = 0;
        redisFree(context);
        context = 0;
        return;
    }
    freeReplyObject(reply);
}

void Redis::processRequest(RedisRequest* req) {
    while (doRun && !context) {
        usleep(REDIS_FAILURE_WAIT);
        connectToRedis();
    }

    if (doRun && context) {
        const RedisMethod method = req->method;
        switch (method) {
            case REDIS_DEL:
            {
                redisReply* reply = (redisReply*) redisCommand(context, "DEL %s", req->key.c_str());
                if (reply)
                    freeReplyObject(reply);
                else {
                    DLOG(WARNING) << "A redis command failed. Restarting the redis system.";
                    redisFree(context);
                    context = 0;
                    delete req;
                    return;
                }
                break;
            }
            case REDIS_SADD:
            case REDIS_SREM:
            case REDIS_LPUSH:
            case REDIS_LREM:
            case REDIS_SET:
            {
                const char* key = req->key.c_str();
                int size = req->values.size();
                for (int i = 0; i < size; ++i) {
                    switch (method) {
                        case REDIS_SADD:
                            redisAppendCommand(context, "SADD %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_SREM:
                            redisAppendCommand(context, "SREM %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_LPUSH:
                            redisAppendCommand(context, "LPUSH %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_LREM:
                            redisAppendCommand(context, "LREM %s %s", key, req->values.front().c_str());
                            break;
                        case REDIS_SET:
                            redisAppendCommand(context, "SET %s %s", key, req->values.front().c_str());
                            break;
                        default:
                            delete req;
                            return;
                    }
                    req->values.pop();
                }
                for (int i = 0; i < size; ++i) {
                    redisReply* reply = 0;
                    int ret = redisGetReply(context, (void**) &reply);
                    if (ret != REDIS_OK) {
                        DLOG(WARNING) << "A redis command failed. Restarting the redis system.";
                        if (reply)
                            freeReplyObject(reply);
                        redisFree(context);
                        context = 0;
                        delete req;
                        return;
                    }
                    if (reply)
                        freeReplyObject(reply);
                }
                break;
            }
            default:
                break;
        }
    }
    delete req;
}

void* Redis::runThread(void* param) {
    DLOG(INFO) << "Starting Redis thread.";
    redis_loop = ev_loop_new(EVFLAG_AUTO);
    redis_timer = new ev_timer;
    ev_timer_init(redis_timer, Redis::timeoutCallback, 0, 5.);
    ev_timer_start(redis_loop, redis_timer);

    ev_loop(redis_loop, 0);

    //Cleanup
    ev_timer_stop(redis_loop, redis_timer);
    delete redis_timer;
    redis_timer = 0;
    ev_loop_destroy(redis_loop);
    redis_loop = 0;
    DLOG(INFO) << "Redis thread exiting.";
    pthread_exit(NULL);
}

void Redis::timeoutCallback(struct ev_loop* loop, ev_timer* w, int revents) {
    if (!doRun) {
        ev_unloop(redis_loop, EVUNLOOP_ONE);
        return;
    }

    static char timebuf[32];
    time_t now = time(NULL);
    strftime(&timebuf[0], sizeof (timebuf), "%s", gmtime(&now));
    RedisRequest* checkin = new RedisRequest;
    checkin->key = lastCheckinKey;
    checkin->method = REDIS_SET;
    checkin->updateContext = RCONTEXT_ONLINE;
    checkin->values.push(&timebuf[0]);
    if (!addRequest(checkin))
        delete checkin;

    RedisRequest* req = getRequest();
    while (doRun && req) {
        processRequest(req);
        req = getRequest();
    }

    ev_timer_again(redis_loop, w);
}

RedisRequest* Redis::getRequest() {
    RedisRequest* ret = 0;
    MUT_LOCK(requestMutex);
    if (requestQueue.size() > 0) {
        ret = requestQueue.front();
        requestQueue.pop();
    }
    MUT_UNLOCK(requestMutex);
    return ret;
}

bool Redis::addRequest(RedisRequest* newRequest) {
    if (!doRun)
        return false;
    struct timespec abs_time;
    clock_gettime(CLOCK_REALTIME, &abs_time);
    abs_time.tv_nsec += REDIS_MUTEX_TIMEOUT;
    if (MUT_TIMEDLOCK(requestMutex, abs_time))
        return false;
    requestQueue.push(newRequest);
    MUT_UNLOCK(requestMutex);
    return true;
}
<|endoftext|>