<commit_before>/*
 * Copyright 2006-2008 The FLWOR Foundation.
 *
 * 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.
 */
#include "stdafx.h"

#include <ostream>
#include <sstream>

#include "compiler/parser/query_loc.h"

namespace zorba
{

QueryLoc QueryLoc::null;

QueryLoc::QueryLoc()
 :
  theFilename(""),
  theLineBegin (0),
  theColumnBegin (0),
  theLineEnd (0),
  theColumnEnd (0)
{
}

QueryLoc::QueryLoc( zstring const &filename, line_type lineBegin,
                    line_type lineEnd, column_type columnBegin,
                    column_type columnEnd ) :
  theFilename(filename),
  theLineBegin(lineBegin),
  theColumnBegin(columnBegin),
  theLineEnd(lineEnd),
  theColumnEnd(columnEnd)
{
}

std::ostream& operator<<(std::ostream& aOstr, const QueryLoc& aQueryLoc)
{
  std::stringstream lLoc;

  if (!aQueryLoc.theFilename.empty())
  {
    lLoc << aQueryLoc.theFilename;
  }

  bool lPrintLines = aQueryLoc.theLineBegin;
  bool lPrintColumns = aQueryLoc.theLineBegin && aQueryLoc.theLineEnd &&
      aQueryLoc.theColumnBegin && aQueryLoc.theColumnEnd;

  if (lPrintLines)
  {
    lLoc << ":" << aQueryLoc.theLineBegin;
    if (lPrintColumns)
      lLoc << "." << aQueryLoc.theColumnBegin;

    lLoc << "-" << (aQueryLoc.theLineEnd ? aQueryLoc.theLineEnd : aQueryLoc.theLineBegin);
    if (lPrintColumns)
      lLoc << "." << aQueryLoc.theColumnEnd;
  }

  return aOstr;
}

bool QueryLoc::equals(const QueryLoc& loc) const
{
  std::stringstream lFile;
  lFile << theFilename.c_str();
  std::string lFile1(lFile.str());
  std::string lFile2;
  std::string::iterator it;

  for (it = lFile1.begin(); it != lFile1.end(); ++it)
  {
    if (*it == '\\')
    {
      lFile2.append(1, *it);
      ++it;
      if (*it == '\\')
      {
        continue;
      }
    }
    lFile2.append(1, *it);
  }

  return (loc.getFilename() == lFile2 &&
          theColumnBegin == loc.getColumnBegin() &&
          theColumnEnd == loc.getColumnEnd() &&
          theLineBegin == loc.getLineBegin() &&
          theLineEnd == loc.getLineEnd());
}

} // namespace zorba
/* vim:set et sw=2 ts=2: */
<commit_msg>Fixed operator<<<commit_after>/*
 * Copyright 2006-2008 The FLWOR Foundation.
 *
 * 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.
 */
#include "stdafx.h"

#include <ostream>
#include <sstream>

#include "compiler/parser/query_loc.h"

namespace zorba
{

QueryLoc QueryLoc::null;

QueryLoc::QueryLoc()
 :
  theFilename(""),
  theLineBegin (0),
  theColumnBegin (0),
  theLineEnd (0),
  theColumnEnd (0)
{
}

QueryLoc::QueryLoc( zstring const &filename, line_type lineBegin,
                    line_type lineEnd, column_type columnBegin,
                    column_type columnEnd ) :
  theFilename(filename),
  theLineBegin(lineBegin),
  theColumnBegin(columnBegin),
  theLineEnd(lineEnd),
  theColumnEnd(columnEnd)
{
}

std::ostream& operator<<(std::ostream& aOstr, const QueryLoc& aQueryLoc)
{
  if (!aQueryLoc.theFilename.empty())
  {
    aOstr << aQueryLoc.theFilename;
  }

  bool lPrintLines = aQueryLoc.theLineBegin;
  bool lPrintColumns = aQueryLoc.theLineBegin && aQueryLoc.theLineEnd &&
      aQueryLoc.theColumnBegin && aQueryLoc.theColumnEnd;

  if (lPrintLines)
  {
    aOstr << ":" << aQueryLoc.theLineBegin;
    if (lPrintColumns)
      aOstr << "." << aQueryLoc.theColumnBegin;

    aOstr << "-" << (aQueryLoc.theLineEnd ? aQueryLoc.theLineEnd : aQueryLoc.theLineBegin);
    if (lPrintColumns)
      aOstr << "." << aQueryLoc.theColumnEnd;
  }

  return aOstr;
}

bool QueryLoc::equals(const QueryLoc& loc) const
{
  std::stringstream lFile;
  lFile << theFilename.c_str();
  std::string lFile1(lFile.str());
  std::string lFile2;
  std::string::iterator it;

  for (it = lFile1.begin(); it != lFile1.end(); ++it)
  {
    if (*it == '\\')
    {
      lFile2.append(1, *it);
      ++it;
      if (*it == '\\')
      {
        continue;
      }
    }
    lFile2.append(1, *it);
  }

  return (loc.getFilename() == lFile2 &&
          theColumnBegin == loc.getColumnBegin() &&
          theColumnEnd == loc.getColumnEnd() &&
          theLineBegin == loc.getLineBegin() &&
          theLineEnd == loc.getLineEnd());
}

} // namespace zorba
/* vim:set et sw=2 ts=2: */
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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.
 *
 ***************************************************************/

#include "condor_common.h"
#include "condor_constants.h"
#include "condor_debug.h"
#include "condor_config.h"
#include "condor_uid.h"
#include "file_lock.h"
#include "utc_time.h"

extern "C" int lock_file( int fd, LOCK_TYPE type, BOOLEAN do_block );

FileLockBase::FileLockBase( void )
{
	m_state = UN_LOCK;
	m_blocking = true;
	recordExistence();
}

FileLockBase::~FileLockBase(void)
{
	eraseExistence();
}

const char *
FileLockBase::getStateString( LOCK_TYPE state ) const
{
	switch( state ) {
	case READ_LOCK:
		return "READ";
	case WRITE_LOCK:
		return "WRITE";
	case UN_LOCK:
		return "UNLOCKED";
	default:
		return "UNKNOWN";
	}
}

void
FileLockBase::updateAllLockTimestamps(void)
{
	FileLockEntry *fle = NULL;

	fle = m_all_locks;

	// walk the locks list and have each one update its timestamp
	while(fle != NULL) {
		fle->fl->updateLockTimestamp();
		fle = fle->next;
	}
}

void
FileLockBase::recordExistence(void)
{
	FileLockEntry *fle = new FileLockEntry;

	fle->fl = this;

	// insert it at the head of the singly linked list
	fle->next = m_all_locks;
	m_all_locks = fle;
}

void
FileLockBase::eraseExistence(void)
{
	// do a little brother style removal in the singly linked list

	FileLockEntry *prev = NULL;
	FileLockEntry *curr = NULL;
	FileLockEntry *del = NULL;

	if (m_all_locks == NULL) {
		goto bail_out;
	}

	// is it the first one?
	if (m_all_locks->fl == this) {
		del = m_all_locks;
		m_all_locks = m_all_locks->next;
		delete del;
		return;
	}

	// if it is the second one or beyond, find it and delete the entry
	// from the list.
	prev = m_all_locks;
	curr = m_all_locks->next;

	while(curr != NULL) {
		if (curr->fl == this) {
			// found it, mark it for deletion.
			del = curr;
			// remove it from the list.
			prev->next = curr->next;
			del->next = NULL;
			delete del;

			// all done.
			return;
		}
		curr = curr->next;
		prev = prev->next;
	}

	bail_out:
	// I *must* have recorded my existence before erasing it, so if not, then
	// a programmer error has happened.
	EXCEPT("FileLock::erase_existence(): Programmer error. A FileLock to "
			"be erased was not found.");
}

FileLockBase::FileLockEntry* FileLockBase::m_all_locks = NULL;

//
// Methods for the "real" file lock class
//
FileLock::FileLock( int fd, FILE *fp_arg, const char* path )
		: FileLockBase( )
{
	Reset( );
	m_fd = fd;
	m_fp = fp_arg;

	// check to ensure that if we have a real fd or fp_arg, the file is
	// defined. However, if the fd nor the fp_arg is defined, the file may be
	// NULL.
	if ((path == NULL && (fd >= 0 || fp_arg != NULL)))
	{
		EXCEPT("FileLock::FileLock(). You must supply a valid file argument "
			"with a valid fd or fp_arg");
	}

	// path could be NULL if fd is -1 and fp is NULL, in which case we don't
	// insert ourselves into a the m_all_locks list.
	if (path) {
		m_path = strdup(path);
		updateLockTimestamp();
	}
}

FileLock::FileLock( const char *path )
		: FileLockBase( )
{
	Reset( );

	ASSERT(path != NULL);

	m_path = strdup(path);
	updateLockTimestamp();
}

FileLock::~FileLock( void )
{
	if( m_state != UN_LOCK ) {
		release();
	}
	m_use_kernel_mutex = -1;
#ifdef WIN32
	if (m_debug_win32_mutex) {
		::CloseHandle(m_debug_win32_mutex);
		m_debug_win32_mutex = NULL;
	}
#endif
	if ( m_path) {
		free(m_path);
		m_path = NULL;
	}
}

void
FileLock::Reset( void )
{
	m_fd = -1;
	m_fp = NULL;
	m_blocking = true;
	m_state = UN_LOCK;
	m_path = NULL;
	m_use_kernel_mutex = -1;
#ifdef WIN32
	m_debug_win32_mutex = NULL;
#endif
}

void
FileLock::SetFdFpFile( int fd, FILE *fp, const char *file )
{
	// if I'm -1, NULL, NULL, that's ok, however, if file != NULL, then
	// either the fd or the fp must also be valid.
	if ((file == NULL && (fd >= 0 || fp != NULL)))
	{
		EXCEPT("FileLock::SetFdFpFile(). You must supply a valid file argument "
			"with a valid fd or fp_arg");
	}

	m_fd = fd;
	m_fp = fp;

	// Make sure we record our existence in the static list properly depending
	// on what the user is setting the variables to...
	if (m_path == NULL && file != NULL) {
		// moving from a NULL object to a object needed to update the timestamp

		m_path = strdup(file);
		// This will use the new lock file in m_path
		updateLockTimestamp();

	} else if (m_path != NULL && file == NULL) {
		// moving from an updatable timestamped object to a NULL object

		free(m_path);
		m_path = NULL;

	} else if (m_path != NULL && file != NULL) {
		// keeping the updatability of the object, but updating the path.

		free(m_path);
		m_path = strdup(file);
		updateLockTimestamp();
	}
}

