<commit_before>/*
 Copyright (c) 2014-15, Richard Eakin - All rights reserved.

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

 1. Redistributions of source code must retain the above copyright notice, this list of conditions and
 the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 the following disclaimer in the documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
*/

#include "mason/FileWatcher.h"
#include "mason/Profiling.h"

#include "cinder/app/App.h"
#include "cinder/Log.h"

//#define LOG_UPDATE( stream )	CI_LOG_I( stream )
#define LOG_UPDATE( stream )	( (void)( 0 ) )

using namespace ci;
using namespace std;

namespace mason {

//! Base class for Watch types, which are returned from FileWatcher::load() and watch()
class Watch : public std::enable_shared_from_this<Watch>, private ci::Noncopyable {
  public:
	virtual ~Watch() = default;

	signals::Connection	connect( const function<void ( const WatchEvent& )> &callback )	{ return mSignalChanged.connect( callback ); }

	//! Checks if the asset file is up-to-date. Also may discard the Watch if there are no more connected slots.
	virtual void checkCurrent() = 0;
	//! Remove any watches for \a filePath. If it is the last file associated with this Watch, discard
	virtual void unwatch( const fs::path &filePath ) = 0;
	//! Emit the signal callback.
	virtual void emitCallback() = 0;
	//! Enables or disables a Watch
	virtual void setEnabled( bool enable, const fs::path &filePath ) = 0;

	//! Marks the Watch as needing its callback to be emitted on the main thread.
	void setNeedsCallback( bool b )	{ mNeedsCallback = b; }
	//! Returns whether the Watch needs its callback emitted on the main thread.
	bool needsCallback() const		{ return mNeedsCallback; }
	//! Marks the Watch as discarded, will be destroyed the next update loop
	void markDiscarded()			{ mDiscarded = true; }
	//! Returns whether the Watch is discarded and should be destroyed.
	bool isDiscarded() const		{ return mDiscarded; }
	//! Returns whether the Watch is enabled or disabled
	bool isEnabled() const			{ return mEnabled; }

  protected:
	bool mDiscarded = false;
	bool mEnabled = true;
	bool mNeedsCallback = false;

	ci::signals::Signal<void ( const WatchEvent& )>	mSignalChanged;
};

//! Handles a single live asset
class WatchSingle : public Watch {
  public:
	WatchSingle( const ci::fs::path &filePath );

	void checkCurrent() override;
	void unwatch( const fs::path &filePath ) override;
	void emitCallback() override;
	void setEnabled( bool enable, const fs::path &filePath ) override;

	const fs::path&	getFilePath() const		{ return mFilePath; }

  private:
	ci::fs::path										mFilePath;
	ci::fs::file_time_type								mTimeLastWrite;
};

//! Handles multiple live assets. Takes a vector of fs::paths as argument, result function gets an array of resolved filepaths.
class WatchMany : public Watch {
  public:
	WatchMany( const std::vector<ci::fs::path> &filePaths );

	void checkCurrent() override;
	void unwatch( const fs::path &filePath ) override;
	void emitCallback() override;
	void setEnabled( bool enable, const fs::path &filePath ) override;

	size_t	getNumFiles() const	{ return mFilePaths.size(); }

  private:
	std::vector<ci::fs::path>			mFilePaths, mModifiedFilePaths;
	std::vector<ci::fs::file_time_type>	mTimeStamps;
};

namespace {

fs::path findFullFilePath( const fs::path &filePath )
{
	if( filePath.empty() )
		throw FileWatcherException( "empty path" );

	if( filePath.is_absolute() && fs::exists( filePath ) )
		return filePath;

	auto resolvedAssetPath = app::getAssetPath( filePath );
	if( ! fs::exists( resolvedAssetPath ) )
		throw FileWatcherException( "could not resolve file path: " + filePath.string() );

	return resolvedAssetPath;
}

} // anonymous namespace

// ----------------------------------------------------------------------------------------------------
// WatchSingle
// ----------------------------------------------------------------------------------------------------

WatchSingle::WatchSingle( const fs::path &filePath )
{
	mFilePath = findFullFilePath( filePath );
	mTimeLastWrite = fs::last_write_time( mFilePath );
}

void WatchSingle::checkCurrent()
{
	if( ! fs::exists( mFilePath ) )
		return;

	// Discard when there are no more connected slots
	if( mSignalChanged.getNumSlots() == 0 ) {
		markDiscarded();
		return;
	}

	auto timeLastWrite = fs::last_write_time( mFilePath );
	if( mTimeLastWrite < timeLastWrite ) {
		mTimeLastWrite = timeLastWrite;
		setNeedsCallback( true );
	}
}

void WatchSingle::unwatch( const fs::path &filePath )
{
	if( mFilePath == filePath )
		markDiscarded();
}

void WatchSingle::setEnabled( bool enable, const fs::path &filePath )
{
	if( mFilePath == filePath ) {
		if( mEnabled == enable )
			return;

		mEnabled = enable;

		if( mEnabled ) {
			// update the timestamp so that any modifications while
			// the watch was disabled don't trigger a callback
			mTimeLastWrite =  fs::last_write_time( mFilePath );
		}
	}
}

void WatchSingle::emitCallback()
{
	vector<fs::path> modifiedFiles = { mFilePath };
	WatchEvent event( modifiedFiles );

	mSignalChanged.emit( event );
	setNeedsCallback( false );
}

// ----------------------------------------------------------------------------------------------------
// WatchMany
// ----------------------------------------------------------------------------------------------------

WatchMany::WatchMany( const vector<fs::path> &filePaths )
{
	mFilePaths.reserve( filePaths.size() );
	mTimeStamps.reserve( filePaths.size() );
	for( const auto &fp : filePaths ) {
		auto fullPath = findFullFilePath( fp );
		mFilePaths.push_back( fullPath );
		mTimeStamps.push_back( fs::last_write_time( fullPath ) );
	}
}

void WatchMany::checkCurrent()
{
	// Discard when there are no more connected slots
	if( mSignalChanged.getNumSlots() == 0 ) {
		markDiscarded();
		return;
	}

	const size_t numFiles = mFilePaths.size();
	mModifiedFilePaths.clear();
	for( size_t i = 0; i < numFiles; i++ ) {
		const fs::path &fp = mFilePaths[i];
		if( fs::exists( fp ) ) {
			auto timeLastWrite = fs::last_write_time( fp );
			auto &currentTimeLastWrite = mTimeStamps[i];
			if( currentTimeLastWrite < timeLastWrite ) {
				currentTimeLastWrite = timeLastWrite;
				mModifiedFilePaths.push_back( fp );
				setNeedsCallback( true );
			}
		}
	}
}

void WatchMany::unwatch( const fs::path &filePath )
{
	mFilePaths.erase( remove( mFilePaths.begin(), mFilePaths.end(), filePath ), mFilePaths.end() );

	if( mFilePaths.empty() )
		markDiscarded();
}

void WatchMany::setEnabled( bool enable, const fs::path &filePath )
{
	// FIXME: enable / disable doesn't make sense for WatchMany, since we don't know which file to disable
	// - possible solution is
}

void WatchMany::emitCallback()
{
	WatchEvent event( mModifiedFilePaths );

	mSignalChanged.emit( event );
	setNeedsCallback( false );
}

// ----------------------------------------------------------------------------------------------------
// FileWatcher
// ----------------------------------------------------------------------------------------------------

namespace {

// Used from the debugger.
void debugPrintWatches( const std::list<std::unique_ptr<Watch>>&watchList )
{
	int i = 0;
	string str;
	for( const auto &watch : watchList ) {
		string needsCallback = watch->needsCallback() ? "true" : "false";
		string discarded = watch->isDiscarded() ? "true" : "false";
		auto watchSingle = dynamic_cast<const WatchSingle *>( watch.get() );
		string filePath = watchSingle ? watchSingle->getFilePath().string() : "(many)";
		str += "[" + to_string( i ) + "] needs callback: " + needsCallback + ", discarded: " + discarded + ", file: " + filePath + "\n";

		i++;
	}

	app::console() << str << std::endl;
}

} // anonymous namespace

// static
const FileWatcherRef& FileWatcher::instance()
{
	static FileWatcherRef sInstance;
	if( ! sInstance )
		sInstance = create();

	return sInstance;
}

// static
FileWatcherRef FileWatcher::create()
{
	return FileWatcherRef( new FileWatcher );
}

FileWatcher::FileWatcher()
{
	// TODO: consider only starting this once a watch has been added
	startWatching();
}

FileWatcher::~FileWatcher()
{
	stopWatching();
}

void FileWatcher::setWatchingEnabled( bool enable )
{
	if( mWatchingEnabled == enable )
		return;

	mWatchingEnabled = enable;
	if( enable )
		instance()->startWatching();
	else
		instance()->stopWatching();
}

ci::signals::Connection FileWatcher::watch( const ci::fs::path &filePath, const std::function<void ( const WatchEvent& )> &callback )
{
	return watch( filePath, Options(), callback );
}

signals::Connection FileWatcher::watch( const fs::path &filePath, const Options &options, const function<void( const WatchEvent& )> &callback )
{
	auto watch = new WatchSingle( filePath );
	auto conn = watch->connect( callback );

	lock_guard<recursive_mutex> lock( mMutex );

	mWatchList.emplace_back( watch );

	if( options.mCallOnWatch )
		watch->emitCallback();

	return watch->connect( callback );
}

ci::signals::Connection FileWatcher::watch( const std::vector<ci::fs::path> &filePaths, const std::function<void ( const WatchEvent& )> &callback )
{
	return watch( filePaths, Options(), callback );
}

signals::Connection FileWatcher::watch( const vector<fs::path> &filePaths, const Options &options, const function<void ( const WatchEvent& )> &callback )
{
	auto watch = new WatchMany( filePaths );
	auto conn = watch->connect( callback );

	lock_guard<recursive_mutex> lock( mMutex );

	mWatchList.emplace_back( watch );

	if( options.mCallOnWatch )
		watch->emitCallback();

	return watch->connect( callback );
}

void FileWatcher::unwatch( const fs::path &filePath )
{
	auto fullPath = findFullFilePath( filePath );

	for( auto &watch : mWatchList ) {
		watch->unwatch( fullPath );
	}
}

void FileWatcher::unwatch( const vector<fs::path> &filePaths )
{
	for( const auto &filePath : filePaths ) {
		unwatch( filePath );
	}
}

void FileWatcher::enable( const fs::path &filePath )
{
	auto fullPath = findFullFilePath( filePath );

	for( auto &watch : mWatchList ) {
		watch->setEnabled( true, fullPath );
	}
}

void FileWatcher::disable( const fs::path &filePath )
{
	auto fullPath = findFullFilePath( filePath );

	for( auto &watch : mWatchList ) {
		watch->setEnabled( false, fullPath );
	}
}

void FileWatcher::startWatching()
{
	if( app::App::get() && ! mUpdateConn.isConnected() )
		mUpdateConn = app::App::get()->getSignalUpdate().connect( bind( &FileWatcher::update, this ) );

	mThreadShouldQuit = false;
	mThread = make_unique<thread>( std::bind( &FileWatcher::threadEntry, this ) );
}

void FileWatcher::stopWatching()
{
	mUpdateConn.disconnect();

	mThreadShouldQuit = true;
	if( mThread && mThread->joinable() ) {
		mThread->join();
		mThread = nullptr;
	}
}

void FileWatcher::threadEntry()
{
	while( ! mThreadShouldQuit ) {
		LOG_UPDATE( "elapsed seconds: " << app::getElapsedSeconds() );
		{
			lock_guard<recursive_mutex> lock( mMutex );

			LOG_UPDATE( "\t - updating watches, elapsed seconds: " << app::getElapsedSeconds() );

			try {
				for( auto it = mWatchList.begin(); it != mWatchList.end(); /* */ ) {
					const auto &watch = *it;

					// erase discarded
					if( watch->isDiscarded() ) {
						it = mWatchList.erase( it );
						continue;
					}
					// check if Watch's target has been modified and needs a callback, if not already marked.
					if( ! watch->needsCallback() ) {
						watch->checkCurrent();

						// If the Watch needs a callback, move it to the front of the list
						if( watch->needsCallback() && it != mWatchList.begin() ) {
							mWatchList.splice( mWatchList.begin(), mWatchList, it );
						}
					}

					++it;
				}
			}
			catch( fs::filesystem_error & ) {
				// some file probably got locked by the system. Do nothing this update frame, we'll check again next
			}
		}

		this_thread::sleep_for( chrono::duration<double>( mThreadUpdateInterval ) );
	}
}

void FileWatcher::update()
{
	LOG_UPDATE( "elapsed seconds: " << app::getElapsedSeconds() );

	// try-lock so we don't block the main thread, if we fail to acquire the mutex then we skip this update
	unique_lock<recursive_mutex> lock( mMutex, std::try_to_lock );
	if( ! lock.owns_lock() )
		return;

	LOG_UPDATE( "\t - checking watches, elapsed seconds: " << app::getElapsedSeconds() );

	CI_PROFILE_CPU( "FileWatcher update" );

	// Watches are sorted so that all that need a callback are in the beginning.
	// So break when we hit the first one that doesn't need a callback
	for( const auto &watch : mWatchList ) {

		if( ! watch->needsCallback() )
			break;

		watch->emitCallback();
	}
}

const size_t FileWatcher::getNumWatchedFiles() const
{
	size_t result = 0;
	for( const auto &w : mWatchList ) {
		auto many = dynamic_cast<WatchMany *>( w.get() );
		if( many )
			result += many->getNumFiles();
		else
			result += 1;
	}

	return result;
}

} // namespace mason
<commit_msg>Collapsing Watch types<commit_after>/*
 Copyright (c) 2014-15, Richard Eakin - All rights reserved.

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

 1. Redistributions of source code must retain the above copyright notice, this list of conditions and
 the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 the following disclaimer in the documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
*/

