<commit_before>
#include <sys/epoll.h>
#include <unistd.h>
#include <pthread.h>
#include <nanosoft/netdaemon.h>
#include <nanosoft/error.h>
#include <iostream>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

using namespace std;
using namespace nanosoft;

#define DEFAULT_WORKER_STACK_SIZE (sizeof(size_t) * 1024 * 1024)

/**
* Конструктор демона
* @param maxStreams максимальное число одновременных виртуальных потоков
*/
NetDaemon::NetDaemon(int maxObjects):
	workerStackSize(DEFAULT_WORKER_STACK_SIZE),
	workerCount(0),
	activeCount(0),
	timerCount(0),
	count(0)
{
	epoll = epoll_create(maxObjects);
}

/**
* Деструктор демона
*/
NetDaemon::~NetDaemon()
{
	int r = ::close(epoll);
	if ( r < 0 ) stderror();
}

/**
* Обработчик ошибок
*
* По умолчанию выводит все ошибки в stderr
*/
void NetDaemon::onError(const char *message)
{
	cerr << "[NetDaemon]: " << message << endl;
}

/**
* Обработка системной ошибки
*/
void NetDaemon::stderror()
{
	onError(strerror(errno));
}

/**
* Вернуть число воркеров
*/
int NetDaemon::getWorkerCount()
{
	return workerCount;
}

/**
* Установить число воркеров
*/
void NetDaemon::setWorkerCount(int count)
{
	workerCount = count;
}

/**
* Вернуть размер стека воркера
*/
size_t NetDaemon::getWorkerStackSize()
{
	return workerStackSize;
}

/**
* Установить размер стека воркера
*/
void NetDaemon::setWorkerStackSize(size_t size)
{
	workerStackSize = size;
}

/**
* Добавить асинхронный объект
*/
bool NetDaemon::addObject(AsyncObject *object)
{
	//fprintf(stderr, "addObject enter, count: %d\n", objects.size());
	struct epoll_event event;
	mutex.lock();
		objects[object->fd] = object;
		count ++;
		//fprintf(stderr, "AddObject(%d), count = %d\n", object->fd, count);
		event.events = object->getEventsMask();
		event.data.fd = object->fd;
		bool r = epoll_ctl(epoll, EPOLL_CTL_ADD, object->fd, &event) == 0;
	mutex.unlock();
	//fprintf(stderr, "addObject leave, count: %d\n", objects.size());
	return r;
}

/**
* Удалить асинхронный объект
*/
bool NetDaemon::removeObject(AsyncObject *object)
{
	mutex.lock();
	//fprintf(stderr, "#%d NetDaemon::removeObject(%d) enter, count = %d\n", object->workerId, object->fd, count);
	map_objects_t::iterator pos = objects.find(object->fd);
	if ( pos != objects.end() )
	{
		objects.erase(object->fd);
		if ( epoll_ctl(epoll, EPOLL_CTL_DEL, object->fd, 0) != 0 ) stderror();
		count --;
		if ( count == 0 ) stopWorkers();
	}
	else
	{
		fprintf(stderr, "#%d NetDaemon::removeObject(%d): not found o.O\n", object->workerId, object->fd);
	}
	//fprintf(stderr, "#%d removeObject(%d) leave, count = %d\n", object->workerId, object->fd, count);
	mutex.unlock();
}

/**
* Возобновить работу с асинхронным объектом
*/
bool NetDaemon::resetObject(AsyncObject *object)
{
	//fprintf(stderr, "#%d: NetDaemon::resetObject(%d)\n", object->workerId, object->fd);
	struct epoll_event event;
	event.events = object->getEventsMask();
	event.data.fd = object->fd;
	if ( epoll_ctl(epoll, EPOLL_CTL_MOD, object->fd, &event) != 0 )
	{
		stderror();
	}
}

/**
* Действие активного цикла
*/
void NetDaemon::doActiveAction(worker_t *worker)
{
	struct epoll_event event;
	int r = epoll_wait(epoll, &event, 1, nextTimer());
	if ( r > 0 )
	{
		mutex.lock();
			AsyncObject *obj = objects[event.data.fd];
		mutex.unlock();
		obj->workerId = worker->workerId;
		obj->onEvent(event.events);
		mutex.lock();
			if ( objects.find(event.data.fd) != objects.end() )
			{
				resetObject(obj);
			}
		mutex.unlock();
	}
	if ( r < 0 ) fprintf(stderr, "#%d: %s\n", worker->workerId, nanosoft::stderror());
	if ( r == 0 ) processTimers(worker->workerId);
}

/**
* Действие спящего цикла
*/
void NetDaemon::doSleepAction(worker_t *worker)
{
	if ( ! worker->checked ) {
		worker->checked = true;
		mutex.lock();
			activeCount --;
			if ( activeCount == 0 )
			{
				iter = objects.begin();
				setWorkersState(TERMINATE);
			}
		mutex.unlock();
		return;
	}

	fprintf(stderr, "#%d: doSleepAction\n", worker->workerId);

	// просто засыпаем на как можно больший срок
	// лишь сигнал нас должен разбудить
	struct timespec tm;
	tm.tv_sec = 999999999;
	tm.tv_nsec = 0;
	nanosleep(&tm, 0);
}

/**
* Действие завещающего цикла
*/
void NetDaemon::doTerminateAction(worker_t *worker)
{
	if ( ! worker->checked )
	{
		worker->checked = true;
		mutex.lock();
			activeCount ++;
		mutex.unlock();
		return;
	}
	fprintf(stderr, "#%d: doTerminateAction\n", worker->workerId);

	AsyncObject *obj;
	mutex.lock();
		if ( iter != objects.end() )
		{
			obj = iter->second;
			++ iter;
		}
		else
		{
			setWorkersState(ACTIVE);
			obj = 0;
		}
	mutex.unlock();
	if ( obj )
	{
		obj->workerId = worker->workerId;
		obj->onTerminate();
	}
}

/**
* Точка входа в воркер
*/
void* NetDaemon::workerEntry(void *pWorker)
{
	worker_t *worker = static_cast<worker_t *>(pWorker);
	NetDaemon *daemon = worker->daemon;

	fprintf(stderr, "#%d: worker started\n", worker->workerId);

	while ( daemon->count > 0 && worker->status != INACTIVE )
	{
		switch ( worker->status )
		{
		case ACTIVE:
			daemon->doActiveAction(worker);
			break;
		case SLEEP:
			daemon->doSleepAction(worker);
			break;
		case TERMINATE:
			daemon->doTerminateAction(worker);
			break;
		}
	}

	fprintf(stderr, "#%d: worker exited\n", worker->workerId);

	return 0;
}

/**
* Запустить воркеров
*/
void NetDaemon::startWorkers()
{
	workers = new worker_t[workerCount];
	for(int i = 0; i < workerCount; i++)
	{
		workers[i].daemon = this;
		workers[i].workerId = i + 1;
		workers[i].status = ACTIVE;
		mutex.lock();
			activeCount++;
		mutex.unlock();
		pthread_attr_init(&workers[i].attr);
		pthread_attr_setstacksize(&workers[i].attr, getWorkerStackSize());
		pthread_attr_setdetachstate(&workers[i].attr, PTHREAD_CREATE_JOINABLE);
		pthread_create(&workers[i].thread, &workers[i].attr, workerEntry, &workers[i]);
	}
}

/**
* Сменить статус воркеров
*/
void NetDaemon::setWorkersState(worker_status_t status)
{
	for(int i = 0; i < workerCount; i++)
	{
		workers[i].status = status;
		workers[i].checked = false;
		pthread_kill(workers[i].thread, SIGHUP);
	}
	main.status = status;
	main.checked = false;
	pthread_kill(main.thread, SIGHUP);
}

/**
* Остановить воркеров
*/
void NetDaemon::stopWorkers()
{
	cerr << "NetDaemon::stopWorkers()...\n";
	setWorkersState(INACTIVE);
}

/**
* Ожидать завершения работы всех воркеров
*/
void NetDaemon::waitWorkers()
{
	for(int i = 0; i < workerCount; i++)
	{
		void *status;
		int rc = pthread_join(workers[i].thread, &status);
		if ( rc ) fprintf(stderr, "[NetDaemon] pthread_join(#%) fault\n", i+1);
	}
}

/**
* Удалить воркеров
*/
void NetDaemon::freeWorkers()
{
	for(int i = 0; i < workerCount; i++)
	{
		pthread_attr_destroy(&workers[i].attr);
	}
	delete [] workers;
}

/**
* Послать сигнал onTerminate() всем подконтрольным объектам
*/
void NetDaemon::killObjects()
{
	// TODO добавить mutex
	for(map_objects_t::iterator pos = objects.begin(); pos != objects.end(); ++pos)
	{
		pos->second->terminate();
	}
}

/**
* Запустить демона
*/
int NetDaemon::run()
{
	startWorkers();
	main.daemon = this;
	main.workerId = 0;
	main.thread = pthread_self();
	main.status = ACTIVE;
	mutex.lock();
		activeCount++;
	mutex.unlock();
	workerEntry(&main);
	waitWorkers();
	freeWorkers();
	return 0;
}

/**
* Вернуть ID текущего воркера
*/
int NetDaemon::wid()
{
	pthread_t tid = pthread_self();
	for(int i = 0; i < workerCount; i++)
	{
		if ( pthread_equal(workers[i].thread, tid) ) return i + 1;
	}
	if ( pthread_equal(main.thread, tid) ) return 0;
	return -1;
}

/**
* Завершить работу демона
*/
void NetDaemon::terminate()
{
	onError("terminate...");
	//killObjects();
	setWorkersState(SLEEP);
}

/**
* Установить таймер
* @param expires время запуска таймера
* @param callback функция обратного вызова
* @param data указатель на пользовательские данные
*/
void NetDaemon::setTimer(int expires, timer_callback_t callback, void *data)
{
	bool hup = false;
	mutex.lock();
		timers.push(timer(expires, callback, data));
		hup = timerCount = 0;
		timerCount++;
	mutex.unlock();
	if ( hup )
	{
		for(int i = 0; i < workerCount; i++)
		{
			pthread_kill(workers[i].thread, SIGHUP);
		}
		pthread_kill(main.thread, SIGHUP);
	}
}

/**
* Вернуть время следущего таймера
* @return время (Unix time) следующего таймера или -1 если таймеров нет
*/
int NetDaemon::nextTimer()
{
	int timeout = -1;
	int expires;
	if ( timerCount > 0 ) {
		mutex.lock();
			expires = timers.top().expires - time(0);
		mutex.unlock();
		timeout = expires > 0 ? expires * 1000 : 0;
	}
	return timeout;
}

/**
* Обработать таймеры
*/
void NetDaemon::processTimers(int wid)
{
	timer t;
	time_t now = time(0);
	while ( timerCount > 0 )
	{
		bool process = false;
		mutex.lock();
			t = timers.top();
			if ( t.expires <= now )
			{
				timers.pop();
				timerCount --;
				process = true;
			}
		mutex.unlock();
		if ( process ) t.fire(wid);
		else break;
	}
}
<commit_msg>NetDaemon: дополнительный отладочный вывод: добавление/удаление объектов в epoll<commit_after>
#include <sys/epoll.h>
#include <unistd.h>
#include <pthread.h>
#include <nanosoft/netdaemon.h>
#include <nanosoft/error.h>
#include <iostream>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