void
FileLock::display( void ) const
{
	dprintf( D_FULLDEBUG, "fd = %d\n", m_fd );
	dprintf( D_FULLDEBUG, "blocking = %s\n", m_blocking ? "TRUE" : "FALSE" );
	dprintf( D_FULLDEBUG, "state = %s\n", getStateString( m_state ) );
}

int
FileLock::lockViaMutex(LOCK_TYPE type)
{
	(void) type;
	int result = -1;

#ifdef WIN32	// only implemented on Win32 so far...
	char * filename = NULL;
	int filename_len;
	char *ptr = NULL;
	char mutex_name[MAX_PATH];

		// If we made it here, we want to use a kernel mutex.
		//
		// We use a kernel mutex by default to fix a major shortcoming
		// with using Win32 file locking: file locking on Win32 is
		// non-deterministic.  Thus, we have observed processes
		// starving to get the lock.  The Win32 mutex object,
		// on the other hand, is FIFO --- thus starvation is avoided.

		// first, open a handle to the mutex if we haven't already
	if ( m_debug_win32_mutex == NULL && m_path ) {
			// Create the mutex name based upon the lock file
			// specified in the config file.
		char * filename = strdup(m_path);
		filename_len = strlen(filename);
			// Note: Win32 will not allow backslashes in the name,
			// so get rid of em here.
		ptr = strchr(filename,'\\');
		while ( ptr ) {
			*ptr = '/';
			ptr = strchr(filename,'\\');
		}
			// The mutex name is case-sensitive, but the NTFS filesystem
			// is not.  So to avoid user confusion, strlwr.
		strlwr(filename);
			// Now, we pre-append "Global\" to the name so that it
			// works properly on systems running Terminal Services
		_snprintf(mutex_name,MAX_PATH,"Global\\%s",filename);
		free(filename);
		filename = NULL;
			// Call CreateMutex - this will create the mutex if it does
			// not exist, or just open it if it already does.  Note that
			// the handle to the mutex is automatically closed by the
			// operating system when the process exits, and the mutex
			// object is automatically destroyed when there are no more
			// handles... go win32 kernel!  Thus, although we are not
			// explicitly closing any handles, nothing is being leaked.
			// Note: someday, to make BoundsChecker happy, we should
			// add a dprintf subsystem shutdown routine to nicely
			// deallocate this stuff instead of relying on the OS.
		m_debug_win32_mutex = ::CreateMutex(0,FALSE,mutex_name);
	}

		// now, if we have mutex, grab it or release it as needed
	if ( m_debug_win32_mutex ) {
		if ( type == UN_LOCK ) {
				// release mutex
			ReleaseMutex(m_debug_win32_mutex);
			result = 0;	// 0 means success
		} else {
				// grab mutex
				// block 10 secs if do_block is false, else block forever
			result = WaitForSingleObject(m_debug_win32_mutex,
				m_blocking ? INFINITE : 10 * 1000);	// time in milliseconds
				// consider WAIT_ABANDONED as success so we do not EXCEPT
			if ( result==WAIT_OBJECT_0 || result==WAIT_ABANDONED ) {
				result = 0;
			} else {
				result = -1;
			}
		}

	}
#endif

	return result;
}

bool
FileLock::obtain( LOCK_TYPE t )
{
// lock_file uses lseeks in order to lock the first 4 bytes of the file on NT
// It DOES properly reset the lseek version of the file position, but that is
// not the same (in some very inconsistent and weird ways) as the fseek one,
// so if the user has given us a FILE *, we need to make sure we don't ruin
// their current position.  The lesson here is don't use fseeks and lseeks
// interchangeably...
	int		status = -1;

	if ( m_use_kernel_mutex == -1 ) {
		m_use_kernel_mutex = param_boolean_int("FILE_LOCK_VIA_MUTEX", TRUE);
	}

		// If we have the path, we can try to lock via a mutex.
	if ( m_path && m_use_kernel_mutex ) {
		status = lockViaMutex(t);
	}

		// We cannot lock via a mutex, or we tried and failed.
		// Try via filesystem lock.
	if ( status < 0) {
		long lPosBeforeLock = 0;
		if (m_fp) // if the user has a FILE * as well as an fd
		{
			// save their FILE*-based current position
			lPosBeforeLock = ftell(m_fp);
		}

			// We're seeing sporadic test suite failures where a daemon
			// takes more than 10 seconds to write to the user log.
			// This will help narrow down where the delay is coming from.
		time_t before = time(NULL);
		status = lock_file( m_fd, t, m_blocking );
		time_t after = time(NULL);
		if ( (after - before) > 5 ) {
			dprintf( D_FULLDEBUG, "FileLock::obtain(%d): lock_file() took %d seconds\n",t);
		}

		if (m_fp)
		{
			// restore their FILE*-position
			fseek(m_fp, lPosBeforeLock, SEEK_SET);
		}
	}

	if( status == 0 ) {
		m_state = t;
	}
	if ( status != 0 ) {
		dprintf( D_ALWAYS, "FileLock::obtain(%d) failed - errno %d (%s)\n",
	                t, errno, strerror(errno) );
	}
	else {
		UtcTime	now( true );
		dprintf( D_FULLDEBUG,
				 "FileLock::obtain(%d) - @%.6f lock on %s now %s\n",
				 t, now.combined(), m_path, getStateString(t) );
	}
	return status == 0;
}

bool
FileLock::release(void)
{
	return obtain( UN_LOCK );
}

void
FileLock::updateLockTimestamp(void)
{
	priv_state p;

	if (m_path) {

		dprintf(D_FULLDEBUG, "FileLock object is updating timestamp on: %s\n",
			m_path);

		// NOTE:
		// At the time of writing, if we try to update the lock and fail
		// because the lock is not owned by Condor, we ignore it and leave the
		// lock timestamp not updated and we don't even write a message about
		// it in the logs. This behaviour is warranted because the main reason
		// why this is here at all if to prevent Condor owned lock files from
		// being deleted by tmpwatch and other administrative programs.
		// According to Todd 07/15/2008, a new feature will shortly be going
		// into Condor which will make *ALL* file locking use separate lock
		// file elsewhere which will all be owned by Condor, in which case this
		// will work perfectly.

		// One of the main reasons why we just don't store the privledge state
		// of the process when this object is created is because the semantics
		// of this object have been that the caller is resposible for
		// maintaining the correct priv state when dealing with the lock
		// object. If we circumvent that by having the lock object alter
		// it privstate to match what it was constructed under, it will be
		// very surprising if the caller knows a file has changed ownership
		// or similar things.

		p = set_condor_priv();

		// set the updated atime and mtime for the file to now.
		if (utime(m_path, NULL) < 0) {

			// Only emit message if it isn't a permission problem....
			if (errno != EACCES && errno != EPERM) {
				dprintf(D_FULLDEBUG, "FileLock::updateLockTime(): utime() "
					"failed %d(%s) on lock file %s. Not updating timestamp.\n",
					errno, strerror(errno), m_path);
			}
		}
		set_priv(p);

		return;
	}
}
<commit_msg>Fixed a compiler warning.<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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.
 *
 ***************************************************************/

#include "condor_common.h"
#include "condor_constants.h"
#include "condor_debug.h"
#include "condor_config.h"
#include "condor_uid.h"
#include "file_lock.h"
#include "utc_time.h"

extern "C" int lock_file( int fd, LOCK_TYPE type, BOOLEAN do_block );

FileLockBase::FileLockBase( void )
{
	m_state = UN_LOCK;
	m_blocking = true;
	recordExistence();
}

FileLockBase::~FileLockBase(void)
{
	eraseExistence();
}

const char *
FileLockBase::getStateString( LOCK_TYPE state ) const
{
	switch( state ) {
	case READ_LOCK:
		return "READ";
	case WRITE_LOCK:
		return "WRITE";
	case UN_LOCK:
		return "UNLOCKED";
	default:
		return "UNKNOWN";
	}
}

void
FileLockBase::updateAllLockTimestamps(void)
{
	FileLockEntry *fle = NULL;

	fle = m_all_locks;

	// walk the locks list and have each one update its timestamp
	while(fle != NULL) {
		fle->fl->updateLockTimestamp();
		fle = fle->next;
	}
}

void
FileLockBase::recordExistence(void)
{
	FileLockEntry *fle = new FileLockEntry;

	fle->fl = this;

	// insert it at the head of the singly linked list
	fle->next = m_all_locks;
	m_all_locks = fle;
}

void
FileLockBase::eraseExistence(void)
{
	// do a little brother style removal in the singly linked list

	FileLockEntry *prev = NULL;
	FileLockEntry *curr = NULL;
	FileLockEntry *del = NULL;

	if (m_all_locks == NULL) {
		goto bail_out;
	}

	// is it the first one?
	if (m_all_locks->fl == this) {
		del = m_all_locks;
		m_all_locks = m_all_locks->next;
		delete del;
		return;
	}

	// if it is the second one or beyond, find it and delete the entry
	// from the list.
	prev = m_all_locks;
	curr = m_all_locks->next;

	while(curr != NULL) {
		if (curr->fl == this) {
			// found it, mark it for deletion.
			del = curr;
			// remove it from the list.
			prev->next = curr->next;
			del->next = NULL;
			delete del;

			// all done.
			return;
		}
		curr = curr->next;
		prev = prev->next;
	}

	bail_out:
	// I *must* have recorded my existence before erasing it, so if not, then
	// a programmer error has happened.
	EXCEPT("FileLock::erase_existence(): Programmer error. A FileLock to "
			"be erased was not found.");
}

FileLockBase::FileLockEntry* FileLockBase::m_all_locks = NULL;

//
// Methods for the "real" file lock class
//
FileLock::FileLock( int fd, FILE *fp_arg, const char* path )
		: FileLockBase( )
{
	Reset( );
	m_fd = fd;
	m_fp = fp_arg;

	// check to ensure that if we have a real fd or fp_arg, the file is
	// defined. However, if the fd nor the fp_arg is defined, the file may be
	// NULL.
	if ((path == NULL && (fd >= 0 || fp_arg != NULL)))
	{
		EXCEPT("FileLock::FileLock(). You must supply a valid file argument "
			"with a valid fd or fp_arg");
	}

	// path could be NULL if fd is -1 and fp is NULL, in which case we don't
	// insert ourselves into a the m_all_locks list.
	if (path) {
		m_path = strdup(path);
		updateLockTimestamp();
	}
}

FileLock::FileLock( const char *path )
		: FileLockBase( )
{
	Reset( );

	ASSERT(path != NULL);

	m_path = strdup(path);
	updateLockTimestamp();
}

FileLock::~FileLock( void )
{
	if( m_state != UN_LOCK ) {
		release();
	}
	m_use_kernel_mutex = -1;
#ifdef WIN32
	if (m_debug_win32_mutex) {
		::CloseHandle(m_debug_win32_mutex);
		m_debug_win32_mutex = NULL;
	}
#endif
	if ( m_path) {
		free(m_path);
		m_path = NULL;
	}
}