#include "mason/FileWatcher.h"
#include "mason/Profiling.h"

#include "cinder/app/App.h"
#include "cinder/Log.h"

//#define LOG_UPDATE( stream )	CI_LOG_I( stream )
#define LOG_UPDATE( stream )	( (void)( 0 ) )

using namespace ci;
using namespace std;

namespace mason {

//! Base class for Watch types, which are returned from FileWatcher::load() and watch()
class Watch : public std::enable_shared_from_this<Watch>, private Noncopyable {
  public:
	Watch( const std::vector<fs::path> &filePaths, bool needsCallback );

	signals::Connection	connect( const function<void ( const WatchEvent& )> &callback )	{ return mSignalChanged.connect( callback ); }

	//! Checks if the asset file is up-to-date. Also may discard the Watch if there are no more connected slots.
	void checkCurrent();
	//! Remove any watches for \a filePath. If it is the last file associated with this Watch, discard
	void unwatch( const fs::path &filePath );
	//! Emit the signal callback.
	void emitCallback();
	//! Enables or disables a Watch
	void setEnabled( bool enable, const fs::path &filePath );

	//! Marks the Watch as needing its callback to be emitted on the main thread.
	void setNeedsCallback( bool b )	{ mNeedsCallback = b; }
	//! Returns whether the Watch needs its callback emitted on the main thread.
	bool needsCallback() const		{ return mNeedsCallback; }
	//! Marks the Watch as discarded, will be destroyed the next update loop
	void markDiscarded()			{ mDiscarded = true; }
	//! Returns whether the Watch is discarded and should be destroyed.
	bool isDiscarded() const		{ return mDiscarded; }

	struct WatchItem {
		fs::path			mFilePath;
		fs::file_time_type	mTimeStamp;
		bool				mEnabled;
	};

	const std::vector<WatchItem>&	getItems() const	{ return mWatchItems; }

  private:
	bool mDiscarded = false;
	bool mEnabled = true;
	bool mNeedsCallback = false;

	std::vector<WatchItem>				mWatchItems;
	std::vector<fs::path>				mModifiedFilePaths;

