<commit_before>// ------------------------------------------------------------------------
// Pion is a development platform for building Reactors that process Events
// ------------------------------------------------------------------------
// Copyright (C) 2007-2009 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Pion is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// Pion 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 Affero General Public License for
// more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pion.  If not, see <http://www.gnu.org/licenses/>.
//

#include <unistd.h>
#include <signal.h>
#include <iostream>
#ifndef _MSC_VER
	#include <sys/select.h>
#endif
#include <boost/scoped_array.hpp>
#include <pion/platform/CodecFactory.hpp>
#include "ScriptReactor.hpp"

using namespace pion::platform;

namespace pion {		// begin namespace pion
namespace plugins {		// begin namespace plugins

// static members of ScriptReactor

const std::string		ScriptReactor::INPUT_CODEC_ELEMENT_NAME = "InputCodec";
const std::string		ScriptReactor::OUTPUT_CODEC_ELEMENT_NAME = "OutputCodec";
const std::string		ScriptReactor::COMMAND_ELEMENT_NAME = "Command";

// ScriptReactor member functions

void ScriptReactor::start(void)
{
	ConfigWriteLock cfg_lock(*this);
	if (! m_is_running) {
		// open pipe to script
		openPipe();

		m_is_running = true;

		// spawn a new thread that will be used to read events from the script
		PION_LOG_DEBUG(m_logger, "Starting reader thread: " << getId());
		m_thread_ptr.reset(new boost::thread(boost::bind(&ScriptReactor::readEvents, this)));
	}
}

bool ScriptReactor::stopIfRunning(void)
{
	bool do_stop = false;
	{
		ConfigWriteLock cfg_lock(*this);
		if (m_is_running) {
			PION_LOG_DEBUG(m_logger, "Waiting for reader thread to finish");
			do_stop = true;
			m_is_running = false;

			// this should break the reader thread out of any blocking operations
			::close(m_output_pipe);
		}
	}

	if (do_stop) {
		// we have to release the write lock before joining, otherwise things deadlock =(
		m_thread_ptr->join();

		ConfigWriteLock cfg_lock(*this);
		m_thread_ptr.reset();
		closePipe();
	}

	return do_stop;
}

void ScriptReactor::setConfig(const Vocabulary& v, const xmlNodePtr config_ptr)
{
	// make sure it's not running (it needs to be restarted in case Command changes)
	const bool was_running = stopIfRunning();

	{
		// first set config options for the Reactor base class
		ConfigWriteLock cfg_lock(*this);
		Reactor::setConfig(v, config_ptr);

		// get the Input Codec that the Reactor should use
		if (! ConfigManager::getConfigOption(INPUT_CODEC_ELEMENT_NAME, m_input_codec_id, config_ptr))
			throw EmptyInputCodecException(getId());
		m_input_codec_ptr = getCodecFactory().getCodec(m_input_codec_id);
		PION_ASSERT(m_input_codec_ptr);

		// get the Output Codec that the Reactor should use
		if (! ConfigManager::getConfigOption(OUTPUT_CODEC_ELEMENT_NAME, m_output_codec_id, config_ptr))
			throw EmptyOutputCodecException(getId());
		m_output_codec_ptr = getCodecFactory().getCodec(m_output_codec_id);
		PION_ASSERT(m_output_codec_ptr);

		// get the script or program command to execute
		if (! ConfigManager::getConfigOption(COMMAND_ELEMENT_NAME, m_command, config_ptr))
			throw EmptyCommandException(getId());

		// break command string into a vector of arguments
		parseArguments();
	}

	// restart if the reactor was running
	if (was_running)
		start();
}

void ScriptReactor::updateVocabulary(const Vocabulary& v)
{
	// first update anything in the Reactor base class that might be needed
	ConfigWriteLock cfg_lock(*this);
	Reactor::updateVocabulary(v);

	if (m_input_codec_ptr)
		m_input_codec_ptr->updateVocabulary(v);

	if (m_output_codec_ptr)
		m_output_codec_ptr->updateVocabulary(v);
}

void ScriptReactor::updateCodecs(void)
{
	// check if a codec was deleted (if so, stop now!)
	if (! getCodecFactory().hasPlugin(m_input_codec_id)
		|| ! getCodecFactory().hasPlugin(m_output_codec_id))
	{
		stop();
	} else {
		// update the codec pointer
		ConfigWriteLock cfg_lock(*this);
		m_input_codec_ptr = getCodecFactory().getCodec(m_input_codec_id);
		m_output_codec_ptr = getCodecFactory().getCodec(m_output_codec_id);
	}
}

void ScriptReactor::process(const EventPtr& e)
{
	PION_ASSERT(m_input_stream_ptr);
	PION_ASSERT(m_input_codec_ptr);

	// lock mutex to ensure that only one Event may be written at a time
	boost::mutex::scoped_lock write_lock(m_write_mutex);

	// write the Event to the pipe
	m_input_codec_ptr->write(*m_input_stream_ptr, *e);
	if (! *m_input_stream_ptr)
		throw WriteToPipeException(getId());

	// unlock mutex after writing to pipe
	write_lock.unlock();

	// note: delivery to other reactors is handled by readEvents()
}

void ScriptReactor::readEvents(void)
{
	PION_LOG_DEBUG(m_logger, "Script reader thread is running: " << getId());

	try {

		const Event::EventType event_type(m_output_codec_ptr->getEventType());
		EventFactory event_factory;
		EventPtr event_ptr;
		bool event_read;

		struct timeval timeout;
		fd_set read_fds;
		int select_result;

		while ( isRunning() ) {

			PION_ASSERT(m_output_stream_ptr);
			PION_ASSERT(m_output_codec_ptr);

			// these need to be reset before each select()
			FD_ZERO(&read_fds);
			FD_SET(m_output_pipe, &read_fds);
			timeout.tv_sec = 0;
			timeout.tv_usec = 100000;	// microseconds (1/10 second)

			// wait for data to be available
			select_result = ::select(m_output_pipe+1, &read_fds, NULL, NULL, NULL);

			if (select_result == 1) {

				ConfigReadLock cfg_lock(*this);
				if ( ! isRunning() )	// re-check after acquiring lock
					break;

				// data is available for reading

				// get a new event from the EventFactory
				event_factory.create(event_ptr, event_type);

				// read an event using the output codec
				event_read = m_output_codec_ptr->read(*m_output_stream_ptr, *event_ptr);

				if (event_read) {

					// deliver the Event to other Reactors
					deliverEvent(event_ptr);

				} else {

					// check for read error
					throw ReadFromPipeException(getId());
				}

			} else if (select_result == 0) {

				// no data currently available
				PionScheduler::sleep(0, 100000000);

			} else {

				// error checking for data availability
				throw SelectPipeException(getId());
			}
		}

	} catch (std::exception& e) {
		ConfigWriteLock cfg_lock(*this);
		if ( isRunning() ) {
			PION_LOG_FATAL(m_logger, e.what());
			closePipe();
			m_is_running = false;
		}
	}

	PION_LOG_DEBUG(m_logger, "Script reader thread is exiting: " << getId());
}

void ScriptReactor::openPipe(void)
{
	// close first if already open
	closePipe();

	PION_LOG_DEBUG(m_logger, "Opening pipe to command: " << m_command);

	PION_ASSERT(! m_args.empty());

	// initialize pipes for reading and writing to script
	int r_pipes[2];
	int w_pipes[2];
	if ( ::pipe(r_pipes) != 0 || ::pipe(w_pipes) != 0)
		throw OpenPipeException(m_command);

	// fork child process
	m_child_pid = ::fork();

	if (m_child_pid == -1) {

		// failed to fork child process
		throw OpenPipeException(m_command);

	} else if (m_child_pid == 0) {

		// inside child process

		// close ends of pipes used by parent
		::close(w_pipes[1]);
		::close(r_pipes[0]);

		// bind other ends of pipes to STDIN/STDOUT
		::dup2(w_pipes[0], 0);	// STDIN
		::dup2(r_pipes[1], 1);	// STDOUT
		::close(w_pipes[0]);
		::close(r_pipes[1]);

		// convert argument string vector into an array of char pointers
		boost::scoped_array<char*> arg_ptr(new char*[m_args.size() + 1]);
		for (std::size_t n = 0; n < m_args.size(); ++n) {
			arg_ptr[n] = const_cast<char*>(m_args[n].c_str());
		}
		arg_ptr[ m_args.size() ] = NULL;

		// execute command (ignore return since we're in a new process anyway)
		// note: execvp is wrapper for execve() that also searches the paths
		::execvp(m_args[0].c_str(), arg_ptr.get());

	} else {

		// inside parent process

		// close ends of pipes used by child
		::close(w_pipes[0]);
		::close(r_pipes[1]);

		// set file descriptors for reading/writing
		m_input_pipe = w_pipes[1];
		m_output_pipe = r_pipes[0];
	}

	// prepare c++ streams to use pipes for reading and writing events
	m_input_streambuf_ptr.reset(new StreamBuffer( m_input_pipe ));
	m_input_stream_ptr.reset(new std::ostream(m_input_streambuf_ptr.get()));
	m_output_streambuf_ptr.reset(new StreamBuffer( m_output_pipe ));
	m_output_stream_ptr.reset(new std::istream(m_output_streambuf_ptr.get()));
}

void ScriptReactor::closePipe(void)
{
	if (m_input_pipe != -1 || m_output_pipe != -1) {
		PION_LOG_DEBUG(m_logger, "Closing pipe to command: " << m_command);
		::close(m_input_pipe);
		::close(m_output_pipe);
		::kill(m_child_pid, SIGKILL);
		m_input_stream_ptr.reset();
		m_output_stream_ptr.reset();
		m_input_streambuf_ptr.reset();
		m_output_streambuf_ptr.reset();
		m_child_pid = 0;
		m_input_pipe = m_output_pipe = -1;
	}
}

void ScriptReactor::parseArguments(void)
{
	std::string arg;			// current argument string
	std::size_t next_pos = 0;	// next parsing position
	char next_delimiter = ' ';	// next argument delimiter

	PION_LOG_DEBUG(m_logger, "Parsing out command string: " << m_command);

	// start by clearing the arguments vector
	m_args.clear();

	// step through each character in the command string
	while (next_pos < m_command.size()) {
		switch ( m_command[next_pos] ) {
		case '\\':
			if ( next_pos + 1 < m_command.size() &&
				(m_command[next_pos+1]==' ' || m_command[next_pos+1]=='\"'
				|| m_command[next_pos+1]=='\'') )
			{
				// escape next character
				arg.push_back( m_command[++next_pos] );
			} else {
				// append slash
				arg.push_back( m_command[next_pos] );
			}
			break;

		case ' ':
			if (next_delimiter == ' ') {
				if (! arg.empty()) {	// skip leading whitespace
					// finished with space-delimited argument
					m_args.push_back(arg);
					arg.clear();
				}
			} else {
				// append space -- not space-delimited
				arg.push_back(' ');
			}
			break;

		case '\'':
		case '\"':
			if (next_delimiter == m_command[next_pos]) {
				// finished with quote/tick-delimited argument
				m_args.push_back(arg);
				arg.clear();
				next_delimiter = ' ';
			} else if (arg.empty()) {
				// first char in argument: set delimiter
				next_delimiter = m_command[next_pos];
			} else {
				// append quote/tick -- not the delimiter or first character
				arg.push_back( m_command[next_pos] );
			}
			break;

		default:
			// append character
			arg.push_back( m_command[next_pos] );
			break;
		}

		++next_pos;
	}

	// push final argument if ending delimiter not found
	if (!arg.empty())
		m_args.push_back(arg);

	// sanity check: args should never be empty
	if (m_args.empty())
		throw CommandParsingException(m_command);

	// log each argument parsed for debugging
	for (std::vector<std::string>::const_iterator it = m_args.begin(); it != m_args.end(); ++it) {
		PION_LOG_DEBUG(m_logger, "Command string argument: " << *it);
	}
}

}	// end namespace plugins
}	// end namespace pion