void
FileLock::Reset( void )
{
	m_fd = -1;
	m_fp = NULL;
	m_blocking = true;
	m_state = UN_LOCK;
	m_path = NULL;
	m_use_kernel_mutex = -1;
#ifdef WIN32
	m_debug_win32_mutex = NULL;
#endif
}

void
FileLock::SetFdFpFile( int fd, FILE *fp, const char *file )
{
	// if I'm -1, NULL, NULL, that's ok, however, if file != NULL, then
	// either the fd or the fp must also be valid.
	if ((file == NULL && (fd >= 0 || fp != NULL)))
	{
		EXCEPT("FileLock::SetFdFpFile(). You must supply a valid file argument "
			"with a valid fd or fp_arg");
	}

	m_fd = fd;
	m_fp = fp;

	// Make sure we record our existence in the static list properly depending
	// on what the user is setting the variables to...
	if (m_path == NULL && file != NULL) {
		// moving from a NULL object to a object needed to update the timestamp

		m_path = strdup(file);
		// This will use the new lock file in m_path
		updateLockTimestamp();

	} else if (m_path != NULL && file == NULL) {
		// moving from an updatable timestamped object to a NULL object

		free(m_path);
		m_path = NULL;

	} else if (m_path != NULL && file != NULL) {
		// keeping the updatability of the object, but updating the path.

		free(m_path);
		m_path = strdup(file);
		updateLockTimestamp();
	}
}

void
FileLock::display( void ) const
{
	dprintf( D_FULLDEBUG, "fd = %d\n", m_fd );
	dprintf( D_FULLDEBUG, "blocking = %s\n", m_blocking ? "TRUE" : "FALSE" );
	dprintf( D_FULLDEBUG, "state = %s\n", getStateString( m_state ) );
}

int
FileLock::lockViaMutex(LOCK_TYPE type)
{
	(void) type;
	int result = -1;

#ifdef WIN32	// only implemented on Win32 so far...
	char * filename = NULL;
	int filename_len;
	char *ptr = NULL;
	char mutex_name[MAX_PATH];

		// If we made it here, we want to use a kernel mutex.
		//
		// We use a kernel mutex by default to fix a major shortcoming
		// with using Win32 file locking: file locking on Win32 is
		// non-deterministic.  Thus, we have observed processes
		// starving to get the lock.  The Win32 mutex object,
		// on the other hand, is FIFO --- thus starvation is avoided.

		// first, open a handle to the mutex if we haven't already
	if ( m_debug_win32_mutex == NULL && m_path ) {
			// Create the mutex name based upon the lock file
			// specified in the config file.
		char * filename = strdup(m_path);
		filename_len = strlen(filename);
			// Note: Win32 will not allow backslashes in the name,
			// so get rid of em here.
		ptr = strchr(filename,'\\');
		while ( ptr ) {
			*ptr = '/';
			ptr = strchr(filename,'\\');
		}
			// The mutex name is case-sensitive, but the NTFS filesystem
			// is not.  So to avoid user confusion, strlwr.
		strlwr(filename);
			// Now, we pre-append "Global\" to the name so that it
			// works properly on systems running Terminal Services
		_snprintf(mutex_name,MAX_PATH,"Global\\%s",filename);
		free(filename);
		filename = NULL;
			// Call CreateMutex - this will create the mutex if it does
			// not exist, or just open it if it already does.  Note that
			// the handle to the mutex is automatically closed by the
			// operating system when the process exits, and the mutex
			// object is automatically destroyed when there are no more
			// handles... go win32 kernel!  Thus, although we are not
			// explicitly closing any handles, nothing is being leaked.
			// Note: someday, to make BoundsChecker happy, we should
			// add a dprintf subsystem shutdown routine to nicely
			// deallocate this stuff instead of relying on the OS.
		m_debug_win32_mutex = ::CreateMutex(0,FALSE,mutex_name);
	}

		// now, if we have mutex, grab it or release it as needed
	if ( m_debug_win32_mutex ) {
		if ( type == UN_LOCK ) {
				// release mutex
			ReleaseMutex(m_debug_win32_mutex);
			result = 0;	// 0 means success
		} else {
				// grab mutex
				// block 10 secs if do_block is false, else block forever
			result = WaitForSingleObject(m_debug_win32_mutex,
				m_blocking ? INFINITE : 10 * 1000);	// time in milliseconds
				// consider WAIT_ABANDONED as success so we do not EXCEPT
			if ( result==WAIT_OBJECT_0 || result==WAIT_ABANDONED ) {
				result = 0;
			} else {
				result = -1;
			}
		}

	}
#endif

	return result;
}

bool
FileLock::obtain( LOCK_TYPE t )
{
// lock_file uses lseeks in order to lock the first 4 bytes of the file on NT
// It DOES properly reset the lseek version of the file position, but that is
// not the same (in some very inconsistent and weird ways) as the fseek one,
// so if the user has given us a FILE *, we need to make sure we don't ruin
// their current position.  The lesson here is don't use fseeks and lseeks
// interchangeably...
	int		status = -1;

	if ( m_use_kernel_mutex == -1 ) {
		m_use_kernel_mutex = param_boolean_int("FILE_LOCK_VIA_MUTEX", TRUE);
	}

		// If we have the path, we can try to lock via a mutex.
	if ( m_path && m_use_kernel_mutex ) {
		status = lockViaMutex(t);
	}

		// We cannot lock via a mutex, or we tried and failed.
		// Try via filesystem lock.
	if ( status < 0) {
		long lPosBeforeLock = 0;
		if (m_fp) // if the user has a FILE * as well as an fd
		{
			// save their FILE*-based current position
			lPosBeforeLock = ftell(m_fp);
		}

			// We're seeing sporadic test suite failures where a daemon
			// takes more than 10 seconds to write to the user log.
			// This will help narrow down where the delay is coming from.
		time_t before = time(NULL);
		status = lock_file( m_fd, t, m_blocking );
		time_t after = time(NULL);
		if ( (after - before) > 5 ) {
			dprintf( D_FULLDEBUG,
					 "FileLock::obtain(%d): lock_file() took %ld seconds\n",
					 t, (after-before) );
		}

		if (m_fp)
		{
			// restore their FILE*-position
			fseek(m_fp, lPosBeforeLock, SEEK_SET);
		}
	}

	if( status == 0 ) {
		m_state = t;
	}
	if ( status != 0 ) {
		dprintf( D_ALWAYS, "FileLock::obtain(%d) failed - errno %d (%s)\n",
	                t, errno, strerror(errno) );
	}
	else {
		UtcTime	now( true );
		dprintf( D_FULLDEBUG,
				 "FileLock::obtain(%d) - @%.6f lock on %s now %s\n",
				 t, now.combined(), m_path, getStateString(t) );
	}
	return status == 0;
}

bool
FileLock::release(void)
{
	return obtain( UN_LOCK );
}

void
FileLock::updateLockTimestamp(void)
{
	priv_state p;

	if (m_path) {

		dprintf(D_FULLDEBUG, "FileLock object is updating timestamp on: %s\n",
			m_path);

		// NOTE:
		// At the time of writing, if we try to update the lock and fail
		// because the lock is not owned by Condor, we ignore it and leave the
		// lock timestamp not updated and we don't even write a message about
		// it in the logs. This behaviour is warranted because the main reason
		// why this is here at all if to prevent Condor owned lock files from
		// being deleted by tmpwatch and other administrative programs.
		// According to Todd 07/15/2008, a new feature will shortly be going
		// into Condor which will make *ALL* file locking use separate lock
		// file elsewhere which will all be owned by Condor, in which case this
		// will work perfectly.

		// One of the main reasons why we just don't store the privledge state
		// of the process when this object is created is because the semantics
		// of this object have been that the caller is resposible for
		// maintaining the correct priv state when dealing with the lock
		// object. If we circumvent that by having the lock object alter
		// it privstate to match what it was constructed under, it will be
		// very surprising if the caller knows a file has changed ownership
		// or similar things.

		p = set_condor_priv();

		// set the updated atime and mtime for the file to now.
		if (utime(m_path, NULL) < 0) {

			// Only emit message if it isn't a permission problem....
			if (errno != EACCES && errno != EPERM) {
				dprintf(D_FULLDEBUG, "FileLock::updateLockTime(): utime() "
					"failed %d(%s) on lock file %s. Not updating timestamp.\n",
					errno, strerror(errno), m_path);
			}
		}
		set_priv(p);

		return;
	}
}
<|endoftext|>


<commit_before>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/
#include "condor_common.h"
#include "condor_debug.h"
#include "condor_classad.h"
#include "HashTable.h"
#include "hashkey.h"
#include "sched.h"
#include "condor_attributes.h"

#ifndef WIN32
#include <netinet/in.h>
#endif

extern "C" char * sin_to_string(struct sockaddr_in *);

template class HashTable<HashKey, ClassAd *>;
extern void parseIpPort (char *, char *);

void HashKey::sprint (char *s)
{
	if (ip_addr[0])
		sprintf (s, "< %s , %s >", name, ip_addr);
	else
		sprintf (s, "< %s >", name);
}

bool operator== (const HashKey &lhs, const HashKey &rhs)
{
    return (strcmp (lhs.name, rhs.name) == 0    &&
			strcmp (lhs.ip_addr, rhs.ip_addr) == 0);
}

ostream &operator<< (ostream &out, const HashKey &hk)
{
	out << "Hashkey: (" << hk.name << "," << hk.ip_addr;
	out << ")" << endl;
	return out;
}

int hashFunction (const HashKey &key, int numBuckets)
{
    unsigned int bkt = 0;
    int i;

    for (i = 0; key.name[i]   ; bkt += key.name[i++]);
    for (i = 0; key.ip_addr[i]; bkt += key.ip_addr[i++]);

    bkt %= numBuckets;

    return bkt;
}

int hashOnName (const HashKey &key, int numBuckets)
{
	unsigned int bkt = 0;
	int i;

	for (i = 0; key.name [i]; bkt += key.name[i++]);
	bkt %= numBuckets;

	return bkt;
}