	signals::Signal<void ( const WatchEvent& )>	mSignalChanged;
};

namespace {

fs::path findFullFilePath( const fs::path &filePath )
{
	if( filePath.empty() )
		throw FileWatcherException( "empty path" );

	if( filePath.is_absolute() && fs::exists( filePath ) )
		return filePath;

	auto resolvedAssetPath = app::getAssetPath( filePath );
	if( ! fs::exists( resolvedAssetPath ) )
		throw FileWatcherException( "could not resolve file path: " + filePath.string() );

	return resolvedAssetPath;
}

} // anonymous namespace

// ----------------------------------------------------------------------------------------------------
// WatchSingle
// ----------------------------------------------------------------------------------------------------

#if 0
WatchSingle::WatchSingle( const fs::path &filePath )
{
	mFilePath = findFullFilePath( filePath );
	mTimeLastWrite = fs::last_write_time( mFilePath );
}

void WatchSingle::checkCurrent()
{
	if( ! fs::exists( mFilePath ) )
		return;

	// Discard when there are no more connected slots
	if( mSignalChanged.getNumSlots() == 0 ) {
		markDiscarded();
		return;
	}

	auto timeLastWrite = fs::last_write_time( mFilePath );
	if( mTimeLastWrite < timeLastWrite ) {
		mTimeLastWrite = timeLastWrite;
		setNeedsCallback( true );
	}
}

void WatchSingle::unwatch( const fs::path &filePath )
{
	if( mFilePath == filePath )
		markDiscarded();
}

void WatchSingle::setEnabled( bool enable, const fs::path &filePath )
{
	if( mFilePath == filePath ) {
		if( mEnabled == enable )
			return;

		mEnabled = enable;

		if( mEnabled ) {
			// update the timestamp so that any modifications while
			// the watch was disabled don't trigger a callback
			mTimeLastWrite =  fs::last_write_time( mFilePath );
		}
	}
}

void WatchSingle::emitCallback()
{
	vector<fs::path> modifiedFiles = { mFilePath };
	WatchEvent event( modifiedFiles );

	mSignalChanged.emit( event );
	setNeedsCallback( false );
}

#endif

// ----------------------------------------------------------------------------------------------------
// WatchMany
// ----------------------------------------------------------------------------------------------------

Watch::Watch( const vector<fs::path> &filePaths, bool needsCallback )
{
	mWatchItems.reserve( filePaths.size() );
	for( const auto &fp : filePaths ) {
		auto fullPath = findFullFilePath( fp );
		mWatchItems.push_back( { fullPath, fs::last_write_time( fullPath ), true } );
	}

	if( needsCallback ) {
		// mark all files as modified, using the full path we just resolved.
		for( const auto &item : mWatchItems )
			mModifiedFilePaths.push_back( item.mFilePath );

		setNeedsCallback( true );
	}

}

void Watch::checkCurrent()
{
	// Discard when there are no more connected slots
	if( mSignalChanged.getNumSlots() == 0 ) {
		markDiscarded();
		return;
	}

	mModifiedFilePaths.clear();
	for( auto &item : mWatchItems ) {
		if( item.mEnabled && fs::exists( item.mFilePath ) ) {
			auto timeLastWrite = fs::last_write_time( item.mFilePath );
			if( item.mTimeStamp < timeLastWrite ) {
				item.mTimeStamp = timeLastWrite;
				mModifiedFilePaths.push_back( item.mFilePath );
				setNeedsCallback( true );
			}
		}
	}
}

void Watch::unwatch( const fs::path &filePath )
{
	mWatchItems.erase( remove_if( mWatchItems.begin(), mWatchItems.end(),
		[&filePath]( const WatchItem &item ) {
			return item.mFilePath == filePath;
		} ),
		mWatchItems.end() );

	if( mWatchItems.empty() )
		markDiscarded();
}

void Watch::setEnabled( bool enable, const fs::path &filePath )
{
	for( auto &item : mWatchItems ) {
		if( item.mFilePath == filePath ) {
			item.mEnabled = enable;

			// update the timestamp so that any modifications while
			// the watch was disabled don't trigger a callback
			item.mTimeStamp = fs::last_write_time( item.mFilePath );
		}
	}
}

void Watch::emitCallback()
{
	WatchEvent event( mModifiedFilePaths );

	mSignalChanged.emit( event );
	setNeedsCallback( false );
}

// ----------------------------------------------------------------------------------------------------
// FileWatcher
// ----------------------------------------------------------------------------------------------------

namespace {

// Used from the debugger.
void debugPrintWatches( const std::list<std::unique_ptr<Watch>>&watchList )
{
	int i = 0;
	string str;
	for( const auto &watch : watchList ) {
		string needsCallback = watch->needsCallback() ? "true" : "false";
		string discarded = watch->isDiscarded() ? "true" : "false";

		const auto &items = watch->getItems();
		string filePathStr = items.front().mFilePath.string();
		if( items.size() > 1 )
			filePathStr += " ...(" + to_string( items.size() ) + " files)";

		str += "[" + to_string( i ) + "] needs callback: " + needsCallback + ", discarded: " + discarded + ", file: " + filePathStr + "\n";

		i++;
	}

	app::console() << str << std::endl;
}

} // anonymous namespace

// static
const FileWatcherRef& FileWatcher::instance()
{
	static FileWatcherRef sInstance;
	if( ! sInstance )
		sInstance = create();

	return sInstance;
}

// static
FileWatcherRef FileWatcher::create()
{
	return FileWatcherRef( new FileWatcher );
}

FileWatcher::FileWatcher()
{
	startWatching();
}

FileWatcher::~FileWatcher()
{
	stopWatching();
}

void FileWatcher::setWatchingEnabled( bool enable )
{
	if( mWatchingEnabled == enable )
		return;

	mWatchingEnabled = enable;
	if( enable )
		instance()->startWatching();
	else
		instance()->stopWatching();
}

signals::Connection FileWatcher::watch( const fs::path &filePath, const function<void ( const WatchEvent& )> &callback )
{
	vector<fs::path> filePaths = { filePath };
	return watch( filePaths, Options(), callback );
}

signals::Connection FileWatcher::watch( const fs::path &filePath, const Options &options, const function<void( const WatchEvent& )> &callback )
{
	vector<fs::path> filePaths = { filePath };
	return watch( filePaths, options, callback );
}

signals::Connection FileWatcher::watch( const vector<fs::path> &filePaths, const function<void ( const WatchEvent& )> &callback )
{
	return watch( filePaths, Options(), callback );
}

signals::Connection FileWatcher::watch( const vector<fs::path> &filePaths, const Options &options, const function<void ( const WatchEvent& )> &callback )
{
	auto watch = new Watch( filePaths, options.mCallOnWatch );
	auto conn = watch->connect( callback );

	lock_guard<recursive_mutex> lock( mMutex );

	mWatchList.emplace_back( watch );

	if( options.mCallOnWatch )
		watch->emitCallback();

	return watch->connect( callback );
}

void FileWatcher::unwatch( const fs::path &filePath )
{
	auto fullPath = findFullFilePath( filePath );

	for( auto &watch : mWatchList ) {
		watch->unwatch( fullPath );
	}
}

void FileWatcher::unwatch( const vector<fs::path> &filePaths )
{
	for( const auto &filePath : filePaths ) {
		unwatch( filePath );
	}
}

void FileWatcher::enable( const fs::path &filePath )
{
	auto fullPath = findFullFilePath( filePath );

	for( auto &watch : mWatchList ) {
		watch->setEnabled( true, fullPath );
	}
}

void FileWatcher::disable( const fs::path &filePath )
{
	auto fullPath = findFullFilePath( filePath );

	for( auto &watch : mWatchList ) {
		watch->setEnabled( false, fullPath );
	}
}

void FileWatcher::startWatching()
{
	if( app::App::get() && ! mUpdateConn.isConnected() )
		mUpdateConn = app::App::get()->getSignalUpdate().connect( bind( &FileWatcher::update, this ) );

	mThreadShouldQuit = false;
	mThread = make_unique<thread>( std::bind( &FileWatcher::threadEntry, this ) );
}

void FileWatcher::stopWatching()
{
	mUpdateConn.disconnect();

	mThreadShouldQuit = true;
	if( mThread && mThread->joinable() ) {
		mThread->join();
		mThread = nullptr;
	}
}

void FileWatcher::threadEntry()
{
	while( ! mThreadShouldQuit ) {
		LOG_UPDATE( "elapsed seconds: " << app::getElapsedSeconds() );
		{
			lock_guard<recursive_mutex> lock( mMutex );

			LOG_UPDATE( "\t - updating watches, elapsed seconds: " << app::getElapsedSeconds() );

			try {
				for( auto it = mWatchList.begin(); it != mWatchList.end(); /* */ ) {
					const auto &watch = *it;

					// erase discarded
					if( watch->isDiscarded() ) {
						it = mWatchList.erase( it );
						continue;
					}
					// check if Watch's target has been modified and needs a callback, if not already marked.
					if( ! watch->needsCallback() ) {
						watch->checkCurrent();

						// If the Watch needs a callback, move it to the front of the list
						if( watch->needsCallback() && it != mWatchList.begin() ) {
							mWatchList.splice( mWatchList.begin(), mWatchList, it );
						}
					}

					++it;
				}
			}
			catch( fs::filesystem_error & ) {
				// some file probably got locked by the system. Do nothing this update frame, we'll check again next
			}
		}

		this_thread::sleep_for( chrono::duration<double>( mThreadUpdateInterval ) );
	}
}

void FileWatcher::update()
{
	LOG_UPDATE( "elapsed seconds: " << app::getElapsedSeconds() );

	// try-lock so we don't block the main thread, if we fail to acquire the mutex then we skip this update
	unique_lock<recursive_mutex> lock( mMutex, std::try_to_lock );
	if( ! lock.owns_lock() )
		return;

	LOG_UPDATE( "\t - checking watches, elapsed seconds: " << app::getElapsedSeconds() );

	CI_PROFILE_CPU( "FileWatcher update" );

	// Watches are sorted so that all that need a callback are in the beginning.
	// So break when we hit the first one that doesn't need a callback
	for( const auto &watch : mWatchList ) {

		if( ! watch->needsCallback() )
			break;

		watch->emitCallback();
	}
}

const size_t FileWatcher::getNumWatchedFiles() const
{
	size_t result = 0;
	for( const auto &w : mWatchList ) {
		result += w->getItems().size();
	}

	return result;
}

} // namespace mason
<|endoftext|>


<commit_before>// Author : Andrei Gheata	02/10/00

#include "HelpTextTV.h"

#ifndef WIN32
const char gTVHelpAbout[] 		= "\
   TTreeView is GUI version of TTreeViewer, designed to handle ROOT trees and\n\
to take advantage of TTree class features in a graphical manner. It uses only\n\
ROOT native GUI widgets and has capability to work with several trees in the\n\
same session. It provides the following functionalities :\n\n\
  - browsing all root files in the working directory and mapping trees inside;\n\
  - once a tree is mapped, the user can browse branches and work with the\n\
  corresponding sub-branches if there is no need for the whole tree;\n\
  - fast drawing of branches by double-click;\n\
  - easy edit the expressions to be drawn on X, Y and Z axis and/or selection;\n\
  - dragging expressions to one axis and aliasing of expression names;\n\
  - handle input/output event lists;\n\
  - usage of predefined compatible drawing options;\n\
  - possibility of executing user commands and macros and echoing of the current\n\
  command;\n\
  - possibility of interrupting the current command or the event loop (not yet);\n\
  - possibility of selecting the tree entries to be processed (not yet);\n\
  - take advantage of TTree class features via context menu;\n\n\
";

const char gTVHelpStart[]		= "\
   The quickest way to start the tree viewer is to start a ROOT session in \n\
your working directory where you have the root files containing trees.\n\
You will need first to load the library for TTreeView and optionally other\n\
libraries for user defined classes (you can do this later in the session) :\n\
   root [0] gSystem->Load(\"TTreeView\");\n\
   root [1] new TTreeView;\n\
or, to load the tree Mytree from the file Myfile :\n\
   root [1] TFile file(\"Myfile\");\n\
   root [2] new TTreeView(\"Mytree\");\n\n\
This will work if uou have the path to the library TTreeView defined in your\n\
.rootrc file.\n\n
";