using namespace std;
using namespace nanosoft;

#define DEFAULT_WORKER_STACK_SIZE (sizeof(size_t) * 1024 * 1024)

/**
* Конструктор демона
* @param maxStreams максимальное число одновременных виртуальных потоков
*/
NetDaemon::NetDaemon(int maxObjects):
	workerStackSize(DEFAULT_WORKER_STACK_SIZE),
	workerCount(0),
	activeCount(0),
	timerCount(0),
	count(0)
{
	epoll = epoll_create(maxObjects);
}

/**
* Деструктор демона
*/
NetDaemon::~NetDaemon()
{
	int r = ::close(epoll);
	if ( r < 0 ) stderror();
}

/**
* Обработчик ошибок
*
* По умолчанию выводит все ошибки в stderr
*/
void NetDaemon::onError(const char *message)
{
	cerr << "[NetDaemon]: " << message << endl;
}

/**
* Обработка системной ошибки
*/
void NetDaemon::stderror()
{
	onError(strerror(errno));
}

/**
* Вернуть число воркеров
*/
int NetDaemon::getWorkerCount()
{
	return workerCount;
}

/**
* Установить число воркеров
*/
void NetDaemon::setWorkerCount(int count)
{
	workerCount = count;
}

/**
* Вернуть размер стека воркера
*/
size_t NetDaemon::getWorkerStackSize()
{
	return workerStackSize;
}

/**
* Установить размер стека воркера
*/
void NetDaemon::setWorkerStackSize(size_t size)
{
	workerStackSize = size;
}

/**
* Добавить асинхронный объект
*/
bool NetDaemon::addObject(AsyncObject *object)
{
	fprintf(stderr, "#d: [NetDaemon] addObject(%d)\n", object->fd);
	struct epoll_event event;
	mutex.lock();
		objects[object->fd] = object;
		count ++;
		//fprintf(stderr, "AddObject(%d), count = %d\n", object->fd, count);
		event.events = object->getEventsMask();
		event.data.fd = object->fd;
		bool r = epoll_ctl(epoll, EPOLL_CTL_ADD, object->fd, &event) == 0;
	mutex.unlock();
	//fprintf(stderr, "addObject leave, count: %d\n", objects.size());
	return r;
}

/**
* Удалить асинхронный объект
*/
bool NetDaemon::removeObject(AsyncObject *object)
{
	fprintf(stderr, "#d: [NetDaemon] removeObject(%d)\n", object->fd);
	mutex.lock();
	//fprintf(stderr, "#%d NetDaemon::removeObject(%d) enter, count = %d\n", object->workerId, object->fd, count);
	map_objects_t::iterator pos = objects.find(object->fd);
	if ( pos != objects.end() )
	{
		objects.erase(object->fd);
		if ( epoll_ctl(epoll, EPOLL_CTL_DEL, object->fd, 0) != 0 ) stderror();
		count --;
		if ( count == 0 ) stopWorkers();
	}
	else
	{
		fprintf(stderr, "#%d NetDaemon::removeObject(%d): not found o.O\n", object->workerId, object->fd);
	}
	//fprintf(stderr, "#%d removeObject(%d) leave, count = %d\n", object->workerId, object->fd, count);
	mutex.unlock();
}

/**
* Возобновить работу с асинхронным объектом
*/
bool NetDaemon::resetObject(AsyncObject *object)
{
	//fprintf(stderr, "#%d: NetDaemon::resetObject(%d)\n", object->workerId, object->fd);
	struct epoll_event event;
	event.events = object->getEventsMask();
	event.data.fd = object->fd;
	if ( epoll_ctl(epoll, EPOLL_CTL_MOD, object->fd, &event) != 0 )
	{
		stderror();
	}
}

/**
* Действие активного цикла
*/
void NetDaemon::doActiveAction(worker_t *worker)
{
	struct epoll_event event;
	int r = epoll_wait(epoll, &event, 1, nextTimer());
	if ( r > 0 )
	{
		mutex.lock();
			AsyncObject *obj = objects[event.data.fd];
		mutex.unlock();
		obj->workerId = worker->workerId;
		obj->onEvent(event.events);
		mutex.lock();
			if ( objects.find(event.data.fd) != objects.end() )
			{
				resetObject(obj);
			}
		mutex.unlock();
	}
	if ( r < 0 ) fprintf(stderr, "#%d: %s\n", worker->workerId, nanosoft::stderror());
	if ( r == 0 ) processTimers(worker->workerId);
}

/**
* Действие спящего цикла
*/
void NetDaemon::doSleepAction(worker_t *worker)
{
	if ( ! worker->checked ) {
		worker->checked = true;
		mutex.lock();
			activeCount --;
			if ( activeCount == 0 )
			{
				iter = objects.begin();
				setWorkersState(TERMINATE);
			}
		mutex.unlock();
		return;
	}

	fprintf(stderr, "#%d: doSleepAction\n", worker->workerId);

	// просто засыпаем на как можно больший срок
	// лишь сигнал нас должен разбудить
	struct timespec tm;
	tm.tv_sec = 999999999;
	tm.tv_nsec = 0;
	nanosleep(&tm, 0);
}

/**
* Действие завещающего цикла
*/
void NetDaemon::doTerminateAction(worker_t *worker)
{
	if ( ! worker->checked )
	{
		worker->checked = true;
		mutex.lock();
			activeCount ++;
		mutex.unlock();
		return;
	}
	fprintf(stderr, "#%d: doTerminateAction\n", worker->workerId);

	AsyncObject *obj;
	mutex.lock();
		if ( iter != objects.end() )
		{
			obj = iter->second;
			++ iter;
		}
		else
		{
			setWorkersState(ACTIVE);
			obj = 0;
		}
	mutex.unlock();
	if ( obj )
	{
		obj->workerId = worker->workerId;
		obj->onTerminate();
	}
}

/**
* Точка входа в воркер
*/
void* NetDaemon::workerEntry(void *pWorker)
{
	worker_t *worker = static_cast<worker_t *>(pWorker);
	NetDaemon *daemon = worker->daemon;

	fprintf(stderr, "#%d: worker started\n", worker->workerId);

	while ( daemon->count > 0 && worker->status != INACTIVE )
	{
		switch ( worker->status )
		{
		case ACTIVE:
			daemon->doActiveAction(worker);
			break;
		case SLEEP:
			daemon->doSleepAction(worker);
			break;
		case TERMINATE:
			daemon->doTerminateAction(worker);
			break;
		}
	}

	fprintf(stderr, "#%d: worker exited\n", worker->workerId);

	return 0;
}

/**
* Запустить воркеров
*/
void NetDaemon::startWorkers()
{
	workers = new worker_t[workerCount];
	for(int i = 0; i < workerCount; i++)
	{
		workers[i].daemon = this;
		workers[i].workerId = i + 1;
		workers[i].status = ACTIVE;
		mutex.lock();
			activeCount++;
		mutex.unlock();
		pthread_attr_init(&workers[i].attr);
		pthread_attr_setstacksize(&workers[i].attr, getWorkerStackSize());
		pthread_attr_setdetachstate(&workers[i].attr, PTHREAD_CREATE_JOINABLE);
		pthread_create(&workers[i].thread, &workers[i].attr, workerEntry, &workers[i]);
	}
}

/**
* Сменить статус воркеров
*/
void NetDaemon::setWorkersState(worker_status_t status)
{
	for(int i = 0; i < workerCount; i++)
	{
		workers[i].status = status;
		workers[i].checked = false;
		pthread_kill(workers[i].thread, SIGHUP);
	}
	main.status = status;
	main.checked = false;
	pthread_kill(main.thread, SIGHUP);
}

/**
* Остановить воркеров
*/
void NetDaemon::stopWorkers()
{
	cerr << "NetDaemon::stopWorkers()...\n";
	setWorkersState(INACTIVE);
}

/**
* Ожидать завершения работы всех воркеров
*/
void NetDaemon::waitWorkers()
{
	for(int i = 0; i < workerCount; i++)
	{
		void *status;
		int rc = pthread_join(workers[i].thread, &status);
		if ( rc ) fprintf(stderr, "[NetDaemon] pthread_join(#%) fault\n", i+1);
	}
}

/**
* Удалить воркеров
*/
void NetDaemon::freeWorkers()
{
	for(int i = 0; i < workerCount; i++)
	{
		pthread_attr_destroy(&workers[i].attr);
	}
	delete [] workers;
}

/**
* Послать сигнал onTerminate() всем подконтрольным объектам
*/
void NetDaemon::killObjects()
{
	// TODO добавить mutex
	for(map_objects_t::iterator pos = objects.begin(); pos != objects.end(); ++pos)
	{
		pos->second->terminate();
	}
}

/**
* Запустить демона
*/
int NetDaemon::run()
{
	startWorkers();
	main.daemon = this;
	main.workerId = 0;
	main.thread = pthread_self();
	main.status = ACTIVE;
	mutex.lock();
		activeCount++;
	mutex.unlock();
	workerEntry(&main);
	waitWorkers();
	freeWorkers();
	return 0;
}

/**
* Вернуть ID текущего воркера
*/
int NetDaemon::wid()
{
	pthread_t tid = pthread_self();
	for(int i = 0; i < workerCount; i++)
	{
		if ( pthread_equal(workers[i].thread, tid) ) return i + 1;
	}
	if ( pthread_equal(main.thread, tid) ) return 0;
	return -1;
}

/**
* Завершить работу демона
*/
void NetDaemon::terminate()
{
	onError("terminate...");
	//killObjects();
	setWorkersState(SLEEP);
}

/**
* Установить таймер
* @param expires время запуска таймера
* @param callback функция обратного вызова
* @param data указатель на пользовательские данные
*/
void NetDaemon::setTimer(int expires, timer_callback_t callback, void *data)
{
	bool hup = false;
	mutex.lock();
		timers.push(timer(expires, callback, data));
		hup = timerCount = 0;
		timerCount++;
	mutex.unlock();
	if ( hup )
	{
		for(int i = 0; i < workerCount; i++)
		{
			pthread_kill(workers[i].thread, SIGHUP);
		}
		pthread_kill(main.thread, SIGHUP);
	}
}

/**
* Вернуть время следущего таймера
* @return время (Unix time) следующего таймера или -1 если таймеров нет
*/
int NetDaemon::nextTimer()
{
	int timeout = -1;
	int expires;
	if ( timerCount > 0 ) {
		mutex.lock();
			expires = timers.top().expires - time(0);
		mutex.unlock();
		timeout = expires > 0 ? expires * 1000 : 0;
	}
	return timeout;
}

/**
* Обработать таймеры
*/
void NetDaemon::processTimers(int wid)
{
	timer t;
	time_t now = time(0);
	while ( timerCount > 0 )
	{
		bool process = false;
		mutex.lock();
			t = timers.top();
			if ( t.expires <= now )
			{
				timers.pop();
				timerCount --;
				process = true;
			}
		mutex.unlock();
		if ( process ) t.fire(wid);
		else break;
	}
}
<|endoftext|>


<commit_before>#ifndef MTUTILS_HPP
#define MTUTILS_HPP