// functions to make the hashkeys ...
// make hashkeys from the obtained ad
bool
makeStartdAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;
	char buffer [30];
	char buf2   [64];
	int  inferred = 0;

	// get the name of the startd
	if (!(tree = ad->Lookup ("Name")))
	{
		// ... if name was not specified
		dprintf(D_FULLDEBUG,"Warning: No 'Name' attribute; trying 'Machine'\n");
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		strcpy (hk.name, ((String *)tree->RArg())->Value());
	}
	else
	{
		// neither Name nor Machine specified in ad
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		return false;
	}

	// get the IP and port of the startd
	tree = ad->Lookup (ATTR_STARTD_IP_ADDR);

	// if not there, try to lookup the old style "STARTD_IP_ADDR" string
	if (!tree) tree = ad->Lookup ("STARTD_IP_ADDR");

	if (tree)
	{
		strcpy (buffer, ((String *)tree->RArg())->Value());
	}
	else
	{
		dprintf (D_FULLDEBUG,"Warning: No STARTD_IP_ADDR; inferring address\n");
		strcpy (buffer, sin_to_string (from));

		sprintf (buf2, "%s = \"%s\"", ATTR_STARTD_IP_ADDR, buffer);
		ad->Insert (buf2);
		dprintf (D_FULLDEBUG, "(Inferred address: %s)\n", buf2);
		inferred = 1;
	}

	parseIpPort (buffer, hk.ip_addr);

	return true;
}

bool
makeScheddAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;
	char buffer [30];
	char buf2   [64];
	int  inferred = 0;

	// get the name of the startd
	if (!(tree = ad->Lookup ("Name")))
	{
		dprintf(D_FULLDEBUG,"Warning: No 'Name' attribute; trying 'Machine'\n");
		// ... if name was not specified
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		strcpy (hk.name, ((String *)tree->RArg())->Value());
	}
	else
	{
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		// neither Name nor Machine specified
		return false;
	}

	// get the IP and port of the startd
	tree = ad->Lookup (ATTR_SCHEDD_IP_ADDR);

	if (!tree)
		tree = ad->Lookup ("SCHEDD_IP_ADDR");

	if (tree)
	{
		strcpy (buffer, ((String *)tree->RArg())->Value());
	}
	else
	{
		dprintf(D_FULLDEBUG,"Warning: No SCHEDD_IP_ADDR; inferring address\n");
		strcpy (buffer, sin_to_string (from));

        // since we have done the work ...
        sprintf (buf2, "%s = \"%s\"", ATTR_SCHEDD_IP_ADDR, buffer);
		ad->Insert (buf2);
		dprintf (D_FULLDEBUG, "(Inferred address: %s)\n", buf2);
		inferred = 1;
	}

	parseIpPort (buffer, hk.ip_addr);

	return true;
}

bool
makeLicenseAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;
	char buffer [30];
	char buf2   [64];
	int  inferred = 0;

	// get the name of the startd
	if (!(tree = ad->Lookup ("Name")))
	{
		dprintf(D_FULLDEBUG,"Warning: No 'Name' attribute; trying 'Machine'\n");
		// ... if name was not specified
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		strcpy (hk.name, ((String *)tree->RArg())->Value());
	}
	else
	{
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		// neither Name nor Machine specified
		return false;
	}

	// get the IP and port of the startd
	tree = ad->Lookup (ATTR_MY_ADDRESS);

	if (tree)
	{
		strcpy (buffer, ((String *)tree->RArg())->Value());
	}
	else
	{
		dprintf(D_FULLDEBUG,"Warning: No MY_ADDRESS; inferring address\n");
		strcpy (buffer, sin_to_string (from));

        // since we have done the work ...
        sprintf (buf2, "%s = \"%s\"", ATTR_MY_ADDRESS, buffer);
		ad->Insert (buf2);
		dprintf (D_FULLDEBUG, "(Inferred address: %s)\n", buf2);
		inferred = 1;
	}

	parseIpPort (buffer, hk.ip_addr);

	return true;
}

bool
makeMasterAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;

	if (!(tree = ad->Lookup ("Name")))
	{
		dprintf( D_FULLDEBUG,
				 "Warning: No 'Name' attribute; trying 'Machine'\n" );
			// ... if name was not specified
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		strcpy (hk.name, ((String *)tree->RArg())->Value ());
	}
	else
	{
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		// neither Name nor Machine specified
		return false;
	}

	// ip_addr not necessary
	hk.ip_addr [0] = '\0';

	return true;
}

bool
makeCkptSrvrAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	if (!ad->LookupString ("Machine", hk.name))
	{
		dprintf (D_ALWAYS, "Error:  No 'Machine' attribute\n");
		return false;
	}

	hk.ip_addr[0] = '\0';

	return true;
}

bool
makeCollectorAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	if (!ad->LookupString ("Machine", hk.name))
	{
		dprintf (D_ALWAYS, "Error:  No 'Machine' attribute\n");
		return false;
	}

	hk.ip_addr[0] = '\0';

	return true;
}

bool
makeStorageAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	if (!ad->LookupString ("Name", hk.name))
	{
		dprintf (D_ALWAYS, "Error:  No 'Name' attribute\n");
		return false;
	}

	hk.ip_addr[0] = '\0';

	return true;
}

// utility function:  parse the string <aaa.bbb.ccc.ddd:pppp>
void
parseIpPort (char *ip_port_pair, char *ip_addr)
{
    char *ip_port = ip_port_pair + 1;
    char *ip = ip_addr;
    while (*ip_port != ':')
    {
        *ip = *ip_port;
        ip++;
        ip_port++;
    }

    *ip = '\0';

	// don't care about port number
}

<commit_msg>Updated to use new ClassAd unparsing<commit_after>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/
#include "condor_common.h"
#include "condor_debug.h"
#include "condor_classad.h"
#include "HashTable.h"
#include "hashkey.h"
#include "sched.h"
#include "condor_attributes.h"

#ifndef WIN32
#include <netinet/in.h>
#endif

extern "C" char * sin_to_string(struct sockaddr_in *);

template class HashTable<HashKey, ClassAd *>;
extern void parseIpPort (char *, char *);

void HashKey::sprint (char *s)
{
	if (ip_addr[0])
		sprintf (s, "< %s , %s >", name, ip_addr);
	else
		sprintf (s, "< %s >", name);
}

bool operator== (const HashKey &lhs, const HashKey &rhs)
{
    return (strcmp (lhs.name, rhs.name) == 0    &&
			strcmp (lhs.ip_addr, rhs.ip_addr) == 0);
}

ostream &operator<< (ostream &out, const HashKey &hk)
{
	out << "Hashkey: (" << hk.name << "," << hk.ip_addr;
	out << ")" << endl;
	return out;
}

int hashFunction (const HashKey &key, int numBuckets)
{
    unsigned int bkt = 0;
    int i;

    for (i = 0; key.name[i]   ; bkt += key.name[i++]);
    for (i = 0; key.ip_addr[i]; bkt += key.ip_addr[i++]);

    bkt %= numBuckets;

    return bkt;
}

int hashOnName (const HashKey &key, int numBuckets)
{
	unsigned int bkt = 0;
	int i;

	for (i = 0; key.name [i]; bkt += key.name[i++]);
	bkt %= numBuckets;

	return bkt;
}

// functions to make the hashkeys ...
// make hashkeys from the obtained ad
bool
makeStartdAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;
	char buffer [30];
	char buf2   [64];
	int  inferred = 0;
	ClassAdUnParser unp;
	string buffString;

	// get the name of the startd
	if (!(tree = ad->Lookup ("Name")))
	{
		// ... if name was not specified
		dprintf(D_FULLDEBUG,"Warning: No 'Name' attribute; trying 'Machine'\n");
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		unp.Unparse( buffString, tree );
//		strcpy (hk.name, ((String *)tree->RArg())->Value());
		strcpy (hk.name, buffString.c_str( ) );
		buffString = "";
	}
	else
	{
		// neither Name nor Machine specified in ad
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		return false;
	}

	// get the IP and port of the startd
	tree = ad->Lookup (ATTR_STARTD_IP_ADDR);

	// if not there, try to lookup the old style "STARTD_IP_ADDR" string
	if (!tree) tree = ad->Lookup ("STARTD_IP_ADDR");

	if (tree)
	{
		unp.Unparse( buffString, tree );
//		strcpy (buffer, ((String *)tree->RArg())->Value());
		strcpy (buffer, buffString.c_str( ) );
		buffString = "";
	}
	else
	{
		dprintf (D_FULLDEBUG,"Warning: No STARTD_IP_ADDR; inferring address\n");
		strcpy (buffer, sin_to_string (from));

		sprintf (buf2, "%s = \"%s\"", ATTR_STARTD_IP_ADDR, buffer);
		ad->Insert (buf2);
		dprintf (D_FULLDEBUG, "(Inferred address: %s)\n", buf2);
		inferred = 1;
	}

	parseIpPort (buffer, hk.ip_addr);

	return true;
}

bool
makeScheddAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;
	char buffer [30];
	char buf2   [64];
	int  inferred = 0;
	ClassAdUnParser unp;
	string buffString;

	// get the name of the startd
	if (!(tree = ad->Lookup ("Name")))
	{
		dprintf(D_FULLDEBUG,"Warning: No 'Name' attribute; trying 'Machine'\n");
		// ... if name was not specified
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		unp.Unparse( buffString, tree );
//		strcpy (hk.name, ((String *)tree->RArg())->Value());
		strcpy (hk.name, buffString.c_str( ) );
		buffString = "";
	}
	else
	{
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		// neither Name nor Machine specified
		return false;
	}

	// get the IP and port of the startd
	tree = ad->Lookup (ATTR_SCHEDD_IP_ADDR);

	if (!tree)
		tree = ad->Lookup ("SCHEDD_IP_ADDR");

	if (tree)
	{
		unp.Unparse( buffString, tree );
//		strcpy (buffer, ((String *)tree->RArg())->Value());
		strcpy (buffer, buffString.c_str( ) );
		buffString = "";
	}
	else
	{
		dprintf(D_FULLDEBUG,"Warning: No SCHEDD_IP_ADDR; inferring address\n");
		strcpy (buffer, sin_to_string (from));

        // since we have done the work ...
        sprintf (buf2, "%s = \"%s\"", ATTR_SCHEDD_IP_ADDR, buffer);
		ad->Insert (buf2);
		dprintf (D_FULLDEBUG, "(Inferred address: %s)\n", buf2);
		inferred = 1;
	}

	parseIpPort (buffer, hk.ip_addr);

	return true;
}

bool
makeLicenseAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;
	char buffer [30];
	char buf2   [64];
	int  inferred = 0;
	ClassAdUnParser unp;
	string buffString;

	// get the name of the startd
	if (!(tree = ad->Lookup ("Name")))
	{
		dprintf(D_FULLDEBUG,"Warning: No 'Name' attribute; trying 'Machine'\n");
		// ... if name was not specified
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		unp.Unparse( buffString, tree );
//		strcpy (hk.name, ((String *)tree->RArg())->Value());
		strcpy (hk.name, buffString.c_str( ) );
		buffString = "";
	}
	else
	{
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		// neither Name nor Machine specified
		return false;
	}

	// get the IP and port of the startd
	tree = ad->Lookup (ATTR_MY_ADDRESS);

	if (tree)
	{
		unp.Unparse( buffString, tree );
//		strcpy (buffer, ((String *)tree->RArg())->Value());
		strcpy (buffer, buffString.c_str( ) );
		buffString = "";
	}
	else
	{
		dprintf(D_FULLDEBUG,"Warning: No MY_ADDRESS; inferring address\n");
		strcpy (buffer, sin_to_string (from));

        // since we have done the work ...
        sprintf (buf2, "%s = \"%s\"", ATTR_MY_ADDRESS, buffer);
		ad->Insert (buf2);
		dprintf (D_FULLDEBUG, "(Inferred address: %s)\n", buf2);
		inferred = 1;
	}

	parseIpPort (buffer, hk.ip_addr);

	return true;
}