/// creates new ScriptReactor objects
extern "C" PION_PLUGIN_API pion::platform::Reactor *pion_create_ScriptReactor(void) {
	return new pion::plugins::ScriptReactor();
}

/// destroys ScriptReactor objects
extern "C" PION_PLUGIN_API void pion_destroy_ScriptReactor(pion::plugins::ScriptReactor *reactor_ptr) {
	delete reactor_ptr;
}
<commit_msg>http://trac.atomiclabs.com/ticket/232<commit_after>// ------------------------------------------------------------------------
// Pion is a development platform for building Reactors that process Events
// ------------------------------------------------------------------------
// Copyright (C) 2007-2009 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Pion is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// Pion 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 Affero General Public License for
// more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pion.  If not, see <http://www.gnu.org/licenses/>.
//

#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <iostream>
#ifndef _MSC_VER
	#include <sys/select.h>
#endif
#include <boost/scoped_array.hpp>
#include <pion/platform/CodecFactory.hpp>
#include "ScriptReactor.hpp"

using namespace pion::platform;

namespace pion {		// begin namespace pion
namespace plugins {		// begin namespace plugins

// static members of ScriptReactor

const std::string		ScriptReactor::INPUT_CODEC_ELEMENT_NAME = "InputCodec";
const std::string		ScriptReactor::OUTPUT_CODEC_ELEMENT_NAME = "OutputCodec";
const std::string		ScriptReactor::COMMAND_ELEMENT_NAME = "Command";

// ScriptReactor member functions

void ScriptReactor::start(void)
{
	ConfigWriteLock cfg_lock(*this);
	if (! m_is_running) {
		// open pipe to script
		openPipe();

		m_is_running = true;

		// spawn a new thread that will be used to read events from the script
		PION_LOG_DEBUG(m_logger, "Starting reader thread: " << getId());
		m_thread_ptr.reset(new boost::thread(boost::bind(&ScriptReactor::readEvents, this)));
	}
}

bool ScriptReactor::stopIfRunning(void)
{
	bool do_stop = false;
	{
		ConfigWriteLock cfg_lock(*this);
		if (m_is_running) {
			PION_LOG_DEBUG(m_logger, "Waiting for reader thread to finish");
			do_stop = true;
			m_is_running = false;

			// this should break the reader thread out of any blocking operations
			::close(m_output_pipe);
		}
	}

	if (do_stop) {
		// we have to release the write lock before joining, otherwise things deadlock =(
		m_thread_ptr->join();

		ConfigWriteLock cfg_lock(*this);
		m_thread_ptr.reset();
		closePipe();
	}

	return do_stop;
}

void ScriptReactor::setConfig(const Vocabulary& v, const xmlNodePtr config_ptr)
{
	// make sure it's not running (it needs to be restarted in case Command changes)
	const bool was_running = stopIfRunning();

	{
		// first set config options for the Reactor base class
		ConfigWriteLock cfg_lock(*this);
		Reactor::setConfig(v, config_ptr);

		// get the Input Codec that the Reactor should use
		if (! ConfigManager::getConfigOption(INPUT_CODEC_ELEMENT_NAME, m_input_codec_id, config_ptr))
			throw EmptyInputCodecException(getId());
		m_input_codec_ptr = getCodecFactory().getCodec(m_input_codec_id);
		PION_ASSERT(m_input_codec_ptr);

		// get the Output Codec that the Reactor should use
		if (! ConfigManager::getConfigOption(OUTPUT_CODEC_ELEMENT_NAME, m_output_codec_id, config_ptr))
			throw EmptyOutputCodecException(getId());
		m_output_codec_ptr = getCodecFactory().getCodec(m_output_codec_id);
		PION_ASSERT(m_output_codec_ptr);

		// get the script or program command to execute
		if (! ConfigManager::getConfigOption(COMMAND_ELEMENT_NAME, m_command, config_ptr))
			throw EmptyCommandException(getId());

		// break command string into a vector of arguments
		parseArguments();
	}

	// restart if the reactor was running
	if (was_running)
		start();
}

void ScriptReactor::updateVocabulary(const Vocabulary& v)
{
	// first update anything in the Reactor base class that might be needed
	ConfigWriteLock cfg_lock(*this);
	Reactor::updateVocabulary(v);

	if (m_input_codec_ptr)
		m_input_codec_ptr->updateVocabulary(v);

	if (m_output_codec_ptr)
		m_output_codec_ptr->updateVocabulary(v);
}

void ScriptReactor::updateCodecs(void)
{
	// check if a codec was deleted (if so, stop now!)
	if (! getCodecFactory().hasPlugin(m_input_codec_id)
		|| ! getCodecFactory().hasPlugin(m_output_codec_id))
	{
		stop();
	} else {
		// update the codec pointer
		ConfigWriteLock cfg_lock(*this);
		m_input_codec_ptr = getCodecFactory().getCodec(m_input_codec_id);
		m_output_codec_ptr = getCodecFactory().getCodec(m_output_codec_id);
	}
}

void ScriptReactor::process(const EventPtr& e)
{
	PION_ASSERT(m_input_stream_ptr);
	PION_ASSERT(m_input_codec_ptr);

	// lock mutex to ensure that only one Event may be written at a time
	boost::mutex::scoped_lock write_lock(m_write_mutex);

	// write the Event to the pipe
	m_input_codec_ptr->write(*m_input_stream_ptr, *e);
	if (! *m_input_stream_ptr)
		throw WriteToPipeException(getId());

	// unlock mutex after writing to pipe
	write_lock.unlock();

	// note: delivery to other reactors is handled by readEvents()
}

void ScriptReactor::readEvents(void)
{
	PION_LOG_DEBUG(m_logger, "Script reader thread is running: " << getId());

	try {

		const Event::EventType event_type(m_output_codec_ptr->getEventType());
		EventFactory event_factory;
		EventPtr event_ptr;
		bool event_read;

		struct timeval timeout;
		fd_set read_fds;
		int select_result;

		while ( isRunning() ) {

			PION_ASSERT(m_output_stream_ptr);
			PION_ASSERT(m_output_codec_ptr);

			// these need to be reset before each select()
			FD_ZERO(&read_fds);
			FD_SET(m_output_pipe, &read_fds);
			timeout.tv_sec = 0;
			timeout.tv_usec = 0;

			// wait for data to be available
			select_result = ::select(m_output_pipe+1, &read_fds, NULL, NULL, &timeout);

			if (select_result == 1) {

				ConfigReadLock cfg_lock(*this);
				if ( ! isRunning() )	// re-check after acquiring lock
					break;

				// data is available for reading

				// get a new event from the EventFactory
				event_factory.create(event_ptr, event_type);

				// read an event using the output codec
				event_read = m_output_codec_ptr->read(*m_output_stream_ptr, *event_ptr);

				if (event_read) {

					// deliver the Event to other Reactors
					deliverEvent(event_ptr);

				} else {

					// check for read error
					throw ReadFromPipeException(getId());
				}

			} else if (select_result == 0) {

				// no data currently available
				PionScheduler::sleep(0, 100000000);

			} else {

				// error checking for data availability
				// ignore EINTR errors (receives signals on Linux)
				if (errno != 4)
					throw SelectPipeException(getId());
			}
		}

	} catch (std::exception& e) {
		ConfigWriteLock cfg_lock(*this);
		if ( isRunning() ) {
			PION_LOG_FATAL(m_logger, e.what());
			closePipe();
			m_is_running = false;
		}
	}

	PION_LOG_DEBUG(m_logger, "Script reader thread is exiting: " << getId());
}

void ScriptReactor::openPipe(void)
{
	// close first if already open
	closePipe();

	PION_LOG_DEBUG(m_logger, "Opening pipe to command: " << m_command);

	PION_ASSERT(! m_args.empty());

	// initialize pipes for reading and writing to script
	int r_pipes[2];
	int w_pipes[2];
	if ( ::pipe(r_pipes) != 0 || ::pipe(w_pipes) != 0)
		throw OpenPipeException(m_command);

	// fork child process
	m_child_pid = ::fork();

	if (m_child_pid == -1) {

		// failed to fork child process
		throw OpenPipeException(m_command);

	} else if (m_child_pid == 0) {

		// inside child process

		// close ends of pipes used by parent
		::close(w_pipes[1]);
		::close(r_pipes[0]);

		// bind other ends of pipes to STDIN/STDOUT
		::dup2(w_pipes[0], 0);	// STDIN
		::dup2(r_pipes[1], 1);	// STDOUT
		::close(w_pipes[0]);
		::close(r_pipes[1]);

		// convert argument string vector into an array of char pointers
		boost::scoped_array<char*> arg_ptr(new char*[m_args.size() + 1]);
		for (std::size_t n = 0; n < m_args.size(); ++n) {
			arg_ptr[n] = const_cast<char*>(m_args[n].c_str());
		}
		arg_ptr[ m_args.size() ] = NULL;

		// execute command (ignore return since we're in a new process anyway)
		// note: execvp is wrapper for execve() that also searches the paths
		::execvp(m_args[0].c_str(), arg_ptr.get());

	} else {

		// inside parent process

		// close ends of pipes used by child
		::close(w_pipes[0]);
		::close(r_pipes[1]);

		// set file descriptors for reading/writing
		m_input_pipe = w_pipes[1];
		m_output_pipe = r_pipes[0];
	}

	// prepare c++ streams to use pipes for reading and writing events
	m_input_streambuf_ptr.reset(new StreamBuffer( m_input_pipe ));
	m_input_stream_ptr.reset(new std::ostream(m_input_streambuf_ptr.get()));
	m_output_streambuf_ptr.reset(new StreamBuffer( m_output_pipe ));
	m_output_stream_ptr.reset(new std::istream(m_output_streambuf_ptr.get()));
}

void ScriptReactor::closePipe(void)
{
	if (m_input_pipe != -1 || m_output_pipe != -1) {
		PION_LOG_DEBUG(m_logger, "Closing pipe to command: " << m_command);
		// file descriptors are closed by the iostreams
		//::close(m_input_pipe);
		//::close(m_output_pipe);
		::kill(m_child_pid, SIGKILL);
		m_input_stream_ptr.reset();
		m_output_stream_ptr.reset();
		m_input_streambuf_ptr.reset();
		m_output_streambuf_ptr.reset();
		m_child_pid = 0;
		m_input_pipe = m_output_pipe = -1;
	}
}

void ScriptReactor::parseArguments(void)
{
	std::string arg;			// current argument string
	std::size_t next_pos = 0;	// next parsing position
	char next_delimiter = ' ';	// next argument delimiter

	PION_LOG_DEBUG(m_logger, "Parsing out command string: " << m_command);

	// start by clearing the arguments vector
	m_args.clear();

	// step through each character in the command string
	while (next_pos < m_command.size()) {
		switch ( m_command[next_pos] ) {
		case '\\':
			if ( next_pos + 1 < m_command.size() &&
				(m_command[next_pos+1]==' ' || m_command[next_pos+1]=='\"'
				|| m_command[next_pos+1]=='\'') )
			{
				// escape next character
				arg.push_back( m_command[++next_pos] );
			} else {
				// append slash
				arg.push_back( m_command[next_pos] );
			}
			break;

		case ' ':
			if (next_delimiter == ' ') {
				if (! arg.empty()) {	// skip leading whitespace
					// finished with space-delimited argument
					m_args.push_back(arg);
					arg.clear();
				}
			} else {
				// append space -- not space-delimited
				arg.push_back(' ');
			}
			break;

		case '\'':
		case '\"':
			if (next_delimiter == m_command[next_pos]) {
				// finished with quote/tick-delimited argument
				m_args.push_back(arg);
				arg.clear();
				next_delimiter = ' ';
			} else if (arg.empty()) {
				// first char in argument: set delimiter
				next_delimiter = m_command[next_pos];
			} else {
				// append quote/tick -- not the delimiter or first character
				arg.push_back( m_command[next_pos] );
			}
			break;

		default:
			// append character
			arg.push_back( m_command[next_pos] );
			break;
		}

		++next_pos;
	}

	// push final argument if ending delimiter not found
	if (!arg.empty())
		m_args.push_back(arg);

	// sanity check: args should never be empty
	if (m_args.empty())
		throw CommandParsingException(m_command);

	// log each argument parsed for debugging
	for (std::vector<std::string>::const_iterator it = m_args.begin(); it != m_args.end(); ++it) {
		PION_LOG_DEBUG(m_logger, "Command string argument: " << *it);
	}
}

}	// end namespace plugins
}	// end namespace pion