#include <atomic>       // for std::atomic_flag and memory orders
#include <mutex>        // for std::lock_guard
#include <functional>   // for std::function
#include <utility>      // for std::forward
#include <vector>
#include <algorithm>
#include <cmath>

#include "libslic3r.h"
#include "Point.hpp"

namespace Slic3r {

/// Handy little spin mutex for the cached meshes.
/// Implements the "Lockable" concept
class SpinMutex
{
    std::atomic_flag                m_flg;
    static const /*constexpr*/ auto MO_ACQ = std::memory_order_acquire;
    static const /*constexpr*/ auto MO_REL = std::memory_order_release;

public:
    inline SpinMutex() { m_flg.clear(MO_REL); }
    inline void lock() { while (m_flg.test_and_set(MO_ACQ)) ; }
    inline bool try_lock() { return !m_flg.test_and_set(MO_ACQ); }
    inline void unlock() { m_flg.clear(MO_REL); }
};

/// A wrapper class around arbitrary object that needs thread safe caching.
template<class T> class CachedObject
{
public:
    // Method type which refreshes the object when it has been invalidated
    using Setter = std::function<void(T &)>;

private:
    T         m_obj;   // the object itself
    bool      m_valid; // invalidation flag
    SpinMutex m_lck;   // to make the caching thread safe

    // the setter will be called just before the object's const value is
    // about to be retrieved.
    std::function<void(T &)> m_setter;

public:
    // Forwarded constructor
    template<class... Args>
    inline CachedObject(Setter fn, Args &&... args)
        : m_obj(std::forward<Args>(args)...), m_valid(false), m_setter(fn)
    {}

    // invalidate the value of the object. The object will be refreshed at
    // the next retrieval (Setter will be called). The data that is used in
    // the setter function should be guarded as well during modification so
    // the modification has to take place in fn.
    inline void invalidate(std::function<void()> fn)
    {
        std::lock_guard<SpinMutex> lck(m_lck);
        fn();
        m_valid = false;
    }

    // Get the const object properly updated.
    inline const T &get()
    {
        std::lock_guard<SpinMutex> lck(m_lck);
        if (!m_valid) {
            m_setter(m_obj);
            m_valid = true;
        }
        return m_obj;
    }
};

/// An std compatible random access iterator which uses indices to the
/// source vector thus resistant to invalidation caused by relocations. It
/// also "knows" its container. No comparison is neccesary to the container
/// "end()" iterator. The template can be instantiated with a different
/// value type than that of the container's but the types must be
/// compatible. E.g. a base class of the contained objects is compatible.
///
/// For a constant iterator, one can instantiate this template with a value
/// type preceded with 'const'.
template<class Vector, // The container type, must be random access...
         class Value = typename Vector::value_type // The value type
         >
class IndexBasedIterator
{
    static const size_t NONE = size_t(-1);

    std::reference_wrapper<Vector> m_index_ref;
    size_t                         m_idx = NONE;

public:
    using value_type        = Value;
    using pointer           = Value *;
    using reference         = Value &;
    using difference_type   = long;
    using iterator_category = std::random_access_iterator_tag;

    inline explicit IndexBasedIterator(Vector &index, size_t idx)
        : m_index_ref(index), m_idx(idx)
    {}

    // Post increment
    inline IndexBasedIterator operator++(int)
    {
        IndexBasedIterator cpy(*this);
        ++m_idx;
        return cpy;
    }

    inline IndexBasedIterator operator--(int)
    {
        IndexBasedIterator cpy(*this);
        --m_idx;
        return cpy;
    }

    inline IndexBasedIterator &operator++()
    {
        ++m_idx;
        return *this;
    }

    inline IndexBasedIterator &operator--()
    {
        --m_idx;
        return *this;
    }

    inline IndexBasedIterator &operator+=(difference_type l)
    {
        m_idx += size_t(l);
        return *this;
    }

    inline IndexBasedIterator operator+(difference_type l)
    {
        auto cpy = *this;
        cpy += l;
        return cpy;
    }

    inline IndexBasedIterator &operator-=(difference_type l)
    {
        m_idx -= size_t(l);
        return *this;
    }

    inline IndexBasedIterator operator-(difference_type l)
    {
        auto cpy = *this;
        cpy -= l;
        return cpy;
    }

    operator difference_type() { return difference_type(m_idx); }

    /// Tesing the end of the container... this is not possible with std
    /// iterators.
    inline bool is_end() const
    {
        return m_idx >= m_index_ref.get().size();
    }

    inline Value &operator*() const
    {
        assert(m_idx < m_index_ref.get().size());
        return m_index_ref.get().operator[](m_idx);
    }

    inline Value *operator->() const
    {
        assert(m_idx < m_index_ref.get().size());
        return &m_index_ref.get().operator[](m_idx);
    }

    /// If both iterators point past the container, they are equal...
    inline bool operator==(const IndexBasedIterator &other)
    {
        size_t e = m_index_ref.get().size();
        return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e);
    }

    inline bool operator!=(const IndexBasedIterator &other)
    {
        return !(*this == other);
    }

    inline bool operator<=(const IndexBasedIterator &other)
    {
        return (m_idx < other.m_idx) || (*this == other);
    }

    inline bool operator<(const IndexBasedIterator &other)
    {
        return m_idx < other.m_idx && (*this != other);
    }

    inline bool operator>=(const IndexBasedIterator &other)
    {
        return m_idx > other.m_idx || *this == other;
    }

    inline bool operator>(const IndexBasedIterator &other)
    {
        return m_idx > other.m_idx && *this != other;
    }
};

/// A very simple range concept implementation with iterator-like objects.
template<class It> class Range
{
    It from, to;

public:
    // The class is ready for range based for loops.
    It begin() const { return from; }
    It end() const { return to; }

    // The iterator type can be obtained this way.
    using Type = It;

    Range() = default;
    Range(It &&b, It &&e)
        : from(std::forward<It>(b)), to(std::forward<It>(e))
    {}

    // Some useful container-like methods...
    inline size_t size() const { return end() - begin(); }
    inline bool   empty() const { return size() == 0; }
};

template<class C> bool all_of(const C &container)
{
    return std::all_of(container.begin(),
                       container.end(),
                       [](const typename C::value_type &v) {
                           return static_cast<bool>(v);
                       });
}

template<class T>
struct remove_cvref
{
    using type =
        typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};

template<class T>
using remove_cvref_t = typename remove_cvref<T>::type;

template<template<class> class C, class T>
class Container: public C<remove_cvref_t<T>> {
public:
    explicit Container(size_t count, T&& initval):
        C<remove_cvref_t<T>>(count, initval) {}
};

template<class T> using DefaultContainer = std::vector<T>;

/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n)
{
    Container<C, T> vals(n, T());
    T stride = (stop - start) / n;

    size_t i = 0;
    std::generate(vals.begin(), vals.end(), [&i, start, stride] {
        return start + i++ * stride;
    });

    return vals;
}

/// A set of equidistant values starting from 'start' (inclusive), ending
/// in the closest multiple of 'stride' less than or equal to 'end' and
/// leaving 'stride' space between each value.
/// Very similar to Matlab [start:stride:end] notation.
template<class T, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> grid(const T &start, const T &stop, const T &stride)
{
    Container<C, T> vals(size_t(std::ceil((stop - start) / stride)), T());

    int i = 0;
    std::generate(vals.begin(), vals.end(), [&i, start, stride] {
        return start + i++ * stride;
    });

    return vals;
}

// A shorter C++14 style form of the enable_if metafunction
template<bool B, class T>
using enable_if_t = typename std::enable_if<B, T>::type;

// /////////////////////////////////////////////////////////////////////////////
// Type safe conversions to and from scaled and unscaled coordinates
// /////////////////////////////////////////////////////////////////////////////

// A meta-predicate which is true for integers wider than or equal to coord_t
template<class I> struct is_scaled_coord
{
    static const SLIC3R_CONSTEXPR bool value =
        std::is_integral<I>::value &&
        std::numeric_limits<I>::digits >=
            std::numeric_limits<coord_t>::digits;
};

// Meta predicates for floating, 'scaled coord' and generic arithmetic types
template<class T>
using FloatingOnly = enable_if_t<std::is_floating_point<T>::value, T>;

template<class T>
using ScaledCoordOnly = enable_if_t<is_scaled_coord<T>::value, T>;

template<class T>
using ArithmeticOnly = enable_if_t<std::is_arithmetic<T>::value, T>;

// A shorter form for a generic Eigen vector which is widely used in PrusaSlicer
template<class T, int N>
using EigenVec = Eigen::Matrix<T, N, 1, Eigen::DontAlign>;

// Semantics are the following:
// Upscaling (scaled()): only from floating point types (or Vec) to either
//                       floating point or integer 'scaled coord' coordinates.
// Downscaling (unscaled()): from arithmetic types (or Vec) to either
//                           floating point only

// Conversion definition from unscaled to floating point scaled
template<class Tout,
         class Tin,
         class = FloatingOnly<Tin>,
         class = FloatingOnly<Tout>>
inline SLIC3R_CONSTEXPR Tout scaled(const Tin &v) SLIC3R_NOEXCEPT
{
    return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR));
}

// Conversion definition from unscaled to integer 'scaled coord'.
// TODO: is the rounding necessary ? Here it is to show that it can be different
// but it does not have to be. Using std::round means loosing noexcept and
// constexpr modifiers
template<class Tout = coord_t, class Tin, class = FloatingOnly<Tin>>
inline SLIC3R_CONSTEXPR ScaledCoordOnly<Tout> scaled(const Tin &v) SLIC3R_NOEXCEPT
{
    //return static_cast<Tout>(std::round(v / SCALING_FACTOR));
    return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR));
}

// Conversion for Eigen vectors (N dimensional points)
template<class Tout = coord_t, class Tin, int N, class = FloatingOnly<Tin>>
inline EigenVec<ArithmeticOnly<Tout>, N> scaled(const EigenVec<Tin, N> &v)
{
    return (v / SCALING_FACTOR).template cast<Tout>();
}

// Conversion from arithmetic scaled type to floating point unscaled
template<class Tout = double,
         class Tin,
         class = ArithmeticOnly<Tin>,
         class = FloatingOnly<Tout>>
inline SLIC3R_CONSTEXPR Tout unscaled(const Tin &v) SLIC3R_NOEXCEPT
{
    return static_cast<Tout>(v * static_cast<Tout>(SCALING_FACTOR));
}

// Unscaling for Eigen vectors. Input base type can be arithmetic, output base
// type can only be floating point.
template<class Tout = double,
         class Tin,
         int N,
         class = ArithmeticOnly<Tin>,
         class = FloatingOnly<Tout>>
inline SLIC3R_CONSTEXPR EigenVec<Tout, N> unscaled(
    const EigenVec<Tin, N> &v) SLIC3R_NOEXCEPT
{
    return v.template cast<Tout>() * SCALING_FACTOR;
}

} // namespace Slic3r

#endif // MTUTILS_HPP
<commit_msg>Fix formatting<commit_after>#ifndef MTUTILS_HPP
#define MTUTILS_HPP

#include <atomic>       // for std::atomic_flag and memory orders
#include <mutex>        // for std::lock_guard
#include <functional>   // for std::function
#include <utility>      // for std::forward
#include <vector>
#include <algorithm>
#include <cmath>