bool
makeMasterAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	ExprTree *tree;
	ClassAdUnParser unp;
	string buffString;

	if (!(tree = ad->Lookup ("Name")))
	{
		dprintf( D_FULLDEBUG,
				 "Warning: No 'Name' attribute; trying 'Machine'\n" );
			// ... if name was not specified
		tree = ad->Lookup ("Machine");
	}

	if (tree)
	{
		unp.Unparse( buffString, tree );
//		strcpy (hk.name, ((String *)tree->RArg())->Value ());
		strcpy (hk.name, buffString.c_str( ) );
		buffString = "";
	}
	else
	{
		dprintf (D_ALWAYS, "Error: Neither 'Name' nor 'Machine' specified\n");
		// neither Name nor Machine specified
		return false;
	}

	// ip_addr not necessary
	hk.ip_addr [0] = '\0';

	return true;
}

bool
makeCkptSrvrAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	if (!ad->LookupString ("Machine", hk.name))
	{
		dprintf (D_ALWAYS, "Error:  No 'Machine' attribute\n");
		return false;
	}

	hk.ip_addr[0] = '\0';

	return true;
}

bool
makeCollectorAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	if (!ad->LookupString ("Machine", hk.name))
	{
		dprintf (D_ALWAYS, "Error:  No 'Machine' attribute\n");
		return false;
	}

	hk.ip_addr[0] = '\0';

	return true;
}

bool
makeStorageAdHashKey (HashKey &hk, ClassAd *ad, sockaddr_in *from)
{
	if (!ad->LookupString ("Name", hk.name))
	{
		dprintf (D_ALWAYS, "Error:  No 'Name' attribute\n");
		return false;
	}

	hk.ip_addr[0] = '\0';

	return true;
}

// utility function:  parse the string <aaa.bbb.ccc.ddd:pppp>
void
parseIpPort (char *ip_port_pair, char *ip_addr)
{
    char *ip_port = ip_port_pair + 1;
    char *ip = ip_addr;
    while (*ip_port != ':')
    {
        *ip = *ip_port;
        ip++;
        ip_port++;
    }

    *ip = '\0';

	// don't care about port number
}

<|endoftext|>


<commit_before><commit_msg>Fix a crash in QSystemLocalePrivate::uiLanguages().<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
 * Copyright (c) 2009 Advanced Micro Devices, Inc.
 * 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.
 */

#include "cpu/testers/rubytest/Check.hh"
#include "mem/ruby/common/SubBlock.hh"
#include "mem/ruby/system/Sequencer.hh"
#include "mem/ruby/system/System.hh"

typedef RubyTester::SenderState SenderState;

Check::Check(const Address& address, const Address& pc,
             int _num_cpu_sequencers, RubyTester* _tester)
    : m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester)
{
    m_status = TesterStatus_Idle;

    pickValue();
    pickInitiatingNode();
    changeAddress(address);
    m_pc = pc;
    m_access_mode = RubyAccessMode(random() % RubyAccessMode_NUM);
    m_store_count = 0;
}

void
Check::initiate()
{
    DPRINTF(RubyTest, "initiating\n");
    debugPrint();

    // currently no protocols support prefetches
    if (false && (random() & 0xf) == 0) {
        initiatePrefetch(); // Prefetch from random processor
    }

    if (m_tester_ptr->getCheckFlush() && (random() & 0xff) == 0) {
        initiateFlush(); // issue a Flush request from random processor
    }

    if (m_status == TesterStatus_Idle) {
        initiateAction();
    } else if (m_status == TesterStatus_Ready) {
        initiateCheck();
    } else {
        // Pending - do nothing
        DPRINTF(RubyTest,
                "initiating action/check - failed: action/check is pending\n");
    }
}

void
Check::initiatePrefetch()
{
    DPRINTF(RubyTest, "initiating prefetch\n");

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;
    flags.set(Request::PREFETCH);

    Packet::Command cmd;

    // 1 in 8 chance this will be an exclusive prefetch
    if ((random() & 0x7) != 0) {
        cmd = MemCmd::ReadReq;

        // 50% chance that the request will be an instruction fetch
        if ((random() & 0x1) == 0) {
            flags.set(Request::INST_FETCH);
        }
    } else {
        cmd = MemCmd::WriteReq;
        flags.set(Request::PF_EXCLUSIVE);
    }

    // Prefetches are assumed to be 0 sized
    Request *req = new Request(m_address.getAddress(), 0, flags, curTick(),
                               m_pc.getAddress());

    PacketPtr pkt = new Packet(req, cmd, port->idx);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(m_address, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "successfully initiated prefetch.\n");
    } else {
        // If the packet did not issue, must delete
        SenderState* senderState =  safe_cast<SenderState*>(pkt->senderState);
        pkt->senderState = senderState->saved;
        delete senderState;
        delete pkt->req;
        delete pkt;

        DPRINTF(RubyTest,
                "prefetch initiation failed because Port was busy.\n");
    }
}

void
Check::initiateFlush()
{

    DPRINTF(RubyTest, "initiating Flush\n");

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;

    Request *req = new Request(m_address.getAddress(), CHECK_SIZE, flags, curTick(),
                               m_pc.getAddress());

    Packet::Command cmd;

    cmd = MemCmd::FlushReq;

    PacketPtr pkt = new Packet(req, cmd, port->idx);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(m_address, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "initiating Flush - successful\n");
    }
}

void
Check::initiateAction()
{
    DPRINTF(RubyTest, "initiating Action\n");
    assert(m_status == TesterStatus_Idle);

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;

    // Create the particular address for the next byte to be written
    Address writeAddr(m_address.getAddress() + m_store_count);

    // Stores are assumed to be 1 byte-sized
    Request *req = new Request(writeAddr.getAddress(), 1, flags, curTick(),
                               m_pc.getAddress());

    Packet::Command cmd;

    // 1 out of 8 chance, issue an atomic rather than a write
    // if ((random() & 0x7) == 0) {
    //     cmd = MemCmd::SwapReq;
    // } else {
    cmd = MemCmd::WriteReq;
    // }

    PacketPtr pkt = new Packet(req, cmd, port->idx);
    uint8_t* writeData = new uint8_t;
    *writeData = m_value + m_store_count;
    pkt->dataDynamic(writeData);

    DPRINTF(RubyTest, "data 0x%x check 0x%x\n",
            *(pkt->getPtr<uint8_t>()), *writeData);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(writeAddr, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "initiating action - successful\n");
        DPRINTF(RubyTest, "status before action update: %s\n",
                (TesterStatus_to_string(m_status)).c_str());
        m_status = TesterStatus_Action_Pending;
    } else {
        // If the packet did not issue, must delete
        // Note: No need to delete the data, the packet destructor
        // will delete it
        SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
        pkt->senderState = senderState->saved;
        delete senderState;
        delete pkt->req;
        delete pkt;

        DPRINTF(RubyTest, "failed to initiate action - sequencer not ready\n");
    }

    DPRINTF(RubyTest, "status after action update: %s\n",
            (TesterStatus_to_string(m_status)).c_str());
}

void
Check::initiateCheck()
{
    DPRINTF(RubyTest, "Initiating Check\n");
    assert(m_status == TesterStatus_Ready);

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;

    // 50% chance that the request will be an instruction fetch
    if ((random() & 0x1) == 0) {
        flags.set(Request::INST_FETCH);
    }

    // Checks are sized depending on the number of bytes written
    Request *req = new Request(m_address.getAddress(), CHECK_SIZE, flags,
                               curTick(), m_pc.getAddress());

    PacketPtr pkt = new Packet(req, MemCmd::ReadReq, port->idx);
    uint8_t* dataArray = new uint8_t[CHECK_SIZE];
    pkt->dataDynamicArray(dataArray);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(m_address, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "initiating check - successful\n");
        DPRINTF(RubyTest, "status before check update: %s\n",
                TesterStatus_to_string(m_status).c_str());
        m_status = TesterStatus_Check_Pending;
    } else {
        // If the packet did not issue, must delete
        // Note: No need to delete the data, the packet destructor
        // will delete it
        SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
        pkt->senderState = senderState->saved;
        delete senderState;
        delete pkt->req;
        delete pkt;

        DPRINTF(RubyTest, "failed to initiate check - cpu port not ready\n");
    }

    DPRINTF(RubyTest, "status after check update: %s\n",
            TesterStatus_to_string(m_status).c_str());
}

void
Check::performCallback(NodeID proc, SubBlock* data)
{
    Address address = data->getAddress();

    // This isn't exactly right since we now have multi-byte checks
    //  assert(getAddress() == address);

    assert(getAddress().getLineAddress() == address.getLineAddress());
    assert(data != NULL);

    DPRINTF(RubyTest, "RubyTester Callback\n");
    debugPrint();

    if (m_status == TesterStatus_Action_Pending) {
        DPRINTF(RubyTest, "Action callback write value: %d, currently %d\n",
                (m_value + m_store_count), data->getByte(0));
        // Perform store one byte at a time
        data->setByte(0, (m_value + m_store_count));
        m_store_count++;
        if (m_store_count == CHECK_SIZE) {
            m_status = TesterStatus_Ready;
        } else {
            m_status = TesterStatus_Idle;
        }
        DPRINTF(RubyTest, "Action callback return data now %d\n",
                data->getByte(0));
    } else if (m_status == TesterStatus_Check_Pending) {
        DPRINTF(RubyTest, "Check callback\n");
        // Perform load/check
        for (int byte_number=0; byte_number<CHECK_SIZE; byte_number++) {
            if (uint8(m_value + byte_number) != data->getByte(byte_number)) {
                panic("Action/check failure: proc: %d address: %s data: %s "
                      "byte_number: %d m_value+byte_number: %d byte: %d %s"
                      "Time: %d\n",
                      proc, address, data, byte_number,
                      (int)m_value + byte_number,
                      (int)data->getByte(byte_number), *this,
                      g_eventQueue_ptr->getTime());
            }
        }
        DPRINTF(RubyTest, "Action/check success\n");
        debugPrint();

        // successful check complete, increment complete
        m_tester_ptr->incrementCheckCompletions();

        m_status = TesterStatus_Idle;
        pickValue();

    } else {
        panic("Unexpected TesterStatus: %s proc: %d data: %s m_status: %s "
              "time: %d\n",
              *this, proc, data, m_status, g_eventQueue_ptr->getTime());
    }

    DPRINTF(RubyTest, "proc: %d, Address: 0x%x\n", proc,
            getAddress().getLineAddress());
    DPRINTF(RubyTest, "Callback done\n");
    debugPrint();
}