/// creates new ScriptReactor objects
extern "C" PION_PLUGIN_API pion::platform::Reactor *pion_create_ScriptReactor(void) {
	return new pion::plugins::ScriptReactor();
}

/// destroys ScriptReactor objects
extern "C" PION_PLUGIN_API void pion_destroy_ScriptReactor(pion::plugins::ScriptReactor *reactor_ptr) {
	delete reactor_ptr;
}
<|endoftext|>


<commit_before>/*
 * File:   UralJudger.cpp
 * Author: payper
 *
 * Created on 2014年3月25日, 下午1:22
 */

#include "UralJudger.h"

/**
 * Create a Ural Judger
 * @param _info Should be a pointer of a JudgerInfo
 */
UralJudger::UralJudger(JudgerInfo * _info) : VirtualJudger(_info) {
    socket->sendMessage(CONFIG->GetJudge_connect_string() + "\nUral");

    language_table["1"] = "26";
    language_table["2"] = "25";
    language_table["3"] = "32";
    language_table["4"] = "31";
    language_table["6"] = "11";

    if (!RE2::PartialMatch(info->GetUsername(), "([0-9]*)", &author_id)) {
        throw Exception("Cannot retrieve author id from username");
    }
}

UralJudger::~UralJudger() {
}

/**
 * Ural doesn't require login, it uses judgeID to identify the submitter
 */
void UralJudger::login() {
}

/**
 * Submit a run
 * @param bott      Bott file for Run info
 * @return Submit status
 */
int UralJudger::submit(Bott * bott) {

    // prepare form for post
    struct curl_httppost * formpost = NULL;
    struct curl_httppost * lastptr  = NULL;
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "Action",
                 CURLFORM_COPYCONTENTS, "submit",
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "SpaceID",
                 CURLFORM_COPYCONTENTS, "1",
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "JudgeID",
                 CURLFORM_COPYCONTENTS, info->GetUsername().c_str(),
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "Language",
                 CURLFORM_COPYCONTENTS, bott->Getlanguage().c_str(),
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "ProblemNum",
                 CURLFORM_COPYCONTENTS, bott->Getvid().c_str(),
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "Source",
                 CURLFORM_COPYCONTENTS, bott->Getsrc().c_str(),
                 CURLFORM_END);

    prepareCurl();
    curl_easy_setopt(curl, CURLOPT_URL, "http://acm.timus.ru/submit.aspx?space=1");
    curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);

    // have to fetch header for runid
    string headerfilename = tmpfilename + "_header";
    FILE * headerfile = fopen(headerfilename.c_str(), "w");
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile);

    performCurl();
    curl_formfree(formpost);
    fclose(headerfile);

    // check submit status
    string html = loadAllFromFile(headerfilename);
    if (html.find("303 See Other") == string::npos) {
        return SUBMIT_OTHER_ERROR;
    }

    return VirtualJudger::SUBMIT_NORMAL;
}