#include "libslic3r.h"
#include "Point.hpp"

namespace Slic3r {

/// Handy little spin mutex for the cached meshes.
/// Implements the "Lockable" concept
class SpinMutex
{
    std::atomic_flag                m_flg;
    static const /*constexpr*/ auto MO_ACQ = std::memory_order_acquire;
    static const /*constexpr*/ auto MO_REL = std::memory_order_release;

public:
    inline SpinMutex() { m_flg.clear(MO_REL); }
    inline void lock() { while (m_flg.test_and_set(MO_ACQ)) ; }
    inline bool try_lock() { return !m_flg.test_and_set(MO_ACQ); }
    inline void unlock() { m_flg.clear(MO_REL); }
};

/// A wrapper class around arbitrary object that needs thread safe caching.
template<class T> class CachedObject
{
public:
    // Method type which refreshes the object when it has been invalidated
    using Setter = std::function<void(T &)>;

private:
    T         m_obj;   // the object itself
    bool      m_valid; // invalidation flag
    SpinMutex m_lck;   // to make the caching thread safe

    // the setter will be called just before the object's const value is
    // about to be retrieved.
    std::function<void(T &)> m_setter;

public:
    // Forwarded constructor
    template<class... Args>
    inline CachedObject(Setter fn, Args &&... args)
        : m_obj(std::forward<Args>(args)...), m_valid(false), m_setter(fn)
    {}

    // invalidate the value of the object. The object will be refreshed at
    // the next retrieval (Setter will be called). The data that is used in
    // the setter function should be guarded as well during modification so
    // the modification has to take place in fn.
    inline void invalidate(std::function<void()> fn)
    {
        std::lock_guard<SpinMutex> lck(m_lck);
        fn();
        m_valid = false;
    }

    // Get the const object properly updated.
    inline const T &get()
    {
        std::lock_guard<SpinMutex> lck(m_lck);
        if (!m_valid) {
            m_setter(m_obj);
            m_valid = true;
        }
        return m_obj;
    }
};

/// An std compatible random access iterator which uses indices to the
/// source vector thus resistant to invalidation caused by relocations. It
/// also "knows" its container. No comparison is neccesary to the container
/// "end()" iterator. The template can be instantiated with a different
/// value type than that of the container's but the types must be
/// compatible. E.g. a base class of the contained objects is compatible.
///
/// For a constant iterator, one can instantiate this template with a value
/// type preceded with 'const'.
template<class Vector, // The container type, must be random access...
         class Value = typename Vector::value_type // The value type
         >
class IndexBasedIterator
{
    static const size_t NONE = size_t(-1);

    std::reference_wrapper<Vector> m_index_ref;
    size_t                         m_idx = NONE;

public:
    using value_type        = Value;
    using pointer           = Value *;
    using reference         = Value &;
    using difference_type   = long;
    using iterator_category = std::random_access_iterator_tag;

    inline explicit IndexBasedIterator(Vector &index, size_t idx)
        : m_index_ref(index), m_idx(idx)
    {}

    // Post increment
    inline IndexBasedIterator operator++(int)
    {
        IndexBasedIterator cpy(*this);
        ++m_idx;
        return cpy;
    }

    inline IndexBasedIterator operator--(int)
    {
        IndexBasedIterator cpy(*this);
        --m_idx;
        return cpy;
    }

    inline IndexBasedIterator &operator++()
    {
        ++m_idx;
        return *this;
    }

    inline IndexBasedIterator &operator--()
    {
        --m_idx;
        return *this;
    }

    inline IndexBasedIterator &operator+=(difference_type l)
    {
        m_idx += size_t(l);
        return *this;
    }

    inline IndexBasedIterator operator+(difference_type l)
    {
        auto cpy = *this;
        cpy += l;
        return cpy;
    }

    inline IndexBasedIterator &operator-=(difference_type l)
    {
        m_idx -= size_t(l);
        return *this;
    }

    inline IndexBasedIterator operator-(difference_type l)
    {
        auto cpy = *this;
        cpy -= l;
        return cpy;
    }

    operator difference_type() { return difference_type(m_idx); }

    /// Tesing the end of the container... this is not possible with std
    /// iterators.
    inline bool is_end() const
    {
        return m_idx >= m_index_ref.get().size();
    }

    inline Value &operator*() const
    {
        assert(m_idx < m_index_ref.get().size());
        return m_index_ref.get().operator[](m_idx);
    }

    inline Value *operator->() const
    {
        assert(m_idx < m_index_ref.get().size());
        return &m_index_ref.get().operator[](m_idx);
    }

    /// If both iterators point past the container, they are equal...
    inline bool operator==(const IndexBasedIterator &other)
    {
        size_t e = m_index_ref.get().size();
        return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e);
    }

    inline bool operator!=(const IndexBasedIterator &other)
    {
        return !(*this == other);
    }

    inline bool operator<=(const IndexBasedIterator &other)
    {
        return (m_idx < other.m_idx) || (*this == other);
    }

    inline bool operator<(const IndexBasedIterator &other)
    {
        return m_idx < other.m_idx && (*this != other);
    }

    inline bool operator>=(const IndexBasedIterator &other)
    {
        return m_idx > other.m_idx || *this == other;
    }

    inline bool operator>(const IndexBasedIterator &other)
    {
        return m_idx > other.m_idx && *this != other;
    }
};

/// A very simple range concept implementation with iterator-like objects.
template<class It> class Range
{
    It from, to;

public:
    // The class is ready for range based for loops.
    It begin() const { return from; }
    It end() const { return to; }

    // The iterator type can be obtained this way.
    using Type = It;

    Range() = default;
    Range(It &&b, It &&e)
        : from(std::forward<It>(b)), to(std::forward<It>(e))
    {}

    // Some useful container-like methods...
    inline size_t size() const { return end() - begin(); }
    inline bool   empty() const { return size() == 0; }
};

template<class C> bool all_of(const C &container)
{
    return std::all_of(container.begin(),
                       container.end(),
                       [](const typename C::value_type &v) {
                           return static_cast<bool>(v);
                       });
}

template<class T> struct remove_cvref
{
    using type =
        typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};

template<class T> using remove_cvref_t = typename remove_cvref<T>::type;

template<template<class> class C, class T>
class Container : public C<remove_cvref_t<T>>
{
public:
    explicit Container(size_t count, T &&initval)
        : C<remove_cvref_t<T>>(count, initval)
    {}
};

template<class T> using DefaultContainer = std::vector<T>;

/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n)
{
    Container<C, T> vals(n, T());

    T      stride = (stop - start) / n;
    size_t i      = 0;
    std::generate(vals.begin(), vals.end(), [&i, start, stride] {
        return start + i++ * stride;
    });

    return vals;
}

/// A set of equidistant values starting from 'start' (inclusive), ending
/// in the closest multiple of 'stride' less than or equal to 'end' and
/// leaving 'stride' space between each value.
/// Very similar to Matlab [start:stride:end] notation.
template<class T, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> grid(const T &start, const T &stop, const T &stride)
{
    Container<C, T> vals(size_t(std::ceil((stop - start) / stride)), T());

    int i = 0;
    std::generate(vals.begin(), vals.end(), [&i, start, stride] {
        return start + i++ * stride;
    });

    return vals;
}

// A shorter C++14 style form of the enable_if metafunction
template<bool B, class T>
using enable_if_t = typename std::enable_if<B, T>::type;

// /////////////////////////////////////////////////////////////////////////////
// Type safe conversions to and from scaled and unscaled coordinates
// /////////////////////////////////////////////////////////////////////////////

// A meta-predicate which is true for integers wider than or equal to coord_t
template<class I> struct is_scaled_coord
{
    static const SLIC3R_CONSTEXPR bool value =
        std::is_integral<I>::value &&
        std::numeric_limits<I>::digits >=
            std::numeric_limits<coord_t>::digits;
};

// Meta predicates for floating, 'scaled coord' and generic arithmetic types
template<class T>
using FloatingOnly = enable_if_t<std::is_floating_point<T>::value, T>;

template<class T>
using ScaledCoordOnly = enable_if_t<is_scaled_coord<T>::value, T>;

template<class T>
using ArithmeticOnly = enable_if_t<std::is_arithmetic<T>::value, T>;

// A shorter form for a generic Eigen vector which is widely used in PrusaSlicer
template<class T, int N>
using EigenVec = Eigen::Matrix<T, N, 1, Eigen::DontAlign>;

// Semantics are the following:
// Upscaling (scaled()): only from floating point types (or Vec) to either
//                       floating point or integer 'scaled coord' coordinates.
// Downscaling (unscaled()): from arithmetic types (or Vec) to either
//                           floating point only

// Conversion definition from unscaled to floating point scaled
template<class Tout,
         class Tin,
         class = FloatingOnly<Tin>,
         class = FloatingOnly<Tout>>
inline SLIC3R_CONSTEXPR Tout scaled(const Tin &v) SLIC3R_NOEXCEPT
{
    return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR));
}

// Conversion definition from unscaled to integer 'scaled coord'.
// TODO: is the rounding necessary ? Here it is to show that it can be different
// but it does not have to be. Using std::round means loosing noexcept and
// constexpr modifiers
template<class Tout = coord_t, class Tin, class = FloatingOnly<Tin>>
inline SLIC3R_CONSTEXPR ScaledCoordOnly<Tout> scaled(const Tin &v) SLIC3R_NOEXCEPT
{
    //return static_cast<Tout>(std::round(v / SCALING_FACTOR));
    return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR));
}

// Conversion for Eigen vectors (N dimensional points)
template<class Tout = coord_t, class Tin, int N, class = FloatingOnly<Tin>>
inline EigenVec<ArithmeticOnly<Tout>, N> scaled(const EigenVec<Tin, N> &v)
{
    return (v / SCALING_FACTOR).template cast<Tout>();
}

// Conversion from arithmetic scaled type to floating point unscaled
template<class Tout = double,
         class Tin,
         class = ArithmeticOnly<Tin>,
         class = FloatingOnly<Tout>>
inline SLIC3R_CONSTEXPR Tout unscaled(const Tin &v) SLIC3R_NOEXCEPT
{
    return static_cast<Tout>(v * static_cast<Tout>(SCALING_FACTOR));
}

// Unscaling for Eigen vectors. Input base type can be arithmetic, output base
// type can only be floating point.
template<class Tout = double,
         class Tin,
         int N,
         class = ArithmeticOnly<Tin>,
         class = FloatingOnly<Tout>>
inline SLIC3R_CONSTEXPR EigenVec<Tout, N> unscaled(
    const EigenVec<Tin, N> &v) SLIC3R_NOEXCEPT
{
    return v.template cast<Tout>() * SCALING_FACTOR;
}

} // namespace Slic3r

#endif // MTUTILS_HPP
<|endoftext|>


<commit_before>#include "pathlocks.hh"
#include "util.hh"

#include <cerrno>
#include <cstdlib>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace nix {

int openLockFile(const Path & path, bool create)
{
    AutoCloseFD fd;

    fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0666);
    if (fd == -1 && (create || errno != ENOENT))
        throw SysError(format("opening lock file `%1%'") % path);

    return fd.borrow();
}