void
Check::changeAddress(const Address& address)
{
    assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready);
    m_status = TesterStatus_Idle;
    m_address = address;
    m_store_count = 0;
}

void
Check::pickValue()
{
    assert(m_status == TesterStatus_Idle);
    m_status = TesterStatus_Idle;
    m_value = random() & 0xff; // One byte
    m_store_count = 0;
}

void
Check::pickInitiatingNode()
{
    assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready);
    m_status = TesterStatus_Idle;
    m_initiatingNode = (random() % m_num_cpu_sequencers);
    DPRINTF(RubyTest, "picked initiating node %d\n", m_initiatingNode);
    m_store_count = 0;
}

void
Check::print(std::ostream& out) const
{
    out << "["
        << m_address << ", value: "
        << (int)m_value << ", status: "
        << m_status << ", initiating node: "
        << m_initiatingNode << ", store_count: "
        << m_store_count
        << "]" << std::flush;
}

void
Check::debugPrint()
{
    DPRINTF(RubyTest,
        "[%#x, value: %d, status: %s, initiating node: %d, store_count: %d]\n",
        m_address.getAddress(), (int)m_value,
        TesterStatus_to_string(m_status).c_str(),
        m_initiatingNode, m_store_count);
}
<commit_msg>Ruby: have the rubytester pass contextId to Ruby.<commit_after>/*
 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
 * Copyright (c) 2009 Advanced Micro Devices, Inc.
 * 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.
 */

#include "cpu/testers/rubytest/Check.hh"
#include "mem/ruby/common/SubBlock.hh"
#include "mem/ruby/system/Sequencer.hh"
#include "mem/ruby/system/System.hh"

typedef RubyTester::SenderState SenderState;

Check::Check(const Address& address, const Address& pc,
             int _num_cpu_sequencers, RubyTester* _tester)
    : m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester)
{
    m_status = TesterStatus_Idle;

    pickValue();
    pickInitiatingNode();
    changeAddress(address);
    m_pc = pc;
    m_access_mode = RubyAccessMode(random() % RubyAccessMode_NUM);
    m_store_count = 0;
}

void
Check::initiate()
{
    DPRINTF(RubyTest, "initiating\n");
    debugPrint();

    // currently no protocols support prefetches
    if (false && (random() & 0xf) == 0) {
        initiatePrefetch(); // Prefetch from random processor
    }

    if (m_tester_ptr->getCheckFlush() && (random() & 0xff) == 0) {
        initiateFlush(); // issue a Flush request from random processor
    }

    if (m_status == TesterStatus_Idle) {
        initiateAction();
    } else if (m_status == TesterStatus_Ready) {
        initiateCheck();
    } else {
        // Pending - do nothing
        DPRINTF(RubyTest,
                "initiating action/check - failed: action/check is pending\n");
    }
}

void
Check::initiatePrefetch()
{
    DPRINTF(RubyTest, "initiating prefetch\n");

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;
    flags.set(Request::PREFETCH);

    Packet::Command cmd;

    // 1 in 8 chance this will be an exclusive prefetch
    if ((random() & 0x7) != 0) {
        cmd = MemCmd::ReadReq;

        // 50% chance that the request will be an instruction fetch
        if ((random() & 0x1) == 0) {
            flags.set(Request::INST_FETCH);
        }
    } else {
        cmd = MemCmd::WriteReq;
        flags.set(Request::PF_EXCLUSIVE);
    }

    // Prefetches are assumed to be 0 sized
    Request *req = new Request(m_address.getAddress(), 0, flags, curTick(),
                               m_pc.getAddress());
    req->setThreadContext(index, 0);

    PacketPtr pkt = new Packet(req, cmd, port->idx);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(m_address, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "successfully initiated prefetch.\n");
    } else {
        // If the packet did not issue, must delete
        SenderState* senderState =  safe_cast<SenderState*>(pkt->senderState);
        pkt->senderState = senderState->saved;
        delete senderState;
        delete pkt->req;
        delete pkt;

        DPRINTF(RubyTest,
                "prefetch initiation failed because Port was busy.\n");
    }
}

void
Check::initiateFlush()
{

    DPRINTF(RubyTest, "initiating Flush\n");

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;

    Request *req = new Request(m_address.getAddress(), CHECK_SIZE, flags, curTick(),
                               m_pc.getAddress());

    Packet::Command cmd;

    cmd = MemCmd::FlushReq;

    PacketPtr pkt = new Packet(req, cmd, port->idx);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(m_address, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "initiating Flush - successful\n");
    }
}

void
Check::initiateAction()
{
    DPRINTF(RubyTest, "initiating Action\n");
    assert(m_status == TesterStatus_Idle);

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;

    // Create the particular address for the next byte to be written
    Address writeAddr(m_address.getAddress() + m_store_count);

    // Stores are assumed to be 1 byte-sized
    Request *req = new Request(writeAddr.getAddress(), 1, flags, curTick(),
                               m_pc.getAddress());

    req->setThreadContext(index, 0);
    Packet::Command cmd;

    // 1 out of 8 chance, issue an atomic rather than a write
    // if ((random() & 0x7) == 0) {
    //     cmd = MemCmd::SwapReq;
    // } else {
    cmd = MemCmd::WriteReq;
    // }

    PacketPtr pkt = new Packet(req, cmd, port->idx);
    uint8_t* writeData = new uint8_t;
    *writeData = m_value + m_store_count;
    pkt->dataDynamic(writeData);

    DPRINTF(RubyTest, "data 0x%x check 0x%x\n",
            *(pkt->getPtr<uint8_t>()), *writeData);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(writeAddr, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "initiating action - successful\n");
        DPRINTF(RubyTest, "status before action update: %s\n",
                (TesterStatus_to_string(m_status)).c_str());
        m_status = TesterStatus_Action_Pending;
    } else {
        // If the packet did not issue, must delete
        // Note: No need to delete the data, the packet destructor
        // will delete it
        SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
        pkt->senderState = senderState->saved;
        delete senderState;
        delete pkt->req;
        delete pkt;

        DPRINTF(RubyTest, "failed to initiate action - sequencer not ready\n");
    }

    DPRINTF(RubyTest, "status after action update: %s\n",
            (TesterStatus_to_string(m_status)).c_str());
}

void
Check::initiateCheck()
{
    DPRINTF(RubyTest, "Initiating Check\n");
    assert(m_status == TesterStatus_Ready);

    int index = random() % m_num_cpu_sequencers;
    RubyTester::CpuPort* port =
        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));

    Request::Flags flags;

    // 50% chance that the request will be an instruction fetch
    if ((random() & 0x1) == 0) {
        flags.set(Request::INST_FETCH);
    }

    // Checks are sized depending on the number of bytes written
    Request *req = new Request(m_address.getAddress(), CHECK_SIZE, flags,
                               curTick(), m_pc.getAddress());

    req->setThreadContext(index, 0);
    PacketPtr pkt = new Packet(req, MemCmd::ReadReq, port->idx);
    uint8_t* dataArray = new uint8_t[CHECK_SIZE];
    pkt->dataDynamicArray(dataArray);

    // push the subblock onto the sender state.  The sequencer will
    // update the subblock on the return
    pkt->senderState =
        new SenderState(m_address, req->getSize(), pkt->senderState);

    if (port->sendTiming(pkt)) {
        DPRINTF(RubyTest, "initiating check - successful\n");
        DPRINTF(RubyTest, "status before check update: %s\n",
                TesterStatus_to_string(m_status).c_str());
        m_status = TesterStatus_Check_Pending;
    } else {
        // If the packet did not issue, must delete
        // Note: No need to delete the data, the packet destructor
        // will delete it
        SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
        pkt->senderState = senderState->saved;
        delete senderState;
        delete pkt->req;
        delete pkt;

        DPRINTF(RubyTest, "failed to initiate check - cpu port not ready\n");
    }

    DPRINTF(RubyTest, "status after check update: %s\n",
            TesterStatus_to_string(m_status).c_str());
}

void
Check::performCallback(NodeID proc, SubBlock* data)
{
    Address address = data->getAddress();

    // This isn't exactly right since we now have multi-byte checks
    //  assert(getAddress() == address);

    assert(getAddress().getLineAddress() == address.getLineAddress());
    assert(data != NULL);

    DPRINTF(RubyTest, "RubyTester Callback\n");
    debugPrint();

    if (m_status == TesterStatus_Action_Pending) {
        DPRINTF(RubyTest, "Action callback write value: %d, currently %d\n",
                (m_value + m_store_count), data->getByte(0));
        // Perform store one byte at a time
        data->setByte(0, (m_value + m_store_count));
        m_store_count++;
        if (m_store_count == CHECK_SIZE) {
            m_status = TesterStatus_Ready;
        } else {
            m_status = TesterStatus_Idle;
        }
        DPRINTF(RubyTest, "Action callback return data now %d\n",
                data->getByte(0));
    } else if (m_status == TesterStatus_Check_Pending) {
        DPRINTF(RubyTest, "Check callback\n");
        // Perform load/check
        for (int byte_number=0; byte_number<CHECK_SIZE; byte_number++) {
            if (uint8(m_value + byte_number) != data->getByte(byte_number)) {
                panic("Action/check failure: proc: %d address: %s data: %s "
                      "byte_number: %d m_value+byte_number: %d byte: %d %s"
                      "Time: %d\n",
                      proc, address, data, byte_number,
                      (int)m_value + byte_number,
                      (int)data->getByte(byte_number), *this,
                      g_eventQueue_ptr->getTime());
            }
        }
        DPRINTF(RubyTest, "Action/check success\n");
        debugPrint();

        // successful check complete, increment complete
        m_tester_ptr->incrementCheckCompletions();

        m_status = TesterStatus_Idle;
        pickValue();

    } else {
        panic("Unexpected TesterStatus: %s proc: %d data: %s m_status: %s "
              "time: %d\n",
              *this, proc, data, m_status, g_eventQueue_ptr->getTime());
    }

    DPRINTF(RubyTest, "proc: %d, Address: 0x%x\n", proc,
            getAddress().getLineAddress());
    DPRINTF(RubyTest, "Callback done\n");
    debugPrint();
}

void
Check::changeAddress(const Address& address)
{
    assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready);
    m_status = TesterStatus_Idle;
    m_address = address;
    m_store_count = 0;
}