const char gTVHelpLayout[]		= "\
   The layout has the following items :\n\n\
  - a menu bar with entries : File, Edit, Run, Options and Help;\n\
  - a toolbar in the upper part where you can issue user commands, change\n\
  the drawing option and the histogram name, two check buttons Hist and Rec\n\
  which toggles histogram drawing mode and command recording respectively;\n\
  - a button bar in the lower part with : buttons DRAW/STOP that issue histogram\n\
  drawing and stop the current command respectively, two text widgets where \n\
  input and output event lists can be specified, a message box and a RESET\n\
  button on the right that clear edited expression content (see Editing...)\n\
  - a tree-type list on the main left panel where you can browse the root files\n\
  from the working directory and load the trees inside by double clicking.\n\
  When the first tree is loaded, a new item called \"TreeList\" will pop-up on\n\
  the list menu and will have the selected tree inside with all branches mapped\n\
  Mapped trees are provided with context menus, activated by right-clicking;\n\
  - a view-type list on the main right panel. The first column contain X, Y and\n\
  Z expression items, an optional cut and ten optional editable expressions.\n\
  The other items in this list are activated when a mapped item from the\n\
  \"TreeList\" is left-clicked (tree or branch) and will describe the conyent\n\
  of the tree (branch). Expressions and leaf-type items can be dragged or\n\
  deleted. A right click on the list-box or item activates a general context\n\
  menu.\n\n\
";

const char gTVHelpBrowse[]		= "\
   Browsing root files from the working directory :\n\n\
Just double-click on the directory item on the left and you will see all\n\
root files from this directory. Do it once more on the files with a leading +\n\
and you will see the trees inside. If you want one or more of those to\n\
be loaded, double-click on them and they will be mapped in a new item called\n\
\"TreeList\".\n\n\
   Browsing trees :\n\n\
Left-clicking on trees from the TreeList will expand their content on the list\n\
from the right side. Double-clicking them will also open their content on the\n\
left, where you can click on branches to expand them on the right.\n\n\
";

const char gTVHelpDraggingItems[]	= "\
   Items that can be dragged from the list in the right : expressions and \n\
leaves. Dragging an item and dropping to another will copy the content of first\n\
to the last (leaf->expression, expression->expression). Items far to the right\n\
side of the list can be easily dragged to the left (where expressions are\n\
placed) by dragging them to the left at least 10 pixels.\n\n\
";

const char gTVHelpEditExpressions[]	= "\
   All editable expressions from the right panel has two components : a\n\
true name (that will be used when TTree::Draw() commands are issued) and an\n\
alias (used for labeling axes - not yet). The visible name is the alias if\n\
there is one and the true name otherwise.\n\n\
   The expression editor can be activated by right clicking on an\n\
expression item via the command EditExpression from the context menu.\n\
An alternative is to use the Edit-Expression menu after the desired expression\n\
is selected. The editor will pop-up in the left part, but it can be moved.\n\
The editor usage is the following :\n\
  - you can write C expressions made of leaf names by hand or you can insert\n\
  any item from the right panel by clicking on it (recommandable);\n\
  - you should write the item alias by hand since it not ony make the expression\n\
  meaningfull, but it also highly improve the layout for big expressions\n\n\
";

const char gTVHelpUserCommands[]	= "\
   User commands can be issued directly from the textbox labeled \"Command\"\n\
from the upper-left toolbar by typing and pressing Enter at the end.\n\
   An other way is from the right panel context menu : ExecuteCommand.\n\n\
All commands can be interrupted at any time by pressing the STOP button\n\
from the bottom-left (not yet)\n\n\
You can toggle recording of the current command in the history file by\n\
checking the Rec button from the top-right\n\n\
";

const char gTVHelpContext[]		= "\
   You can activate context menus by right-clicking on items or inside the\n\
box from the right.\n\n\
Context menus for mapped items from the left tree-type list :\n\n\
  The items from the left that are provided with context menus are tree and\n\
branch items. You can directly activate the *MENU* marked methods of TTree\n\
from this menu.\n\n\
Context menu for the right panel :\n\n\
  A general context manu of class TTreeView is acivated if the user\n\
right-clicks the right panel. Commands are :\n\
  - ClearAll        : clears the content of all expressions;\n\
  - ClearExpression : clear the content of the clicked expression;\n\
  - EditExpression  : pops-up the expression editor;\n\
  - ExecuteCommand  : execute a user command;\n\
  - MakeSelector    : equivalent of TTree::MakeSelector();\n\
  - Process         : equivalent of TTree::Process();\n\
  - RemoveExpression: removes clicked item from the list;\n\n\
";

const char gTVHelpDrawing[]		= "\
Fast variable drawing : Just double-click an item from the right list.\n\n\
Normal drawing : Edit the X, Y, Z fields as you wish, fill input-output list\n\
names if you have them. You can change output histogram name, or toggle Hist\n\
and Scan modes by checking the corresponding buttons.\n\
  Hist mode implies that the current histogram will be redrawn with the current\n\
graphics options, while Scan mode implies TTree::Scan()\n\n\
  You have a complete list of histogram options in multicheckable lists from\n\
the Option menu. When using this menu, only the options compatible with the\n\
current histogram dimension will be available. You can check multiple options\n\
and reset them by checking the Default entries.\n\n\
  After completing the previous operations you can issue the draw command by\n\
pressing the DRAW button.\n\n\
";

const char gTVHelpMacros[]		= "\
   Macros can be loaded and executed in this version only by issuing\n\
the corresponding user commands (see help on user commands)\n\n\
";
#else
const char gTVHelpAbout[] 		= "empty";
const char gTVHelpStart[]		= "empty";
const char gTVHelpLayout[]		= "empty";
const char gTVHelpBrowse[]		= "empty";
const char gTVHelpDraggingItems[]	= "empty";
const char gTVHelpEditExpressions[]	= "empty";
const char gTVHelpUserCommands[]	= "empty";
const char gTVHelpLoopingEvents[]	= "empty";
const char gTVHelpDrawing[]		= "empty";
const char gTVHelpMacros[]		= "empty";

#endif
<commit_msg>Formatting for the alpha/cxx<commit_after>// Author : Andrei Gheata	02/10/00

#include "HelpTextTV.h"

#ifndef WIN32
const char gTVHelpAbout[] 		= "\
   TTreeView is GUI version of TTreeViewer, designed to handle ROOT trees and\n\
to take advantage of TTree class features in a graphical manner. It uses only\n\
ROOT native GUI widgets and has capability to work with several trees in the\n\
same session. It provides the following functionalities :\n\n\
  - browsing all root files in the working directory and mapping trees inside;\n\
  - once a tree is mapped, the user can browse branches and work with the\n\
  corresponding sub-branches if there is no need for the whole tree;\n\
  - fast drawing of branches by double-click;\n\
  - easy edit the expressions to be drawn on X, Y and Z axis and/or selection;\n\
  - dragging expressions to one axis and aliasing of expression names;\n\
  - handle input/output event lists;\n\
  - usage of predefined compatible drawing options;\n\
  - possibility of executing user commands and macros and echoing of the current\n\
  command;\n\
  - possibility of interrupting the current command or the event loop (not yet);\n\
  - possibility of selecting the tree entries to be processed (not yet);\n\
  - take advantage of TTree class features via context menu;\n\n\
 ";

const char gTVHelpStart[]		= "\
   The quickest way to start the tree viewer is to start a ROOT session in \n\
your working directory where you have the root files containing trees.\n\
You will need first to load the library for TTreeView and optionally other\n\
libraries for user defined classes (you can do this later in the session) :\n\
   root [0] gSystem->Load(\"TTreeView\");\n\
   root [1] new TTreeView;\n\
or, to load the tree Mytree from the file Myfile :\n\
   root [1] TFile file(\"Myfile\");\n\
   root [2] new TTreeView(\"Mytree\");\n\n\
This will work if uou have the path to the library TTreeView defined in your\n\
.rootrc file.\n\n
 ";

const char gTVHelpLayout[]		= "\
   The layout has the following items :\n\n\
  - a menu bar with entries : File, Edit, Run, Options and Help;\n\
  - a toolbar in the upper part where you can issue user commands, change\n\
  the drawing option and the histogram name, two check buttons Hist and Rec\n\
  which toggles histogram drawing mode and command recording respectively;\n\
  - a button bar in the lower part with : buttons DRAW/STOP that issue histogram\n\
  drawing and stop the current command respectively, two text widgets where \n\
  input and output event lists can be specified, a message box and a RESET\n\
  button on the right that clear edited expression content (see Editing...)\n\
  - a tree-type list on the main left panel where you can browse the root files\n\
  from the working directory and load the trees inside by double clicking.\n\
  When the first tree is loaded, a new item called \"TreeList\" will pop-up on\n\
  the list menu and will have the selected tree inside with all branches mapped\n\
  Mapped trees are provided with context menus, activated by right-clicking;\n\
  - a view-type list on the main right panel. The first column contain X, Y and\n\
  Z expression items, an optional cut and ten optional editable expressions.\n\
  The other items in this list are activated when a mapped item from the\n\
  \"TreeList\" is left-clicked (tree or branch) and will describe the conyent\n\
  of the tree (branch). Expressions and leaf-type items can be dragged or\n\
  deleted. A right click on the list-box or item activates a general context\n\
  menu.\n\n\
 ";

const char gTVHelpBrowse[]		= "\
   Browsing root files from the working directory :\n\n\
Just double-click on the directory item on the left and you will see all\n\
root files from this directory. Do it once more on the files with a leading +\n\
and you will see the trees inside. If you want one or more of those to\n\
be loaded, double-click on them and they will be mapped in a new item called\n\
\"TreeList\".\n\n\
   Browsing trees :\n\n\
Left-clicking on trees from the TreeList will expand their content on the list\n\
from the right side. Double-clicking them will also open their content on the\n\
left, where you can click on branches to expand them on the right.\n\n\
 ";

const char gTVHelpDraggingItems[]	= "\
   Items that can be dragged from the list in the right : expressions and \n\
leaves. Dragging an item and dropping to another will copy the content of first\n\
to the last (leaf->expression, expression->expression). Items far to the right\n\
side of the list can be easily dragged to the left (where expressions are\n\
placed) by dragging them to the left at least 10 pixels.\n\n\
 ";