void deleteLockFile(const Path & path, int fd)
{
    /* Get rid of the lock file.  Have to be careful not to introduce
       races.  Write a (meaningless) token to the file to indicate to
       other processes waiting on this lock that the lock is stale
       (deleted). */
    unlink(path.c_str());
    writeFull(fd, (const unsigned char *) "d", 1);
    /* Note that the result of unlink() is ignored; removing the lock
       file is an optimisation, not a necessity. */
}

bool lockFile(int fd, LockType lockType, bool wait)
{
    struct flock lock;
    if (lockType == ltRead) lock.l_type = F_RDLCK;
    else if (lockType == ltWrite) lock.l_type = F_WRLCK;
    else if (lockType == ltNone) lock.l_type = F_UNLCK;
    else abort();
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; /* entire file */

    if (wait) {
        while (fcntl(fd, F_SETLKW, &lock) != 0) {
            checkInterrupt();
            if (errno != EINTR)
                throw SysError(format("acquiring/releasing lock"));
        }
    } else {
        while (fcntl(fd, F_SETLK, &lock) != 0) {
            checkInterrupt();
            if (errno == EACCES || errno == EAGAIN) return false;
            if (errno != EINTR)
                throw SysError(format("acquiring/releasing lock"));
        }
    }

    return true;
}

/* This enables us to check whether are not already holding a lock on
   a file ourselves.  POSIX locks (fcntl) suck in this respect: if we
   close a descriptor, the previous lock will be closed as well.  And
   there is no way to query whether we already have a lock (F_GETLK
   only works on locks held by other processes). */
static StringSet lockedPaths; /* !!! not thread-safe */

PathLocks::PathLocks()
    : deletePaths(false)
{
}

PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
    : deletePaths(false)
{
    lockPaths(paths, waitMsg);
}

bool PathLocks::lockPaths(const PathSet & _paths,
    const string & waitMsg, bool wait)
{
    assert(fds.empty());

    /* Note that `fds' is built incrementally so that the destructor
       will only release those locks that we have already acquired. */

    /* Sort the paths.  This assures that locks are always acquired in
       the same order, thus preventing deadlocks. */
    Paths paths(_paths.begin(), _paths.end());
    paths.sort();

    /* Acquire the lock for each path. */
    foreach (Paths::iterator, i, paths) {
        checkInterrupt();
        Path path = *i;
        Path lockPath = path + ".lock";

        debug(format("locking path `%1%'") % path);

        if (lockedPaths.find(lockPath) != lockedPaths.end())
            throw Error("deadlock: trying to re-acquire self-held lock");

        AutoCloseFD fd;

        while (1) {

            /* Open/create the lock file. */
	    fd = openLockFile(lockPath, true);

            /* Acquire an exclusive lock. */
            if (!lockFile(fd, ltWrite, false)) {
                if (wait) {
                    if (waitMsg != "") printMsg(lvlError, waitMsg);
                    lockFile(fd, ltWrite, true);
                } else {
                    /* Failed to lock this path; release all other
                       locks. */
                    unlock();
                    return false;
                }
            }

            debug(format("lock acquired on `%1%'") % lockPath);

            /* Check that the lock file hasn't become stale (i.e.,
               hasn't been unlinked). */
            struct stat st;
            if (fstat(fd, &st) == -1)
                throw SysError(format("statting lock file `%1%'") % lockPath);
            if (st.st_size != 0)
                /* This lock file has been unlinked, so we're holding
                   a lock on a deleted file.  This means that other
                   processes may create and acquire a lock on
                   `lockPath', and proceed.  So we must retry. */
                debug(format("open lock file `%1%' has become stale") % lockPath);
            else
                break;
        }

        /* Use borrow so that the descriptor isn't closed. */
        fds.push_back(FDPair(fd.borrow(), lockPath));
        lockedPaths.insert(lockPath);
    }

    return true;
}

PathLocks::~PathLocks()
{
    unlock();
}

void PathLocks::unlock()
{
    foreach (list<FDPair>::iterator, i, fds) {
        if (deletePaths) deleteLockFile(i->second, i->first);

        lockedPaths.erase(i->second);
        if (close(i->first) == -1)
            printMsg(lvlError,
                format("error (ignored): cannot close lock file on `%1%'") % i->second);

        debug(format("lock released on `%1%'") % i->second);
    }

    fds.clear();
}

void PathLocks::setDeletion(bool deletePaths)
{
    this->deletePaths = deletePaths;
}

bool pathIsLockedByMe(const Path & path)
{
    Path lockPath = path + ".lock";
    return lockedPaths.find(lockPath) != lockedPaths.end();
}

}
<commit_msg>* Another case of lock file permissions being too liberal.<commit_after>#include "pathlocks.hh"
#include "util.hh"

#include <cerrno>
#include <cstdlib>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace nix {

int openLockFile(const Path & path, bool create)
{
    AutoCloseFD fd;

    fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0600);
    if (fd == -1 && (create || errno != ENOENT))
        throw SysError(format("opening lock file `%1%'") % path);

    return fd.borrow();
}

void deleteLockFile(const Path & path, int fd)
{
    /* Get rid of the lock file.  Have to be careful not to introduce
       races.  Write a (meaningless) token to the file to indicate to
       other processes waiting on this lock that the lock is stale
       (deleted). */
    unlink(path.c_str());
    writeFull(fd, (const unsigned char *) "d", 1);
    /* Note that the result of unlink() is ignored; removing the lock
       file is an optimisation, not a necessity. */
}

bool lockFile(int fd, LockType lockType, bool wait)
{
    struct flock lock;
    if (lockType == ltRead) lock.l_type = F_RDLCK;
    else if (lockType == ltWrite) lock.l_type = F_WRLCK;
    else if (lockType == ltNone) lock.l_type = F_UNLCK;
    else abort();
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; /* entire file */

    if (wait) {
        while (fcntl(fd, F_SETLKW, &lock) != 0) {
            checkInterrupt();
            if (errno != EINTR)
                throw SysError(format("acquiring/releasing lock"));
        }
    } else {
        while (fcntl(fd, F_SETLK, &lock) != 0) {
            checkInterrupt();
            if (errno == EACCES || errno == EAGAIN) return false;
            if (errno != EINTR)
                throw SysError(format("acquiring/releasing lock"));
        }
    }

    return true;
}

/* This enables us to check whether are not already holding a lock on
   a file ourselves.  POSIX locks (fcntl) suck in this respect: if we
   close a descriptor, the previous lock will be closed as well.  And
   there is no way to query whether we already have a lock (F_GETLK
   only works on locks held by other processes). */
static StringSet lockedPaths; /* !!! not thread-safe */

PathLocks::PathLocks()
    : deletePaths(false)
{
}

PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
    : deletePaths(false)
{
    lockPaths(paths, waitMsg);
}

bool PathLocks::lockPaths(const PathSet & _paths,
    const string & waitMsg, bool wait)
{
    assert(fds.empty());

    /* Note that `fds' is built incrementally so that the destructor
       will only release those locks that we have already acquired. */

    /* Sort the paths.  This assures that locks are always acquired in
       the same order, thus preventing deadlocks. */
    Paths paths(_paths.begin(), _paths.end());
    paths.sort();

    /* Acquire the lock for each path. */
    foreach (Paths::iterator, i, paths) {
        checkInterrupt();
        Path path = *i;
        Path lockPath = path + ".lock";

        debug(format("locking path `%1%'") % path);

        if (lockedPaths.find(lockPath) != lockedPaths.end())
            throw Error("deadlock: trying to re-acquire self-held lock");

        AutoCloseFD fd;

        while (1) {

            /* Open/create the lock file. */
	    fd = openLockFile(lockPath, true);

            /* Acquire an exclusive lock. */
            if (!lockFile(fd, ltWrite, false)) {
                if (wait) {
                    if (waitMsg != "") printMsg(lvlError, waitMsg);
                    lockFile(fd, ltWrite, true);
                } else {
                    /* Failed to lock this path; release all other
                       locks. */
                    unlock();
                    return false;
                }
            }

            debug(format("lock acquired on `%1%'") % lockPath);

            /* Check that the lock file hasn't become stale (i.e.,
               hasn't been unlinked). */
            struct stat st;
            if (fstat(fd, &st) == -1)
                throw SysError(format("statting lock file `%1%'") % lockPath);
            if (st.st_size != 0)
                /* This lock file has been unlinked, so we're holding
                   a lock on a deleted file.  This means that other
                   processes may create and acquire a lock on
                   `lockPath', and proceed.  So we must retry. */
                debug(format("open lock file `%1%' has become stale") % lockPath);
            else
                break;
        }

        /* Use borrow so that the descriptor isn't closed. */
        fds.push_back(FDPair(fd.borrow(), lockPath));
        lockedPaths.insert(lockPath);
    }

    return true;
}

PathLocks::~PathLocks()
{
    unlock();
}

void PathLocks::unlock()
{
    foreach (list<FDPair>::iterator, i, fds) {
        if (deletePaths) deleteLockFile(i->second, i->first);

        lockedPaths.erase(i->second);
        if (close(i->first) == -1)
            printMsg(lvlError,
                format("error (ignored): cannot close lock file on `%1%'") % i->second);

        debug(format("lock released on `%1%'") % i->second);
    }

    fds.clear();
}

void PathLocks::setDeletion(bool deletePaths)
{
    this->deletePaths = deletePaths;
}

bool pathIsLockedByMe(const Path & path)
{
    Path lockPath = path + ".lock";
    return lockedPaths.find(lockPath) != lockedPaths.end();
}

}
<|endoftext|>


<commit_before>#include <map>

#include <llvm/IR/Value.h>
#include <llvm/IR/Instruction.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/raw_ostream.h>

#include "LLVMNode.h"
#include "LLVMDependenceGraph.h"
#include "ReachingDefs.h"
#include "AnalysisGeneric.h"
#include "DefMap.h"

#include "analysis/DFS.h"
#include "llvm-debug.h"

using namespace llvm;