void
Check::pickValue()
{
    assert(m_status == TesterStatus_Idle);
    m_status = TesterStatus_Idle;
    m_value = random() & 0xff; // One byte
    m_store_count = 0;
}

void
Check::pickInitiatingNode()
{
    assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready);
    m_status = TesterStatus_Idle;
    m_initiatingNode = (random() % m_num_cpu_sequencers);
    DPRINTF(RubyTest, "picked initiating node %d\n", m_initiatingNode);
    m_store_count = 0;
}

void
Check::print(std::ostream& out) const
{
    out << "["
        << m_address << ", value: "
        << (int)m_value << ", status: "
        << m_status << ", initiating node: "
        << m_initiatingNode << ", store_count: "
        << m_store_count
        << "]" << std::flush;
}

void
Check::debugPrint()
{
    DPRINTF(RubyTest,
        "[%#x, value: %d, status: %s, initiating node: %d, store_count: %d]\n",
        m_address.getAddress(), (int)m_value,
        TesterStatus_to_string(m_status).c_str(),
        m_initiatingNode, m_store_count);
}
<|endoftext|>


<commit_before>//
//  jsb_templates.hpp
//  hello_world-mobile
//
//  Created by Duc Nguyen on 7/13/18.
//

#ifndef JSB_TEMPLATES_HPP__
#define JSB_TEMPLATES_HPP__

#include <type_traits>

#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_conversions.hpp"

#include <ee/Core.hpp>