const char gTVHelpEditExpressions[]	= "\
   All editable expressions from the right panel has two components : a\n\
true name (that will be used when TTree::Draw() commands are issued) and an\n\
alias (used for labeling axes - not yet). The visible name is the alias if\n\
there is one and the true name otherwise.\n\n\
   The expression editor can be activated by right clicking on an\n\
expression item via the command EditExpression from the context menu.\n\
An alternative is to use the Edit-Expression menu after the desired expression\n\
is selected. The editor will pop-up in the left part, but it can be moved.\n\
The editor usage is the following :\n\
  - you can write C expressions made of leaf names by hand or you can insert\n\
  any item from the right panel by clicking on it (recommandable);\n\
  - you should write the item alias by hand since it not ony make the expression\n\
  meaningfull, but it also highly improve the layout for big expressions\n\n\
 ";

const char gTVHelpUserCommands[]	= "\
   User commands can be issued directly from the textbox labeled \"Command\"\n\
from the upper-left toolbar by typing and pressing Enter at the end.\n\
   An other way is from the right panel context menu : ExecuteCommand.\n\n\
All commands can be interrupted at any time by pressing the STOP button\n\
from the bottom-left (not yet)\n\n\
You can toggle recording of the current command in the history file by\n\
checking the Rec button from the top-right\n\n\
 ";

const char gTVHelpContext[]		= "\
   You can activate context menus by right-clicking on items or inside the\n\
box from the right.\n\n\
Context menus for mapped items from the left tree-type list :\n\n\
  The items from the left that are provided with context menus are tree and\n\
branch items. You can directly activate the *MENU* marked methods of TTree\n\
from this menu.\n\n\
Context menu for the right panel :\n\n\
  A general context manu of class TTreeView is acivated if the user\n\
right-clicks the right panel. Commands are :\n\
  - ClearAll        : clears the content of all expressions;\n\
  - ClearExpression : clear the content of the clicked expression;\n\
  - EditExpression  : pops-up the expression editor;\n\
  - ExecuteCommand  : execute a user command;\n\
  - MakeSelector    : equivalent of TTree::MakeSelector();\n\
  - Process         : equivalent of TTree::Process();\n\
  - RemoveExpression: removes clicked item from the list;\n\n\
 ";

const char gTVHelpDrawing[]		= "\
Fast variable drawing : Just double-click an item from the right list.\n\n\
Normal drawing : Edit the X, Y, Z fields as you wish, fill input-output list\n\
names if you have them. You can change output histogram name, or toggle Hist\n\
and Scan modes by checking the corresponding buttons.\n\
  Hist mode implies that the current histogram will be redrawn with the current\n\
graphics options, while Scan mode implies TTree::Scan()\n\n\
  You have a complete list of histogram options in multicheckable lists from\n\
the Option menu. When using this menu, only the options compatible with the\n\
current histogram dimension will be available. You can check multiple options\n\
and reset them by checking the Default entries.\n\n\
  After completing the previous operations you can issue the draw command by\n\
pressing the DRAW button.\n\n\
 ";

const char gTVHelpMacros[]		= "\
   Macros can be loaded and executed in this version only by issuing\n\
the corresponding user commands (see help on user commands)\n\n\
 ";
#else
const char gTVHelpAbout[] 		= "empty";
const char gTVHelpStart[]		= "empty";
const char gTVHelpLayout[]		= "empty";
const char gTVHelpBrowse[]		= "empty";
const char gTVHelpDraggingItems[]	= "empty";
const char gTVHelpEditExpressions[]	= "empty";
const char gTVHelpUserCommands[]	= "empty";
const char gTVHelpLoopingEvents[]	= "empty";
const char gTVHelpDrawing[]		= "empty";
const char gTVHelpMacros[]		= "empty";

#endif
<|endoftext|>


<commit_before>#ifndef BFC_PARSER_HPP
#define BFC_PARSER_HPP

#include <stdexcept>
#include <sstream>
#include "lexer.hpp"
#include "result.hpp"
#include "ast.hpp"

namespace bfc {

template <class Source, class Traits = source_traits<Source>>
class parser {

    public:

        using Lexer = lexer<Source, Traits>;

        parser(Lexer lexer) : lexer(std::move(lexer)) {}

        ast_node parse() {
            /* create result to hold lexer result */
            result_type res = result_type::OK;
            /* create token to pass into lexer */
            token tok{};

            for(;;) {
                res = lexer.next(tok);
                switch (res) {
                    case result_type::OK:
                        updateAst(tok);
                        break;
                    case result_type::DONE:
                        {
                        ast_node program(new ast_program(tok.loc, astSeq));
                        return program;
                        }
                    case result_type::FAIL:
                    default:
                        throw std::runtime_error("Error: Exception occured when reading input file");
                        break;
                }
            }
        }

    private:

        Lexer lexer;

        /* vector of instructions to return */
        ast_seq astSeq;

        ast_node updateLoop(source_loc loopPos){
            /* create result to hold lexer result */
            result_type res = result_type::OK;
            /* create token to pass into lexer */
            token tok;
            /* create vector to use to create ast_loop */
            ast_seq loopAst{};

            for(;;) {
                res = lexer.next(tok);
                token::type kind = tok.kind;
                source_loc loc = tok.loc;
                switch (res) {
                    case result_type::OK:
                        switch (kind) {
                            case token::INC:
                              {
                                loopAst.emplace_back(new ast_add(loc, 0, 1));
                                break;
                              }
                            case token::DEC:
                              {
                                loopAst.emplace_back(new ast_sub(loc, 0, 1));
                                break;
                              }
                            case token::MOVE_R:
                              {
                                loopAst.emplace_back(new ast_mov(loc, 1));
                                break;
                              }
                            case token::MOVE_L:
                              {
                                loopAst.emplace_back(new ast_mov(loc, -1));
                                break;
                              }
                            case token::LOOP_BEGIN:
                              {
                                ast_node innerLoop = updateLoop(loc);
                                loopAst.push_back(innerLoop);
                                break;
                              }
                            case token::LOOP_END:
                              {
                                ast_node loop(new ast_loop(loopPos, loopAst));
                                return loop;
                              }
                            case token::PUT_CHAR:
                              {
                                loopAst.emplace_back(new ast_write(loc, 0));
                                break;
                              }
                            case token::GET_CHAR:
                              {
                                loopAst.emplace_back(new ast_read(loc, 0));
                                break;
                              }
                            default:
                                break;
                        }
                        break; /* result_type::OK */
                    case result_type::DONE:
                    case result_type::FAIL:
                    default:
                      {
                        std::stringstream msg;
                        msg << loopPos << ": unmatched \'[\'";
                        throw std::runtime_error(msg.str());
                        break;
                      }
                }
            }
        }

        void updateAst(token &tok) {
            token::type kind = tok.kind;
            source_loc loc = tok.loc;

            switch (kind) {
                case token::INC:
                  {
                    astSeq.emplace_back(new ast_add(loc, 0, 1));
                    break;
                  }
                case token::DEC:
                  {
                    astSeq.emplace_back(new ast_sub(loc, 0, 1));
                    break;
                  }
                case token::MOVE_R:
                  {
                    astSeq.emplace_back(new ast_mov(loc, 1));
                    break;
                  }
                case token::MOVE_L:
                  {
                    astSeq.emplace_back(new ast_mov(loc, -1));
                    break;
                  }
                case token::LOOP_BEGIN:
                  {
                    ast_node loop = updateLoop(loc);
                    astSeq.push_back(loop);
                    break;
                  }
                case token::LOOP_END:
                  {
                    std::stringstream msg;
                    msg << loc << ": unmatched \'[\'";
                    throw std::runtime_error(msg.str());
                    break;
                  }
                case token::PUT_CHAR:
                  {
                    astSeq.emplace_back(new ast_write(loc, 0));
                    break;
                  }
                case token::GET_CHAR:
                  {
                    astSeq.emplace_back(new ast_read(loc, 0));
                    break;
                  }
                default:
                    break;
            }
        }

};

}

#endif /* !BFC_PARSER_HPP */
<commit_msg>Fix name resolution for g++<commit_after>#ifndef BFC_PARSER_HPP
#define BFC_PARSER_HPP

#include <stdexcept>
#include <sstream>
#include "lexer.hpp"
#include "result.hpp"
#include "ast.hpp"

namespace bfc {

template <class Source, class Traits = source_traits<Source>>
class parser {

    public:

        using Lexer = lexer<Source, Traits>;

        parser(Lexer lexer) : lex(std::move(lexer)) {}

        ast_node parse() {
            /* create result to hold lexer result */
            result_type res = result_type::OK;
            /* create token to pass into lexer */
            token tok{};

            for(;;) {
                res = lex.next(tok);
                switch (res) {
                    case result_type::OK:
                        updateAst(tok);
                        break;
                    case result_type::DONE:
                        {
                        ast_node program(new ast_program(tok.loc, astSeq));
                        return program;
                        }
                    case result_type::FAIL:
                    default:
                        throw std::runtime_error("Error: Exception occured when reading input file");
                        break;
                }
            }
        }

    private:

        Lexer lex;

        /* vector of instructions to return */
        ast_seq astSeq;