namespace dg {
namespace analysis {

LLVMReachingDefsAnalysis::LLVMReachingDefsAnalysis(LLVMDependenceGraph *dg)
    : DataFlowAnalysis<LLVMNode>(dg->getEntryBB(), DATAFLOW_INTERPROCEDURAL),
      dg(dg)
{
    Module *m = dg->getModule();
    // set data layout
    DL = m->getDataLayout();
}

Pointer LLVMReachingDefsAnalysis::getConstantExprPointer(const ConstantExpr *CE)
{
    return dg::analysis::getConstantExprPointer(CE, dg, DL);
}

LLVMNode *LLVMReachingDefsAnalysis::getOperand(LLVMNode *node,
                                               const Value *val,
                                               unsigned int idx)
{
    return dg::analysis::getOperand(node, val, idx, DL);
}

// FIXME don't duplicate the code from DefUse.cpp
static DefMap *getDefMap(LLVMNode *n)
{
    DefMap *r = n->getData<DefMap>();
    if (!r) {
        r = new DefMap();
        n->setData(r);
    }

    // must always have
    assert(r);

    return r;
}

/// --------------------------------------------------
//   Reaching definitions analysis
/// --------------------------------------------------

static bool handleParam(LLVMNode *node, LLVMNode *to,
                        DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;
    for (const Pointer& ptr : node->getPointsTo()) {
        // check if memory that is pointed by ptr
        // with _arbitrary_ offset is defined in the
        // subprocedure
        for (auto it : *subgraph_df) {
            if (it.first.obj == ptr.obj)
                changed |= df->add(it.first, to);
        }
    }

    return changed;
}

static bool handleParamsGlobals(LLVMDependenceGraph *dg,
                                LLVMDGParameters *params,
                                DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;
    for (auto I = params->global_begin(), E = params->global_end(); I != E; ++I) {
        LLVMDGParameter& p = I->second;

        // get the global node, it contains the points-to set
        LLVMNode *glob = dg->getNode(I->first);
        if (!glob) {
            errs() << "ERR: no global node for param\n";
            continue;
        }

        changed |= handleParam(glob, p.out, df, subgraph_df);
    }

    return changed;
}

static bool handleParams(LLVMNode *callNode, LLVMDGParameters *params,
                         DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;

    // operand[0] is the called func
    for (int i = 1, e = callNode->getOperandsNum(); i < e; ++i) {
        LLVMNode *op = callNode->getOperand(i);
        if (!op)
            continue;

        if (!op->isPointerTy())
            continue;

        LLVMDGParameter *p = params->find(op->getKey());
        if (!p) {
            DBG("ERR: no actual param for " << *op->getKey());
            continue;
        }

        changed |= handleParam(op, p->out, df, subgraph_df);
    }

    return changed;
}

static bool handleParams(LLVMNode *callNode, DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;

    // get actual parameters (operands) and for every pointer in there
    // check if the memory location it points to gets defined
    // in the subprocedure
    LLVMDGParameters *params = callNode->getParameters();
    // if we have params, process params
    if (!params)
        return false;

    changed |= handleParams(callNode, params, df, subgraph_df);
    changed |= handleParamsGlobals(callNode->getDG(), params, df, subgraph_df);

    return changed;
}

bool LLVMReachingDefsAnalysis::handleUndefinedCall(LLVMNode *callNode,
                                                   const CallInst *CI,
                                                   DefMap *df)
{
    bool changed = false;
    for (unsigned n = 1, e = callNode->getOperandsNum(); n < e; ++n) {
        const Value *llvmOp = CI->getOperand(n - 1);
        if (!llvmOp->getType()->isPointerTy())
            continue;

        LLVMNode *op = getOperand(callNode, llvmOp, n);
        assert(op && "unhandled pointer operand in undef call");

        // with undefined call we must assume that any
        // memory that was passed via pointer was modified
        // and on unknown offset
        // XXX we should handle external globals too
        for (const Pointer& ptr : op->getPointsTo())
            changed |= df->add(Pointer(ptr.obj, UNKNOWN_OFFSET), callNode);
    }

    return changed;
}

bool LLVMReachingDefsAnalysis::handleIntrinsicCall(LLVMNode *callNode,
                                                   const CallInst *CI,
                                                   DefMap *df)
{
    bool changed = false;
    const IntrinsicInst *I = cast<IntrinsicInst>(CI);
    const Value *dest;

    switch (I->getIntrinsicID())
    {
        case Intrinsic::memmove:
        case Intrinsic::memcpy:
        case Intrinsic::memset:
            dest = I->getOperand(0);
            break;
        default:
            return handleUndefinedCall(callNode, CI, df);
    }

    LLVMNode *destNode = getOperand(callNode, dest, 1);
    assert(destNode && "No operand for intrinsic call");

    for (const Pointer& ptr : destNode->getPointsTo()) {
        // we could compute all the concrete offsets, but
        // these functions usually set the whole memory,
        // so if we use UNKNOWN_OFFSET, the effect is the same
        changed |= df->add(Pointer(ptr.obj, UNKNOWN_OFFSET), callNode);
    }

    return changed;
}

bool LLVMReachingDefsAnalysis::handleUndefinedCall(LLVMNode *callNode,
                                                   DefMap *df)
{
    const CallInst *CI = cast<CallInst>(callNode->getKey());
    const Function *func = dyn_cast<Function>(CI->getCalledValue()->stripPointerCasts());
    if (func && func->isIntrinsic())
        return handleIntrinsicCall(callNode, CI, df);

    return handleUndefinedCall(callNode, CI, df);
}

bool LLVMReachingDefsAnalysis::handleCallInst(LLVMDependenceGraph *graph,
                                              LLVMNode *callNode, DefMap *df)
{
    bool changed = false;

    LLVMNode *exitNode = graph->getExit();
    assert(exitNode && "No exit node in subgraph");

    DefMap *subgraph_df = getDefMap(exitNode);
    // now handle all parameters
    // and global variables that are as parameters
    changed |= handleParams(callNode, df, subgraph_df);

    return changed;
}

bool LLVMReachingDefsAnalysis::handleCallInst(LLVMNode *callNode, DefMap *df)
{
    bool changed = false;

    if (!callNode->hasSubgraphs())
        return handleUndefinedCall(callNode, df);

    for (LLVMDependenceGraph *subgraph : callNode->getSubgraphs())
        changed |= handleCallInst(subgraph, callNode, df);

    return changed;
}

static bool handleStoreInst(LLVMNode *storeNode, DefMap *df,
                            PointsToSetT *&strong_update)
{
    bool changed = false;
    LLVMNode *ptrNode = storeNode->getOperand(0);
    assert(ptrNode && "No pointer operand");

    // update definitions
    PointsToSetT& S = ptrNode->getPointsTo();
    if (S.size() == 1) {// strong update
        changed |= df->update(*S.begin(), storeNode);
        strong_update = &S;
    } else { // weak update
        for (const Pointer& ptr : ptrNode->getPointsTo())
            changed |= df->add(ptr, storeNode);
    }

    return changed;
}

bool LLVMReachingDefsAnalysis::runOnNode(LLVMNode *node)
{
    bool changed = false;
    // pointers that should not be updated
    // because they were updated strongly
    PointsToSetT *strong_update = nullptr;

    // update states according to predcessors
    DefMap *df = getDefMap(node);
    LLVMNode *pred = node->getPredcessor();
    if (pred) {
        const Value *predVal = pred->getKey();
        // if the predcessor is StoreInst, it add and may kill some definitions
        if (isa<StoreInst>(predVal))
            changed |= dg::analysis::handleStoreInst(pred, df, strong_update);
        // call inst may add some definitions to (StoreInst in subgraph)
        else if (isa<CallInst>(predVal))
            changed |= handleCallInst(pred, df);

        DefMap *pred_df = getDefMap(pred);
        changed |= df->merge(pred_df, strong_update);
        // either we have nothing to merge or we merged something
        // if predecessor has something
        assert(pred_df->empty() || !df->empty());
    } else { // BB predcessors
        LLVMBBlock *BB = node->getBBlock();
        assert(BB && "Node has no BB");

        for (LLVMBBlock *predBB : BB->predcessors()) {
            pred = predBB->getLastNode();
            assert(pred && "BB has no last node");

            const Value *predVal = pred->getKey();

            if (isa<StoreInst>(predVal))
                changed |= dg::analysis::handleStoreInst(pred, df, strong_update);
            else if (isa<CallInst>(predVal))
                changed |= handleCallInst(pred, df);

            DefMap *pred_df = getDefMap(pred);
            changed |= df->merge(pred_df, nullptr);
            assert(pred_df->empty() || !df->empty());
        }
    }

    return changed;
}

} // namespace analysis
} // namespace dg
<commit_msg>reaching-def: fix mem locations with unknown offset<commit_after>#include <map>

#include <llvm/IR/Value.h>
#include <llvm/IR/Instruction.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/raw_ostream.h>

#include "LLVMNode.h"
#include "LLVMDependenceGraph.h"
#include "ReachingDefs.h"
#include "AnalysisGeneric.h"
#include "DefMap.h"

#include "analysis/DFS.h"
#include "llvm-debug.h"

using namespace llvm;