namespace ee {
namespace core {

template <typename T> T get_value(const se::Value& value);

template <> inline bool get_value(const se::Value& value) {
    return value.toBoolean();
}

template <> inline std::int32_t get_value(const se::Value& value) {
    return value.toInt32();
}

template <> inline std::string get_value(const se::Value& value) {
    return std::move(value.toString());
}

template <> inline const std::string& get_value(const se::Value& value) {
    return value.toString();
}

template <typename T> void set_value(se::Value& value, T input);

template <> inline void set_value(se::Value& value, std::int32_t input) {
    value.setInt32(input);
}

template <> inline void set_value(se::Value& value, std::string input) {
    value.setString(input);
}

template <> inline void set_value(se::Value& value, bool input) {
    value.setBoolean(input);
}

template <> inline void set_value(se::Value& value, se::Object* obj) {
    value.setObject(obj);
}

template <auto FunctionPtr, typename... Args, std::size_t... Indices>
auto call_static_func(const se::ValueArray& args,
                      std::index_sequence<Indices...>) {
    return (*FunctionPtr)(get_value<Args>(args[Indices])...);
}

template <typename InstanceType, auto FunctionPtr, typename... Args,
          std::size_t... Indices>
auto call_instance_func(InstanceType* instance, const se::ValueArray& args,
                        std::index_sequence<Indices...>) {
    return (instance->*FunctionPtr)(get_value<Args>(args[Indices])...);
}

template <typename T, typename... Args, std::size_t... Indices>
auto make_object(const se::ValueArray& args, std::index_sequence<Indices...>) {
    return new T(get_value<Args>(args[Indices])...);
}

template <typename T, typename... Args>
static bool jsb_constructor(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();

    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = make_object<T, Args...>(args, Indices());
        s.thisObject()->setPrivateData(cObj);
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.", argc,
                    2);
    return false;
}

template <typename T> static bool jsb_finalize(se::State& s) {
    T* cObj = static_cast<T*>(s.nativeThisObject());
    delete cObj;
    return true;
}

template <auto Function, typename... Args> bool jsb_static_call(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();

    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        call_static_func<Function, Args...>(args, Indices());
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename ReturnType, auto FunctionPtr, typename... Args>
bool jsb_static_get(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();

    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto result = call_static_func<FunctionPtr, Args...>(args, Indices());
        set_value<ReturnType>(s.rval(), result);
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename ReturnType, auto MemberPtr>
bool jsb_static_property_get(se::State& s) {
    set_value<ReturnType>(s.rval(), *MemberPtr);
    return true;
}

template <typename InstanceType, auto FunctionPtr, typename... Args>
bool jsb_method_call(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        call_instance_func<InstanceType, FunctionPtr, Args...>(cObj, args,
                                                               Indices());
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename... Args>
bool jsb_method_call_on_ui_thread(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        ee::runOnUiThread([cObj, args] {
            call_instance_func<InstanceType, FunctionPtr, Args...>(cObj, args,
                                                                   Indices());
        });
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename... Args>
bool jsb_method_call_on_ui_thread_and_wait(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        ee::runOnUiThreadAndWait([cObj, args] {
            call_instance_func<InstanceType, FunctionPtr, Args...>(cObj, args,
                                                                   Indices());
        });
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType,
          typename... Args>
bool jsb_method_get(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        set_value<ReturnType>(
            s.rval(), call_instance_func<InstanceType, FunctionPtr, Args...>(
                          cObj, args, Indices()));
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType,
          typename... Args>
bool jsb_method_get_on_ui_thread(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        set_value<ReturnType>(
            s.rval(),
            ee::runOnUiThreadAndWaitResult([cObj, args]() -> ReturnType {
                call_instance_func<InstanceType, FunctionPtr, Args...>(
                    cObj, args, Indices());
            }));
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType>
bool jsb_accessor_get(se::State& s) {
    auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
    set_value<ReturnType>(s.rval(), std::bind(FunctionPtr, cObj)());
    return true;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType>
bool jsb_accessor_get_on_ui_thread(se::State& s) {
    auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
    set_value<ReturnType>(
        s.rval(),
        ee::runOnUiThreadAndWaitResult<ReturnType>(
            [cObj]() -> ReturnType { return std::bind(FunctionPtr, cObj)(); }));
    return true;
}

template <typename InstanceType, auto FunctionPtr, typename ArgumentType>
bool jsb_accessor_set(se::State& s) {
    const auto& args = s.args();
    if (args.size() == 1) {
        auto* cObj = static_cast<InstanceType*>(s.nativeThisObject());
        std::bind(FunctionPtr, cObj, get_value<ArgumentType>(args[0]))();
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.",
                    args.size(), 1);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ArgumentType>
bool jsb_accessor_set_on_ui_thread(se::State& s) {
    const auto& args = s.args();
    if (args.size() == 1) {
        auto* cObj = static_cast<InstanceType*>(s.nativeThisObject());
        ee::runOnUiThread([cObj, args] {
            std::bind(FunctionPtr, cObj, get_value<ArgumentType>(args[0]))();
        });
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.",
                    args.size(), 1);
    return false;
}

template <typename InstanceType, auto MemberPtr, typename ReturnType>
bool jsb_propterty_get(se::State& s) {
    auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
    auto input(cObj->*MemberPtr);
    set_value<ReturnType>(s.rval(), std::move(input));
    return true;
}

template <typename InstanceType, auto MemberPtr, typename ArgumentType>
bool jsb_propterty_set(se::State& s) {
    const auto& args = s.args();
    if (args.size() == 1) {
        auto* cObj = static_cast<InstanceType*>(s.nativeThisObject());
        cObj->*MemberPtr = get_value<std::decay_t<ArgumentType>>(args[0]);
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.",
                    args.size(), 1);
    return false;
}

} // namespace core
} // namespace ee

#endif /* JSB_TEMPLATES_HPP__ */
<commit_msg>- Adding utility methods.<commit_after>//
//  jsb_templates.hpp
//  hello_world-mobile
//
//  Created by Duc Nguyen on 7/13/18.
//

#ifndef JSB_TEMPLATES_HPP__
#define JSB_TEMPLATES_HPP__

#include <type_traits>

#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_conversions.hpp"

#include <ee/Core.hpp>
#include <ee/nlohmann/json.hpp>

namespace ee {
namespace core {

template <typename T> se::Object* create_JSON_object(const T& value);

template <>
inline se::Object* create_JSON_object(const std::pair<float, float>& value) {
    auto&& jsonArray = nlohmann::json::array();
    jsonArray.push_back(value.first);
    jsonArray.push_back(value.second);
    return se::Object::createJSONObject(jsonArray.dump());
}

template <>
inline se::Object* create_JSON_object(const std::pair<int, int>& value) {
    auto&& jsonArray = nlohmann::json::array();
    jsonArray.push_back(value.first);
    jsonArray.push_back(value.second);
    return se::Object::createJSONObject(jsonArray.dump());
}

template <typename T> T get_value(const se::Value& value);

template <> inline bool get_value(const se::Value& value) {
    return value.toBoolean();
}

template <> inline std::int32_t get_value(const se::Value& value) {
    return value.toInt32();
}

template <> inline float get_value(const se::Value& value) {
    return value.toFloat();
}

template <> inline std::string get_value(const se::Value& value) {
    return std::move(value.toString());
}

template <> inline const std::string& get_value(const se::Value& value) {
    return value.toString();
}

template <typename T> void set_value(se::Value& value, T input);

template <> inline void set_value(se::Value& value, std::int32_t input) {
    value.setInt32(input);
}

template <> inline void set_value(se::Value& value, std::string input) {
    value.setString(input);
}

template <> inline void set_value(se::Value& value, bool input) {
    value.setBoolean(input);
}

template <> inline void set_value(se::Value& value, se::Object* obj) {
    value.setObject(obj);
}

template <>
inline void set_value(se::Value& value, std::pair<float, float> input) {
    auto obj = create_JSON_object<std::pair<float, float>>(input);
    value.setObject(obj);
}

template <> inline void set_value(se::Value& value, std::pair<int, int> input) {
    auto obj = create_JSON_object<std::pair<int, int>>(input);
    value.setObject(obj);
}

template <auto FunctionPtr, typename... Args, std::size_t... Indices>
auto call_static_func(const se::ValueArray& args,
                      std::index_sequence<Indices...>) {
    return (*FunctionPtr)(get_value<Args>(args[Indices])...);
}

template <typename InstanceType, auto FunctionPtr, typename... Args,
          std::size_t... Indices>
auto call_instance_func(InstanceType* instance, const se::ValueArray& args,
                        std::index_sequence<Indices...>) {
    return (instance->*FunctionPtr)(get_value<Args>(args[Indices])...);
}

template <typename T, typename... Args, std::size_t... Indices>
auto make_object(const se::ValueArray& args, std::index_sequence<Indices...>) {
    return new T(get_value<Args>(args[Indices])...);
}

template <typename T, typename... Args>
static bool jsb_constructor(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();

    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = make_object<T, Args...>(args, Indices());
        s.thisObject()->setPrivateData(cObj);
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.", argc,
                    2);
    return false;
}

template <typename T> static bool jsb_finalize(se::State& s) {
    T* cObj = static_cast<T*>(s.nativeThisObject());
    delete cObj;
    return true;
}

template <auto Function, typename... Args> bool jsb_static_call(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();

    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        call_static_func<Function, Args...>(args, Indices());
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename ReturnType, auto FunctionPtr, typename... Args>
bool jsb_static_get(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();

    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto result = call_static_func<FunctionPtr, Args...>(args, Indices());
        set_value<ReturnType>(s.rval(), result);
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename ReturnType, auto MemberPtr>
bool jsb_static_property_get(se::State& s) {
    set_value<ReturnType>(s.rval(), *MemberPtr);
    return true;
}

template <typename InstanceType, auto FunctionPtr, typename... Args>
bool jsb_method_call(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        call_instance_func<InstanceType, FunctionPtr, Args...>(cObj, args,
                                                               Indices());
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename... Args>
bool jsb_method_call_on_ui_thread(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        ee::runOnUiThread([cObj, args] {
            call_instance_func<InstanceType, FunctionPtr, Args...>(cObj, args,
                                                                   Indices());
        });
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename... Args>
bool jsb_method_call_on_ui_thread_and_wait(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        ee::runOnUiThreadAndWait([cObj, args] {
            call_instance_func<InstanceType, FunctionPtr, Args...>(cObj, args,
                                                                   Indices());
        });
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType,
          typename... Args>
bool jsb_method_get(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        set_value<ReturnType>(
            s.rval(), call_instance_func<InstanceType, FunctionPtr, Args...>(
                          cObj, args, Indices()));
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType,
          typename... Args>
bool jsb_method_get_on_ui_thread(se::State& s) {
    auto argc = sizeof...(Args);
    const auto& args = s.args();
    using Indices = std::make_index_sequence<sizeof...(Args)>;

    if (argc == args.size()) {
        auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
        set_value<ReturnType>(
            s.rval(),
            ee::runOnUiThreadAndWaitResult<ReturnType>([cObj,
                                                        args]() -> ReturnType {
                return std::forward<ReturnType>(call_instance_func<InstanceType, FunctionPtr, Args...>(
                    cObj, args, Indices()));
            }));
        return true;
    }

    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %ld.",
                    args.size(), argc);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType>
bool jsb_accessor_get(se::State& s) {
    auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
    set_value<ReturnType>(s.rval(), std::bind(FunctionPtr, cObj)());
    return true;
}

template <typename InstanceType, auto FunctionPtr, typename ReturnType>
bool jsb_accessor_get_on_ui_thread(se::State& s) {
    auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
    set_value<ReturnType>(
        s.rval(),
        ee::runOnUiThreadAndWaitResult<ReturnType>(
            [cObj]() -> ReturnType { return std::bind(FunctionPtr, cObj)(); }));
    return true;
}

template <typename InstanceType, auto FunctionPtr, typename ArgumentType>
bool jsb_accessor_set(se::State& s) {
    const auto& args = s.args();
    if (args.size() == 1) {
        auto* cObj = static_cast<InstanceType*>(s.nativeThisObject());
        std::bind(FunctionPtr, cObj, get_value<ArgumentType>(args[0]))();
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.",
                    args.size(), 1);
    return false;
}

template <typename InstanceType, auto FunctionPtr, typename ArgumentType>
bool jsb_accessor_set_on_ui_thread(se::State& s) {
    const auto& args = s.args();
    if (args.size() == 1) {
        auto* cObj = static_cast<InstanceType*>(s.nativeThisObject());
        ee::runOnUiThread([cObj, args] {
            std::bind(FunctionPtr, cObj, get_value<ArgumentType>(args[0]))();
        });
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.",
                    args.size(), 1);
    return false;
}

template <typename InstanceType, auto MemberPtr, typename ReturnType>
bool jsb_propterty_get(se::State& s) {
    auto cObj = static_cast<InstanceType*>(s.nativeThisObject());
    auto input(cObj->*MemberPtr);
    set_value<ReturnType>(s.rval(), std::move(input));
    return true;
}

template <typename InstanceType, auto MemberPtr, typename ArgumentType>
bool jsb_propterty_set(se::State& s) {
    const auto& args = s.args();
    if (args.size() == 1) {
        auto* cObj = static_cast<InstanceType*>(s.nativeThisObject());
        cObj->*MemberPtr = get_value<std::decay_t<ArgumentType>>(args[0]);
        return true;
    }
    SE_REPORT_ERROR("Wrong number of arguments: %ld, was expecting: %d.",
                    args.size(), 1);
    return false;
}

} // namespace core
} // namespace ee

#endif /* JSB_TEMPLATES_HPP__ */
<|endoftext|>


<commit_before>#include <xzero/executor/ThreadedExecutor.h>
#include <xzero/sysconfig.h>
#include <memory>
#include <algorithm>
#include <limits>
#include <pthread.h>

namespace xzero {

#if 0 //!defined(NDEBUG)
static std::mutex m;
#define TRACE(msg...)  do { \
    m.lock(); \
    printf("ThreadedExecutor: " msg); \
    printf("\n"); \
    m.unlock(); \
  } while (0);
#else
#define TRACE(msg...) do { } while (0)
#endif

ThreadedExecutor::ThreadedExecutor() : threads_() {
}

ThreadedExecutor::~ThreadedExecutor() {
  joinAll();
}

static std::string getThreadName(pthread_t tid) {
  char name[16];
  name[0] = '\0';
  pthread_getname_np(tid, name, sizeof(name));
  return name;
}

void ThreadedExecutor::joinAll() {
  for (;;) {
    pthread_t tid = 0;
    {
      TRACE("joinAll: getting lock for getting TID");
      std::lock_guard<std::mutex> lock(mutex_);
      if (threads_.empty())
        break;

      tid = threads_.front();
      threads_.pop_front();
    }
    TRACE("joinAll: join(%lu) %s", tid, getThreadName(tid).c_str());
    pthread_join(tid, nullptr);
  }
  TRACE("joinAll: done");
}

void* ThreadedExecutor::launchme(void* ptr) {
  TRACE("launchme[%d](%p) enter", pthread_self(), ptr);
  std::unique_ptr<Executor::Task> task(reinterpret_cast<Executor::Task*>(ptr));
  (*task)();
  TRACE("launchme[%d](%p) leave", pthread_self(), ptr);
  return nullptr;
}

void ThreadedExecutor::execute(const std::string& name, Task&& task) {
  pthread_t tid;
  pthread_create(&tid, NULL, &launchme, new Task{std::move(task)});
  pthread_setname_np(tid, name.c_str());

  std::lock_guard<std::mutex> lock(mutex_);
  threads_.push_back(tid);
}

void ThreadedExecutor::execute(Task&& task) {
  pthread_t tid = 0;
  //pthread_create(&tid, NULL, &launchme, new Task{std::move(task)});
  pthread_create(&tid, NULL, &launchme, new Task([this, tid, task]{
    task();
    {
      TRACE("task %s finished. getting lock for cleanup", getThreadName(pthread_self()).c_str());
      std::lock_guard<std::mutex> lock(mutex_);
      pthread_detach(tid);
      auto i = std::find(threads_.begin(), threads_.end(), tid);
      if (i != threads_.end()) {
        threads_.erase(i);
      }
    }
  }));
  std::lock_guard<std::mutex> lock(mutex_);
  threads_.push_back(tid);
}

size_t ThreadedExecutor::maxConcurrency() const noexcept {
  return std::numeric_limits<size_t>::max();
}

std::string ThreadedExecutor::toString() const {
  char buf[32];
  snprintf(buf, sizeof(buf), "ThreadedExecutor@%p", this);
  return buf;
}

} // namespace xzero
<commit_msg>apple-port: defer ThreadeExecutor's native thread naming until later<commit_after>#include <xzero/executor/ThreadedExecutor.h>
#include <xzero/sysconfig.h>
#include <memory>
#include <algorithm>
#include <limits>
#include <pthread.h>

namespace xzero {

#if 0 //!defined(NDEBUG)
static std::mutex m;
#define TRACE(msg...)  do { \
    m.lock(); \
    printf("ThreadedExecutor: " msg); \
    printf("\n"); \
    m.unlock(); \
  } while (0);
#else
#define TRACE(msg...) do { } while (0)
#endif

ThreadedExecutor::ThreadedExecutor() : threads_() {
}

ThreadedExecutor::~ThreadedExecutor() {
  joinAll();
}

static std::string getThreadName(pthread_t tid) {
  char name[16];
  name[0] = '\0';
  pthread_getname_np(tid, name, sizeof(name));
  return name;
}

void ThreadedExecutor::joinAll() {
  for (;;) {
    pthread_t tid = 0;
    {
      TRACE("joinAll: getting lock for getting TID");
      std::lock_guard<std::mutex> lock(mutex_);
      if (threads_.empty())
        break;

      tid = threads_.front();
      threads_.pop_front();
    }
    TRACE("joinAll: join(%lu) %s", tid, getThreadName(tid).c_str());
    pthread_join(tid, nullptr);
  }
  TRACE("joinAll: done");
}

void* ThreadedExecutor::launchme(void* ptr) {
  TRACE("launchme[%d](%p) enter", pthread_self(), ptr);
  std::unique_ptr<Executor::Task> task(reinterpret_cast<Executor::Task*>(ptr));
  (*task)();
  TRACE("launchme[%d](%p) leave", pthread_self(), ptr);
  return nullptr;
}

void ThreadedExecutor::execute(const std::string& name, Task&& task) {
  pthread_t tid;
  pthread_create(&tid, NULL, &launchme, new Task{std::move(task)});

#if !defined(__APPLE__)
  // OS/x doesn't support setting thread names for other threads
  // TODO: pass thread name to target thread and set it inside
  pthread_setname_np(tid, name.c_str());
#endif

  std::lock_guard<std::mutex> lock(mutex_);
  threads_.push_back(tid);
}

void ThreadedExecutor::execute(Task&& task) {
  pthread_t tid = 0;
  //pthread_create(&tid, NULL, &launchme, new Task{std::move(task)});
  pthread_create(&tid, NULL, &launchme, new Task([this, tid, task]{
    task();
    {
      TRACE("task %s finished. getting lock for cleanup", getThreadName(pthread_self()).c_str());
      std::lock_guard<std::mutex> lock(mutex_);
      pthread_detach(tid);
      auto i = std::find(threads_.begin(), threads_.end(), tid);
      if (i != threads_.end()) {
        threads_.erase(i);
      }
    }
  }));
  std::lock_guard<std::mutex> lock(mutex_);
  threads_.push_back(tid);
}

size_t ThreadedExecutor::maxConcurrency() const noexcept {
  return std::numeric_limits<size_t>::max();
}

std::string ThreadedExecutor::toString() const {
  char buf[32];
  snprintf(buf, sizeof(buf), "ThreadedExecutor@%p", this);
  return buf;
}

} // namespace xzero
<|endoftext|>