        ast_node updateLoop(source_loc loopPos){
            /* create result to hold lexer result */
            result_type res = result_type::OK;
            /* create token to pass into lexer */
            token tok;
            /* create vector to use to create ast_loop */
            ast_seq loopAst{};

            for(;;) {
                res = lex.next(tok);
                token::type kind = tok.kind;
                source_loc loc = tok.loc;
                switch (res) {
                    case result_type::OK:
                        switch (kind) {
                            case token::INC:
                              {
                                loopAst.emplace_back(new ast_add(loc, 0, 1));
                                break;
                              }
                            case token::DEC:
                              {
                                loopAst.emplace_back(new ast_sub(loc, 0, 1));
                                break;
                              }
                            case token::MOVE_R:
                              {
                                loopAst.emplace_back(new ast_mov(loc, 1));
                                break;
                              }
                            case token::MOVE_L:
                              {
                                loopAst.emplace_back(new ast_mov(loc, -1));
                                break;
                              }
                            case token::LOOP_BEGIN:
                              {
                                ast_node innerLoop = updateLoop(loc);
                                loopAst.push_back(innerLoop);
                                break;
                              }
                            case token::LOOP_END:
                              {
                                ast_node loop(new ast_loop(loopPos, loopAst));
                                return loop;
                              }
                            case token::PUT_CHAR:
                              {
                                loopAst.emplace_back(new ast_write(loc, 0));
                                break;
                              }
                            case token::GET_CHAR:
                              {
                                loopAst.emplace_back(new ast_read(loc, 0));
                                break;
                              }
                            default:
                                break;
                        }
                        break; /* result_type::OK */
                    case result_type::DONE:
                    case result_type::FAIL:
                    default:
                      {
                        std::stringstream msg;
                        msg << loopPos << ": unmatched \'[\'";
                        throw std::runtime_error(msg.str());
                        break;
                      }
                }
            }
        }

        void updateAst(token &tok) {
            token::type kind = tok.kind;
            source_loc loc = tok.loc;

            switch (kind) {
                case token::INC:
                  {
                    astSeq.emplace_back(new ast_add(loc, 0, 1));
                    break;
                  }
                case token::DEC:
                  {
                    astSeq.emplace_back(new ast_sub(loc, 0, 1));
                    break;
                  }
                case token::MOVE_R:
                  {
                    astSeq.emplace_back(new ast_mov(loc, 1));
                    break;
                  }
                case token::MOVE_L:
                  {
                    astSeq.emplace_back(new ast_mov(loc, -1));
                    break;
                  }
                case token::LOOP_BEGIN:
                  {
                    ast_node loop = updateLoop(loc);
                    astSeq.push_back(loop);
                    break;
                  }
                case token::LOOP_END:
                  {
                    std::stringstream msg;
                    msg << loc << ": unmatched \'[\'";
                    throw std::runtime_error(msg.str());
                    break;
                  }
                case token::PUT_CHAR:
                  {
                    astSeq.emplace_back(new ast_write(loc, 0));
                    break;
                  }
                case token::GET_CHAR:
                  {
                    astSeq.emplace_back(new ast_read(loc, 0));
                    break;
                  }
                default:
                    break;
            }
        }

};

}

#endif /* !BFC_PARSER_HPP */
<|endoftext|>


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

#ifndef _UCBHELPER_SIMPLEAUTHENTICATIONREQUEST_HXX
#define _UCBHELPER_SIMPLEAUTHENTICATIONREQUEST_HXX

#ifndef _RTL_REF_HXX_
#include <rtl/ref.hxx>
#endif
#ifndef _UCBHELPER_INTERATIONREQUEST_HXX
#include <ucbhelper/interactionrequest.hxx>
#endif
#ifndef INCLUDED_UCBHELPERDLLAPI_H
#include "ucbhelper/ucbhelperdllapi.h"
#endif

namespace ucbhelper {

/**
  * This class implements a simple authentication interaction request.
  * Instances can be passed directly to XInteractionHandler::handle(...). Each
  * instance contains an AuthenticationRequest and three interaction
  * continuations: "Abort", "Retry" and "SupplyAuthentication". The parameters
  * for the AuthenticationRequest and the InteractionSupplyAuthentication
  * objects are partly taken from contructors parameters and partly defaulted
  * as follows:
  *
  * Read-only values : servername, realm
  * Read-write values: username, password, account
  * All remember-authentication values: RememberAuthentication_NO
  *
  * @see com::sun::star::ucb::AuthenticationRequest
  * @see com::sun::star::ucb::RememberAuthentication
  * @see InteractionAbort
  * @see InteractionRetry
  * @see InteractionSupplyAuthentication
  */
class UCBHELPER_DLLPUBLIC SimpleAuthenticationRequest : public ucbhelper::InteractionRequest
{
    rtl::Reference<
        ucbhelper::InteractionSupplyAuthentication > m_xAuthSupplier;

public:
    /** Specification whether some entity (realm, username, password, account)
        is either not applicable at all, has a fixed value, or is modifiable.
     */
    enum EntityType
    {
        ENTITY_NA,
        ENTITY_FIXED,
        ENTITY_MODIFY
    };

    /**
      * Constructor.
      *
      * @param rServerName contains a server name.
      * @param rRealm contains a realm, if applicable.
      * @param rUserName contains a username, if available (for instance from
      *        a previous try).
      * @param rPassword contains a password, if available (for instance from
      *        a previous try).
      * @param rAccount contains an account, if applicable.
      */
    SimpleAuthenticationRequest( const rtl::OUString & rServerName,
                                 const rtl::OUString & rRealm,
                                 const rtl::OUString & rUserName,
                                 const rtl::OUString & rPassword,
                                 const rtl::OUString & rAccount
                                    = rtl::OUString() );

    /**
      * Constructor.
      *
      * @param rServerName contains a server name.
      * @param eRealmType specifies whether a realm is applicable and
               modifiable.
      * @param rRealm contains a realm, if applicable.
      * @param eUserNameType specifies whether a username is applicable and
               modifiable.
      * @param rUserName contains a username, if available (for instance from
      *        a previous try).
      * @param ePasswordType specifies whether a password is applicable and
               modifiable.
      * @param rPassword contains a password, if available (for instance from
      *        a previous try).
      * @param eAccountType specifies whether an account is applicable and
               modifiable.
      * @param rAccount contains an account, if applicable.
      */
    SimpleAuthenticationRequest( const rtl::OUString & rServerName,
                                 EntityType eRealmType,
                                 const rtl::OUString & rRealm,
                                 EntityType eUserNameType,
                                 const rtl::OUString & rUserName,
                                 EntityType ePasswordType,
                                 const rtl::OUString & rPassword,
                                 EntityType eAccountType = ENTITY_NA,
                                 const rtl::OUString & rAccount
                                    = rtl::OUString() );

    /**
      * This method returns the supplier for the missing authentication data,
      * that, for instance can be used to query the password supplied by the
      * interaction handler.
      *
      * @return the supplier for the missing authentication data.
      */
    const rtl::Reference< ucbhelper::InteractionSupplyAuthentication > &
    getAuthenticationSupplier() const { return m_xAuthSupplier; }
};

} // namespace ucbhelper

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

#ifndef _UCBHELPER_SIMPLEAUTHENTICATIONREQUEST_HXX
#define _UCBHELPER_SIMPLEAUTHENTICATIONREQUEST_HXX

#include <rtl/ref.hxx>
#include <ucbhelper/interactionrequest.hxx>
#include "ucbhelper/ucbhelperdllapi.h"

namespace ucbhelper {

/**
  * This class implements a simple authentication interaction request.
  * Instances can be passed directly to XInteractionHandler::handle(...). Each
  * instance contains an AuthenticationRequest and three interaction
  * continuations: "Abort", "Retry" and "SupplyAuthentication". The parameters
  * for the AuthenticationRequest and the InteractionSupplyAuthentication
  * objects are partly taken from contructors parameters and partly defaulted
  * as follows:
  *
  * Read-only values : servername, realm
  * Read-write values: username, password, account
  * All remember-authentication values: RememberAuthentication_NO
  *
  * @see com::sun::star::ucb::AuthenticationRequest
  * @see com::sun::star::ucb::RememberAuthentication
  * @see InteractionAbort
  * @see InteractionRetry
  * @see InteractionSupplyAuthentication
  */
class UCBHELPER_DLLPUBLIC SimpleAuthenticationRequest : public ucbhelper::InteractionRequest
{
    rtl::Reference<
        ucbhelper::InteractionSupplyAuthentication > m_xAuthSupplier;

public:
    /** Specification whether some entity (realm, username, password, account)
        is either not applicable at all, has a fixed value, or is modifiable.
     */
    enum EntityType
    {
        ENTITY_NA,
        ENTITY_FIXED,
        ENTITY_MODIFY
    };

    /**
      * Constructor.
      *
      * @param rServerName contains a server name.
      * @param rRealm contains a realm, if applicable.
      * @param rUserName contains a username, if available (for instance from
      *        a previous try).
      * @param rPassword contains a password, if available (for instance from
      *        a previous try).
      * @param rAccount contains an account, if applicable.
      */
    SimpleAuthenticationRequest( const rtl::OUString & rServerName,
                                 const rtl::OUString & rRealm,
                                 const rtl::OUString & rUserName,
                                 const rtl::OUString & rPassword,
                                 const rtl::OUString & rAccount
                                    = rtl::OUString() );

    /**
      * Constructor.
      *
      * @param rServerName contains a server name.
      * @param eRealmType specifies whether a realm is applicable and
               modifiable.
      * @param rRealm contains a realm, if applicable.
      * @param eUserNameType specifies whether a username is applicable and
               modifiable.
      * @param rUserName contains a username, if available (for instance from
      *        a previous try).
      * @param ePasswordType specifies whether a password is applicable and
               modifiable.
      * @param rPassword contains a password, if available (for instance from
      *        a previous try).
      * @param eAccountType specifies whether an account is applicable and
               modifiable.
      * @param rAccount contains an account, if applicable.
      */
    SimpleAuthenticationRequest( const rtl::OUString & rServerName,
                                 EntityType eRealmType,
                                 const rtl::OUString & rRealm,
                                 EntityType eUserNameType,
                                 const rtl::OUString & rUserName,
                                 EntityType ePasswordType,
                                 const rtl::OUString & rPassword,
                                 EntityType eAccountType = ENTITY_NA,
                                 const rtl::OUString & rAccount
                                    = rtl::OUString() );