/**
 * Get result and related info
 * @param bott  Original Bott info
 * @return Result Bott file
 */
Bott * UralJudger::getStatus(Bott * bott) {
    time_t begin_time = time(NULL);
    int count=10;

    Bott * result_bott = new Bott;

    // fetch runid
    string runid, header = loadAllFromFile(tmpfilename + "_header");
    if (!RE2::PartialMatch(header, "(?s)X-SubmitID: ([0-9]*)", &runid)) {
        throw Exception("Failed to get remote runid.");
    }
    result_bott->Setremote_runid(trim(runid));

    while (true) {
        // check wait time
        if (time(NULL) - begin_time > info->GetMax_wait_time()) {
            throw Exception("Failed to get current result, judge time out.");
        }

        prepareCurl();
        // count can be set to 100 if Ural is very busy
        curl_easy_setopt(curl, CURLOPT_URL, ("http://acm.timus.ru/status.aspx?space=1&count="+intToString(count)).c_str());
        performCurl();

        string html = loadAllFromFile(tmpfilename);
        string status;
        string result, time_used, memory_used;

        // get the row for runid, if not found, try fetch more runs
        if (!RE2::PartialMatch(html, "(?s)(
"+runid+".*?
)", &status)) {
            log("Trying to fetch more runs...");
            count += 10;
            continue;
        }

        // get result
        if (!RE2::PartialMatch(status, "(?s)(.*?)	", &result)) {
            throw Exception("Failed to get current result.");
        }
        result = trim(result);
        if (isFinalResult(result)) {
            // result is the final one
            result = convertResult(result);
            if (result != "Compile Error") {
                // no details for CE results
                if (!RE2::PartialMatch(status,
                        "(.*?)	(.*?) KB	",
                        &time_used, &memory_used)) {
                    throw Exception("Failed to parse details from status row.");
                }
                int time_ms = stringToDouble(time_used) * 1000 + 0.001;
                time_used = intToString(time_ms);
            } else {
                memory_used = time_used = "0";
            }
            result_bott->Settype(RESULT_REPORT);
            result_bott->Setresult(result);
            result_bott->Settime_used(trim(time_used));
            result_bott->Setmemory_used(trim(memory_used));
            break;
        }
    }
    return result_bott;
}

/**
 * Get compile error info from Ural
 * @param bott      Result bott file
 * @return Compile error info
 */
string UralJudger::getCEinfo(Bott * bott) {
    prepareCurl();
    curl_easy_setopt(curl, CURLOPT_URL, ("http://acm.timus.ru/ce.aspx?id=" + bott->Getremote_runid()).c_str());
    performCurl();

    string info = loadAllFromFile(tmpfilename);
    return info;
}

/**
 * Check whether the result is final
 * @param result        Current result
 * @return Is final one or not
 */
string UralJudger::convertResult(string result) {
    if (result.find("Compilation error") != string::npos) return "Compile Error";
    if (result.find("Time limit exceeded") != string::npos) return "Time Limit Exceed";
    if (result.find("Memory limit exceeded") != string::npos) return "Memory Limit Exceed";
    if (result.find("Output limit exceeded") != string::npos) return "Output Limit Exceed";
    if (result.find("Wrong answer") != string::npos) return "Wrong Answer";
    if (result.find("Crash") != string::npos) return "Runtime Error";
    if (result.find("Runtime error") != string::npos) return "Runtime Error";
    return trim(result);
}
<commit_msg>assign runid when everything is ready, and fetch at most 100 runs EOM<commit_after>/*
 * File:   UralJudger.cpp
 * Author: payper
 *
 * Created on 2014年3月25日, 下午1:22
 */

#include "UralJudger.h"

/**
 * Create a Ural Judger
 * @param _info Should be a pointer of a JudgerInfo
 */
UralJudger::UralJudger(JudgerInfo * _info) : VirtualJudger(_info) {
    socket->sendMessage(CONFIG->GetJudge_connect_string() + "\nUral");

    language_table["1"] = "26";
    language_table["2"] = "25";
    language_table["3"] = "32";
    language_table["4"] = "31";
    language_table["6"] = "11";

    if (!RE2::PartialMatch(info->GetUsername(), "([0-9]*)", &author_id)) {
        throw Exception("Cannot retrieve author id from username");
    }
}

UralJudger::~UralJudger() {
}

/**
 * Ural doesn't require login, it uses judgeID to identify the submitter
 */
void UralJudger::login() {
}

/**
 * Submit a run
 * @param bott      Bott file for Run info
 * @return Submit status
 */
int UralJudger::submit(Bott * bott) {

    // prepare form for post
    struct curl_httppost * formpost = NULL;
    struct curl_httppost * lastptr  = NULL;
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "Action",
                 CURLFORM_COPYCONTENTS, "submit",
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "SpaceID",
                 CURLFORM_COPYCONTENTS, "1",
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "JudgeID",
                 CURLFORM_COPYCONTENTS, info->GetUsername().c_str(),
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "Language",
                 CURLFORM_COPYCONTENTS, bott->Getlanguage().c_str(),
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "ProblemNum",
                 CURLFORM_COPYCONTENTS, bott->Getvid().c_str(),
                 CURLFORM_END);
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "Source",
                 CURLFORM_COPYCONTENTS, bott->Getsrc().c_str(),
                 CURLFORM_END);

    prepareCurl();
    curl_easy_setopt(curl, CURLOPT_URL, "http://acm.timus.ru/submit.aspx?space=1");
    curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);

    // have to fetch header for runid
    string headerfilename = tmpfilename + "_header";
    FILE * headerfile = fopen(headerfilename.c_str(), "w");
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile);

    performCurl();
    curl_formfree(formpost);
    fclose(headerfile);

    // check submit status
    string html = loadAllFromFile(headerfilename);
    if (html.find("303 See Other") == string::npos) {
        return SUBMIT_OTHER_ERROR;
    }

    return VirtualJudger::SUBMIT_NORMAL;
}

/**
 * Get result and related info
 * @param bott  Original Bott info
 * @return Result Bott file
 */
Bott * UralJudger::getStatus(Bott * bott) {
    time_t begin_time = time(NULL);
    int count=10;

    Bott * result_bott;

    // fetch runid
    string runid, header = loadAllFromFile(tmpfilename + "_header");
    if (!RE2::PartialMatch(header, "(?s)X-SubmitID: ([0-9]*)", &runid)) {
        throw Exception("Failed to get remote runid.");
    }

    while (true) {
        // check wait time
        if (time(NULL) - begin_time > info->GetMax_wait_time()) {
            throw Exception("Failed to get current result, judge time out.");
        }

        prepareCurl();
        // count can be set to 100 if Ural is very busy
        curl_easy_setopt(curl, CURLOPT_URL, ("http://acm.timus.ru/status.aspx?space=1&count="+intToString(count)).c_str());
        performCurl();

        string html = loadAllFromFile(tmpfilename);
        string status;
        string result, time_used, memory_used;

        // get the row for runid, if not found, try fetch more runs
        if (!RE2::PartialMatch(html, "(?s)(
"+runid+".*?
)", &status)) {
            if (count==100){
                throw Exception("Failed to find current submission in the last 100.");
            }
            log("Trying to fetch more runs...");
            count += 10;
            continue;
        }

        // get result
        if (!RE2::PartialMatch(status, "(?s)(.*?)	", &result)) {
            throw Exception("Failed to get current result.");
        }
        result = trim(result);
        if (isFinalResult(result)) {
            // result is the final one
            result = convertResult(result);
            if (result != "Compile Error") {
                // no details for CE results
                if (!RE2::PartialMatch(status,
                        "(.*?)	(.*?) KB	",
                        &time_used, &memory_used)) {
                    throw Exception("Failed to parse details from status row.");
                }
                int time_ms = stringToDouble(time_used) * 1000 + 0.001;
                time_used = intToString(time_ms);
            } else {
                memory_used = time_used = "0";
            }
            result_bott = new Bott;
            result_bott->Settype(RESULT_REPORT);
            result_bott->Setresult(result);
            result_bott->Settime_used(trim(time_used));
            result_bott->Setmemory_used(trim(memory_used));
            result_bott->Setremote_runid(trim(runid));
            break;
        }
    }
    return result_bott;
}

/**
 * Get compile error info from Ural
 * @param bott      Result bott file
 * @return Compile error info
 */
string UralJudger::getCEinfo(Bott * bott) {
    prepareCurl();
    curl_easy_setopt(curl, CURLOPT_URL, ("http://acm.timus.ru/ce.aspx?id=" + bott->Getremote_runid()).c_str());
    performCurl();

    string info = loadAllFromFile(tmpfilename);
    return info;
}

/**
 * Check whether the result is final
 * @param result        Current result
 * @return Is final one or not
 */