namespace dg {
namespace analysis {

LLVMReachingDefsAnalysis::LLVMReachingDefsAnalysis(LLVMDependenceGraph *dg)
    : DataFlowAnalysis<LLVMNode>(dg->getEntryBB(), DATAFLOW_INTERPROCEDURAL),
      dg(dg)
{
    Module *m = dg->getModule();
    // set data layout
    DL = m->getDataLayout();
}

Pointer LLVMReachingDefsAnalysis::getConstantExprPointer(const ConstantExpr *CE)
{
    return dg::analysis::getConstantExprPointer(CE, dg, DL);
}

LLVMNode *LLVMReachingDefsAnalysis::getOperand(LLVMNode *node,
                                               const Value *val,
                                               unsigned int idx)
{
    return dg::analysis::getOperand(node, val, idx, DL);
}

// FIXME don't duplicate the code from DefUse.cpp
static DefMap *getDefMap(LLVMNode *n)
{
    DefMap *r = n->getData<DefMap>();
    if (!r) {
        r = new DefMap();
        n->setData(r);
    }

    // must always have
    assert(r);

    return r;
}

/// --------------------------------------------------
//   Reaching definitions analysis
/// --------------------------------------------------

static bool handleParam(LLVMNode *node, LLVMNode *to,
                        DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;
    for (const Pointer& ptr : node->getPointsTo()) {
        // check if memory that is pointed by ptr
        // with _arbitrary_ offset is defined in the
        // subprocedure
        for (auto it : *subgraph_df) {
            if (it.first.obj == ptr.obj)
                changed |= df->add(it.first, to);
        }
    }

    return changed;
}

static bool handleParamsGlobals(LLVMDependenceGraph *dg,
                                LLVMDGParameters *params,
                                DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;
    for (auto I = params->global_begin(), E = params->global_end(); I != E; ++I) {
        LLVMDGParameter& p = I->second;

        // get the global node, it contains the points-to set
        LLVMNode *glob = dg->getNode(I->first);
        if (!glob) {
            errs() << "ERR: no global node for param\n";
            continue;
        }

        changed |= handleParam(glob, p.out, df, subgraph_df);
    }

    return changed;
}

static bool handleParams(LLVMNode *callNode, LLVMDGParameters *params,
                         DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;

    // operand[0] is the called func
    for (int i = 1, e = callNode->getOperandsNum(); i < e; ++i) {
        LLVMNode *op = callNode->getOperand(i);
        if (!op)
            continue;

        if (!op->isPointerTy())
            continue;

        LLVMDGParameter *p = params->find(op->getKey());
        if (!p) {
            DBG("ERR: no actual param for " << *op->getKey());
            continue;
        }

        changed |= handleParam(op, p->out, df, subgraph_df);
    }

    return changed;
}

static bool handleParams(LLVMNode *callNode, DefMap *df, DefMap *subgraph_df)
{
    bool changed = false;

    // get actual parameters (operands) and for every pointer in there
    // check if the memory location it points to gets defined
    // in the subprocedure
    LLVMDGParameters *params = callNode->getParameters();
    // if we have params, process params
    if (!params)
        return false;

    changed |= handleParams(callNode, params, df, subgraph_df);
    changed |= handleParamsGlobals(callNode->getDG(), params, df, subgraph_df);

    return changed;
}

bool LLVMReachingDefsAnalysis::handleUndefinedCall(LLVMNode *callNode,
                                                   const CallInst *CI,
                                                   DefMap *df)
{
    bool changed = false;
    for (unsigned n = 1, e = callNode->getOperandsNum(); n < e; ++n) {
        const Value *llvmOp = CI->getOperand(n - 1);
        if (!llvmOp->getType()->isPointerTy())
            continue;

        LLVMNode *op = getOperand(callNode, llvmOp, n);
        assert(op && "unhandled pointer operand in undef call");

        // with undefined call we must assume that any
        // memory that was passed via pointer was modified
        // and on unknown offset
        // XXX we should handle external globals too
        for (const Pointer& ptr : op->getPointsTo())
            changed |= df->add(Pointer(ptr.obj, UNKNOWN_OFFSET), callNode);
    }

    return changed;
}

bool LLVMReachingDefsAnalysis::handleIntrinsicCall(LLVMNode *callNode,
                                                   const CallInst *CI,
                                                   DefMap *df)
{
    bool changed = false;
    const IntrinsicInst *I = cast<IntrinsicInst>(CI);
    const Value *dest;

    switch (I->getIntrinsicID())
    {
        case Intrinsic::memmove:
        case Intrinsic::memcpy:
        case Intrinsic::memset:
            dest = I->getOperand(0);
            break;
        default:
            return handleUndefinedCall(callNode, CI, df);
    }

    LLVMNode *destNode = getOperand(callNode, dest, 1);
    assert(destNode && "No operand for intrinsic call");

    for (const Pointer& ptr : destNode->getPointsTo()) {
        // we could compute all the concrete offsets, but
        // these functions usually set the whole memory,
        // so if we use UNKNOWN_OFFSET, the effect is the same
        changed |= df->add(Pointer(ptr.obj, UNKNOWN_OFFSET), callNode);
    }

    return changed;
}

bool LLVMReachingDefsAnalysis::handleUndefinedCall(LLVMNode *callNode,
                                                   DefMap *df)
{
    const CallInst *CI = cast<CallInst>(callNode->getKey());
    const Function *func = dyn_cast<Function>(CI->getCalledValue()->stripPointerCasts());
    if (func && func->isIntrinsic())
        return handleIntrinsicCall(callNode, CI, df);

    return handleUndefinedCall(callNode, CI, df);
}

bool LLVMReachingDefsAnalysis::handleCallInst(LLVMDependenceGraph *graph,
                                              LLVMNode *callNode, DefMap *df)
{
    bool changed = false;

    LLVMNode *exitNode = graph->getExit();
    assert(exitNode && "No exit node in subgraph");

    DefMap *subgraph_df = getDefMap(exitNode);
    // now handle all parameters
    // and global variables that are as parameters
    changed |= handleParams(callNode, df, subgraph_df);

    return changed;
}

bool LLVMReachingDefsAnalysis::handleCallInst(LLVMNode *callNode, DefMap *df)
{
    bool changed = false;

    if (!callNode->hasSubgraphs())
        return handleUndefinedCall(callNode, df);

    for (LLVMDependenceGraph *subgraph : callNode->getSubgraphs())
        changed |= handleCallInst(subgraph, callNode, df);

    return changed;
}

static bool handleStoreInst(LLVMNode *storeNode, DefMap *df,
                            PointsToSetT *&strong_update)
{
    bool changed = false;
    LLVMNode *ptrNode = storeNode->getOperand(0);
    assert(ptrNode && "No pointer operand");

    // update definitions
    PointsToSetT& S = ptrNode->getPointsTo();
    // if we have only one concrete pointer (known pointer
    // with known offset), it is safe to do strong update
    if (S.size() == 1) {
        const Pointer& ptr = *S.begin();
        if (ptr.isKnown() && !ptr.offset.isUnknown()) {
            changed |= df->update(ptr, storeNode);
            strong_update = &S;
            return changed;
        }

        // else fall-through to weak update
    }

    // weak update
    for (const Pointer& ptr : ptrNode->getPointsTo())
        changed |= df->add(ptr, storeNode);

    return changed;
}

bool LLVMReachingDefsAnalysis::runOnNode(LLVMNode *node)
{
    bool changed = false;
    // pointers that should not be updated
    // because they were updated strongly
    PointsToSetT *strong_update = nullptr;

    // update states according to predcessors
    DefMap *df = getDefMap(node);
    LLVMNode *pred = node->getPredcessor();
    if (pred) {
        const Value *predVal = pred->getKey();
        // if the predcessor is StoreInst, it add and may kill some definitions
        if (isa<StoreInst>(predVal))
            changed |= dg::analysis::handleStoreInst(pred, df, strong_update);
        // call inst may add some definitions to (StoreInst in subgraph)
        else if (isa<CallInst>(predVal))
            changed |= handleCallInst(pred, df);

        DefMap *pred_df = getDefMap(pred);
        changed |= df->merge(pred_df, strong_update);
        // either we have nothing to merge or we merged something
        // if predecessor has something
        assert(pred_df->empty() || !df->empty());
    } else { // BB predcessors
        LLVMBBlock *BB = node->getBBlock();
        assert(BB && "Node has no BB");

        for (LLVMBBlock *predBB : BB->predcessors()) {
            pred = predBB->getLastNode();
            assert(pred && "BB has no last node");

            const Value *predVal = pred->getKey();

            if (isa<StoreInst>(predVal))
                changed |= dg::analysis::handleStoreInst(pred, df, strong_update);
            else if (isa<CallInst>(predVal))
                changed |= handleCallInst(pred, df);

            DefMap *pred_df = getDefMap(pred);
            changed |= df->merge(pred_df, nullptr);
            assert(pred_df->empty() || !df->empty());
        }
    }

    return changed;
}

} // namespace analysis
} // namespace dg
<|endoftext|>


<commit_before>//This is the ROOT program that will take in the
//root file, extract some specific entries,
//calculate the bjet, and normalize the event weight, then recombing it to the new root file.
//User should modify the path and name of the file to meet his or her own concern.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Author: Maxwell Cui
//Date: 5.20.2017
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include<cstdlib>

void gnr(TFile* inputFile,TString outputName)
{
  TTree *oldTree=new TTree;
  inputFile->GetObject("nominal_Loose",oldTree);

  //Deactive all branches
  oldTree->SetBranchStatus("*",0);

  //Active the interested branches
  oldTree->SetBranchStatus("ht",1);
  oldTree->SetBranchStatus("met_met",1);
  oldTree->SetBranchStatus("met_sumet",1);
  oldTree->SetBranchStatus("mu_pt",1);
  oldTree->SetBranchStatus("el_pt",1);
  oldTree->SetBranchStatus("mu",1);
  oldTree->SetBranchStatus("jet_pt",1);
  oldTree->SetBranchStatus("met_phi",1);
  oldTree->SetBranchStatus("SSee",1);
  oldTree->SetBranchStatus("SSmm",1);
  oldTree->SetBranchStatus("SSem",1);
  oldTree->SetBranchStatus("eee",1);
  oldTree->SetBranchStatus("eem",1);
  oldTree->SetBranchStatus("emm",1);
  oldTree->SetBranchStatus("mmm",1);
  oldTree->SetBranchStatus("lep_pt",1);
  oldTree->SetBranchStatus("jet_mv2c20",1);
  oldTree->SetBranchStatus("weight_mc",1);

  //====================Output file==========================
  //
  TFile *outputFile=new TFile(outputName,"recreate");
  //
  //=========================================================

  //Copy to new tree
  TTree *newTree=oldTree->CloneTree();

  //Working with b-jet
  //
  // Declaration of leaf types
  Float_t         mu;
  vector<float>   *el_pt;
  vector<float>   *mu_pt;
  vector<float>   *jet_pt;
  vector<float>   *jet_mv2c20;
  Float_t         met_met;
  Float_t         met_phi;
  Float_t         met_sumet;
  Int_t           SSee;
  Int_t           SSmm;
  Int_t           SSem;
  Int_t           eee;
  Int_t           eem;
  Int_t           emm;
  Int_t           mmm;
  vector<float>   *lep_pt;
  Float_t         ht;

  // List of branches
  TBranch        *b_mu;   //!
  TBranch        *b_el_pt;   //!
  TBranch        *b_mu_pt;   //!
  TBranch        *b_jet_pt;   //!
  TBranch        *b_jet_mv2c20;   //!
  TBranch        *b_met_met;   //!
  TBranch        *b_met_sumet;
  TBranch        *b_met_phi;   //!
  TBranch        *b_SSee;   //!
  TBranch        *b_SSmm;   //!
  TBranch        *b_SSem;   //!
  TBranch        *b_eee;   //!
  TBranch        *b_eem;   //!
  TBranch        *b_emm;   //!
  TBranch        *b_mmm;   //!
  TBranch        *b_lep_pt;   //!
  TBranch        *b_ht;   //!

  //Set object pointer
  el_pt = 0;
  mu_pt = 0;
  jet_pt = 0;
  jet_mv2c20 = 0;
  lep_pt = 0;

  //Set branch addresses and brunch pointers
  oldTree->SetBranchAddress("mu", &mu, &b_mu);
  oldTree->SetBranchAddress("el_pt", &el_pt, &b_el_pt);
  oldTree->SetBranchAddress("mu_pt", &mu_pt, &b_mu_pt);
  oldTree->SetBranchAddress("jet_pt", &jet_pt, &b_jet_pt);
  oldTree->SetBranchAddress("jet_mv2c20", &jet_mv2c20, &b_jet_mv2c20);
  oldTree->SetBranchAddress("met_met", &met_met, &b_met_met);
  oldTree->SetBranchAddress("met_phi", &met_phi, &b_met_phi);
  oldTree->SetBranchAddress("met_sumet", &met_sumet, &b_met_sumet);
  oldTree->SetBranchAddress("SSee", &SSee, &b_SSee);
  oldTree->SetBranchAddress("SSmm", &SSmm, &b_SSmm);
  oldTree->SetBranchAddress("SSem", &SSem, &b_SSem);
  oldTree->SetBranchAddress("eee", &eee, &b_eee);
  oldTree->SetBranchAddress("eem", &eem, &b_eem);
  oldTree->SetBranchAddress("emm", &emm, &b_emm);
  oldTree->SetBranchAddress("mmm", &mmm, &b_mmm);
  oldTree->SetBranchAddress("lep_pt", &lep_pt, &b_lep_pt);
  oldTree->SetBranchAddress("ht", &ht, &b_ht);

  //Declare bjet variable
  Int_t bj;

  //Add new branch
  TBranch *bjBranch=newTree->Branch("bj",&bj,"bj/I");

  Int_t nentries=(Int_t)oldTree->GetEntries();

  //Calculate bjet, algorithm is from SelOpt.C provided by Prof. Varnes
  for(Int_t i=0;i<nentries;i++)
    {
      oldTree->GetEntry(i);
      bj=0;
      if(SSee || SSem || SSmm || eee || eem || emm || mmm)
      	{
      	  for(unsigned int ibjet=0;ibjet<jet_mv2c20->size();ibjet++)
      	    {
      	      if(jet_mv2c20->at(ibjet)>-0.4434) //don't know why is this. //It's actually from some other ML result
      		{
      		  bj++;
      		}

      	    }
      	}
      bjBranch->Fill();
    }

  //Working with normalization
  //
  Float_t lumi=30;   //Number is from Prof. Varnes

  //Declare leaf and branch
  Float_t         weight_mc;
  TBranch        *b_weight_mc;   //!

  //Set branch addresses and brunch pointers
  oldTree->SetBranchAddress("weight_mc", &weight_mc, &b_weight_mc);

  //Get the data...algrithm is from Prof. Varnes
  TH1F *lumInt=new TH1F;
  inputFile->GetObject("hIntLum",lumInt);
  Float_t mcnorm=lumInt->GetBinContent(1);

  //Declare variable for event weight
  Float_t evtWeight;
  TBranch *ewBranch=newTree->Branch("evtWeight",&evtWeight,"evtWeight/F");

  for(Int_t i=0;i<nentries;i++)
    {
      oldTree->GetEntry(i);
      evtWeight=weight_mc*lumi/mcnorm;
      ewBranch->Fill();
    }

  //------------------
  newTree->Print();
  newTree->Write();

  outputFile->Close();

  delete oldTree;
  delete lumInt;
}