    /**
      * This method returns the supplier for the missing authentication data,
      * that, for instance can be used to query the password supplied by the
      * interaction handler.
      *
      * @return the supplier for the missing authentication data.
      */
    const rtl::Reference< ucbhelper::InteractionSupplyAuthentication > &
    getAuthenticationSupplier() const { return m_xAuthSupplier; }
};

} // namespace ucbhelper

#endif /* !_UCBHELPER_SIMPLEAUTHENTICATIONREQUEST_HXX */
<|endoftext|>


<commit_before>
#pragma once

template <typename LEFT, typename RIGHT>
class Pair {
public:
  Pair(LEFT left, RIGHT right) {
    _left = left;
    _right = right;
  }

  LEFT left()   { return _left;  }
  RIGHT right() { return _right; }

  void set_left(LEFT left)    { _left = left;   }
  void set_right(RIGHT right) { _right = right; }

private:
  LEFT _left;
  RIGHT _right;
};
<commit_msg>fix whitespace<commit_after>
#pragma once

template <typename LEFT, typename RIGHT>
class Pair {
public:
  Pair(LEFT left, RIGHT right) {
    _left = left;
    _right = right;
  }

  LEFT left() { return _left;  }
  RIGHT right() { return _right; }

  void set_left(LEFT left) { _left = left;   }
  void set_right(RIGHT right) { _right = right; }

private:
  LEFT _left;
  RIGHT _right;
};
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 */
/*
 * @file       descriptor-set.cpp
 * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
 * @version    1.0
 */

#include "descriptor-set.h"
#include <dpl/log/log.h>
#include <string.h>

namespace CKM {

DescriptorSet::DescriptorSet() : m_dirty(true), m_fds(NULL) {
}

DescriptorSet::~DescriptorSet() {
    purge();
}

void DescriptorSet::purge() {
    for(auto it:m_descriptors)
        close(it.first);
    m_descriptors.clear();
}

void DescriptorSet::add(int fd, short events, Callback&& callback) {
    // map operator[] requires empty DescriptorData constructor
    auto it = m_descriptors.find(fd);
    if (it == m_descriptors.end()) {
        m_descriptors.insert(std::make_pair(fd,DescriptorData(events, std::move(callback))));
    } else {
        it->second.events = events;
        it->second.callback = std::move(callback);
    }
    m_dirty = true;
}

void DescriptorSet::remove(int fd) {
    if (0 != m_descriptors.erase(fd)) {
        close(fd);
        m_dirty = true;
    }
}

void DescriptorSet::wait(int timeout_ms) {
    if(!rebuildPollfd())
        return;

    // wait
    int ret = TEMP_FAILURE_RETRY(poll(m_fds, m_descriptors.size(), timeout_ms));
    if (ret == 0) {
        ThrowMsg(Timeout, "Poll timeout");
    } else if (ret < 0) {
        int err = errno;
        ThrowMsg(InternalError, "Poll failed " << strerror(err));
    }

    notify(ret);
}

bool DescriptorSet::rebuildPollfd() {
    if (m_dirty) {
       delete[] m_fds;
       m_fds = NULL;
       if (m_descriptors.empty()) {
           LogWarning("Nothing to wait for");
           return false;
       }

       m_fds = new pollfd[m_descriptors.size()];
       size_t idx = 0;
       for(const auto& it : m_descriptors) {
           m_fds[idx].fd = it.first;
           m_fds[idx].events = it.second.events;
           idx++;
       }
       m_dirty = false;
    }
    return true;
}

void DescriptorSet::notify(int descCount) {
    size_t size = m_descriptors.size();
    for(size_t idx = 0;idx < size;++idx) {
        const pollfd& pfd = m_fds[idx];
        if (pfd.revents == 0)
            continue;

        /*
         * Descriptors can be added/removed inside observer callback but:
         * 1. m_fds is not affected. It will be regenerated in next wait()
         * 2. No m_descriptors iterator will be invalidated
         * 3. m_descriptors size is stored in local variable
         */
        m_descriptors.at(pfd.fd).callback(pfd.fd, pfd.revents);
        descCount--;

        // no more descriptors to check
        if (descCount == 0)
            break;
    }
    if (descCount != 0)
        ThrowMsg(InternalError, "Number of notified descriptors do not match");
}

} /* namespace CKM */
<commit_msg>Fix build break on gcc4.8<commit_after>/*
 *  Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 */
/*
 * @file       descriptor-set.cpp
 * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
 * @version    1.0
 */

#include "descriptor-set.h"
#include <dpl/log/log.h>
#include <string.h>
#include <unistd.h>

namespace CKM {

DescriptorSet::DescriptorSet() : m_dirty(true), m_fds(NULL) {
}

DescriptorSet::~DescriptorSet() {
    purge();
}

void DescriptorSet::purge() {
    for(auto it:m_descriptors)
        close(it.first);
    m_descriptors.clear();
}

void DescriptorSet::add(int fd, short events, Callback&& callback) {
    // map operator[] requires empty DescriptorData constructor
    auto it = m_descriptors.find(fd);
    if (it == m_descriptors.end()) {
        m_descriptors.insert(std::make_pair(fd,DescriptorData(events, std::move(callback))));
    } else {
        it->second.events = events;
        it->second.callback = std::move(callback);
    }
    m_dirty = true;
}

void DescriptorSet::remove(int fd) {
    if (0 != m_descriptors.erase(fd)) {
        close(fd);
        m_dirty = true;
    }
}

void DescriptorSet::wait(int timeout_ms) {
    if(!rebuildPollfd())
        return;

    // wait
    int ret = TEMP_FAILURE_RETRY(poll(m_fds, m_descriptors.size(), timeout_ms));
    if (ret == 0) {
        ThrowMsg(Timeout, "Poll timeout");
    } else if (ret < 0) {
        int err = errno;
        ThrowMsg(InternalError, "Poll failed " << strerror(err));
    }

    notify(ret);
}

bool DescriptorSet::rebuildPollfd() {
    if (m_dirty) {
       delete[] m_fds;
       m_fds = NULL;
       if (m_descriptors.empty()) {
           LogWarning("Nothing to wait for");
           return false;
       }

       m_fds = new pollfd[m_descriptors.size()];
       size_t idx = 0;
       for(const auto& it : m_descriptors) {
           m_fds[idx].fd = it.first;
           m_fds[idx].events = it.second.events;
           idx++;
       }
       m_dirty = false;
    }
    return true;
}

void DescriptorSet::notify(int descCount) {
    size_t size = m_descriptors.size();
    for(size_t idx = 0;idx < size;++idx) {
        const pollfd& pfd = m_fds[idx];
        if (pfd.revents == 0)
            continue;

        /*
         * Descriptors can be added/removed inside observer callback but:
         * 1. m_fds is not affected. It will be regenerated in next wait()
         * 2. No m_descriptors iterator will be invalidated
         * 3. m_descriptors size is stored in local variable
         */
        m_descriptors.at(pfd.fd).callback(pfd.fd, pfd.revents);
        descCount--;

        // no more descriptors to check
        if (descCount == 0)
            break;
    }
    if (descCount != 0)
        ThrowMsg(InternalError, "Number of notified descriptors do not match");
}

} /* namespace CKM */
<|endoftext|>


<commit_before>/*
 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
 * 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 the copyright holders 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.
 */

/*
 * Unordered buffer of messages that can be inserted such
 * that they can be dequeued after a given delta time has expired.
 */

#ifndef __MEM_RUBY_BUFFERS_MESSAGEBUFFER_HH__
#define __MEM_RUBY_BUFFERS_MESSAGEBUFFER_HH__

#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

#include "mem/packet.hh"
#include "mem/ruby/buffers/MessageBufferNode.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Consumer.hh"
#include "mem/ruby/slicc_interface/Message.hh"

class MessageBuffer
{
  public:
    MessageBuffer(const std::string &name = "");

    std::string name() const { return m_name; }

    void setRecycleLatency(Cycles recycle_latency)
    { m_recycle_latency = recycle_latency; }

    void reanalyzeMessages(const Address& addr);
    void reanalyzeAllMessages();
    void stallMessage(const Address& addr);

    // TRUE if head of queue timestamp <= SystemTime
    bool isReady() const;

    void
    delayHead()
    {
        MessageBufferNode node = m_prio_heap.front();
        std::pop_heap(m_prio_heap.begin(), m_prio_heap.end(),
                      std::greater<MessageBufferNode>());
        m_prio_heap.pop_back();
        enqueue(node.m_msgptr, Cycles(1));
    }

    bool areNSlotsAvailable(int n);
    int getPriority() { return m_priority_rank; }
    void setPriority(int rank) { m_priority_rank = rank; }
    void setConsumer(Consumer* consumer)
    {
        assert(m_consumer == NULL);
        m_consumer = consumer;
    }

    void setSender(ClockedObject* obj)
    {
        assert(m_sender == NULL || m_sender == obj);
        m_sender = obj;
    }

    void setReceiver(ClockedObject* obj)
    {
        assert(m_receiver == NULL || m_receiver == obj);
        m_receiver = obj;
    }

    void setDescription(const std::string& name) { m_name = name; }
    std::string getDescription() { return m_name;}

    Consumer* getConsumer() { return m_consumer; }

    const Message* peekAtHeadOfQueue() const;
    const Message* peek() const { return peekAtHeadOfQueue(); }
    const MsgPtr getMsgPtrCopy() const;