string UralJudger::convertResult(string result) {
    if (result.find("Compilation error") != string::npos) return "Compile Error";
    if (result.find("Time limit exceeded") != string::npos) return "Time Limit Exceed";
    if (result.find("Memory limit exceeded") != string::npos) return "Memory Limit Exceed";
    if (result.find("Output limit exceeded") != string::npos) return "Output Limit Exceed";
    if (result.find("Wrong answer") != string::npos) return "Wrong Answer";
    if (result.find("Crash") != string::npos) return "Runtime Error";
    if (result.find("Runtime error") != string::npos) return "Runtime Error";
    return trim(result);
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 2011 vlc-phonon AUTHORS

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

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "libvlc.h"

#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QLibrary>
#include <QtCore/QSettings>
#include <QtCore/QString>
#include <QtCore/QStringBuilder>
#include <QtCore/QVarLengthArray>

#include <vlc/libvlc.h>

#include "debug.h"

LibVLC *LibVLC::self;

LibVLC::LibVLC() :
    m_vlcInstance(0)
{
}

LibVLC::~LibVLC()
{
    libvlc_release(m_vlcInstance);
    vlcUnload();
    self = 0;
}

bool LibVLC::init()
{
    Q_ASSERT_X(!self, "LibVLC", "there should be only one LibVLC object");
    LibVLC::self = new LibVLC;

    QString path = self->vlcPath();
    if (!path.isEmpty()) {
        QList<QByteArray> args;

        args << QFile::encodeName(path);

        QString pluginsPath = QLatin1Literal("--plugin-path=") %
                QDir::toNativeSeparators(QFileInfo(self->vlcPath()).dir().path());
#if defined(Q_OS_UNIX)
        pluginsPath.append("/vlc");
#elif defined(Q_OS_WIN)
        pluginsPath.append("\\plugins");
#endif
        args << QFile::encodeName(pluginsPath);

        // Ends up as something like $HOME/.config/Phonon/vlc.conf
        const QString configFileName = QSettings("Phonon", "vlc").fileName();
        if (QFile::exists(configFileName)) {
            args << QByteArray("--config=").append(QFile::encodeName(configFileName));
        }

        int debugLevel = 3 - (int) Debug::minimumDebugLevel();
        if (debugLevel > 0) {
            args << QByteArray("--verbose=").append(QString::number(debugLevel));
            args << QByteArray("--extraintf=logger");
#ifdef Q_WS_WIN
            QDir logFilePath(QString(qgetenv("APPDATA")).append("/vlc"));
#else
            QDir logFilePath(QDir::homePath().append("/.vlc"));
#endif //Q_WS_WIN
            logFilePath.mkdir("log");
            const QString logFile = logFilePath.path()
                    .append("/log/vlc-log-")
                    .append(QString::number(qApp->applicationPid()))
                    .append(".txt");
            args << QByteArray("--logfile=").append(QFile::encodeName(QDir::toNativeSeparators(logFile)));
        }

        args << "--reset-plugins-cache";
        args << "--no-media-library";
        args << "--no-osd";
        args << "--no-stats";
        args << "--no-video-title-show";
        args << "--album-art=0";
        args << "--no-xlib";

#ifndef Q_OS_MAC
        args << "--no-one-instance";
#endif

        // Build const char* array
        QVarLengthArray<const char*, 64> vlcArgs(args.size());
        for (int i = 0; i < args.size(); ++i) {
            vlcArgs[i] = args.at(i).constData();
        }

        // Create and initialize a libvlc instance (it should be done only once)
        self->m_vlcInstance = libvlc_new(vlcArgs.size(), vlcArgs.constData());
        if (!self->m_vlcInstance) {
            fatal() << "libVLC:" << LibVLC::errorMessage();
            return false;
        }
        return true;
    }
    return false;
}

const char *LibVLC::errorMessage()
{
    return libvlc_errmsg();
}

#if defined(Q_OS_UNIX)
bool LibVLC::libGreaterThan(const QString &lhs, const QString &rhs)
{
    const QStringList lhsparts = lhs.split(QLatin1Char('.'));
    const QStringList rhsparts = rhs.split(QLatin1Char('.'));
    Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1);

    for (int i = 1; i < rhsparts.count(); ++i) {
        if (lhsparts.count() <= i) {
            // left hand side is shorter, so it's less than rhs
            return false;
        }

        bool ok = false;
        int b = 0;
        int a = lhsparts.at(i).toInt(&ok);
        if (ok) {
            b = rhsparts.at(i).toInt(&ok);
        }
        if (ok) {
            // both toInt succeeded
            if (a == b) {
                continue;
            }
            return a > b;
        } else {
            // compare as strings;
            if (lhsparts.at(i) == rhsparts.at(i)) {
                continue;
            }
            return lhsparts.at(i) > rhsparts.at(i);
        }
    }

    // they compared strictly equally so far
    // lhs cannot be less than rhs
    return true;
}
#endif

QStringList LibVLC::findAllLibVlcPaths()
{
    QStringList paths;

#ifdef Q_OS_UNIX
    paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH"))
            .split(QLatin1Char(':'), QString::SkipEmptyParts);
    paths << QLatin1String(PHONON_LIB_INSTALL_DIR) << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib");

#if defined(Q_WS_MAC)
    paths
            << QCoreApplication::applicationDirPath()
            << QCoreApplication::applicationDirPath() % QLatin1Literal("/../Frameworks")
            << QCoreApplication::applicationDirPath() % QLatin1Literal("/../PlugIns")
            << QCoreApplication::applicationDirPath() % QLatin1Literal("/lib");
#endif

    QStringList foundVlcs;
    foreach (const QString & path, paths) {
        QDir dir = QDir(path);
        QStringList entryList = dir.entryList(QStringList() << QLatin1String("libvlc.*"), QDir::Files);

        qSort(entryList.begin(), entryList.end(), LibVLC::libGreaterThan);
        foreach(const QString & entry, entryList) {
            if (entry.contains(".debug")) {
                continue;
            }
            foundVlcs << path % QLatin1Char('/') % entry;
        }
    }

    return foundVlcs;
#elif defined(Q_OS_WIN)
    // Current application path has highest priority.
    // Application could be delivered with own copy of qt, phonon, LibVLC, vlc and vlc plugins.
    QString appDirVlc =
            QDir::toNativeSeparators(QCoreApplication::applicationDirPath()
                                     % QLatin1Char('\\')
                                     % QLatin1Literal("libvlc.dll"));
    if (QFile::exists(appDirVlc)) {
        paths << appDirVlc;
    }

    // Read VLC version and installation directory from Windows registry
    QSettings settings(QSettings::SystemScope, "VideoLAN", "VLC");
    QString vlcVersion = settings.value("Version").toString();
    QString vlcInstallDir = settings.value("InstallDir").toString();
    if (vlcVersion.startsWith(QLatin1String("1.1")) && !vlcInstallDir.isEmpty()) {
        paths << vlcInstallDir % QLatin1Char('\\') % QLatin1Literal("libvlc.dll");
        return paths;
    } else {
        //If nothing is found in the registry try %PATH%
        QStringList searchPaths = QString::fromLatin1(qgetenv("PATH"))
                                  .split(QLatin1Char(';'), QString::SkipEmptyParts);

        QStringList foundVlcs;
        foreach(const QString & sp, searchPaths) {
            QDir dir = QDir(sp);
            QStringList entryList = dir.entryList(QStringList() << QLatin1String("libvlc.dll"), QDir::Files);
            foreach(const QString & entry, entryList){
                foundVlcs << sp % QLatin1Char('\\') % entry;
            }
        }
        paths << foundVlcs;
        return paths;
    }
#endif
}

QString LibVLC::vlcPath()
{
    static QString path;
    if (!path.isEmpty()) {
        return path;
    }

    m_vlcLibrary = new QLibrary();
    QStringList paths = LibVLC::findAllLibVlcPaths();
    foreach(path, paths) {
        m_vlcLibrary->setFileName(path);

        if (!m_vlcLibrary->resolve("libvlc_exception_init")) { //"libvlc_exception_init" not contained in 1.1+
            return path;
        } else {
            debug() << "Cannot resolve the symbol or load VLC library";
        }
        warning() << m_vlcLibrary->errorString();
    }

    vlcUnload();

    return QString();
}