void gnrTree()
{

  //=========================Input file===========================================
  //Signal
  //TFile *s1=new TFile("../data/signal/BBS_M800_302490_sig_2016_25nsTOPQ1.root");
  //----------
  //Background
  // TFile *b1=new TFile("../data/background/ttW_np0_410066_bkg_2016_25nsTOPQ1.root");
  // TFile *b2=new TFile("../data/background/ttW_np1_410067_bkg_2016_25nsTOPQ1.root");
  // TFile *b3=new TFile("../data/background/ttW_np2_410068_bkg_2016_25nsTOPQ1.root");
  // TFile *b4=new TFile("../data/background/ttH.root");
  // TFile *b5=new TFile("../data/background/VV.root");
  // TFile *b6=new TFile("../data/background/VVV.root");
  //==============================================================================

  //Data path
  const char* PATH=getenv("MCDATA");
  std::cout<<"\nThe data file path is: "<<PATH<<std::endl;

  //Signal
  TFile *s1=new TFile(PATH+
		      "signals/BBS_M800_302490_SSsig_deterre_Apr2017_36p1ifb_25nsTOPQ1_eLHM.root");
  TFile *b1=new TFile(PATH+"diboson_llll_361063_SSbkg_deterre_Apr2017_36p1ifb_25nsTOPQ1_eLHM.root");

  gnr(s1,"BBS_sig.root");
  delete s1;

  gnr(b1,"ttW_np0_bkg.root");
  delete b1;

  // gnr(b2,"ttW_np1_bkg.root");
  // delete b2;

  // gnr(b3,"ttW_np2_bkg.root");
  // delete b3;

  // gnr(b4,"ttH.root");
  // delete b4;

  // gnr(b5,"VV.root");
  // delete b5;

  // gnr(b6,"VVV.root");
  // delete b6;

}
<commit_msg>update input<commit_after>//This is the ROOT program that will take in the
//root file, extract some specific entries,
//calculate the bjet, and normalize the event weight, then recombing it to the new root file.
//User should modify the path and name of the file to meet his or her own concern.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Author: Maxwell Cui
//Date: 5.20.2017
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include<cstdlib>
#include<iostream>
#include<cstdlib>

void gnr(TFile* inputFile,TString outputName)
{
  TTree *oldTree=new TTree;
  inputFile->GetObject("nominal_Loose",oldTree);

  //Deactive all branches
  oldTree->SetBranchStatus("*",0);

  //Active the interested branches
  oldTree->SetBranchStatus("ht",1);
  oldTree->SetBranchStatus("met_met",1);
  oldTree->SetBranchStatus("met_sumet",1);
  oldTree->SetBranchStatus("mu_pt",1);
  oldTree->SetBranchStatus("el_pt",1);
  oldTree->SetBranchStatus("mu",1);
  oldTree->SetBranchStatus("jet_pt",1);
  oldTree->SetBranchStatus("met_phi",1);
  oldTree->SetBranchStatus("SSee",1);
  oldTree->SetBranchStatus("SSmm",1);
  oldTree->SetBranchStatus("SSem",1);
  oldTree->SetBranchStatus("eee",1);
  oldTree->SetBranchStatus("eem",1);
  oldTree->SetBranchStatus("emm",1);
  oldTree->SetBranchStatus("mmm",1);
  oldTree->SetBranchStatus("lep_pt",1);
  oldTree->SetBranchStatus("jet_mv2c20",1);
  oldTree->SetBranchStatus("weight_mc",1);

  //====================Output file==========================
  //
  TFile *outputFile=new TFile(outputName,"recreate");
  //
  //=========================================================

  //Copy to new tree
  TTree *newTree=oldTree->CloneTree();

  //Working with b-jet
  //
  // Declaration of leaf types
  Float_t         mu;
  vector<float>   *el_pt;
  vector<float>   *mu_pt;
  vector<float>   *jet_pt;
  vector<float>   *jet_mv2c20;
  Float_t         met_met;
  Float_t         met_phi;
  Float_t         met_sumet;
  Int_t           SSee;
  Int_t           SSmm;
  Int_t           SSem;
  Int_t           eee;
  Int_t           eem;
  Int_t           emm;
  Int_t           mmm;
  vector<float>   *lep_pt;
  Float_t         ht;

  // List of branches
  TBranch        *b_mu;   //!
  TBranch        *b_el_pt;   //!
  TBranch        *b_mu_pt;   //!
  TBranch        *b_jet_pt;   //!
  TBranch        *b_jet_mv2c20;   //!
  TBranch        *b_met_met;   //!
  TBranch        *b_met_sumet;
  TBranch        *b_met_phi;   //!
  TBranch        *b_SSee;   //!
  TBranch        *b_SSmm;   //!
  TBranch        *b_SSem;   //!
  TBranch        *b_eee;   //!
  TBranch        *b_eem;   //!
  TBranch        *b_emm;   //!
  TBranch        *b_mmm;   //!
  TBranch        *b_lep_pt;   //!
  TBranch        *b_ht;   //!

  //Set object pointer
  el_pt = 0;
  mu_pt = 0;
  jet_pt = 0;
  jet_mv2c20 = 0;
  lep_pt = 0;

  //Set branch addresses and brunch pointers
  oldTree->SetBranchAddress("mu", &mu, &b_mu);
  oldTree->SetBranchAddress("el_pt", &el_pt, &b_el_pt);
  oldTree->SetBranchAddress("mu_pt", &mu_pt, &b_mu_pt);
  oldTree->SetBranchAddress("jet_pt", &jet_pt, &b_jet_pt);
  oldTree->SetBranchAddress("jet_mv2c20", &jet_mv2c20, &b_jet_mv2c20);
  oldTree->SetBranchAddress("met_met", &met_met, &b_met_met);
  oldTree->SetBranchAddress("met_phi", &met_phi, &b_met_phi);
  oldTree->SetBranchAddress("met_sumet", &met_sumet, &b_met_sumet);
  oldTree->SetBranchAddress("SSee", &SSee, &b_SSee);
  oldTree->SetBranchAddress("SSmm", &SSmm, &b_SSmm);
  oldTree->SetBranchAddress("SSem", &SSem, &b_SSem);
  oldTree->SetBranchAddress("eee", &eee, &b_eee);
  oldTree->SetBranchAddress("eem", &eem, &b_eem);
  oldTree->SetBranchAddress("emm", &emm, &b_emm);
  oldTree->SetBranchAddress("mmm", &mmm, &b_mmm);
  oldTree->SetBranchAddress("lep_pt", &lep_pt, &b_lep_pt);
  oldTree->SetBranchAddress("ht", &ht, &b_ht);

  //Declare bjet variable
  Int_t bj;

  //Add new branch
  TBranch *bjBranch=newTree->Branch("bj",&bj,"bj/I");

  Int_t nentries=(Int_t)oldTree->GetEntries();

  //Calculate bjet, algorithm is from SelOpt.C provided by Prof. Varnes
  for(Int_t i=0;i<nentries;i++)
    {
      oldTree->GetEntry(i);
      bj=0;
      if(SSee || SSem || SSmm || eee || eem || emm || mmm)
      	{
      	  for(unsigned int ibjet=0;ibjet<jet_mv2c20->size();ibjet++)
      	    {
      	      if(jet_mv2c20->at(ibjet)>-0.4434) //don't know why is this. //It's actually from some other ML result
      		{
      		  bj++;
      		}

      	    }
      	}
      bjBranch->Fill();
    }

  //Working with normalization
  //
  Float_t lumi=30;   //Number is from Prof. Varnes

  //Declare leaf and branch
  Float_t         weight_mc;
  TBranch        *b_weight_mc;   //!

  //Set branch addresses and brunch pointers
  oldTree->SetBranchAddress("weight_mc", &weight_mc, &b_weight_mc);

  //Get the data...algrithm is from Prof. Varnes
  TH1F *lumInt=new TH1F;
  inputFile->GetObject("hIntLum",lumInt);
  Float_t mcnorm=lumInt->GetBinContent(1);

  //Declare variable for event weight
  Float_t evtWeight;
  TBranch *ewBranch=newTree->Branch("evtWeight",&evtWeight,"evtWeight/F");

  for(Int_t i=0;i<nentries;i++)
    {
      oldTree->GetEntry(i);
      evtWeight=weight_mc*lumi/mcnorm;
      ewBranch->Fill();
    }

  //------------------
  newTree->Print();
  newTree->Write();

  outputFile->Close();

  delete oldTree;
  delete lumInt;
}

void gnrTree()
{

  //=========================Input file===========================================
  //Signal
  //TFile *s1=new TFile("../data/signal/BBS_M800_302490_sig_2016_25nsTOPQ1.root");
  //----------
  //Background
  // TFile *b1=new TFile("../data/background/ttW_np0_410066_bkg_2016_25nsTOPQ1.root");
  // TFile *b2=new TFile("../data/background/ttW_np1_410067_bkg_2016_25nsTOPQ1.root");
  // TFile *b3=new TFile("../data/background/ttW_np2_410068_bkg_2016_25nsTOPQ1.root");
  // TFile *b4=new TFile("../data/background/ttH.root");
  // TFile *b5=new TFile("../data/background/VV.root");
  // TFile *b6=new TFile("../data/background/VVV.root");
  //==============================================================================

  //Data path
  const std::string* PATH=getenv("MCDATA");
  std::cout<<"\nThe data file path is: "<<PATH<<std::endl;

  //Signal
  TFile *s1=new TFile(PATH+
		      "signals/BBS_M800_302490_SSsig_deterre_Apr2017_36p1ifb_25nsTOPQ1_eLHM.root");
  TFile *b1=new TFile(PATH+"diboson_llll_361063_SSbkg_deterre_Apr2017_36p1ifb_25nsTOPQ1_eLHM.root");

  gnr(s1,"BBS_sig.root");
  delete s1;

  gnr(b1,"ttW_np0_bkg.root");
  delete b1;

  // gnr(b2,"ttW_np1_bkg.root");
  // delete b2;

  // gnr(b3,"ttW_np2_bkg.root");
  // delete b3;

  // gnr(b4,"ttH.root");
  // delete b4;

  // gnr(b5,"VV.root");
  // delete b5;

  // gnr(b6,"VVV.root");
  // delete b6;

}
<|endoftext|>