    const MsgPtr&
    peekMsgPtr() const
    {
        assert(isReady());
        return m_prio_heap.front().m_msgptr;
    }

    const MsgPtr&
    peekMsgPtrEvenIfNotReady() const
    {
        return m_prio_heap.front().m_msgptr;
    }

    void enqueue(MsgPtr message) { enqueue(message, Cycles(1)); }
    void enqueue(MsgPtr message, Cycles delta);

    //!  returns delay ticks of the message.
    Cycles dequeue_getDelayCycles(MsgPtr& message);
    void dequeue(MsgPtr& message);

    //! returns delay cycles of the message
    Cycles dequeue_getDelayCycles();
    void dequeue() { pop(); }
    void pop();
    void recycle();
    bool isEmpty() const { return m_prio_heap.size() == 0; }

    void
    setOrdering(bool order)
    {
        m_strict_fifo = order;
        m_ordering_set = true;
    }
    void resize(int size) { m_max_size = size; }
    int getSize();
    void setRandomization(bool random_flag) { m_randomization = random_flag; }

    void clear();

    void print(std::ostream& out) const;
    void printStats(std::ostream& out);
    void clearStats() { m_not_avail_count = 0; m_msg_counter = 0; }

    void setIncomingLink(int link_id) { m_input_link_id = link_id; }
    void setVnet(int net) { m_vnet_id = net; }

    // Function for figuring out if any of the messages in the buffer can
    // satisfy the read request for the address in the packet.
    // Return value, if true, indicates that the request was fulfilled.
    bool functionalRead(Packet *pkt);

    // Function for figuring out if any of the messages in the buffer need
    // to be updated with the data from the packet.
    // Return value indicates the number of messages that were updated.
    // This required for debugging the code.
    uint32_t functionalWrite(Packet *pkt);

  private:
    //added by SS
    Cycles m_recycle_latency;

    // Private Methods
    Cycles setAndReturnDelayCycles(MsgPtr message);

    // Private copy constructor and assignment operator
    MessageBuffer(const MessageBuffer& obj);
    MessageBuffer& operator=(const MessageBuffer& obj);

    // Data Members (m_ prefix)
    //! The two ends of the buffer.
    ClockedObject* m_sender;
    ClockedObject* m_receiver;

    //! Consumer to signal a wakeup(), can be NULL
    Consumer* m_consumer;
    std::vector<MessageBufferNode> m_prio_heap;

    // use a std::map for the stalled messages as this container is
    // sorted and ensures a well-defined iteration order
    typedef std::map< Address, std::list<MsgPtr> > StallMsgMapType;
    typedef std::vector<MsgPtr>::iterator MsgListIter;

    StallMsgMapType m_stall_msg_map;
    std::string m_name;

    int m_max_size;
    int m_size;

    Cycles m_time_last_time_size_checked;
    int m_size_last_time_size_checked;

    // variables used so enqueues appear to happen imediately, while
    // pop happen the next cycle
    Cycles m_time_last_time_enqueue;
    Cycles m_time_last_time_pop;
    int m_size_at_cycle_start;
    int m_msgs_this_cycle;

    int m_not_avail_count;  // count the # of times I didn't have N
                            // slots available
    uint64 m_msg_counter;
    int m_priority_rank;
    bool m_strict_fifo;
    bool m_ordering_set;
    bool m_randomization;

    Tick m_last_arrival_time;

    int m_input_link_id;
    int m_vnet_id;
};

Cycles random_time();

inline std::ostream&
operator<<(std::ostream& out, const MessageBuffer& obj)
{
    obj.print(out);
    out << std::flush;
    return out;
}

#endif // __MEM_RUBY_BUFFERS_MESSAGEBUFFER_HH__
<commit_msg>Ruby: More descriptive message buffer connection fatal<commit_after>/*
 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
 * 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 the copyright holders 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.
 */

/*
 * Unordered buffer of messages that can be inserted such
 * that they can be dequeued after a given delta time has expired.
 */

#ifndef __MEM_RUBY_BUFFERS_MESSAGEBUFFER_HH__
#define __MEM_RUBY_BUFFERS_MESSAGEBUFFER_HH__

#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

#include "mem/packet.hh"
#include "mem/ruby/buffers/MessageBufferNode.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Consumer.hh"
#include "mem/ruby/slicc_interface/Message.hh"

class MessageBuffer
{
  public:
    MessageBuffer(const std::string &name = "");

    std::string name() const { return m_name; }

    void setRecycleLatency(Cycles recycle_latency)
    { m_recycle_latency = recycle_latency; }

    void reanalyzeMessages(const Address& addr);
    void reanalyzeAllMessages();
    void stallMessage(const Address& addr);

    // TRUE if head of queue timestamp <= SystemTime
    bool isReady() const;

    void
    delayHead()
    {
        MessageBufferNode node = m_prio_heap.front();
        std::pop_heap(m_prio_heap.begin(), m_prio_heap.end(),
                      std::greater<MessageBufferNode>());
        m_prio_heap.pop_back();
        enqueue(node.m_msgptr, Cycles(1));
    }

    bool areNSlotsAvailable(int n);
    int getPriority() { return m_priority_rank; }
    void setPriority(int rank) { m_priority_rank = rank; }
    void setConsumer(Consumer* consumer)
    {
        if (m_consumer != NULL) {
            fatal("Trying to connect %s to MessageBuffer %s. \
                  \n%s already connected. Check the cntrl_id's.\n",
                  *consumer, *this, *m_consumer);
        }
        m_consumer = consumer;
    }

    void setSender(ClockedObject* obj)
    {
        assert(m_sender == NULL || m_sender == obj);
        m_sender = obj;
    }

    void setReceiver(ClockedObject* obj)
    {
        assert(m_receiver == NULL || m_receiver == obj);
        m_receiver = obj;
    }

    void setDescription(const std::string& name) { m_name = name; }
    std::string getDescription() { return m_name;}

    Consumer* getConsumer() { return m_consumer; }

    const Message* peekAtHeadOfQueue() const;
    const Message* peek() const { return peekAtHeadOfQueue(); }
    const MsgPtr getMsgPtrCopy() const;

    const MsgPtr&
    peekMsgPtr() const
    {
        assert(isReady());
        return m_prio_heap.front().m_msgptr;
    }

    const MsgPtr&
    peekMsgPtrEvenIfNotReady() const
    {
        return m_prio_heap.front().m_msgptr;
    }

    void enqueue(MsgPtr message) { enqueue(message, Cycles(1)); }
    void enqueue(MsgPtr message, Cycles delta);

    //!  returns delay ticks of the message.
    Cycles dequeue_getDelayCycles(MsgPtr& message);
    void dequeue(MsgPtr& message);

    //! returns delay cycles of the message
    Cycles dequeue_getDelayCycles();
    void dequeue() { pop(); }
    void pop();
    void recycle();
    bool isEmpty() const { return m_prio_heap.size() == 0; }

    void
    setOrdering(bool order)
    {
        m_strict_fifo = order;
        m_ordering_set = true;
    }
    void resize(int size) { m_max_size = size; }
    int getSize();
    void setRandomization(bool random_flag) { m_randomization = random_flag; }

    void clear();

    void print(std::ostream& out) const;
    void printStats(std::ostream& out);
    void clearStats() { m_not_avail_count = 0; m_msg_counter = 0; }

    void setIncomingLink(int link_id) { m_input_link_id = link_id; }
    void setVnet(int net) { m_vnet_id = net; }

    // Function for figuring out if any of the messages in the buffer can
    // satisfy the read request for the address in the packet.
    // Return value, if true, indicates that the request was fulfilled.
    bool functionalRead(Packet *pkt);

    // Function for figuring out if any of the messages in the buffer need
    // to be updated with the data from the packet.
    // Return value indicates the number of messages that were updated.
    // This required for debugging the code.
    uint32_t functionalWrite(Packet *pkt);

  private:
    //added by SS
    Cycles m_recycle_latency;

    // Private Methods
    Cycles setAndReturnDelayCycles(MsgPtr message);

    // Private copy constructor and assignment operator
    MessageBuffer(const MessageBuffer& obj);
    MessageBuffer& operator=(const MessageBuffer& obj);

    // Data Members (m_ prefix)
    //! The two ends of the buffer.
    ClockedObject* m_sender;
    ClockedObject* m_receiver;

    //! Consumer to signal a wakeup(), can be NULL
    Consumer* m_consumer;
    std::vector<MessageBufferNode> m_prio_heap;

    // use a std::map for the stalled messages as this container is
    // sorted and ensures a well-defined iteration order
    typedef std::map< Address, std::list<MsgPtr> > StallMsgMapType;
    typedef std::vector<MsgPtr>::iterator MsgListIter;

    StallMsgMapType m_stall_msg_map;
    std::string m_name;

    int m_max_size;
    int m_size;

    Cycles m_time_last_time_size_checked;
    int m_size_last_time_size_checked;

    // variables used so enqueues appear to happen imediately, while
    // pop happen the next cycle
    Cycles m_time_last_time_enqueue;
    Cycles m_time_last_time_pop;
    int m_size_at_cycle_start;
    int m_msgs_this_cycle;

    int m_not_avail_count;  // count the # of times I didn't have N
                            // slots available
    uint64 m_msg_counter;
    int m_priority_rank;
    bool m_strict_fifo;
    bool m_ordering_set;
    bool m_randomization;

    Tick m_last_arrival_time;

    int m_input_link_id;
    int m_vnet_id;
};

Cycles random_time();

inline std::ostream&
operator<<(std::ostream& out, const MessageBuffer& obj)
{
    obj.print(out);
    out << std::flush;
    return out;
}

#endif // __MEM_RUBY_BUFFERS_MESSAGEBUFFER_HH__
<|endoftext|>