void LibVLC::vlcUnload()
{
    m_vlcLibrary->unload();
    delete m_vlcLibrary;
    m_vlcLibrary = 0;
}
<commit_msg>passing the libvlc path to init seems utter pointless -> remove<commit_after>/*
    Copyright (C) 2011 vlc-phonon AUTHORS

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

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "libvlc.h"

#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QLibrary>
#include <QtCore/QSettings>
#include <QtCore/QString>
#include <QtCore/QStringBuilder>
#include <QtCore/QVarLengthArray>

#include <vlc/libvlc.h>

#include "debug.h"

LibVLC *LibVLC::self;

LibVLC::LibVLC() :
    m_vlcInstance(0)
{
}

LibVLC::~LibVLC()
{
    libvlc_release(m_vlcInstance);
    vlcUnload();
    self = 0;
}

bool LibVLC::init()
{
    Q_ASSERT_X(!self, "LibVLC", "there should be only one LibVLC object");
    LibVLC::self = new LibVLC;

    QString path = self->vlcPath();
    if (!path.isEmpty()) {
        QList<QByteArray> args;

        QString pluginsPath = QLatin1Literal("--plugin-path=") %
                QDir::toNativeSeparators(QFileInfo(self->vlcPath()).dir().path());
#if defined(Q_OS_UNIX)
        pluginsPath.append("/vlc");
#elif defined(Q_OS_WIN)
        pluginsPath.append("\\plugins");
#endif
        args << QFile::encodeName(pluginsPath);

        // Ends up as something like $HOME/.config/Phonon/vlc.conf
        const QString configFileName = QSettings("Phonon", "vlc").fileName();
        if (QFile::exists(configFileName)) {
            args << QByteArray("--config=").append(QFile::encodeName(configFileName));
        }

        int debugLevel = 3 - (int) Debug::minimumDebugLevel();
        if (debugLevel > 0) {
            args << QByteArray("--verbose=").append(QString::number(debugLevel));
            args << QByteArray("--extraintf=logger");
#ifdef Q_WS_WIN
            QDir logFilePath(QString(qgetenv("APPDATA")).append("/vlc"));
#else
            QDir logFilePath(QDir::homePath().append("/.vlc"));
#endif //Q_WS_WIN
            logFilePath.mkdir("log");
            const QString logFile = logFilePath.path()
                    .append("/log/vlc-log-")
                    .append(QString::number(qApp->applicationPid()))
                    .append(".txt");
            args << QByteArray("--logfile=").append(QFile::encodeName(QDir::toNativeSeparators(logFile)));
        }

        args << "--reset-plugins-cache";
        args << "--no-media-library";
        args << "--no-osd";
        args << "--no-stats";
        args << "--no-video-title-show";
        args << "--album-art=0";
        args << "--no-xlib";

#ifndef Q_OS_MAC
        args << "--no-one-instance";
#endif

        // Build const char* array
        QVarLengthArray<const char*, 64> vlcArgs(args.size());
        for (int i = 0; i < args.size(); ++i) {
            vlcArgs[i] = args.at(i).constData();
        }

        // Create and initialize a libvlc instance (it should be done only once)
        self->m_vlcInstance = libvlc_new(vlcArgs.size(), vlcArgs.constData());
        if (!self->m_vlcInstance) {
            fatal() << "libVLC:" << LibVLC::errorMessage();
            return false;
        }
        return true;
    }
    return false;
}

const char *LibVLC::errorMessage()
{
    return libvlc_errmsg();
}

#if defined(Q_OS_UNIX)
bool LibVLC::libGreaterThan(const QString &lhs, const QString &rhs)
{
    const QStringList lhsparts = lhs.split(QLatin1Char('.'));
    const QStringList rhsparts = rhs.split(QLatin1Char('.'));
    Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1);

    for (int i = 1; i < rhsparts.count(); ++i) {
        if (lhsparts.count() <= i) {
            // left hand side is shorter, so it's less than rhs
            return false;
        }

        bool ok = false;
        int b = 0;
        int a = lhsparts.at(i).toInt(&ok);
        if (ok) {
            b = rhsparts.at(i).toInt(&ok);
        }
        if (ok) {
            // both toInt succeeded
            if (a == b) {
                continue;
            }
            return a > b;
        } else {
            // compare as strings;
            if (lhsparts.at(i) == rhsparts.at(i)) {
                continue;
            }
            return lhsparts.at(i) > rhsparts.at(i);
        }
    }

    // they compared strictly equally so far
    // lhs cannot be less than rhs
    return true;
}
#endif

QStringList LibVLC::findAllLibVlcPaths()
{
    QStringList paths;

#ifdef Q_OS_UNIX
    paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH"))
            .split(QLatin1Char(':'), QString::SkipEmptyParts);
    paths << QLatin1String(PHONON_LIB_INSTALL_DIR) << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib");

#if defined(Q_WS_MAC)
    paths
            << QCoreApplication::applicationDirPath()
            << QCoreApplication::applicationDirPath() % QLatin1Literal("/../Frameworks")
            << QCoreApplication::applicationDirPath() % QLatin1Literal("/../PlugIns")
            << QCoreApplication::applicationDirPath() % QLatin1Literal("/lib");
#endif

    QStringList foundVlcs;
    foreach (const QString & path, paths) {
        QDir dir = QDir(path);
        QStringList entryList = dir.entryList(QStringList() << QLatin1String("libvlc.*"), QDir::Files);

        qSort(entryList.begin(), entryList.end(), LibVLC::libGreaterThan);
        foreach(const QString & entry, entryList) {
            if (entry.contains(".debug")) {
                continue;
            }
            foundVlcs << path % QLatin1Char('/') % entry;
        }
    }

    return foundVlcs;
#elif defined(Q_OS_WIN)
    // Current application path has highest priority.
    // Application could be delivered with own copy of qt, phonon, LibVLC, vlc and vlc plugins.
    QString appDirVlc =
            QDir::toNativeSeparators(QCoreApplication::applicationDirPath()
                                     % QLatin1Char('\\')
                                     % QLatin1Literal("libvlc.dll"));
    if (QFile::exists(appDirVlc)) {
        paths << appDirVlc;
    }

    // Read VLC version and installation directory from Windows registry
    QSettings settings(QSettings::SystemScope, "VideoLAN", "VLC");
    QString vlcVersion = settings.value("Version").toString();
    QString vlcInstallDir = settings.value("InstallDir").toString();
    if (vlcVersion.startsWith(QLatin1String("1.1")) && !vlcInstallDir.isEmpty()) {
        paths << vlcInstallDir % QLatin1Char('\\') % QLatin1Literal("libvlc.dll");
        return paths;
    } else {
        //If nothing is found in the registry try %PATH%
        QStringList searchPaths = QString::fromLatin1(qgetenv("PATH"))
                                  .split(QLatin1Char(';'), QString::SkipEmptyParts);

        QStringList foundVlcs;
        foreach(const QString & sp, searchPaths) {
            QDir dir = QDir(sp);
            QStringList entryList = dir.entryList(QStringList() << QLatin1String("libvlc.dll"), QDir::Files);
            foreach(const QString & entry, entryList){
                foundVlcs << sp % QLatin1Char('\\') % entry;
            }
        }
        paths << foundVlcs;
        return paths;
    }
#endif
}

QString LibVLC::vlcPath()
{
    static QString path;
    if (!path.isEmpty()) {
        return path;
    }

    m_vlcLibrary = new QLibrary();
    QStringList paths = LibVLC::findAllLibVlcPaths();
    foreach(path, paths) {
        m_vlcLibrary->setFileName(path);

        if (!m_vlcLibrary->resolve("libvlc_exception_init")) { //"libvlc_exception_init" not contained in 1.1+
            return path;
        } else {
            debug() << "Cannot resolve the symbol or load VLC library";
        }
        warning() << m_vlcLibrary->errorString();
    }

    vlcUnload();

    return QString();
}

void LibVLC::vlcUnload()
{
    m_vlcLibrary->unload();
    delete m_vlcLibrary;
    m_vlcLibrary = 0;
}
<|endoftext|>


<commit_before>#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <signal.h>
#include <pthread.h>

namespace factor
{

typedef char vm_char;
typedef char symbol_char;

#define STRING_LITERAL(string) string

#define SSCANF sscanf
#define STRCMP strcmp
#define STRNCMP strncmp
#define STRDUP strdup

#define FSEEK fseeko

#define FIXNUM_FORMAT "%ld"
#define CELL_FORMAT "%lu"
#define CELL_HEX_FORMAT "%lx"

#ifdef FACTOR_64
	#define CELL_HEX_PAD_FORMAT "%016lx"
#else
	#define CELL_HEX_PAD_FORMAT "%08lx"
#endif

#define FIXNUM_FORMAT "%ld"

#define OPEN_READ(path) fopen(path,"rb")
#define OPEN_WRITE(path) fopen(path,"wb")

#define print_native_string(string) vm->print_string(string)

void start_thread(void *(*start_routine)(void *));

void init_ffi();
void ffi_dlopen(dll *dll);
void *ffi_dlsym(dll *dll, symbol_char *symbol);
void ffi_dlclose(dll *dll);

void unix_init_signals();
void signal_handler(int signal, siginfo_t* siginfo, void* uap);
void dump_stack_signal(int signal, siginfo_t* siginfo, void* uap);

s64 current_micros();
void sleep_micros(cell usec);

void open_console();

}
<commit_msg>print_native_string doesn't need singleton ptr<commit_after>#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <signal.h>
#include <pthread.h>

namespace factor
{

typedef char vm_char;
typedef char symbol_char;

#define STRING_LITERAL(string) string

#define SSCANF sscanf
#define STRCMP strcmp
#define STRNCMP strncmp
#define STRDUP strdup

#define FSEEK fseeko

#define FIXNUM_FORMAT "%ld"
#define CELL_FORMAT "%lu"
#define CELL_HEX_FORMAT "%lx"

#ifdef FACTOR_64
	#define CELL_HEX_PAD_FORMAT "%016lx"
#else
	#define CELL_HEX_PAD_FORMAT "%08lx"
#endif

#define FIXNUM_FORMAT "%ld"

#define OPEN_READ(path) fopen(path,"rb")
#define OPEN_WRITE(path) fopen(path,"wb")

#define print_native_string(string) print_string(string)

void start_thread(void *(*start_routine)(void *));

void init_ffi();
void ffi_dlopen(dll *dll);
void *ffi_dlsym(dll *dll, symbol_char *symbol);
void ffi_dlclose(dll *dll);

void unix_init_signals();
void signal_handler(int signal, siginfo_t* siginfo, void* uap);
void dump_stack_signal(int signal, siginfo_t* siginfo, void* uap);

s64 current_micros();
void sleep_micros(cell usec);

void open_console();

}
<|endoftext|>


<commit_before>#include "arguments.hpp"
#include "builtin/class.hpp"
#include "builtin/call_custom_cache.hpp"
#include "builtin/call_site.hpp"
#include "builtin/call_unit.hpp"
#include "builtin/exception.hpp"
#include "builtin/executable.hpp"
#include "builtin/mono_inline_cache.hpp"
#include "builtin/object.hpp"
#include "builtin/poly_inline_cache.hpp"
#include "call_frame.hpp"
#include "global_cache.hpp"
#include "lookup_data.hpp"
#include "object_utils.hpp"
#include "ontology.hpp"

#include <sstream>

namespace rubinius {

  void CallSite::init(STATE) {
    GO(call_site).set(ontology::new_class(state, "CallSite", G(object), G(rubinius)));
    G(call_site)->set_object_type(state, CallSiteType);
  }

  CallSite* CallSite::empty(STATE, Symbol* name, Executable* executable, int ip) {
    CallSite* cache =
      state->new_object_dirty<CallSite>(G(call_site));
    cache->name_ = name;
    cache->executor_ = empty_cache;
    cache->fallback_ = empty_cache;
    cache->updater_  = empty_cache_updater;
    cache->executable(state, executable);
    cache->ip_ = ip;
    return cache;
  }

  Integer* CallSite::ip_prim(STATE) {
    return Integer::from(state, ip_);
  }

  Object* CallSite::empty_cache_custom(STATE, CallSite* call_site, CallFrame* call_frame,
                                          Arguments& args)
  {
    Object* const recv = args.recv();

    Array*  ary = Array::create(state, args.total() + 2);
    ary->set(state, 0, recv);
    ary->set(state, 1, call_site->name_);

    for(size_t i = 0; i < args.total(); i++) {
      ary->set(state, i + 2, args.get_argument(i));
    }

    Object* ret = G(rubinius)->send(state, call_frame, state->symbol("bind_call"), ary);

    if(CallUnit* cu = try_as<CallUnit>(ret)) {
      CallCustomCache* cache = CallCustomCache::create(state, call_site, cu);
      call_site->update_call_site(state, cache);
      return cu->execute(state, call_frame, cu, cu->executable(), cu->module(), args);
    } else {
      Exception::internal_error(state, call_frame, "bind_call must return CallUnit");
      return 0;
    }
  }

  Object* CallSite::empty_cache(STATE, CallSite* call_site, CallFrame* call_frame,
                                   Arguments& args)
  {
    Object* const recv = args.recv();
    Class*  const recv_class  = recv->direct_class(state);

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_public));
    Dispatch dis(call_site->name());

    if(!dis.resolve(state, call_site->name(), lookup)) {
      if(!lookup_method_missing(state, call_frame, args,
          dis, call_frame->self(), recv->lookup_begin(state))) {
        return NULL;
      }
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  Object* CallSite::empty_cache_private(STATE, CallSite* call_site, CallFrame* call_frame,
                                   Arguments& args)
  {
    Object* const recv = args.recv();
    Class* const recv_class = recv->direct_class(state);

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_private));
    Dispatch dis(call_site->name());

    if(!dis.resolve(state, dis.name, lookup)) {
      if(!lookup_method_missing(state, call_frame, args,
          dis, call_frame->self(), recv->lookup_begin(state))) {
        return NULL;
      }
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  Object* CallSite::empty_cache_vcall(STATE, CallSite* call_site, CallFrame* call_frame,
                                         Arguments& args)
  {

    Object* const recv = args.recv();
    Class* const recv_class = recv->direct_class(state);

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_private));
    Dispatch dis(call_site->name());

    if(!dis.resolve(state, call_site->name(), lookup)) {
      if(!lookup_method_missing(state, call_frame, args,
          dis, call_frame->self(), recv->lookup_begin(state))) {
        return NULL;
      }
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  Object* CallSite::empty_cache_super(STATE, CallSite* call_site, CallFrame* call_frame,
                                   Arguments& args)
  {
    Symbol* original_name = call_frame->original_name();
    if(call_site->name_ != original_name) {
      call_site->name_ = original_name;
      args.set_name(call_site->name_);
    }

    Object* const recv = args.recv();
    Class* const recv_class = recv->direct_class(state);
    Module* const start = call_frame->module()->superclass();

    LookupData lookup(call_frame->self(), start, G(sym_private));
    Dispatch dis(call_site->name());

    if(start->nil_p() || !dis.resolve(state, call_site->name(), lookup)) {

      LookupData missing_lookup(call_frame->self(), recv->lookup_begin(state), G(sym_private));
      Dispatch missing_dis(G(sym_method_missing));

      if(!missing_dis.resolve(state, G(sym_method_missing), missing_lookup)) {
        std::ostringstream msg;
        msg << "no method_missing for ";
        msg << recv_class->to_string(state);
        msg << "#" << call_site->name()->to_string(state);

        Exception::internal_error(state, call_frame, msg.str().c_str());
        return 0;
      }

      args.unshift(state, call_site->name());
      dis.method = missing_dis.method;
      dis.module = missing_dis.module;
      dis.method_missing = eSuper;
      state->vm()->set_method_missing_reason(dis.method_missing);
      state->vm()->global_cache()->add_seen(state, call_site->name());
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  void CallSite::empty_cache_updater(STATE, CallSite* call_site, Class* klass, Dispatch& dispatch) {
    MonoInlineCache* cache = MonoInlineCache::create(state, call_site, klass, dispatch);
    call_site->update_call_site(state, cache);
  }

  bool CallSite::update_and_validate(STATE, CallFrame* call_frame, Object* recv, Symbol* vis, int serial) {

    Class* const recv_class = recv->direct_class(state);

    if(MonoInlineCache* mono = try_as<MonoInlineCache>(this)) {
      if(recv_class->data_raw() == mono->receiver_data_raw()) {
        return mono->method()->serial()->to_native() == serial;
      }
    }

    if(PolyInlineCache* cache = try_as<PolyInlineCache>(this)) {
      InlineCacheEntry* ice = cache->get_entry(recv_class);
      if(likely(ice)) return ice->method()->serial()->to_native() == serial;
    }

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_public));
    Dispatch dis(name_);

    if(dis.resolve(state, name_, lookup)) {
      update(state, recv_class, dis);
      return dis.method->serial()->to_native() == serial;
    }
    return false;
  }

  bool CallSite::lookup_method_missing(STATE, CallFrame* call_frame, Arguments& args, Dispatch& dis, Object* self, Module* begin) {
    LookupData missing_lookup(self, begin, G(sym_private));
    Dispatch missing_dis(G(sym_method_missing));

    if(!missing_dis.resolve(state, G(sym_method_missing), missing_lookup)) {
      std::ostringstream msg;
      msg << "no method_missing for ";
      msg << begin->to_string(state);
      msg << "#" << dis.name->to_string(state);

      Exception::internal_error(state, call_frame, msg.str().c_str());
      return false;
    }

    args.unshift(state, dis.name);
    dis.method = missing_dis.method;
    dis.module = missing_dis.module;
    state->vm()->set_method_missing_reason(dis.method_missing);
    state->vm()->global_cache()->add_seen(state, dis.name);
    return true;
  }

  void CallSite::Info::mark(Object* obj, ObjectMark& mark) {
    auto_mark(obj, mark);
  }

}
<commit_msg>Correctly distinguish between a vcall and method when missing. (Missing method => NoMethodError, missing vcall => NameError) Fixes #3101.<commit_after>#include "arguments.hpp"
#include "builtin/class.hpp"
#include "builtin/call_custom_cache.hpp"
#include "builtin/call_site.hpp"
#include "builtin/call_unit.hpp"
#include "builtin/exception.hpp"
#include "builtin/executable.hpp"
#include "builtin/mono_inline_cache.hpp"
#include "builtin/object.hpp"
#include "builtin/poly_inline_cache.hpp"
#include "call_frame.hpp"
#include "global_cache.hpp"
#include "lookup_data.hpp"
#include "object_utils.hpp"
#include "ontology.hpp"

#include <sstream>

namespace rubinius {

  void CallSite::init(STATE) {
    GO(call_site).set(ontology::new_class(state, "CallSite", G(object), G(rubinius)));
    G(call_site)->set_object_type(state, CallSiteType);
  }

  CallSite* CallSite::empty(STATE, Symbol* name, Executable* executable, int ip) {
    CallSite* cache =
      state->new_object_dirty<CallSite>(G(call_site));
    cache->name_ = name;
    cache->executor_ = empty_cache;
    cache->fallback_ = empty_cache;
    cache->updater_  = empty_cache_updater;
    cache->executable(state, executable);
    cache->ip_ = ip;
    return cache;
  }

  Integer* CallSite::ip_prim(STATE) {
    return Integer::from(state, ip_);
  }

  Object* CallSite::empty_cache_custom(STATE, CallSite* call_site, CallFrame* call_frame,
                                          Arguments& args)
  {
    Object* const recv = args.recv();

    Array*  ary = Array::create(state, args.total() + 2);
    ary->set(state, 0, recv);
    ary->set(state, 1, call_site->name_);

    for(size_t i = 0; i < args.total(); i++) {
      ary->set(state, i + 2, args.get_argument(i));
    }

    Object* ret = G(rubinius)->send(state, call_frame, state->symbol("bind_call"), ary);

    if(CallUnit* cu = try_as<CallUnit>(ret)) {
      CallCustomCache* cache = CallCustomCache::create(state, call_site, cu);
      call_site->update_call_site(state, cache);
      return cu->execute(state, call_frame, cu, cu->executable(), cu->module(), args);
    } else {
      Exception::internal_error(state, call_frame, "bind_call must return CallUnit");
      return 0;
    }
  }

  Object* CallSite::empty_cache(STATE, CallSite* call_site, CallFrame* call_frame,
                                   Arguments& args)
  {
    Object* const recv = args.recv();
    Class*  const recv_class  = recv->direct_class(state);

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_public));
    Dispatch dis(call_site->name());

    if(!dis.resolve(state, call_site->name(), lookup)) {
      if(!lookup_method_missing(state, call_frame, args,
          dis, call_frame->self(), recv->lookup_begin(state))) {
        return NULL;
      }
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  Object* CallSite::empty_cache_private(STATE, CallSite* call_site, CallFrame* call_frame,
                                   Arguments& args)
  {
    Object* const recv = args.recv();
    Class* const recv_class = recv->direct_class(state);

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_private));
    Dispatch dis(call_site->name());

    if(!dis.resolve(state, dis.name, lookup)) {
      if(!lookup_method_missing(state, call_frame, args,
          dis, call_frame->self(), recv->lookup_begin(state))) {
        return NULL;
      }
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  Object* CallSite::empty_cache_vcall(STATE, CallSite* call_site, CallFrame* call_frame,
                                         Arguments& args)
  {

    Object* const recv = args.recv();
    Class* const recv_class = recv->direct_class(state);

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_private));
    Dispatch dis(call_site->name());

    if(!dis.resolve(state, call_site->name(), lookup)) {
      dis.method_missing = eVCall;
      if(!lookup_method_missing(state, call_frame, args,
          dis, call_frame->self(), recv->lookup_begin(state))) {
        return NULL;
      }
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  Object* CallSite::empty_cache_super(STATE, CallSite* call_site, CallFrame* call_frame,
                                   Arguments& args)
  {
    Symbol* original_name = call_frame->original_name();
    if(call_site->name_ != original_name) {
      call_site->name_ = original_name;
      args.set_name(call_site->name_);
    }

    Object* const recv = args.recv();
    Class* const recv_class = recv->direct_class(state);
    Module* const start = call_frame->module()->superclass();

    LookupData lookup(call_frame->self(), start, G(sym_private));
    Dispatch dis(call_site->name());

    if(start->nil_p() || !dis.resolve(state, call_site->name(), lookup)) {

      LookupData missing_lookup(call_frame->self(), recv->lookup_begin(state), G(sym_private));
      Dispatch missing_dis(G(sym_method_missing));

      if(!missing_dis.resolve(state, G(sym_method_missing), missing_lookup)) {
        std::ostringstream msg;
        msg << "no method_missing for ";
        msg << recv_class->to_string(state);
        msg << "#" << call_site->name()->to_string(state);

        Exception::internal_error(state, call_frame, msg.str().c_str());
        return 0;
      }

      args.unshift(state, call_site->name());
      dis.method = missing_dis.method;
      dis.module = missing_dis.module;
      dis.method_missing = eSuper;
      state->vm()->set_method_missing_reason(dis.method_missing);
      state->vm()->global_cache()->add_seen(state, call_site->name());
    }

    call_site->update(state, recv_class, dis);

    Executable* meth = dis.method;
    Module* mod = dis.module;

    if(meth->custom_call_site_p()) {
      CallSiteInformation info(call_site->executable(), call_site->ip());
      state->set_call_site_information(&info);
      Object* res = meth->execute(state, call_frame, meth, mod, args);
      state->set_call_site_information(NULL);
      return res;
    } else {
      return meth->execute(state, call_frame, meth, mod, args);
    }
  }

  void CallSite::empty_cache_updater(STATE, CallSite* call_site, Class* klass, Dispatch& dispatch) {
    MonoInlineCache* cache = MonoInlineCache::create(state, call_site, klass, dispatch);
    call_site->update_call_site(state, cache);
  }

  bool CallSite::update_and_validate(STATE, CallFrame* call_frame, Object* recv, Symbol* vis, int serial) {

    Class* const recv_class = recv->direct_class(state);

    if(MonoInlineCache* mono = try_as<MonoInlineCache>(this)) {
      if(recv_class->data_raw() == mono->receiver_data_raw()) {
        return mono->method()->serial()->to_native() == serial;
      }
    }

    if(PolyInlineCache* cache = try_as<PolyInlineCache>(this)) {
      InlineCacheEntry* ice = cache->get_entry(recv_class);
      if(likely(ice)) return ice->method()->serial()->to_native() == serial;
    }

    LookupData lookup(call_frame->self(), recv->lookup_begin(state), G(sym_public));
    Dispatch dis(name_);

    if(dis.resolve(state, name_, lookup)) {
      update(state, recv_class, dis);
      return dis.method->serial()->to_native() == serial;
    }
    return false;
  }

  bool CallSite::lookup_method_missing(STATE, CallFrame* call_frame, Arguments& args, Dispatch& dis, Object* self, Module* begin) {
    LookupData missing_lookup(self, begin, G(sym_private));
    Dispatch missing_dis(G(sym_method_missing));

    if(!missing_dis.resolve(state, G(sym_method_missing), missing_lookup)) {
      std::ostringstream msg;
      msg << "no method_missing for ";
      msg << begin->to_string(state);
      msg << "#" << dis.name->to_string(state);

      Exception::internal_error(state, call_frame, msg.str().c_str());
      return false;
    }

    args.unshift(state, dis.name);
    dis.method = missing_dis.method;
    dis.module = missing_dis.module;
    state->vm()->set_method_missing_reason(dis.method_missing);
    state->vm()->global_cache()->add_seen(state, dis.name);
    return true;
  }

  void CallSite::Info::mark(Object* obj, ObjectMark& mark) {
    auto_mark(obj, mark);
  }

}
<|endoftext|>


<commit_before>/**
Copyright (c) 2013, Philip Deegan.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of Philip Deegan nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

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.
*/
#ifndef _KUL_CPU_HPP_
#define _KUL_CPU_HPP_

