<commit_before>/*
 * Win32FileMonitor.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

// TODO: consider whether we should go with a more convervative style
// of watch/scan (basically directory modification only and go full
// mac style)

// TODO: handle ERROR_NOTIFY_ENUM_DIR
//  (see http://blogs.msdn.com/b/oldnewthing/archive/2011/08/12/10195186.aspx)

// TODO: ensure that ReadDirectoryChangesW definitely doesn't drop
// events in between calls (because we could be doing arbitrarily long
// scanning operations)

// TODO: explicitly handle case of volume type not supporting monitoring
// (on windows indicated by ERROR_INVALID_FUNCTION)

// TODO: investigate ERROR_TOO_MANY_CMDS network bios error referenced
// in article. believe it has to do with many simultaneous reads/writes.
// one possible workaround is to queue an APC for a retry. open incident
// with Microsof Professional Advisory Services (reference article)
//
//   http://support.microsoft.com/gp/advisoryservice#tab0
//

// TODO: investigate whether we need to call AdjustTokenPrivilleges in order
// to call ReadDirectoryChangesW (DirectoryChangeWatcher sample does. see
// the CPrivillegeEnabler)

// TOOD: for non-recursive mode don't do full recursive re-scan on
// dwBytesTransferred == 0 sentienl for buffer overflow

#include <core/system/FileMonitor.hpp>

#include <windows.h>

#include <memory>

#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/classification.hpp>

#include <core/FilePath.hpp>

#include <core/system/FileScanner.hpp>
#include <core/system/System.hpp>

#include "FileMonitorImpl.hpp"

namespace core {
namespace system {
namespace file_monitor {

namespace {

// buffer size for notifications (cannot be > 64kb for network drives)
const std::size_t kBuffSize = 32768;

class FileEventContext : boost::noncopyable
{
public:
   FileEventContext()
      : hDirectory(NULL)
   {
      receiveBuffer.resize(kBuffSize);
      handlingBuffer.resize(kBuffSize);
   }
   virtual ~FileEventContext() {}

   FilePath rootPath;
   HANDLE hDirectory;
   OVERLAPPED overlapped;
   std::vector<BYTE> receiveBuffer;
   std::vector<BYTE> handlingBuffer;
   tree<FileInfo> fileTree;
   Callbacks::ReportError onMonitoringError;
   Callbacks::FilesChanged onFilesChanged;
};

void closeDirectoryHandle(HANDLE hDirectory)
{
   if (!::CloseHandle(hDirectory))
      LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
}

void cleanupContext(FileEventContext* pContext)
{
   if (pContext->hDirectory != NULL)
   {
      if (!::CancelIo(pContext->hDirectory))
         LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

      closeDirectoryHandle(pContext->hDirectory);

      pContext->hDirectory = NULL;
   }
}

void removeTrailingSlash(std::wstring* pPath)
{
   boost::algorithm::trim_right_if(*pPath, boost::algorithm::is_any_of(L"\\"));
}

void ensureLongFilePath(FilePath* pFilePath)
{
   // get the filename, if it is 12 characters or less and it contains
   // a "~" then it may be a short file name. in that case do the conversion
   std::string filename = pFilePath->filename();
   if (filename.length() <= 12 && filename.find('~') != std::string::npos)
   {
      const std::size_t kBuffSize = (MAX_PATH*2) + 1;
      char buffer[kBuffSize];
      if (::GetLongPathName(pFilePath->absolutePath().c_str(),
                            buffer,
                            kBuffSize) > 0)
      {
         *pFilePath = FilePath(buffer);
      }
   }
}

void processFileChange(DWORD action,
                       const FilePath& filePath,
                       tree<FileInfo>* pTree,
                       std::vector<FileChangeEvent>* pFileChanges)
{
   // ignore all directory modified actions (we rely instead on the
   // actions which occur inside the directory)
   if (filePath.isDirectory() && (action == FILE_ACTION_MODIFIED))
      return;

   // screen out the root directory (this should never occur but if it
   // does for any reason we want to prevent it from interfering
   // with the logic below (which assumes a child path)
   if (filePath.isDirectory() &&
      (filePath.absolutePath() == pTree->begin()->absolutePath()))
   {
      return;
   }

   // get an iterator to this file's parent
   FileInfo parentFileInfo = FileInfo(filePath.parent());
   tree<FileInfo>::iterator parentIt = impl::findFile(pTree->begin(),
                                                      pTree->end(),
                                                      parentFileInfo);

   // bail if there is no parent (we never expect this to occur so log it)
   if (parentIt == pTree->end())
   {
      LOG_WARNING_MESSAGE("Unable to find parent: " +
                          parentFileInfo.absolutePath());
      return;
   }

   // handle the various types of actions
   FileInfo fileInfo(filePath);
   switch(action)
   {
      case FILE_ACTION_ADDED:
      case FILE_ACTION_RENAMED_NEW_NAME:
      {
         FileChangeEvent event(FileChangeEvent::FileAdded, fileInfo);
         Error error = impl::processFileAdded(parentIt,
                                              event,
                                              pTree,
                                              pFileChanges);
         if (error)
            LOG_ERROR(error);
         break;
      }
      case FILE_ACTION_REMOVED:
      case FILE_ACTION_RENAMED_OLD_NAME:
      {
         FileChangeEvent event(FileChangeEvent::FileRemoved, fileInfo);
         impl::processFileRemoved(parentIt, event, pTree, pFileChanges);
         break;
      }
      case FILE_ACTION_MODIFIED:
      {
         FileChangeEvent event(FileChangeEvent::FileModified, fileInfo);
         impl::processFileModified(parentIt, event, pTree, pFileChanges);
         break;
      }
   }
}

void processFileChanges(FileEventContext* pContext,
                        DWORD dwNumberOfBytesTransfered)
{
   // accumulate file changes
   std::vector<FileChangeEvent> fileChanges;

   // cycle through the entries in the buffer
   char* pBuffer = (char*)&pContext->handlingBuffer[0];
   while(true)
   {
      // check for buffer pointer which has overflowed the end (apparently this
      // can happen if the underlying directory is deleted)
      if( (DWORD)((BYTE*)pBuffer - &(pContext->handlingBuffer[0])) >
          dwNumberOfBytesTransfered )
      {
         Error error = systemError(ERROR_BUFFER_OVERFLOW, ERROR_LOCATION);
         LOG_ERROR(error);
         break;
      }

      // get file notify struct
      FILE_NOTIFY_INFORMATION& fileNotify = (FILE_NOTIFY_INFORMATION&)*pBuffer;

      // compute a full wide path
      std::wstring name(fileNotify.FileName,
                        fileNotify.FileNameLength/sizeof(wchar_t));
      removeTrailingSlash(&name);
      FilePath filePath(pContext->rootPath.absolutePathW() + L"\\" + name);

      // ensure this is a long file name (docs say it could be short or long!)
      // (note that the call to GetLongFileNameW will fail if the file has
      // already been deleted, therefore if a delete notification using a
      // short file name comes in we may not successfully match it to
      // our in-memory tree and thus "miss" the delete
      ensureLongFilePath(&filePath);

      // process the file change
      processFileChange(fileNotify.Action,
                        filePath,
                        &(pContext->fileTree),
                        &fileChanges);

      // break or advance to next notification as necessary
      if (!fileNotify.NextEntryOffset)
         break;
      else
         pBuffer += fileNotify.NextEntryOffset;
   };

   // notify client of file changes
   pContext->onFilesChanged(fileChanges);
}

void terminateWithMonitoringError(FileEventContext* pContext,
                                  const Error& error)
{
   pContext->onMonitoringError(error);
   file_monitor::unregisterMonitor((Handle)pContext);
}

// track number of active requests (we wait for this to get to zero before
// allowing the exit of the monitoring thread -- this ensures that we have
// performed full cleanup for all monitoring contexts before exiting.
volatile LONG s_activeRequests = 0;

Error readDirectoryChanges(FileEventContext* pContext);

VOID CALLBACK FileChangeCompletionRoutine(DWORD dwErrorCode,									// completion code
                                          DWORD dwNumberOfBytesTransfered,
                                          LPOVERLAPPED lpOverlapped)
{
   // get the context
   FileEventContext* pContext = (FileEventContext*)(lpOverlapped->hEvent);

   // check for aborted
   if (dwErrorCode == ERROR_OPERATION_ABORTED)
   {
      // decrement the active request counter
      ::InterlockedDecrement(&s_activeRequests);

      // we wait to delete the pContext until here because it owns the
      // OVERLAPPED structure and buffers, and so if we deleted it earlier
      // and the OS tried to access those memory regions we would crash
      delete pContext;

      return;
   }

   // bail if we don't have an onFilesChanged callback (could have occurred
   // if we encountered an error during file scanning which caused us to
   // fail but then a file notification still snuck through)
   if (!pContext->onFilesChanged)
      return;

   // make sure the root path still exists (if it doesn't then bail)
   if (!pContext->rootPath.exists())
   {
      Error error = fileNotFoundError(pContext->rootPath.absolutePath(),
                                      ERROR_LOCATION);
      terminateWithMonitoringError(pContext, error);
      return;
   }

   // check for buffer overflow
   if(dwNumberOfBytesTransfered == 0)
   {
      // start reading again (always do this before the scan so we don't
      // miss any events which occur while we are scanning)
      Error error = readDirectoryChanges(pContext);
      if (error)
         terminateWithMonitoringError(pContext, error);

      // full recursive scan to detect changes and refresh the tree
      error = impl::discoverAndProcessFileChanges(*(pContext->fileTree.begin()),
                                                  true,
                                                  &(pContext->fileTree),
                                                  pContext->onFilesChanged);
      if (error)
         LOG_ERROR(error);

      return;
   }

   // copy to processing buffer (so we can immediately begin another read)
   ::CopyMemory(&(pContext->handlingBuffer[0]),
                &(pContext->receiveBuffer[0]),
                dwNumberOfBytesTransfered);

   // begin the next read -- if this fails then the file change notification
   // is effectively dead in the water
   Error error = readDirectoryChanges(pContext);

   // process file changes
   processFileChanges(pContext, dwNumberOfBytesTransfered);

   // report the (fatal) error if necessary and unregister the monitor
   // (do this here so file notifications are received prior to the error)
   if (error)
      terminateWithMonitoringError(pContext, error);
}

Error readDirectoryChanges(FileEventContext* pContext)
{
   DWORD dwBytes = 0;
   if(!::ReadDirectoryChangesW(pContext->hDirectory,
                               &(pContext->receiveBuffer[0]),
                               pContext->receiveBuffer.size(),
                               TRUE,
                               FILE_NOTIFY_CHANGE_FILE_NAME |
                               FILE_NOTIFY_CHANGE_DIR_NAME |
                               FILE_NOTIFY_CHANGE_LAST_WRITE,
                               &dwBytes,
                               &(pContext->overlapped),
                               &FileChangeCompletionRoutine))
   {
      return systemError(::GetLastError(), ERROR_LOCATION);
   }
   else
   {
      return Success();
   }
}

} // anonymous namespace

namespace detail {

// register a new file monitor
Handle registerMonitor(const core::FilePath& filePath,
                       bool recursive,
                       const Callbacks& callbacks)
{
   // create and allocate FileEventContext (create auto-ptr in case we
   // return early, we'll call release later before returning)
   FileEventContext* pContext = new FileEventContext();
   std::auto_ptr<FileEventContext> autoPtrContext(pContext);

   // save the wide absolute path (notifications only come in wide strings)
   // strip any trailing slash for predictable append semantics
   std::wstring wpath = filePath.absolutePathW();
   removeTrailingSlash(&wpath);
   pContext->rootPath = FilePath(wpath);

   // open the directory
   pContext->hDirectory = ::CreateFile(
                     filePath.absolutePath().c_str(),
                     FILE_LIST_DIRECTORY,
                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                     NULL,
                     OPEN_EXISTING,
                     FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                     NULL);
   if (pContext->hDirectory == INVALID_HANDLE_VALUE)
   {
      callbacks.onRegistrationError(
                     systemError(::GetLastError(),ERROR_LOCATION));
      return NULL;
   }

   // initialize overlapped structure to point to our context
   ::ZeroMemory(&(pContext->overlapped), sizeof(OVERLAPPED));
   pContext->overlapped.hEvent = pContext;

   // get the monitoring started
   Error error = readDirectoryChanges(pContext);
   if (error)
   {
      // cleanup
      closeDirectoryHandle(pContext->hDirectory);

      // return error
      callbacks.onRegistrationError(error);

      return NULL;
   }

   // we have passed the pContext into the system so it's ownership will
   // now be governed by the receipt of ERROR_OPERATION_ABORTED within
   // the completion callback
   autoPtrContext.release();

   // increment the number of active requests
   ::InterlockedIncrement(&s_activeRequests);

   // scan the files
   error = scanFiles(FileInfo(filePath), true, &pContext->fileTree);
   if (error)
   {
       // cleanup
       cleanupContext(pContext);

       // return error
       callbacks.onRegistrationError(error);

       return NULL;
   }

   // now that we have finished the file listing we know we have a valid
   // file-monitor so set the callbacks
   pContext->onMonitoringError = callbacks.onMonitoringError;
   pContext->onFilesChanged = callbacks.onFilesChanged;

   // notify the caller that we have successfully registered
   callbacks.onRegistered((Handle)pContext, pContext->fileTree);

   // return the handle
   return (Handle)pContext;
}

// unregister a file monitor
void unregisterMonitor(Handle handle)
{
   // this will end up calling the completion routine with
   // ERROR_OPERATION_ABORTED at which point we'll delete the context
   cleanupContext((FileEventContext*)handle);
}

void run(const boost::function<void()>& checkForInput)
{
   // initialize active requests to zero
   s_activeRequests = 0;

   // loop waiting for:
   //   - completion routine callbacks (occur during SleepEx); or
   //   - inbound commands (occur during checkForInput)
   while (true)
   {
      ::SleepEx(1000, TRUE);
      checkForInput();
   }
}

void stop()
{
   // call ::SleepEx until all active requests hae terminated
   while (s_activeRequests > 0)
   {
      ::SleepEx(100, TRUE);
   }
}

} // namespace detail
} // namespace file_monitor
} // namespace system
} // namespace core

<commit_msg>add more comments<commit_after>/*
 * Win32FileMonitor.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

// TODO: implications of underlying unreliabilty:
//   http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/

// TODO: do we need to call GetLongFileName on all of the files as
// we scan them (for consistency)

// TODO: windows file names are case insensitive and the notification
// is determined by the command that caused the notification

// TODO: consider whether we should go with a more convervative style
// of watch/scan (basically directory modification only and go full
// mac style)

// TODO: handle ERROR_NOTIFY_ENUM_DIR
//  (see http://blogs.msdn.com/b/oldnewthing/archive/2011/08/12/10195186.aspx)

// TODO: ensure that ReadDirectoryChangesW definitely doesn't drop
// events in between calls (because we could be doing arbitrarily long
// scanning operations)

// TODO: explicitly handle case of volume type not supporting monitoring
// (on windows indicated by ERROR_INVALID_FUNCTION)

// TODO: investigate ERROR_TOO_MANY_CMDS network bios error referenced
// in article. believe it has to do with many simultaneous reads/writes.
// one possible workaround is to queue an APC for a retry. open incident
// with Microsof Professional Advisory Services (reference article)
//
//   http://support.microsoft.com/gp/advisoryservice#tab0
//

// TODO: investigate whether we need to call AdjustTokenPrivilleges in order
// to call ReadDirectoryChangesW (DirectoryChangeWatcher sample does. see
// the CPrivillegeEnabler)

// TOOD: for non-recursive mode don't do full recursive re-scan on
// dwBytesTransferred == 0 sentienl for buffer overflow

#include <core/system/FileMonitor.hpp>

#include <windows.h>

#include <memory>

#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/classification.hpp>

#include <core/FilePath.hpp>

#include <core/system/FileScanner.hpp>
#include <core/system/System.hpp>

#include "FileMonitorImpl.hpp"

namespace core {
namespace system {
namespace file_monitor {

namespace {

// buffer size for notifications (cannot be > 64kb for network drives)
const std::size_t kBuffSize = 32768;

class FileEventContext : boost::noncopyable
{
public:
   FileEventContext()
      : hDirectory(NULL)
   {
      receiveBuffer.resize(kBuffSize);
      handlingBuffer.resize(kBuffSize);
   }
   virtual ~FileEventContext() {}

   FilePath rootPath;
   HANDLE hDirectory;
   OVERLAPPED overlapped;
   std::vector<BYTE> receiveBuffer;
   std::vector<BYTE> handlingBuffer;
   tree<FileInfo> fileTree;
   Callbacks::ReportError onMonitoringError;
   Callbacks::FilesChanged onFilesChanged;
};

void closeDirectoryHandle(HANDLE hDirectory)
{
   if (!::CloseHandle(hDirectory))
      LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
}

void cleanupContext(FileEventContext* pContext)
{
   if (pContext->hDirectory != NULL)
   {
      if (!::CancelIo(pContext->hDirectory))
         LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

      closeDirectoryHandle(pContext->hDirectory);

      pContext->hDirectory = NULL;
   }
}

void removeTrailingSlash(std::wstring* pPath)
{
   boost::algorithm::trim_right_if(*pPath, boost::algorithm::is_any_of(L"\\"));
}

void ensureLongFilePath(FilePath* pFilePath)
{
   // get the filename, if it is 12 characters or less and it contains
   // a "~" then it may be a short file name. in that case do the conversion
   std::string filename = pFilePath->filename();
   if (filename.length() <= 12 && filename.find('~') != std::string::npos)
   {
      const std::size_t kBuffSize = (MAX_PATH*2) + 1;
      char buffer[kBuffSize];
      if (::GetLongPathName(pFilePath->absolutePath().c_str(),
                            buffer,
                            kBuffSize) > 0)
      {
         *pFilePath = FilePath(buffer);
      }
   }
}

void processFileChange(DWORD action,
                       const FilePath& filePath,
                       tree<FileInfo>* pTree,
                       std::vector<FileChangeEvent>* pFileChanges)
{
   // ignore all directory modified actions (we rely instead on the
   // actions which occur inside the directory)
   if (filePath.isDirectory() && (action == FILE_ACTION_MODIFIED))
      return;

   // screen out the root directory (this should never occur but if it
   // does for any reason we want to prevent it from interfering
   // with the logic below (which assumes a child path)
   if (filePath.isDirectory() &&
      (filePath.absolutePath() == pTree->begin()->absolutePath()))
   {
      return;
   }

   // get an iterator to this file's parent
   FileInfo parentFileInfo = FileInfo(filePath.parent());
   tree<FileInfo>::iterator parentIt = impl::findFile(pTree->begin(),
                                                      pTree->end(),
                                                      parentFileInfo);

   // bail if there is no parent (we never expect this to occur so log it)
   if (parentIt == pTree->end())
   {
      LOG_WARNING_MESSAGE("Unable to find parent: " +
                          parentFileInfo.absolutePath());
      return;
   }

   // handle the various types of actions
   FileInfo fileInfo(filePath);
   switch(action)
   {
      case FILE_ACTION_ADDED:
      case FILE_ACTION_RENAMED_NEW_NAME:
      {
         FileChangeEvent event(FileChangeEvent::FileAdded, fileInfo);
         Error error = impl::processFileAdded(parentIt,
                                              event,
                                              pTree,
                                              pFileChanges);
         if (error)
            LOG_ERROR(error);
         break;
      }
      case FILE_ACTION_REMOVED:
      case FILE_ACTION_RENAMED_OLD_NAME:
      {
         FileChangeEvent event(FileChangeEvent::FileRemoved, fileInfo);
         impl::processFileRemoved(parentIt, event, pTree, pFileChanges);
         break;
      }
      case FILE_ACTION_MODIFIED:
      {
         FileChangeEvent event(FileChangeEvent::FileModified, fileInfo);
         impl::processFileModified(parentIt, event, pTree, pFileChanges);
         break;
      }
   }
}

void processFileChanges(FileEventContext* pContext,
                        DWORD dwNumberOfBytesTransfered)
{
   // accumulate file changes
   std::vector<FileChangeEvent> fileChanges;

   // cycle through the entries in the buffer
   char* pBuffer = (char*)&pContext->handlingBuffer[0];
   while(true)
   {
      // check for buffer pointer which has overflowed the end (apparently this
      // can happen if the underlying directory is deleted)
      if( (DWORD)((BYTE*)pBuffer - &(pContext->handlingBuffer[0])) >
          dwNumberOfBytesTransfered )
      {
         Error error = systemError(ERROR_BUFFER_OVERFLOW, ERROR_LOCATION);
         LOG_ERROR(error);
         break;
      }

      // get file notify struct
      FILE_NOTIFY_INFORMATION& fileNotify = (FILE_NOTIFY_INFORMATION&)*pBuffer;

      // compute a full wide path
      std::wstring name(fileNotify.FileName,
                        fileNotify.FileNameLength/sizeof(wchar_t));
      removeTrailingSlash(&name);
      FilePath filePath(pContext->rootPath.absolutePathW() + L"\\" + name);

      // ensure this is a long file name (docs say it could be short or long!)
      // (note that the call to GetLongFileNameW will fail if the file has
      // already been deleted, therefore if a delete notification using a
      // short file name comes in we may not successfully match it to
      // our in-memory tree and thus "miss" the delete
      ensureLongFilePath(&filePath);

      // process the file change
      processFileChange(fileNotify.Action,
                        filePath,
                        &(pContext->fileTree),
                        &fileChanges);

      // break or advance to next notification as necessary
      if (!fileNotify.NextEntryOffset)
         break;
      else
         pBuffer += fileNotify.NextEntryOffset;
   };

   // notify client of file changes
   pContext->onFilesChanged(fileChanges);
}

void terminateWithMonitoringError(FileEventContext* pContext,
                                  const Error& error)
{
   pContext->onMonitoringError(error);
   file_monitor::unregisterMonitor((Handle)pContext);
}

// track number of active requests (we wait for this to get to zero before
// allowing the exit of the monitoring thread -- this ensures that we have
// performed full cleanup for all monitoring contexts before exiting.
volatile LONG s_activeRequests = 0;

Error readDirectoryChanges(FileEventContext* pContext);

VOID CALLBACK FileChangeCompletionRoutine(DWORD dwErrorCode,									// completion code
                                          DWORD dwNumberOfBytesTransfered,
                                          LPOVERLAPPED lpOverlapped)
{
   // get the context
   FileEventContext* pContext = (FileEventContext*)(lpOverlapped->hEvent);

   // check for aborted
   if (dwErrorCode == ERROR_OPERATION_ABORTED)
   {
      // decrement the active request counter
      ::InterlockedDecrement(&s_activeRequests);

      // we wait to delete the pContext until here because it owns the
      // OVERLAPPED structure and buffers, and so if we deleted it earlier
      // and the OS tried to access those memory regions we would crash
      delete pContext;

      return;
   }

   // bail if we don't have an onFilesChanged callback (could have occurred
   // if we encountered an error during file scanning which caused us to
   // fail but then a file notification still snuck through)
   if (!pContext->onFilesChanged)
      return;

   // make sure the root path still exists (if it doesn't then bail)
   if (!pContext->rootPath.exists())
   {
      Error error = fileNotFoundError(pContext->rootPath.absolutePath(),
                                      ERROR_LOCATION);
      terminateWithMonitoringError(pContext, error);
      return;
   }

   // check for buffer overflow
   if(dwNumberOfBytesTransfered == 0)
   {
      // start reading again (always do this before the scan so we don't
      // miss any events which occur while we are scanning)
      Error error = readDirectoryChanges(pContext);
      if (error)
         terminateWithMonitoringError(pContext, error);

      // full recursive scan to detect changes and refresh the tree
      error = impl::discoverAndProcessFileChanges(*(pContext->fileTree.begin()),
                                                  true,
                                                  &(pContext->fileTree),
                                                  pContext->onFilesChanged);
      if (error)
         LOG_ERROR(error);

      return;
   }

   // copy to processing buffer (so we can immediately begin another read)
   ::CopyMemory(&(pContext->handlingBuffer[0]),
                &(pContext->receiveBuffer[0]),
                dwNumberOfBytesTransfered);

   // begin the next read -- if this fails then the file change notification
   // is effectively dead in the water
   Error error = readDirectoryChanges(pContext);

   // process file changes
   processFileChanges(pContext, dwNumberOfBytesTransfered);

   // report the (fatal) error if necessary and unregister the monitor
   // (do this here so file notifications are received prior to the error)
   if (error)
      terminateWithMonitoringError(pContext, error);
}

Error readDirectoryChanges(FileEventContext* pContext)
{
   DWORD dwBytes = 0;
   if(!::ReadDirectoryChangesW(pContext->hDirectory,
                               &(pContext->receiveBuffer[0]),
                               pContext->receiveBuffer.size(),
                               TRUE,
                               FILE_NOTIFY_CHANGE_FILE_NAME |
                               FILE_NOTIFY_CHANGE_DIR_NAME |
                               FILE_NOTIFY_CHANGE_LAST_WRITE,
                               &dwBytes,
                               &(pContext->overlapped),
                               &FileChangeCompletionRoutine))
   {
      return systemError(::GetLastError(), ERROR_LOCATION);
   }
   else
   {
      return Success();
   }
}

} // anonymous namespace

namespace detail {

// register a new file monitor
Handle registerMonitor(const core::FilePath& filePath,
                       bool recursive,
                       const Callbacks& callbacks)
{
   // create and allocate FileEventContext (create auto-ptr in case we
   // return early, we'll call release later before returning)
   FileEventContext* pContext = new FileEventContext();
   std::auto_ptr<FileEventContext> autoPtrContext(pContext);

   // save the wide absolute path (notifications only come in wide strings)
   // strip any trailing slash for predictable append semantics
   std::wstring wpath = filePath.absolutePathW();
   removeTrailingSlash(&wpath);
   pContext->rootPath = FilePath(wpath);

   // open the directory
   pContext->hDirectory = ::CreateFile(
                     filePath.absolutePath().c_str(),
                     FILE_LIST_DIRECTORY,
                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                     NULL,
                     OPEN_EXISTING,
                     FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                     NULL);
   if (pContext->hDirectory == INVALID_HANDLE_VALUE)
   {
      callbacks.onRegistrationError(
                     systemError(::GetLastError(),ERROR_LOCATION));
      return NULL;
   }

   // initialize overlapped structure to point to our context
   ::ZeroMemory(&(pContext->overlapped), sizeof(OVERLAPPED));
   pContext->overlapped.hEvent = pContext;

   // get the monitoring started
   Error error = readDirectoryChanges(pContext);
   if (error)
   {
      // cleanup
      closeDirectoryHandle(pContext->hDirectory);

      // return error
      callbacks.onRegistrationError(error);

      return NULL;
   }

   // we have passed the pContext into the system so it's ownership will
   // now be governed by the receipt of ERROR_OPERATION_ABORTED within
   // the completion callback
   autoPtrContext.release();

   // increment the number of active requests
   ::InterlockedIncrement(&s_activeRequests);

   // scan the files
   error = scanFiles(FileInfo(filePath), true, &pContext->fileTree);
   if (error)
   {
       // cleanup
       cleanupContext(pContext);

       // return error
       callbacks.onRegistrationError(error);

       return NULL;
   }

   // now that we have finished the file listing we know we have a valid
   // file-monitor so set the callbacks
   pContext->onMonitoringError = callbacks.onMonitoringError;
   pContext->onFilesChanged = callbacks.onFilesChanged;

   // notify the caller that we have successfully registered
   callbacks.onRegistered((Handle)pContext, pContext->fileTree);

   // return the handle
   return (Handle)pContext;
}

// unregister a file monitor
void unregisterMonitor(Handle handle)
{
   // this will end up calling the completion routine with
   // ERROR_OPERATION_ABORTED at which point we'll delete the context
   cleanupContext((FileEventContext*)handle);
}

void run(const boost::function<void()>& checkForInput)
{
   // initialize active requests to zero
   s_activeRequests = 0;

   // loop waiting for:
   //   - completion routine callbacks (occur during SleepEx); or
   //   - inbound commands (occur during checkForInput)
   while (true)
   {
      ::SleepEx(1000, TRUE);
      checkForInput();
   }
}

void stop()
{
   // call ::SleepEx until all active requests hae terminated
   while (s_activeRequests > 0)
   {
      ::SleepEx(100, TRUE);
   }
}

} // namespace detail
} // namespace file_monitor
} // namespace system
} // namespace core

<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/mc/mc.C $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2017                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mc.C
/// @brief Subroutines to manipulate the memory controller
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Jacob Harvey <jlharvey@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#include <fapi2.H>
#include <lib/utils/dump_regs.H>
#include <lib/mc/mc.H>
#include <generic/memory/lib/utils/find.H>

using fapi2::TARGET_TYPE_MCA;
using fapi2::TARGET_TYPE_MCS;

namespace mss
{

///
/// @brief Dump the registers of the MC (MCA_MBA, MCS)
/// @param[in] i_target the MCS target
/// @return fapi2::FAPI2_RC_SUCCESS if ok
///
template<>
fapi2::ReturnCode dump_regs( const fapi2::Target<TARGET_TYPE_MCS>& i_target )
{
    return fapi2::FAPI2_RC_SUCCESS;
}

namespace mc
{

enum throttle_enums
{
    NM_RAS_WEIGHT = 0b000,
    NM_CAS_WEIGHT = 0b001,

    CHANGE_AFTER_SYNC_ON = 0b1,
    CHANGE_AFTER_SYNC_OFF = 0b0,

    MAXALL_MINALL = 0b000,

    //wait 16 refresh intervals of idle before powering down all ranks
    MIN_DOMAIN_REDUCTION_TIME = 0x10,

    //wait 64 refresh intervals of idle before going into STR on all ranks
    ENTER_STR_TIME = 0x40
};

///
/// @brief set the PWR CNTRL register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MCA_MBARPC0Q
///
fapi2::ReturnCode set_pwr_cntrl_reg(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;
    uint8_t l_pwr_cntrl = 0;
    fapi2::buffer<uint64_t> l_data;

    FAPI_TRY(mrw_power_control_requested(l_pwr_cntrl));
    FAPI_TRY(mss::getScom(i_target, MCA_MBARPC0Q, l_data));

    l_data.insertFromRight<TT::CFG_MIN_MAX_DOMAINS, TT::CFG_MIN_MAX_DOMAINS_LEN>(MAXALL_MINALL);

    //Write data if PWR_CNTRL_REQUESTED includes power down
    switch (l_pwr_cntrl)
    {
        case PD_AND_STR:
        case POWER_DOWN:
        case PD_AND_STR_CLK_STOP:
            l_data.setBit<TT::MIN_DOMAIN_REDUCTION_ENABLE>();
            break;

        case PD_AND_STR_OFF:
            l_data.clearBit<TT::MIN_DOMAIN_REDUCTION_ENABLE>();
            break;

        default:
            // Chief system engineer would reaally have to mess up here since _OFF is 0
            FAPI_ERR("ATTR_MSS_MRW_POWER_CONTROL_REQUESTED not set correctly in MRW");
            fapi2::Assert(false);
            break;
    }

    //Set the MIN_DOMAIN_REDUCTION time
    l_data.insertFromRight<TT::MIN_DOMAIN_REDUCTION_TIME, TT::MIN_DOMAIN_REDUCTION_TIME_LEN>
    (MIN_DOMAIN_REDUCTION_TIME);

    FAPI_TRY(mss::putScom(i_target, MCA_MBARPC0Q, l_data) );

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting power control register MBARPC0Q for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the STR register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBASTR0Q
///
fapi2::ReturnCode set_str_reg(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;
    uint8_t l_str_enable = 0;
    fapi2::buffer<uint64_t> l_data;

    FAPI_TRY(mrw_power_control_requested(l_str_enable));
    FAPI_TRY(mss::getScom(i_target, MCA_MBASTR0Q, l_data));

    //Write bit if STR should be enabled
    switch (l_str_enable)
    {
        case PD_AND_STR:
        case PD_AND_STR_CLK_STOP:
            l_data.setBit<TT::CFG_STR_ENABLE>();
            break;

        case POWER_DOWN:
        case PD_AND_STR_OFF:
            l_data.clearBit<TT::CFG_STR_ENABLE>();
            break;

        default:
            // System engineer would reaally have to mess up here since _OFF is 0
            FAPI_ERR("ATTR_MSS_MRW_POWER_CONTROL_REQUESTED not set correctly in MRW");
            fapi2::Assert(false);
            break;
    }

    // MCA_MBASTR0Q_CFG_DIS_CLK_IN_STR:  Set to 1 for PD_AND_STR_CLK_STOP, otherwise clear the bit
    l_data.writeBit<MCA_MBASTR0Q_CFG_DIS_CLK_IN_STR>(l_str_enable == PD_AND_STR_CLK_STOP);

    l_data.insertFromRight<TT::ENTER_STR_TIME_POS, TT::ENTER_STR_TIME_LEN>(ENTER_STR_TIME);

    FAPI_TRY(mss::putScom(i_target, MCA_MBASTR0Q, l_data) );

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting the STR register MBASTR0Q for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the general N/M throttle register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBA_FARB3Q
///

fapi2::ReturnCode set_nm_support (const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;

    uint16_t l_run_slot = 0;
    uint16_t l_run_port = 0;
    uint32_t l_throttle_denominator = 0;

    fapi2::buffer<uint64_t> l_data;

    //runtime should be calculated in eff_config_thermal, which is called before scominit in ipl
    FAPI_TRY(runtime_mem_throttled_n_commands_per_port(i_target, l_run_port));
    FAPI_TRY(runtime_mem_throttled_n_commands_per_slot(i_target, l_run_slot));
    FAPI_TRY(mss::mrw_mem_m_dram_clocks(l_throttle_denominator) );
    FAPI_INF("For target %s throttled n commands per port are %d, per slot are %d, and dram m clocks are %d",
             mss::c_str(i_target),
             l_run_port,
             l_run_slot,
             l_throttle_denominator);
    FAPI_TRY(mss::getScom(i_target, MCA_MBA_FARB3Q, l_data));

    l_data.insertFromRight<TT::RUNTIME_N_SLOT, TT::RUNTIME_N_SLOT_LEN>(l_run_slot);
    l_data.insertFromRight<TT::RUNTIME_N_PORT, TT::RUNTIME_N_PORT_LEN>(l_run_port);
    l_data.insertFromRight<TT::RUNTIME_M, TT::RUNTIME_M_LEN>(l_throttle_denominator);
    l_data.insertFromRight<TT::RAS_WEIGHT_POS, TT::RAS_WEIGHT_LEN>(NM_RAS_WEIGHT);
    l_data.insertFromRight<TT::CAS_WEIGHT_POS, TT::CAS_WEIGHT_LEN>(NM_CAS_WEIGHT);

    // If set, changes to cfg_nm_n_per_slot, cfg_nm_n_per_port, cfg_nm_m, min_max_domains will only be applied after a pc_sync command is seen
    // Set to disable permanently due to hardware design bug (HW403028) that won't be changed
    l_data.writeBit<TT::NM_CHANGE_AFTER_SYNC>(CHANGE_AFTER_SYNC_OFF);

    FAPI_TRY(mss::putScom(i_target, MCA_MBA_FARB3Q, l_data) );

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting nm throttles for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the safemode throttle register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBA_FARB4Q
/// @note used to set throttle window (N throttles  / M clocks)
///
fapi2::ReturnCode set_safemode_throttles(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;

    fapi2::buffer<uint64_t> l_data;
    uint16_t l_throttle_per_slot = 0;
    uint32_t l_throttle_denominator = 0;

    FAPI_TRY(mss::mrw_mem_m_dram_clocks(l_throttle_denominator) );
    FAPI_TRY(mss::mrw_safemode_mem_throttled_n_commands_per_port(l_throttle_per_slot) );
    FAPI_TRY(mss::getScom(i_target, MCA_MBA_FARB4Q, l_data) );

    l_data.insertFromRight<TT::EMERGENCY_M, TT::EMERGENCY_M_LEN>(l_throttle_denominator);
    l_data.insertFromRight<TT::EMERGENCY_N, TT::EMERGENCY_N_LEN>(l_throttle_per_slot);
    FAPI_TRY(mss::putScom(i_target, MCA_MBA_FARB4Q, l_data) );
    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    FAPI_ERR("Error setting safemode throttles for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the runtime throttle register to safemode values
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBA_FARB3Q
/// @Will be overwritten by OCC/cronus later in IPL
/// @called in thermal_init
///
fapi2::ReturnCode set_runtime_throttles_to_safe(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;

    fapi2::buffer<uint64_t> l_data;
    uint16_t l_throttle_per_port = 0;
    uint32_t l_throttle_denominator = 0;

    FAPI_TRY(mss::mrw_mem_m_dram_clocks(l_throttle_denominator) );
    FAPI_TRY(mss::mrw_safemode_mem_throttled_n_commands_per_port(l_throttle_per_port) );
    FAPI_TRY(mss::getScom(i_target, MCA_MBA_FARB3Q, l_data) );

    //Same value for both throttles
    l_data.insertFromRight<TT::RUNTIME_N_SLOT, TT::RUNTIME_N_SLOT_LEN>(l_throttle_per_port);
    l_data.insertFromRight<TT::RUNTIME_N_PORT, TT::RUNTIME_N_PORT_LEN>(l_throttle_per_port);
    l_data.insertFromRight<TT::RUNTIME_M, TT::RUNTIME_M_LEN>(l_throttle_denominator);

    FAPI_TRY(mss::putScom(i_target, MCA_MBA_FARB3Q, l_data) );
    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    FAPI_ERR("Error setting safemode throttles for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief safemode throttle values defined from MRW attributes
/// @param[in] i_target the MCA target
/// @return fapi2::FAPI2_RC_SUCCESS if ok
/// @note sets safemode values for emergency mode and regular throttling
///
fapi2::ReturnCode thermal_throttle_scominit (const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    FAPI_TRY(set_pwr_cntrl_reg(i_target));
    FAPI_TRY(set_str_reg(i_target));
    FAPI_TRY(set_nm_support(i_target));
    FAPI_TRY(set_safemode_throttles(i_target));

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting scominits for power_thermal values");
    return fapi2::current_err;
}

///
/// @brief disable emergency mode throttle for thermal_init
/// @param[in] i_target the mcs target
/// @return fapi2::fapi2_rc_success if ok
/// @note clears mcmode0_enable_emer_throttle bit in mcsmode0
///
fapi2::ReturnCode disable_emergency_throttle (const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target)
{
    fapi2::buffer<uint64_t> l_data;

    FAPI_TRY(mss::getScom(i_target, MCS_MCMODE0, l_data));
    l_data.clearBit<MCS_MCMODE0_ENABLE_EMER_THROTTLE>();
    FAPI_TRY(mss::putScom(i_target, MCS_MCMODE0, l_data));

fapi_try_exit:
    return fapi2::current_err;
}

} // namespace mc

} //close namespace mss
<commit_msg>Change power controll settings for PD/STR<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/mc/mc.C $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2017                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mc.C
/// @brief Subroutines to manipulate the memory controller
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Jacob Harvey <jlharvey@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#include <fapi2.H>
#include <lib/utils/dump_regs.H>
#include <lib/mc/mc.H>
#include <generic/memory/lib/utils/find.H>

using fapi2::TARGET_TYPE_MCA;
using fapi2::TARGET_TYPE_MCS;

namespace mss
{

///
/// @brief Dump the registers of the MC (MCA_MBA, MCS)
/// @param[in] i_target the MCS target
/// @return fapi2::FAPI2_RC_SUCCESS if ok
///
template<>
fapi2::ReturnCode dump_regs( const fapi2::Target<TARGET_TYPE_MCS>& i_target )
{
    return fapi2::FAPI2_RC_SUCCESS;
}

namespace mc
{

enum throttle_enums
{
    NM_RAS_WEIGHT = 0b000,
    NM_CAS_WEIGHT = 0b001,

    CHANGE_AFTER_SYNC_ON = 0b1,
    CHANGE_AFTER_SYNC_OFF = 0b0,

    MAXALL_MINALL = 0b000,

    // Wait 959 refresh intervals of idle before powering down all ranks
    MIN_DOMAIN_REDUCTION_TIME = 959,

    // Wait 1023 refresh intervals of idle before going into STR on all ranks
    // 1023 is the max allowed value
    ENTER_STR_TIME = 1023
};

///
/// @brief set the PWR CNTRL register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MCA_MBARPC0Q
///
fapi2::ReturnCode set_pwr_cntrl_reg(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;
    uint8_t l_pwr_cntrl = 0;
    fapi2::buffer<uint64_t> l_data;

    FAPI_TRY(mrw_power_control_requested(l_pwr_cntrl));
    FAPI_TRY(mss::getScom(i_target, MCA_MBARPC0Q, l_data));

    l_data.insertFromRight<TT::CFG_MIN_MAX_DOMAINS, TT::CFG_MIN_MAX_DOMAINS_LEN>(MAXALL_MINALL);

    //Write data if PWR_CNTRL_REQUESTED includes power down
    switch (l_pwr_cntrl)
    {
        case PD_AND_STR:
        case POWER_DOWN:
        case PD_AND_STR_CLK_STOP:
            l_data.setBit<TT::MIN_DOMAIN_REDUCTION_ENABLE>();
            break;

        case PD_AND_STR_OFF:
            l_data.clearBit<TT::MIN_DOMAIN_REDUCTION_ENABLE>();
            break;

        default:
            // Chief system engineer would reaally have to mess up here since _OFF is 0
            FAPI_ERR("ATTR_MSS_MRW_POWER_CONTROL_REQUESTED not set correctly in MRW");
            fapi2::Assert(false);
            break;
    }

    //Set the MIN_DOMAIN_REDUCTION time
    l_data.insertFromRight<TT::MIN_DOMAIN_REDUCTION_TIME, TT::MIN_DOMAIN_REDUCTION_TIME_LEN>
    (MIN_DOMAIN_REDUCTION_TIME);

    FAPI_TRY(mss::putScom(i_target, MCA_MBARPC0Q, l_data) );

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting power control register MBARPC0Q for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the STR register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBASTR0Q
///
fapi2::ReturnCode set_str_reg(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;
    uint8_t l_str_enable = 0;
    fapi2::buffer<uint64_t> l_data;

    FAPI_TRY(mrw_power_control_requested(l_str_enable));
    FAPI_TRY(mss::getScom(i_target, MCA_MBASTR0Q, l_data));

    //Write bit if STR should be enabled
    switch (l_str_enable)
    {
        case PD_AND_STR:
        case PD_AND_STR_CLK_STOP:
            l_data.setBit<TT::CFG_STR_ENABLE>();
            break;

        case POWER_DOWN:
        case PD_AND_STR_OFF:
            l_data.clearBit<TT::CFG_STR_ENABLE>();
            break;

        default:
            // System engineer would reaally have to mess up here since _OFF is 0
            FAPI_ERR("ATTR_MSS_MRW_POWER_CONTROL_REQUESTED not set correctly in MRW");
            fapi2::Assert(false);
            break;
    }

    // MCA_MBASTR0Q_CFG_DIS_CLK_IN_STR:  Set to 1 for PD_AND_STR_CLK_STOP, otherwise clear the bit
    l_data.writeBit<MCA_MBASTR0Q_CFG_DIS_CLK_IN_STR>(l_str_enable == PD_AND_STR_CLK_STOP);

    l_data.insertFromRight<TT::ENTER_STR_TIME_POS, TT::ENTER_STR_TIME_LEN>(ENTER_STR_TIME);

    FAPI_TRY(mss::putScom(i_target, MCA_MBASTR0Q, l_data) );

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting the STR register MBASTR0Q for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the general N/M throttle register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBA_FARB3Q
///

fapi2::ReturnCode set_nm_support (const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;

    uint16_t l_run_slot = 0;
    uint16_t l_run_port = 0;
    uint32_t l_throttle_denominator = 0;

    fapi2::buffer<uint64_t> l_data;

    //runtime should be calculated in eff_config_thermal, which is called before scominit in ipl
    FAPI_TRY(runtime_mem_throttled_n_commands_per_port(i_target, l_run_port));
    FAPI_TRY(runtime_mem_throttled_n_commands_per_slot(i_target, l_run_slot));
    FAPI_TRY(mss::mrw_mem_m_dram_clocks(l_throttle_denominator) );
    FAPI_INF("For target %s throttled n commands per port are %d, per slot are %d, and dram m clocks are %d",
             mss::c_str(i_target),
             l_run_port,
             l_run_slot,
             l_throttle_denominator);
    FAPI_TRY(mss::getScom(i_target, MCA_MBA_FARB3Q, l_data));

    l_data.insertFromRight<TT::RUNTIME_N_SLOT, TT::RUNTIME_N_SLOT_LEN>(l_run_slot);
    l_data.insertFromRight<TT::RUNTIME_N_PORT, TT::RUNTIME_N_PORT_LEN>(l_run_port);
    l_data.insertFromRight<TT::RUNTIME_M, TT::RUNTIME_M_LEN>(l_throttle_denominator);
    l_data.insertFromRight<TT::RAS_WEIGHT_POS, TT::RAS_WEIGHT_LEN>(NM_RAS_WEIGHT);
    l_data.insertFromRight<TT::CAS_WEIGHT_POS, TT::CAS_WEIGHT_LEN>(NM_CAS_WEIGHT);

    // If set, changes to cfg_nm_n_per_slot, cfg_nm_n_per_port, cfg_nm_m, min_max_domains will only be applied after a pc_sync command is seen
    // Set to disable permanently due to hardware design bug (HW403028) that won't be changed
    l_data.writeBit<TT::NM_CHANGE_AFTER_SYNC>(CHANGE_AFTER_SYNC_OFF);

    FAPI_TRY(mss::putScom(i_target, MCA_MBA_FARB3Q, l_data) );

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting nm throttles for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the safemode throttle register
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBA_FARB4Q
/// @note used to set throttle window (N throttles  / M clocks)
///
fapi2::ReturnCode set_safemode_throttles(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;

    fapi2::buffer<uint64_t> l_data;
    uint16_t l_throttle_per_slot = 0;
    uint32_t l_throttle_denominator = 0;

    FAPI_TRY(mss::mrw_mem_m_dram_clocks(l_throttle_denominator) );
    FAPI_TRY(mss::mrw_safemode_mem_throttled_n_commands_per_port(l_throttle_per_slot) );
    FAPI_TRY(mss::getScom(i_target, MCA_MBA_FARB4Q, l_data) );

    l_data.insertFromRight<TT::EMERGENCY_M, TT::EMERGENCY_M_LEN>(l_throttle_denominator);
    l_data.insertFromRight<TT::EMERGENCY_N, TT::EMERGENCY_N_LEN>(l_throttle_per_slot);
    FAPI_TRY(mss::putScom(i_target, MCA_MBA_FARB4Q, l_data) );
    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    FAPI_ERR("Error setting safemode throttles for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief set the runtime throttle register to safemode values
/// @param[in] i_target the mca target
/// @return fapi2::fapi2_rc_success if ok
/// @note sets MCA_MBA_FARB3Q
/// @Will be overwritten by OCC/cronus later in IPL
/// @called in thermal_init
///
fapi2::ReturnCode set_runtime_throttles_to_safe(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    typedef mss::mcTraits<fapi2::TARGET_TYPE_MCA> TT;

    fapi2::buffer<uint64_t> l_data;
    uint16_t l_throttle_per_port = 0;
    uint32_t l_throttle_denominator = 0;

    FAPI_TRY(mss::mrw_mem_m_dram_clocks(l_throttle_denominator) );
    FAPI_TRY(mss::mrw_safemode_mem_throttled_n_commands_per_port(l_throttle_per_port) );
    FAPI_TRY(mss::getScom(i_target, MCA_MBA_FARB3Q, l_data) );

    //Same value for both throttles
    l_data.insertFromRight<TT::RUNTIME_N_SLOT, TT::RUNTIME_N_SLOT_LEN>(l_throttle_per_port);
    l_data.insertFromRight<TT::RUNTIME_N_PORT, TT::RUNTIME_N_PORT_LEN>(l_throttle_per_port);
    l_data.insertFromRight<TT::RUNTIME_M, TT::RUNTIME_M_LEN>(l_throttle_denominator);

    FAPI_TRY(mss::putScom(i_target, MCA_MBA_FARB3Q, l_data) );
    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    FAPI_ERR("Error setting safemode throttles for target %s", mss::c_str(i_target));
    return fapi2::current_err;
}

///
/// @brief safemode throttle values defined from MRW attributes
/// @param[in] i_target the MCA target
/// @return fapi2::FAPI2_RC_SUCCESS if ok
/// @note sets safemode values for emergency mode and regular throttling
///
fapi2::ReturnCode thermal_throttle_scominit (const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target)
{
    FAPI_TRY(set_pwr_cntrl_reg(i_target));
    FAPI_TRY(set_str_reg(i_target));
    FAPI_TRY(set_nm_support(i_target));
    FAPI_TRY(set_safemode_throttles(i_target));

    return fapi2::FAPI2_RC_SUCCESS;
fapi_try_exit:
    FAPI_ERR("Error setting scominits for power_thermal values");
    return fapi2::current_err;
}

///
/// @brief disable emergency mode throttle for thermal_init
/// @param[in] i_target the mcs target
/// @return fapi2::fapi2_rc_success if ok
/// @note clears mcmode0_enable_emer_throttle bit in mcsmode0
///
fapi2::ReturnCode disable_emergency_throttle (const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target)
{
    fapi2::buffer<uint64_t> l_data;

    FAPI_TRY(mss::getScom(i_target, MCS_MCMODE0, l_data));
    l_data.clearBit<MCS_MCMODE0_ENABLE_EMER_THROTTLE>();
    FAPI_TRY(mss::putScom(i_target, MCS_MCMODE0, l_data));

fapi_try_exit:
    return fapi2::current_err;
}

} // namespace mc

} //close namespace mss
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/perv/p9_ram_core.H $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
//-----------------------------------------------------------------------------------
///
/// @file p9_ram_core.H
/// @brief Class that implements the base ramming capability
///
//-----------------------------------------------------------------------------------
// *HWP HWP Owner        : Liu Yang Fan <shliuyf@cn.ibm.com>
// *HWP HWP Backup Owner : Gou Peng Fei <shgoupf@cn.ibm.com>
// *HWP FW Owner         : Sachin Gupta <sgupta2m@in.ibm.com>
// *HWP Team             : Perv
// *HWP Level            : 2
// *HWP Consumed by      : SBE
//-----------------------------------------------------------------------------------

#ifndef _P9_RAM_CORE_H_
#define _P9_RAM_CORE_H_

//-----------------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------------
#include <fapi2.H>

//-----------------------------------------------------------------------------------
// Structure definitions
//-----------------------------------------------------------------------------------
// register access type
enum Enum_RegType
{
    REG_GPR,
    REG_SPR,
    REG_FPR,
    REG_VSR
};

class RamCore
{
    public:
//-----------------------------------------------------------------------------------
// Function prototype
//-----------------------------------------------------------------------------------
/// @brief Constructor of the class that implements the base ramming capability
/// @param[in] i_target => core target
/// @param[in] i_thread => thread number
//
        RamCore(const fapi2::Target<fapi2::TARGET_TYPE_CORE>& i_target, const uint8_t i_thread);

//-----------------------------------------------------------------------------------
/// @brief Destructor of the class that implements the base ramming capability
//
        ~RamCore();

//-----------------------------------------------------------------------------------
/// @brief Enable RAM mode and backup the registers(SCR0/GPR0/GPR1) that will be destroyed later during ramming
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode ram_setup();

//-----------------------------------------------------------------------------------
/// @brief Perform the ram and check ram is done
/// @param[in] i_opcode     => opcode to ram
/// @param[in] i_allow_mult => indicate whether to setup and cleanup
///                            true: only perform ram, not to call ram_setup and ram_cleanup
///                            false: call ram_setup and ram_cleanup
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode ram_opcode(const uint32_t i_opcode, const bool i_allow_mult = false);

//-----------------------------------------------------------------------------------
/// @brief Clear RAM mode and restore the backup registers
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode ram_cleanup();

//-----------------------------------------------------------------------------------
/// @brief Get a register value by ramming
/// @param[in] i_type       => register type (REG_SPR/REG_GPR/REG_FPR/REG_VSR)
/// @param[in] i_reg_num    => register nubmer
/// @param[out] o_buffer    => register value
/// @param[in] i_allow_mult => indicate whether to setup and cleanup
///                            true: only perform ram, not to call ram_setup and ram_cleanup
///                            false: call ram_setup and ram_cleanup
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode get_reg(const Enum_RegType i_type, const uint32_t i_reg_num, fapi2::buffer<uint64_t>* o_buffer,
                                  const bool i_allow_mult = false);

//-----------------------------------------------------------------------------------
/// @brief Put a register value by ramming
/// @param[in] i_type       => register type (REG_SPR/REG_GPR/REG_FPR/REG_VSR)
/// @param[in] i_reg_num    => register nubmer
/// @param[in] i_buffer     => register value
/// @param[in] i_allow_mult => indicate whether to setup and cleanup
///                            true: only perform ram, not to call ram_setup and ram_cleanup
///                            false: call ram_setup and ram_cleanup
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode put_reg(const Enum_RegType i_type, const uint32_t i_reg_num, const fapi2::buffer<uint64_t>* i_buffer,
                                  const bool i_allow_mult = false);

//-----------------------------------------------------------------------------------
/// @brief Generate predecode for the opcode to ramming
/// @param[in] i_opcode => opcode to ram
/// @return the predecode
//
        uint8_t gen_predecode(const uint32_t i_opcode);

//-----------------------------------------------------------------------------------
/// @brief Check the opcode is load/store or not
/// @param[in] i_opcode => opcode to ram
/// @return TRUE if it is load/store
//
        bool is_load_store(const uint32_t i_opcode);

    private:
        fapi2::Target<fapi2::TARGET_TYPE_CORE> iv_target;  // core target
        uint8_t iv_thread;  // thread number
        bool iv_ram_enable; // ram mode is enabled
        bool iv_ram_setup;  // ram mode is enabled and register backup is done
        bool iv_write_gpr0; // putGPR0 operation is executed
        bool iv_write_gpr1; // putGPR1 operatoin is executed
        fapi2::buffer<uint64_t> iv_backup_buf0;  // register backup data
        fapi2::buffer<uint64_t> iv_backup_buf1;  // register backup data
        fapi2::buffer<uint64_t> iv_backup_buf2;  // register backup data
};

#endif //_P9_RAM_CORE_H_
<commit_msg>Update RAM procedures.<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/perv/p9_ram_core.H $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
//-----------------------------------------------------------------------------------
///
/// @file p9_ram_core.H
/// @brief Class that implements the base ramming capability
///
//-----------------------------------------------------------------------------------
// *HWP HWP Owner        : Liu Yang Fan <shliuyf@cn.ibm.com>
// *HWP HWP Backup Owner : Gou Peng Fei <shgoupf@cn.ibm.com>
// *HWP FW Owner         : Thi Tran <thi@us.ibm.com>
// *HWP Team             : Perv
// *HWP Level            : 2
// *HWP Consumed by      : SBE
//-----------------------------------------------------------------------------------

#ifndef _P9_RAM_CORE_H_
#define _P9_RAM_CORE_H_

//-----------------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------------
#include <fapi2.H>

//-----------------------------------------------------------------------------------
// Structure definitions
//-----------------------------------------------------------------------------------
// register access type
enum Enum_RegType
{
    REG_GPR,
    REG_SPR,
    REG_FPR,
    REG_VSR
};

class RamCore
{
    public:
//-----------------------------------------------------------------------------------
// Function prototype
//-----------------------------------------------------------------------------------
/// @brief Constructor of the class that implements the base ramming capability
/// @param[in] i_target => core target
/// @param[in] i_thread => thread number
//
        RamCore(const fapi2::Target<fapi2::TARGET_TYPE_CORE>& i_target, const uint8_t i_thread);

//-----------------------------------------------------------------------------------
/// @brief Destructor of the class that implements the base ramming capability
//
        ~RamCore();

//-----------------------------------------------------------------------------------
/// @brief Enable RAM mode and backup the registers(SCR0/GPR0/GPR1) that will be destroyed later during ramming
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode ram_setup();

//-----------------------------------------------------------------------------------
/// @brief Perform the ram and check ram is done
/// @param[in] i_opcode     => opcode to ram
/// @param[in] i_allow_mult => indicate whether to setup and cleanup
///                            true: only perform ram, not to call ram_setup and ram_cleanup
///                            false: call ram_setup and ram_cleanup
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode ram_opcode(const uint32_t i_opcode, const bool i_allow_mult = false);

//-----------------------------------------------------------------------------------
/// @brief Clear RAM mode and restore the backup registers
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode ram_cleanup();

//-----------------------------------------------------------------------------------
/// @brief Get a register value by ramming
/// @param[in] i_type       => register type (REG_SPR/REG_GPR/REG_FPR/REG_VSR)
/// @param[in] i_reg_num    => register nubmer
/// @param[out] o_buffer    => register value
/// @param[in] i_allow_mult => indicate whether to setup and cleanup
///                            true: only perform ram, not to call ram_setup and ram_cleanup
///                            false: call ram_setup and ram_cleanup
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode get_reg(const Enum_RegType i_type, const uint32_t i_reg_num, fapi2::buffer<uint64_t>* o_buffer,
                                  const bool i_allow_mult = false);

//-----------------------------------------------------------------------------------
/// @brief Put a register value by ramming
/// @param[in] i_type       => register type (REG_SPR/REG_GPR/REG_FPR/REG_VSR)
/// @param[in] i_reg_num    => register nubmer
/// @param[in] i_buffer     => register value
/// @param[in] i_allow_mult => indicate whether to setup and cleanup
///                            true: only perform ram, not to call ram_setup and ram_cleanup
///                            false: call ram_setup and ram_cleanup
/// @return FAPI2_RC_SUCCESS if success, else error code.
//
        fapi2::ReturnCode put_reg(const Enum_RegType i_type, const uint32_t i_reg_num, const fapi2::buffer<uint64_t>* i_buffer,
                                  const bool i_allow_mult = false);

//-----------------------------------------------------------------------------------
/// @brief Generate predecode for the opcode to ramming
/// @param[in] i_opcode => opcode to ram
/// @return the predecode
//
        uint8_t gen_predecode(const uint32_t i_opcode);

//-----------------------------------------------------------------------------------
/// @brief Check the opcode is load/store or not
/// @param[in] i_opcode => opcode to ram
/// @return TRUE if it is load/store
//
        bool is_load_store(const uint32_t i_opcode);

    private:
        fapi2::Target<fapi2::TARGET_TYPE_CORE> iv_target;  // core target
        uint8_t iv_thread;     // thread number
        bool iv_ram_enable;    // ram mode is enabled
        bool iv_ram_scr0_save; // SCR0 is saved when setup
        bool iv_ram_setup;     // ram mode is enabled and register backup is done
        bool iv_ram_err;       // error happened during ram
        bool iv_write_gpr0;    // putGPR0 operation is executed
        bool iv_write_gpr1;    // putGPR1 operatoin is executed
        fapi2::buffer<uint64_t> iv_backup_buf0;  // register backup data
        fapi2::buffer<uint64_t> iv_backup_buf1;  // register backup data
        fapi2::buffer<uint64_t> iv_backup_buf2;  // register backup data
};

#endif //_P9_RAM_CORE_H_
<|endoftext|>


<commit_before>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkBenchmark.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkPaint.h"
#include "SkRandom.h"
#include "SkShader.h"
#include "SkString.h"

enum Flags {
    kStroke_Flag = 1 << 0,
    kBig_Flag    = 1 << 1
};

#define FLAGS00  Flags(0)
#define FLAGS01  Flags(kStroke_Flag)
#define FLAGS10  Flags(kBig_Flag)
#define FLAGS11  Flags(kStroke_Flag | kBig_Flag)

class PathBench : public SkBenchmark {
    SkPaint     fPaint;
    SkString    fName;
    Flags       fFlags;
    enum { N = SkBENCHLOOP(1000) };
public:
    PathBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) {
        fPaint.setStyle(flags & kStroke_Flag ? SkPaint::kStroke_Style :
                        SkPaint::kFill_Style);
        fPaint.setStrokeWidth(SkIntToScalar(5));
        fPaint.setStrokeJoin(SkPaint::kBevel_Join);
    }

    virtual void appendName(SkString*) = 0;
    virtual void makePath(SkPath*) = 0;
    virtual int complexity() { return 0; }

protected:
    virtual const char* onGetName() {
        fName.printf("path_%s_%s_",
                     fFlags & kStroke_Flag ? "stroke" : "fill",
                     fFlags & kBig_Flag ? "big" : "small");
        this->appendName(&fName);
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkPaint paint(fPaint);
        this->setupPaint(&paint);

        SkPath path;
        this->makePath(&path);
        if (fFlags & kBig_Flag) {
            SkMatrix m;
            m.setScale(SkIntToScalar(10), SkIntToScalar(10));
            path.transform(m);
        }

        int count = N;
        if (fFlags & kBig_Flag) {
            count >>= 2;
        }
        count >>= (3 * complexity());

        for (int i = 0; i < count; i++) {
            canvas->drawPath(path, paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

class TrianglePathBench : public PathBench {
public:
    TrianglePathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("triangle");
    }
    virtual void makePath(SkPath* path) {
        static const int gCoord[] = {
            10, 10, 15, 5, 20, 20
        };
        path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
        path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
        path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
        path->close();
    }
private:
    typedef PathBench INHERITED;
};

class RectPathBench : public PathBench {
public:
    RectPathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("rect");
    }
    virtual void makePath(SkPath* path) {
        SkRect r = { 10, 10, 20, 20 };
        path->addRect(r);
    }
private:
    typedef PathBench INHERITED;
};

class OvalPathBench : public PathBench {
public:
    OvalPathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("oval");
    }
    virtual void makePath(SkPath* path) {
        SkRect r = { 10, 10, 20, 20 };
        path->addOval(r);
    }
private:
    typedef PathBench INHERITED;
};

class SawToothPathBench : public PathBench {
public:
    SawToothPathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("sawtooth");
    }
    virtual void makePath(SkPath* path) {
        SkScalar x = SkIntToScalar(20);
        SkScalar y = SkIntToScalar(20);
        const SkScalar x0 = x;
        const SkScalar dx = SK_Scalar1 * 5;
        const SkScalar dy = SK_Scalar1 * 10;

        path->moveTo(x, y);
        for (int i = 0; i < 32; i++) {
            x += dx;
            path->lineTo(x, y - dy);
            x += dx;
            path->lineTo(x, y + dy);
        }
        path->lineTo(x, y + 2 * dy);
        path->lineTo(x0, y + 2 * dy);
        path->close();
    }
    virtual int complexity() { return 1; }
private:
    typedef PathBench INHERITED;
};

class LongCurvedPathBench : public PathBench {
public:
    LongCurvedPathBench(void * param, Flags flags)
        : INHERITED(param, flags) {
    }

    virtual void appendName(SkString* name) {
        name->append("long_curved");
    }
    virtual void makePath(SkPath* path) {
        SkRandom rand (12);
        int i;
        for (i = 0; i < 100; i++) {
            path->quadTo(SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)),
                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480)),
                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)),
                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480)));
        }
        path->close();
    }
    virtual int complexity() { return 2; }
private:
    typedef PathBench INHERITED;
};

static SkBenchmark* FactT00(void* p) { return new TrianglePathBench(p, FLAGS00); }
static SkBenchmark* FactT01(void* p) { return new TrianglePathBench(p, FLAGS01); }
static SkBenchmark* FactT10(void* p) { return new TrianglePathBench(p, FLAGS10); }
static SkBenchmark* FactT11(void* p) { return new TrianglePathBench(p, FLAGS11); }

static SkBenchmark* FactR00(void* p) { return new RectPathBench(p, FLAGS00); }
static SkBenchmark* FactR01(void* p) { return new RectPathBench(p, FLAGS01); }
static SkBenchmark* FactR10(void* p) { return new RectPathBench(p, FLAGS10); }
static SkBenchmark* FactR11(void* p) { return new RectPathBench(p, FLAGS11); }

static SkBenchmark* FactO00(void* p) { return new OvalPathBench(p, FLAGS00); }
static SkBenchmark* FactO01(void* p) { return new OvalPathBench(p, FLAGS01); }
static SkBenchmark* FactO10(void* p) { return new OvalPathBench(p, FLAGS10); }
static SkBenchmark* FactO11(void* p) { return new OvalPathBench(p, FLAGS11); }

static SkBenchmark* FactS00(void* p) { return new SawToothPathBench(p, FLAGS00); }
static SkBenchmark* FactS01(void* p) { return new SawToothPathBench(p, FLAGS01); }

static SkBenchmark* FactLC00(void* p) {
    return new LongCurvedPathBench(p, FLAGS00);
}
static SkBenchmark* FactLC01(void* p) {
    return new LongCurvedPathBench(p, FLAGS01);
}

static BenchRegistry gRegT00(FactT00);
static BenchRegistry gRegT01(FactT01);
static BenchRegistry gRegT10(FactT10);
static BenchRegistry gRegT11(FactT11);

static BenchRegistry gRegR00(FactR00);
static BenchRegistry gRegR01(FactR01);
static BenchRegistry gRegR10(FactR10);
static BenchRegistry gRegR11(FactR11);

static BenchRegistry gRegO00(FactO00);
static BenchRegistry gRegO01(FactO01);
static BenchRegistry gRegO10(FactO10);
static BenchRegistry gRegO11(FactO11);

static BenchRegistry gRegS00(FactS00);
static BenchRegistry gRegS01(FactS01);

static BenchRegistry gRegLC00(FactLC00);
static BenchRegistry gRegLC01(FactLC01);

<commit_msg>Add a wide stroked line path benchmark to bench.  It uses the same stroke width and endcap style as the stroked curved path.<commit_after>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkBenchmark.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkPaint.h"
#include "SkRandom.h"
#include "SkShader.h"
#include "SkString.h"

enum Flags {
    kStroke_Flag = 1 << 0,
    kBig_Flag    = 1 << 1
};

#define FLAGS00  Flags(0)
#define FLAGS01  Flags(kStroke_Flag)
#define FLAGS10  Flags(kBig_Flag)
#define FLAGS11  Flags(kStroke_Flag | kBig_Flag)

class PathBench : public SkBenchmark {
    SkPaint     fPaint;
    SkString    fName;
    Flags       fFlags;
    enum { N = SkBENCHLOOP(1000) };
public:
    PathBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) {
        fPaint.setStyle(flags & kStroke_Flag ? SkPaint::kStroke_Style :
                        SkPaint::kFill_Style);
        fPaint.setStrokeWidth(SkIntToScalar(5));
        fPaint.setStrokeJoin(SkPaint::kBevel_Join);
    }

    virtual void appendName(SkString*) = 0;
    virtual void makePath(SkPath*) = 0;
    virtual int complexity() { return 0; }

protected:
    virtual const char* onGetName() {
        fName.printf("path_%s_%s_",
                     fFlags & kStroke_Flag ? "stroke" : "fill",
                     fFlags & kBig_Flag ? "big" : "small");
        this->appendName(&fName);
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkPaint paint(fPaint);
        this->setupPaint(&paint);

        SkPath path;
        this->makePath(&path);
        if (fFlags & kBig_Flag) {
            SkMatrix m;
            m.setScale(SkIntToScalar(10), SkIntToScalar(10));
            path.transform(m);
        }

        int count = N;
        if (fFlags & kBig_Flag) {
            count >>= 2;
        }
        count >>= (3 * complexity());

        for (int i = 0; i < count; i++) {
            canvas->drawPath(path, paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

class TrianglePathBench : public PathBench {
public:
    TrianglePathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("triangle");
    }
    virtual void makePath(SkPath* path) {
        static const int gCoord[] = {
            10, 10, 15, 5, 20, 20
        };
        path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
        path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
        path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
        path->close();
    }
private:
    typedef PathBench INHERITED;
};

class RectPathBench : public PathBench {
public:
    RectPathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("rect");
    }
    virtual void makePath(SkPath* path) {
        SkRect r = { 10, 10, 20, 20 };
        path->addRect(r);
    }
private:
    typedef PathBench INHERITED;
};

class OvalPathBench : public PathBench {
public:
    OvalPathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("oval");
    }
    virtual void makePath(SkPath* path) {
        SkRect r = { 10, 10, 20, 20 };
        path->addOval(r);
    }
private:
    typedef PathBench INHERITED;
};

class SawToothPathBench : public PathBench {
public:
    SawToothPathBench(void* param, Flags flags) : INHERITED(param, flags) {}

    virtual void appendName(SkString* name) {
        name->append("sawtooth");
    }
    virtual void makePath(SkPath* path) {
        SkScalar x = SkIntToScalar(20);
        SkScalar y = SkIntToScalar(20);
        const SkScalar x0 = x;
        const SkScalar dx = SK_Scalar1 * 5;
        const SkScalar dy = SK_Scalar1 * 10;

        path->moveTo(x, y);
        for (int i = 0; i < 32; i++) {
            x += dx;
            path->lineTo(x, y - dy);
            x += dx;
            path->lineTo(x, y + dy);
        }
        path->lineTo(x, y + 2 * dy);
        path->lineTo(x0, y + 2 * dy);
        path->close();
    }
    virtual int complexity() { return 1; }
private:
    typedef PathBench INHERITED;
};

class LongCurvedPathBench : public PathBench {
public:
    LongCurvedPathBench(void * param, Flags flags)
        : INHERITED(param, flags) {
    }

    virtual void appendName(SkString* name) {
        name->append("long_curved");
    }
    virtual void makePath(SkPath* path) {
        SkRandom rand (12);
        int i;
        for (i = 0; i < 100; i++) {
            path->quadTo(SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)),
                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480)),
                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)),
                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480)));
        }
        path->close();
    }
    virtual int complexity() { return 2; }
private:
    typedef PathBench INHERITED;
};

class LongLinePathBench : public PathBench {
public:
    LongLinePathBench(void * param, Flags flags)
        : INHERITED(param, flags) {
    }

    virtual void appendName(SkString* name) {
        name->append("long_line");
    }
    virtual void makePath(SkPath* path) {
        SkRandom rand;
        path->moveTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
        for (size_t i = 1; i < 100; i++) {
            path->lineTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
        }
    }
    virtual int complexity() { return 2; }
private:
    typedef PathBench INHERITED;
};

static SkBenchmark* FactT00(void* p) { return new TrianglePathBench(p, FLAGS00); }
static SkBenchmark* FactT01(void* p) { return new TrianglePathBench(p, FLAGS01); }
static SkBenchmark* FactT10(void* p) { return new TrianglePathBench(p, FLAGS10); }
static SkBenchmark* FactT11(void* p) { return new TrianglePathBench(p, FLAGS11); }

static SkBenchmark* FactR00(void* p) { return new RectPathBench(p, FLAGS00); }
static SkBenchmark* FactR01(void* p) { return new RectPathBench(p, FLAGS01); }
static SkBenchmark* FactR10(void* p) { return new RectPathBench(p, FLAGS10); }
static SkBenchmark* FactR11(void* p) { return new RectPathBench(p, FLAGS11); }

static SkBenchmark* FactO00(void* p) { return new OvalPathBench(p, FLAGS00); }
static SkBenchmark* FactO01(void* p) { return new OvalPathBench(p, FLAGS01); }
static SkBenchmark* FactO10(void* p) { return new OvalPathBench(p, FLAGS10); }
static SkBenchmark* FactO11(void* p) { return new OvalPathBench(p, FLAGS11); }

static SkBenchmark* FactS00(void* p) { return new SawToothPathBench(p, FLAGS00); }
static SkBenchmark* FactS01(void* p) { return new SawToothPathBench(p, FLAGS01); }

static SkBenchmark* FactLC00(void* p) {
    return new LongCurvedPathBench(p, FLAGS00);
}
static SkBenchmark* FactLC01(void* p) {
    return new LongCurvedPathBench(p, FLAGS01);
}

static SkBenchmark* FactLL00(void* p) {
    return new LongLinePathBench(p, FLAGS00);
}

static SkBenchmark* FactLL01(void* p) {
    return new LongLinePathBench(p, FLAGS01);
}

static BenchRegistry gRegT00(FactT00);
static BenchRegistry gRegT01(FactT01);
static BenchRegistry gRegT10(FactT10);
static BenchRegistry gRegT11(FactT11);

static BenchRegistry gRegR00(FactR00);
static BenchRegistry gRegR01(FactR01);
static BenchRegistry gRegR10(FactR10);
static BenchRegistry gRegR11(FactR11);

static BenchRegistry gRegO00(FactO00);
static BenchRegistry gRegO01(FactO01);
static BenchRegistry gRegO10(FactO10);
static BenchRegistry gRegO11(FactO11);

static BenchRegistry gRegS00(FactS00);
static BenchRegistry gRegS01(FactS01);

static BenchRegistry gRegLC00(FactLC00);
static BenchRegistry gRegLC01(FactLC01);

static BenchRegistry gRegLL00(FactLL00);
static BenchRegistry gRegLL01(FactLL01);

<|endoftext|>