#include <pwd.h>
#include <thread>
#include <fstream>
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <algorithm>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>

namespace kul{
namespace cpu{
inline uint32_t cores() {
    uint16_t numCPU = sysconf( _SC_NPROCESSORS_ONLN );
    int16_t mib[4];
    size_t len = sizeof(numCPU);

    mib[0] = CTL_HW;
#if defined(__APPLE__)
    mib[1] = HW_AVAILCPU;
#else
    mib[1] = HW_NCPU;
#endif
    sysctl(mib, 2, &numCPU, &len, NULL, 0);

    if(numCPU < 1){
        mib[1] = HW_NCPU;
        sysctl( mib, 2, &numCPU, &len, NULL, 0 );
        if(numCPU < 1) numCPU = 1;
    }
    return numCPU;
}
inline uint16_t threads() {
    return std::thread::hardware_concurrency();
}
} // END NAMESPACE cpu
} // END NAMESPACE kul

#endif /* _KUL_CPU_HPP_ */

<commit_msg>Update cpu.hpp<commit_after>/**
Copyright (c) 2013, Philip Deegan.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of Philip Deegan nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

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.
*/
#ifndef _KUL_CPU_HPP_
#define _KUL_CPU_HPP_

#include <pwd.h>
#include <thread>
#include <fstream>
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <algorithm>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>

namespace kul{
namespace cpu{
inline uint32_t cores() {
    uint16_t numCPU = sysconf( _SC_NPROCESSORS_ONLN );
    int mib[4];
    size_t len = sizeof(numCPU);

    mib[0] = CTL_HW;
#if defined(__APPLE__)
    mib[1] = HW_AVAILCPU;
#else
    mib[1] = HW_NCPU;
#endif
    sysctl(mib, 2, &numCPU, &len, NULL, 0);

    if(numCPU < 1){
        mib[1] = HW_NCPU;
        sysctl( mib, 2, &numCPU, &len, NULL, 0 );
        if(numCPU < 1) numCPU = 1;
    }
    return numCPU;
}
inline uint16_t threads() {
    return std::thread::hardware_concurrency();
}
} // END NAMESPACE cpu
} // END NAMESPACE kul

#endif /* _KUL_CPU_HPP_ */

<|endoftext|>