<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief task for http communication
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2011 triagens GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Achim Brandt
/// @author Copyright 2009-2011, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "HttpCommTask.h"

#include <Basics/StringUtils.h>
#include <Rest/HttpRequest.h>
#include <Rest/HttpResponse.h>

#include "Scheduler/Scheduler.h"
#include "GeneralServer/GeneralFigures.h"
#include "HttpServer/HttpHandlerFactory.h"
#include "HttpServer/HttpHandler.h"
#include "HttpServer/HttpServerImpl.h"

using namespace triagens::basics;
using namespace triagens::rest::GeneralFigures;

namespace triagens {
  namespace rest {

    // -----------------------------------------------------------------------------
    // constructors and destructors
    // -----------------------------------------------------------------------------

    HttpCommTask::HttpCommTask (HttpServerImpl* server, socket_t fd, ConnectionInfo const& info)
      : Task("HttpCommTask"),
        GeneralCommTask<HttpServerImpl, HttpHandlerFactory>(server, fd, info), _handler(0) {
      incCounter<GeneralServerStatistics::httpAccessor>();
    }

    HttpCommTask::~HttpCommTask () {
      decCounter<GeneralServerStatistics::httpAccessor>();
      destroyHandler();
    }

    // -----------------------------------------------------------------------------
    // GeneralCommTask methods
    // -----------------------------------------------------------------------------

    bool HttpCommTask::processRead () {
      if (requestPending || readBuffer->c_str() == 0) {
        return true;
      }

      bool handleRequest = false;

      if (! readRequestBody) {
        const char * ptr = readBuffer->c_str() + readPosition;
        // TODO FIXME: HTTP request might be shorter than 4 bytes if malformed
        const char * end = readBuffer->end() - 3;

        // TODO FIXME: HTTP request might not contain \r\n\r\n at all if malformed
        for (;  ptr < end;  ptr++) {
          if (ptr[0] == '\r' && ptr[1] == '\n' && ptr[2] == '\r' && ptr[3] == '\n') {
            break;
          }
        }

        size_t headerLength = ptr - readBuffer->c_str();

        if (headerLength > maximalHeaderSize) {
          LOGGER_WARNING << "maximal header size is " << maximalHeaderSize << ", request header size is "
                         << headerLength;
          return false;
        }

        if (ptr < end) {
          readPosition = ptr - readBuffer->c_str() + 4;

          LOGGER_TRACE << "HTTP READ FOR " << static_cast<Task*>(this) << ":\n"
                       << string(readBuffer->c_str(), readPosition);

          // check that we know, how to serve this request
          request = server->createRequest(readBuffer->c_str(), readPosition);

          if (request == 0) {
            LOGGER_ERROR << "cannot generate request";
            return false;
          }

          // update the connection information, i. e. client and server addresses and ports
          request->setConnectionInfo(connectionInfo);

          LOGGER_TRACE << "server port = " << connectionInfo.serverPort << ", client port = " << connectionInfo.clientPort;

          // set body start to current position
          bodyPosition = readPosition;

          // and different methods
          switch (request->requestType()) {
            case HttpRequest::HTTP_REQUEST_GET:
            case HttpRequest::HTTP_REQUEST_DELETE:
            case HttpRequest::HTTP_REQUEST_HEAD:
              bodyLength = request->contentLength();

              if (bodyLength > 0) {
                LOGGER_WARNING << "received http GET/DELETE/HEAD request with body length, this should not happen";
                readRequestBody = true;
              }
              else {
                handleRequest = true;
              }
              break;

            case HttpRequest::HTTP_REQUEST_POST:
            case HttpRequest::HTTP_REQUEST_PUT:
              bodyLength = request->contentLength();

              if (bodyLength > 0) {
                readRequestBody = true;
              }
              else {
                handleRequest = true;
              }
              break;

            default:
              LOGGER_WARNING << "got corrupted HTTP request '" << string(readBuffer->c_str(), (readPosition < 6 ? readPosition : 6)) << "'";
              return false;
          }

          // check for a 100-continue
          if (readRequestBody) {
            bool found;
            string const& expect = request->header("expect", found);

            if (found && StringUtils::trim(expect) == "100-continue") {
              LOGGER_TRACE << "received a 100-continue request";

              StringBuffer* buffer = new StringBuffer(TRI_UNKNOWN_MEM_ZONE);
              buffer->appendText("HTTP/1.1 100 (Continue)\r\n\r\n");

              writeBuffers.push_back(buffer);
              fillWriteBuffer();
            }
          }
        }
        else {
          if (readBuffer->c_str() < end) {
            readPosition = end - readBuffer->c_str();
          }
        }
      }

      // readRequestBody might have changed, so cannot use else
      if (readRequestBody) {
        if (bodyLength > maximalBodySize) {
          LOGGER_WARNING << "maximal body size is " << maximalBodySize << ", request body size is " << bodyLength;
          return false;
        }

        if (readBuffer->length() - bodyPosition < bodyLength) {
          return true;
        }

        // read "bodyLength" from read buffer and add this body to "httpRequest"
        request->setBody(readBuffer->c_str() + bodyPosition, bodyLength);

        LOGGER_TRACE << string(readBuffer->c_str() + bodyPosition, bodyLength);

        // remove body from read buffer and reset read position
        readRequestBody = false;
        handleRequest = true;
      }

      // we have to delete request in here or pass it to a handler, which will delete it
      if (handleRequest) {
        readBuffer->erase_front(bodyPosition + bodyLength);

        requestPending = true;

        string connectionType = StringUtils::tolower(StringUtils::trim(request->header("connection")));

        if (connectionType == "close") {
          LOGGER_DEBUG << "connection close requested by client";
          closeRequested = true;
        }
        else if (server->getCloseWithoutKeepAlive() && connectionType != "keep-alive") {
          LOGGER_DEBUG << "no keep-alive, connection close requested by client";
          closeRequested = true;
        }

        readPosition = 0;
        bodyPosition = 0;
        bodyLength = 0;

         _handler = server->createHandler(request);
        bool ok = false;

        if (_handler == 0) {
          LOGGER_TRACE << "no handler is known, giving up";
          delete request;
          request = 0;

          HttpResponse response(HttpResponse::NOT_FOUND);
          handleResponse(&response);
        }
        else {
          // let the handler know the comm task
          _handler->setTask(this);

          request = 0;
          ok = server->handleRequest(this, _handler);

          if (! ok) {
            HttpResponse response(HttpResponse::SERVER_ERROR);
            handleResponse(&response);
          }
        }

        return processRead();
      }

      return true;
    }

    void HttpCommTask::addResponse (HttpResponse* response) {
      StringBuffer * buffer;

      // save header
      buffer = new StringBuffer(TRI_UNKNOWN_MEM_ZONE);
      response->writeHeader(buffer);
      buffer->appendText(response->body());

      writeBuffers.push_back(buffer);

      LOGGER_TRACE << "HTTP WRITE FOR " << static_cast<Task*>(this) << ":\n" << buffer->c_str();

      // clear body
      response->body().clear();

      // start output
      fillWriteBuffer();
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// @brief destroy the handler if any present
    ////////////////////////////////////////////////////////////////////////////////

    void HttpCommTask::destroyHandler () {
      if (_handler) {
        _handler->setTask(0);
        server->destroyHandler(_handler);
        _handler = 0;
      }
    }

  }
}
<commit_msg>removed bogus FIXME comments<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief task for http communication
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2011 triagens GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Achim Brandt
/// @author Copyright 2009-2011, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "HttpCommTask.h"

#include <Basics/StringUtils.h>
#include <Rest/HttpRequest.h>
#include <Rest/HttpResponse.h>

#include "Scheduler/Scheduler.h"
#include "GeneralServer/GeneralFigures.h"
#include "HttpServer/HttpHandlerFactory.h"
#include "HttpServer/HttpHandler.h"
#include "HttpServer/HttpServerImpl.h"

using namespace triagens::basics;
using namespace triagens::rest::GeneralFigures;

namespace triagens {
  namespace rest {

    // -----------------------------------------------------------------------------
    // constructors and destructors
    // -----------------------------------------------------------------------------

    HttpCommTask::HttpCommTask (HttpServerImpl* server, socket_t fd, ConnectionInfo const& info)
      : Task("HttpCommTask"),
        GeneralCommTask<HttpServerImpl, HttpHandlerFactory>(server, fd, info), _handler(0) {
      incCounter<GeneralServerStatistics::httpAccessor>();
    }

    HttpCommTask::~HttpCommTask () {
      decCounter<GeneralServerStatistics::httpAccessor>();
      destroyHandler();
    }

    // -----------------------------------------------------------------------------
    // GeneralCommTask methods
    // -----------------------------------------------------------------------------

    bool HttpCommTask::processRead () {
      if (requestPending || readBuffer->c_str() == 0) {
        return true;
      }

      bool handleRequest = false;

      if (! readRequestBody) {
        const char * ptr = readBuffer->c_str() + readPosition;
        const char * end = readBuffer->end() - 3;

        for (;  ptr < end;  ptr++) {
          if (ptr[0] == '\r' && ptr[1] == '\n' && ptr[2] == '\r' && ptr[3] == '\n') {
            break;
          }
        }

        size_t headerLength = ptr - readBuffer->c_str();

        if (headerLength > maximalHeaderSize) {
          LOGGER_WARNING << "maximal header size is " << maximalHeaderSize << ", request header size is "
                         << headerLength;
          return false;
        }

        if (ptr < end) {
          readPosition = ptr - readBuffer->c_str() + 4;

          LOGGER_TRACE << "HTTP READ FOR " << static_cast<Task*>(this) << ":\n"
                       << string(readBuffer->c_str(), readPosition);

          // check that we know, how to serve this request
          request = server->createRequest(readBuffer->c_str(), readPosition);

          if (request == 0) {
            LOGGER_ERROR << "cannot generate request";
            return false;
          }

          // update the connection information, i. e. client and server addresses and ports
          request->setConnectionInfo(connectionInfo);

          LOGGER_TRACE << "server port = " << connectionInfo.serverPort << ", client port = " << connectionInfo.clientPort;

          // set body start to current position
          bodyPosition = readPosition;

          // and different methods
          switch (request->requestType()) {
            case HttpRequest::HTTP_REQUEST_GET:
            case HttpRequest::HTTP_REQUEST_DELETE:
            case HttpRequest::HTTP_REQUEST_HEAD:
              bodyLength = request->contentLength();

              if (bodyLength > 0) {
                LOGGER_WARNING << "received http GET/DELETE/HEAD request with body length, this should not happen";
                readRequestBody = true;
              }
              else {
                handleRequest = true;
              }
              break;

            case HttpRequest::HTTP_REQUEST_POST:
            case HttpRequest::HTTP_REQUEST_PUT:
              bodyLength = request->contentLength();

              if (bodyLength > 0) {
                readRequestBody = true;
              }
              else {
                handleRequest = true;
              }
              break;

            default:
              LOGGER_WARNING << "got corrupted HTTP request '" << string(readBuffer->c_str(), (readPosition < 6 ? readPosition : 6)) << "'";
              return false;
          }

          // check for a 100-continue
          if (readRequestBody) {
            bool found;
            string const& expect = request->header("expect", found);

            if (found && StringUtils::trim(expect) == "100-continue") {
              LOGGER_TRACE << "received a 100-continue request";

              StringBuffer* buffer = new StringBuffer(TRI_UNKNOWN_MEM_ZONE);
              buffer->appendText("HTTP/1.1 100 (Continue)\r\n\r\n");

              writeBuffers.push_back(buffer);
              fillWriteBuffer();
            }
          }
        }
        else {
          if (readBuffer->c_str() < end) {
            readPosition = end - readBuffer->c_str();
          }
        }
      }

      // readRequestBody might have changed, so cannot use else
      if (readRequestBody) {
        if (bodyLength > maximalBodySize) {
          LOGGER_WARNING << "maximal body size is " << maximalBodySize << ", request body size is " << bodyLength;
          return false;
        }

        if (readBuffer->length() - bodyPosition < bodyLength) {
          return true;
        }

        // read "bodyLength" from read buffer and add this body to "httpRequest"
        request->setBody(readBuffer->c_str() + bodyPosition, bodyLength);

        LOGGER_TRACE << string(readBuffer->c_str() + bodyPosition, bodyLength);

        // remove body from read buffer and reset read position
        readRequestBody = false;
        handleRequest = true;
      }

      // we have to delete request in here or pass it to a handler, which will delete it
      if (handleRequest) {
        readBuffer->erase_front(bodyPosition + bodyLength);

        requestPending = true;

        string connectionType = StringUtils::tolower(StringUtils::trim(request->header("connection")));

        if (connectionType == "close") {
          LOGGER_DEBUG << "connection close requested by client";
          closeRequested = true;
        }
        else if (server->getCloseWithoutKeepAlive() && connectionType != "keep-alive") {
          LOGGER_DEBUG << "no keep-alive, connection close requested by client";
          closeRequested = true;
        }

        readPosition = 0;
        bodyPosition = 0;
        bodyLength = 0;

         _handler = server->createHandler(request);
        bool ok = false;

        if (_handler == 0) {
          LOGGER_TRACE << "no handler is known, giving up";
          delete request;
          request = 0;

          HttpResponse response(HttpResponse::NOT_FOUND);
          handleResponse(&response);
        }
        else {
          // let the handler know the comm task
          _handler->setTask(this);

          request = 0;
          ok = server->handleRequest(this, _handler);

          if (! ok) {
            HttpResponse response(HttpResponse::SERVER_ERROR);
            handleResponse(&response);
          }
        }

        return processRead();
      }

      return true;
    }

    void HttpCommTask::addResponse (HttpResponse* response) {
      StringBuffer * buffer;

      // save header
      buffer = new StringBuffer(TRI_UNKNOWN_MEM_ZONE);
      response->writeHeader(buffer);
      buffer->appendText(response->body());

      writeBuffers.push_back(buffer);

      LOGGER_TRACE << "HTTP WRITE FOR " << static_cast<Task*>(this) << ":\n" << buffer->c_str();

      // clear body
      response->body().clear();

      // start output
      fillWriteBuffer();
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// @brief destroy the handler if any present
    ////////////////////////////////////////////////////////////////////////////////

    void HttpCommTask::destroyHandler () {
      if (_handler) {
        _handler->setTask(0);
        server->destroyHandler(_handler);
        _handler = 0;
      }
    }

  }
}
<|endoftext|>


<commit_before>/*  Sirikata
 *  Breakpad.cpp
 *
 *  Copyright (c) 2011, Ewen Cheslack-Postava
 *  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 Sirikata 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 <sirikata/core/util/Standard.hh>
#include <sirikata/core/service/Breakpad.hpp>

#include <sirikata/core/util/Platform.hpp>
#include <sirikata/core/options/CommonOptions.hpp>

#ifdef HAVE_BREAKPAD
#if SIRIKATA_PLATFORM == PLATFORM_WINDOWS
#include <client/windows/handler/exception_handler.h>
#elif SIRIKATA_PLATFORM == PLATFORM_LINUX
#include <client/linux/handler/exception_handler.h>
#endif
#endif // HAVE_BREAKPAD

namespace Sirikata {
namespace Breakpad {

#ifdef HAVE_BREAKPAD

// Each implementation of ExceptionHandler and the setup are different enough
// that these are worth just completely separating. Each just needs to setup the
// exception handler. Currently, all should set it up to save minidumps to the
// current directory.

#if SIRIKATA_PLATFORM  == PLATFORM_WINDOWS
namespace {

static google_breakpad::ExceptionHandler* breakpad_handler = NULL;
static std::string breakpad_url;

std::string wchar_to_string(const wchar_t* orig) {
  size_t origsize = wcslen(orig) + 1;
  const size_t newsize = origsize;
  size_t convertedChars = 0;
  char* nstring = new char[newsize];
  wcstombs_s(&convertedChars, nstring, origsize, orig, _TRUNCATE);
  std::string res(nstring);
  delete nstring;
  return res;
}

bool finishedDump(const wchar_t* dump_path,
    const wchar_t* minidump_id,
    void* context,
    EXCEPTION_POINTERS* exinfo,
    MDRawAssertionInfo* assertion,
    bool succeeded) {
    printf("Finished breakpad dump at %s/%s.dmp: success %d\n", dump_path, minidump_id, succeeded ? 1 : -1);

    if (breakpad_url.empty()) return succeeded;

    const char* reporter_name =
#if SIRIKATA_DEBUG
      "crashreporter_d.exe"
#else
      "crashreporter.exe"
#endif
      ;

    STARTUPINFO info={sizeof(info)};
    PROCESS_INFORMATION processInfo;
    std::string cmd = reporter_name + std::string(" ") + breakpad_url + std::string(" ") + wchar_to_string(dump_path) + std::string(" ") + wchar_to_string(minidump_id);
    CreateProcess(reporter_name, (LPSTR)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);

    return succeeded;
}
}

void init() {
    if (breakpad_handler != NULL) return;

    // This is needed for CRT to not show dialog for invalid param
    // failures and instead let the code handle it.
    _CrtSetReportMode(_CRT_ASSERT, 0);

    breakpad_url = GetOptionValue<String>(OPT_CRASHREPORT_URL);

    using namespace google_breakpad;
    breakpad_handler = new ExceptionHandler(L".\\",
        NULL,
        finishedDump,
        NULL,
        ExceptionHandler::HANDLER_ALL,
        MiniDumpNormal,
        NULL,
        NULL);
}

#elif SIRIKATA_PLATFORM == PLATFORM_LINUX

namespace {

static google_breakpad::ExceptionHandler* breakpad_handler = NULL;
static std::string breakpad_url;

bool finishedDump(const char* dump_path,
    const char* minidump_id,
    void* context,
    bool succeeded) {
    printf("Finished breakpad dump at %s/%s.dmp: success %d\n", dump_path, minidump_id, succeeded ? 1 : -1);

    // If no URL, just finish crashing after the dump.
    if (breakpad_url.empty()) return succeeded;

    // Fork and exec the crashreporter
    pid_t pID = fork();

    if (pID == 0) {
        const char* reporter_name =
#if SIRIKATA_DEBUG
            "crashreporter_d"
#else
            "crashreporter"
#endif
            ;

        execlp(reporter_name, reporter_name, breakpad_url.c_str(), dump_path, minidump_id, (char*)NULL);
        // If crashreporter not in path, try current directory
        execl(reporter_name, reporter_name, breakpad_url.c_str(), dump_path, minidump_id, (char*)NULL);
    }
    else if (pID < 0) {
        printf("Failed to fork crashreporter\n");
    }

    return succeeded;
}
}

void init() {
    if (breakpad_handler != NULL) return;

    breakpad_url = GetOptionValue<String>(OPT_CRASHREPORT_URL);

    using namespace google_breakpad;
    breakpad_handler = new ExceptionHandler("./", NULL, finishedDump, NULL, true);
}

#elif SIRIKATA_PLATFORM == PLATFORM_MAC
// No mac support currently
void init() {
}

#endif

#else //def HAVE_BREAKPAD
// Dummy implementation
void init() {
}
#endif

} // namespace Breakpad
} // namespace Sirikata
<commit_msg>Only invoke the crash reporter in release builds so that you aren't always thrown into a browser during normal development.<commit_after>/*  Sirikata
 *  Breakpad.cpp
 *
 *  Copyright (c) 2011, Ewen Cheslack-Postava
 *  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 Sirikata 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 <sirikata/core/util/Standard.hh>
#include <sirikata/core/service/Breakpad.hpp>

#include <sirikata/core/util/Platform.hpp>
#include <sirikata/core/options/CommonOptions.hpp>

#ifdef HAVE_BREAKPAD
#if SIRIKATA_PLATFORM == PLATFORM_WINDOWS
#include <client/windows/handler/exception_handler.h>
#elif SIRIKATA_PLATFORM == PLATFORM_LINUX
#include <client/linux/handler/exception_handler.h>
#endif
#endif // HAVE_BREAKPAD

namespace Sirikata {
namespace Breakpad {

#ifdef HAVE_BREAKPAD

// Each implementation of ExceptionHandler and the setup are different enough
// that these are worth just completely separating. Each just needs to setup the
// exception handler. Currently, all should set it up to save minidumps to the
// current directory.

#if SIRIKATA_PLATFORM  == PLATFORM_WINDOWS
namespace {

static google_breakpad::ExceptionHandler* breakpad_handler = NULL;
static std::string breakpad_url;

std::string wchar_to_string(const wchar_t* orig) {
  size_t origsize = wcslen(orig) + 1;
  const size_t newsize = origsize;
  size_t convertedChars = 0;
  char* nstring = new char[newsize];
  wcstombs_s(&convertedChars, nstring, origsize, orig, _TRUNCATE);
  std::string res(nstring);
  delete nstring;
  return res;
}

bool finishedDump(const wchar_t* dump_path,
    const wchar_t* minidump_id,
    void* context,
    EXCEPTION_POINTERS* exinfo,
    MDRawAssertionInfo* assertion,
    bool succeeded) {
    printf("Finished breakpad dump at %s/%s.dmp: success %d\n", dump_path, minidump_id, succeeded ? 1 : -1);

// Only run the reporter in release mode. This is a decent heuristic --
// generally you'll only run the debug mode when you have a dev environment.
#if SIRIKATA_DEBUG
    return succeeded;
#else
    if (breakpad_url.empty()) return succeeded;

    const char* reporter_name =
#if SIRIKATA_DEBUG
      "crashreporter_d.exe"
#else
      "crashreporter.exe"
#endif
      ;

    STARTUPINFO info={sizeof(info)};
    PROCESS_INFORMATION processInfo;
    std::string cmd = reporter_name + std::string(" ") + breakpad_url + std::string(" ") + wchar_to_string(dump_path) + std::string(" ") + wchar_to_string(minidump_id);
    CreateProcess(reporter_name, (LPSTR)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);

    return succeeded;
#endif // SIRIKATA_DEBUG
}
}

void init() {
    if (breakpad_handler != NULL) return;

    // This is needed for CRT to not show dialog for invalid param
    // failures and instead let the code handle it.
    _CrtSetReportMode(_CRT_ASSERT, 0);

    breakpad_url = GetOptionValue<String>(OPT_CRASHREPORT_URL);

    using namespace google_breakpad;
    breakpad_handler = new ExceptionHandler(L".\\",
        NULL,
        finishedDump,
        NULL,
        ExceptionHandler::HANDLER_ALL,
        MiniDumpNormal,
        NULL,
        NULL);
}

#elif SIRIKATA_PLATFORM == PLATFORM_LINUX

namespace {

static google_breakpad::ExceptionHandler* breakpad_handler = NULL;
static std::string breakpad_url;

bool finishedDump(const char* dump_path,
    const char* minidump_id,
    void* context,
    bool succeeded) {
    printf("Finished breakpad dump at %s/%s.dmp: success %d\n", dump_path, minidump_id, succeeded ? 1 : -1);

// Only run the reporter in release mode. This is a decent heuristic --
// generally you'll only run the debug mode when you have a dev environment.
#if SIRIKATA_DEBUG
    return succeeded;
#else
    // If no URL, just finish crashing after the dump.
    if (breakpad_url.empty()) return succeeded;

    // Fork and exec the crashreporter
    pid_t pID = fork();

    if (pID == 0) {
        const char* reporter_name =
#if SIRIKATA_DEBUG
            "crashreporter_d"
#else
            "crashreporter"
#endif
            ;

        execlp(reporter_name, reporter_name, breakpad_url.c_str(), dump_path, minidump_id, (char*)NULL);
        // If crashreporter not in path, try current directory
        execl(reporter_name, reporter_name, breakpad_url.c_str(), dump_path, minidump_id, (char*)NULL);
    }
    else if (pID < 0) {
        printf("Failed to fork crashreporter\n");
    }

    return succeeded;
#endif //SIRIKATA_DEBUG
}
}

void init() {
    if (breakpad_handler != NULL) return;

    breakpad_url = GetOptionValue<String>(OPT_CRASHREPORT_URL);

    using namespace google_breakpad;
    breakpad_handler = new ExceptionHandler("./", NULL, finishedDump, NULL, true);
}

#elif SIRIKATA_PLATFORM == PLATFORM_MAC
// No mac support currently
void init() {
}

#endif

#else //def HAVE_BREAKPAD
// Dummy implementation
void init() {
}
#endif

} // namespace Breakpad
} // namespace Sirikata
<|endoftext|>


<commit_before>#include "worker.h"

#include "utils.h"

#include <pynumbuf/serialize.h>

extern "C" {
  static PyObject *RayError;
}

Status WorkerServiceImpl::ExecuteTask(ServerContext* context, const ExecuteTaskRequest* request, ExecuteTaskReply* reply) {
  task_ = request->task(); // Copy task
  RAY_LOG(RAY_INFO, "invoked task " << request->task().name());
  Task* taskptr = &task_;
  send_queue_.send(&taskptr);
  return Status::OK;
}

Worker::Worker(const std::string& worker_address, std::shared_ptr<Channel> scheduler_channel, std::shared_ptr<Channel> objstore_channel)
    : worker_address_(worker_address),
      scheduler_stub_(Scheduler::NewStub(scheduler_channel)),
      objstore_stub_(ObjStore::NewStub(objstore_channel)) {
  receive_queue_.connect(worker_address_, true);
  connected_ = true;
}

SubmitTaskReply Worker::submit_task(SubmitTaskRequest* request) {
  RAY_CHECK(connected_, "Attempted to perform submit_task but failed.");
  SubmitTaskReply reply;
  ClientContext context;
  Status status = scheduler_stub_->SubmitTask(&context, *request, &reply);
  return reply;
}

void Worker::register_worker(const std::string& worker_address, const std::string& objstore_address) {
  RegisterWorkerRequest request;
  request.set_worker_address(worker_address);
  request.set_objstore_address(objstore_address);
  RegisterWorkerReply reply;
  ClientContext context;
  Status status = scheduler_stub_->RegisterWorker(&context, request, &reply);
  workerid_ = reply.workerid();
  objstoreid_ = reply.objstoreid();
  segmentpool_ = std::make_shared<MemorySegmentPool>(objstoreid_, false);
  request_obj_queue_.connect(std::string("queue:") + objstore_address + std::string(":obj"), false);
  std::string queue_name = std::string("queue:") + objstore_address + std::string(":worker:") + std::to_string(workerid_) + std::string(":obj");
  receive_obj_queue_.connect(queue_name, true);
  return;
}

void Worker::request_object(ObjRef objref) {
  RAY_CHECK(connected_, "Attempted to perform request_object but failed.");
  RequestObjRequest request;
  request.set_workerid(workerid_);
  request.set_objref(objref);
  AckReply reply;
  ClientContext context;
  Status status = scheduler_stub_->RequestObj(&context, request, &reply);
  return;
}

ObjRef Worker::get_objref() {
  // first get objref for the new object
  RAY_CHECK(connected_, "Attempted to perform get_objref but failed.");
  PushObjRequest push_request;
  PushObjReply push_reply;
  ClientContext push_context;
  Status push_status = scheduler_stub_->PushObj(&push_context, push_request, &push_reply);
  return push_reply.objref();
}

slice Worker::get_object(ObjRef objref) {
  // get_object assumes that objref is a canonical objref
  RAY_CHECK(connected_, "Attempted to perform get_object but failed.");
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::GET;
  request.objref = objref;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  slice slice;
  slice.data = segmentpool_->get_address(result);
  slice.len = result.size();
  return slice;
}

// TODO(pcm): More error handling
// contained_objrefs is a vector of all the objrefs contained in obj
void Worker::put_object(ObjRef objref, const Obj* obj, std::vector<ObjRef> &contained_objrefs) {
  RAY_CHECK(connected_, "Attempted to perform put_object but failed.");
  std::string data;
  obj->SerializeToString(&data); // TODO(pcm): get rid of this serialization
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::ALLOC;
  request.objref = objref;
  request.size = data.size();
  request_obj_queue_.send(&request);
  if (contained_objrefs.size() > 0) {
    RAY_LOG(RAY_REFCOUNT, "In put_object, calling increment_reference_count for contained objrefs");
    increment_reference_count(contained_objrefs); // Notify the scheduler that some object references are serialized in the objstore.
  }
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  uint8_t* target = segmentpool_->get_address(result);
  std::memcpy(target, &data[0], data.size());
  request.type = ObjRequestType::WORKER_DONE;
  request.metadata_offset = 0;
  request_obj_queue_.send(&request);

  // Notify the scheduler about the objrefs that we are serializing in the objstore.
  AddContainedObjRefsRequest contained_objrefs_request;
  contained_objrefs_request.set_objref(objref);
  for (int i = 0; i < contained_objrefs.size(); ++i) {
    contained_objrefs_request.add_contained_objref(contained_objrefs[i]); // TODO(rkn): The naming here is bad
  }
  AckReply reply;
  ClientContext context;
  scheduler_stub_->AddContainedObjRefs(&context, contained_objrefs_request, &reply);
}

#define CHECK_ARROW_STATUS(s, msg)                              \
  do {                                                          \
    arrow::Status _s = (s);                                     \
    if (!_s.ok()) {                                             \
      std::string _errmsg = std::string(msg) + _s.ToString();   \
      PyErr_SetString(RayError, _errmsg.c_str());            \
      return NULL;                                              \
    }                                                           \
  } while (0);

PyObject* Worker::put_arrow(ObjRef objref, PyObject* value) {
  RAY_CHECK(connected_, "Attempted to perform put_arrow but failed.");
  ObjRequest request;
  pynumbuf::PythonObjectWriter writer;
  int64_t size;
  CHECK_ARROW_STATUS(writer.AssemblePayload(value), "error during AssemblePayload: ");
  CHECK_ARROW_STATUS(writer.GetTotalSize(&size), "error during GetTotalSize: ");
  request.workerid = workerid_;
  request.type = ObjRequestType::ALLOC;
  request.objref = objref;
  request.size = size;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  int64_t metadata_offset;
  uint8_t* address = segmentpool_->get_address(result);
  auto source = std::make_shared<BufferMemorySource>(address, size);
  CHECK_ARROW_STATUS(writer.Write(source.get(), &metadata_offset), "error during Write: ");
  request.type = ObjRequestType::WORKER_DONE;
  request.metadata_offset = metadata_offset;
  request_obj_queue_.send(&request);
  Py_RETURN_NONE;
}

PyObject* Worker::get_arrow(ObjRef objref) {
  RAY_CHECK(connected_, "Attempted to perform get_arrow but failed.");
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::GET;
  request.objref = objref;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  uint8_t* address = segmentpool_->get_address(result);
  auto source = std::make_shared<BufferMemorySource>(address, result.size());
  PyObject* value;
  CHECK_ARROW_STATUS(pynumbuf::ReadPythonObjectFrom(source.get(), result.metadata_offset(), &value), "error during ReadPythonObjectFrom: ");
  return value;
}

bool Worker::is_arrow(ObjRef objref) {
  RAY_CHECK(connected_, "Attempted to perform is_arrow but failed.");
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::GET;
  request.objref = objref;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  return result.metadata_offset() != 0;
}

void Worker::alias_objrefs(ObjRef alias_objref, ObjRef target_objref) {
  RAY_CHECK(connected_, "Attempted to perform alias_objrefs but failed.");
  ClientContext context;
  AliasObjRefsRequest request;
  request.set_alias_objref(alias_objref);
  request.set_target_objref(target_objref);
  AckReply reply;
  scheduler_stub_->AliasObjRefs(&context, request, &reply);
}

void Worker::increment_reference_count(std::vector<ObjRef> &objrefs) {
  if (!connected_) {
    RAY_LOG(RAY_INFO, "Attempting to increment_reference_count for objrefs, but connected_ = " << connected_ << " so returning instead.");
    return;
  }
  if (objrefs.size() > 0) {
    ClientContext context;
    IncrementRefCountRequest request;
    for (int i = 0; i < objrefs.size(); ++i) {
      RAY_LOG(RAY_REFCOUNT, "Incrementing reference count for objref " << objrefs[i]);
      request.add_objref(objrefs[i]);
    }
    AckReply reply;
    scheduler_stub_->IncrementRefCount(&context, request, &reply);
  }
}

void Worker::decrement_reference_count(std::vector<ObjRef> &objrefs) {
  if (!connected_) {
    RAY_LOG(RAY_INFO, "Attempting to decrement_reference_count, but connected_ = " << connected_ << " so returning instead.");
    return;
  }
  if (objrefs.size() > 0) {
    ClientContext context;
    DecrementRefCountRequest request;
    for (int i = 0; i < objrefs.size(); ++i) {
      RAY_LOG(RAY_REFCOUNT, "Decrementing reference count for objref " << objrefs[i]);
      request.add_objref(objrefs[i]);
    }
    AckReply reply;
    scheduler_stub_->DecrementRefCount(&context, request, &reply);
  }
}

void Worker::register_function(const std::string& name, size_t num_return_vals) {
  RAY_CHECK(connected_, "Attempted to perform register_function but failed.");
  ClientContext context;
  RegisterFunctionRequest request;
  request.set_fnname(name);
  request.set_num_return_vals(num_return_vals);
  request.set_workerid(workerid_);
  AckReply reply;
  scheduler_stub_->RegisterFunction(&context, request, &reply);
}

Task* Worker::receive_next_task() {
  Task* task;
  receive_queue_.receive(&task);
  return task;
}

void Worker::notify_task_completed(bool task_succeeded, std::string error_message) {
  RAY_CHECK(connected_, "Attempted to perform notify_task_completed but failed.");
  ClientContext context;
  NotifyTaskCompletedRequest request;
  request.set_workerid(workerid_);
  request.set_task_succeeded(task_succeeded);
  request.set_error_message(error_message);
  AckReply reply;
  scheduler_stub_->NotifyTaskCompleted(&context, request, &reply);
}

void Worker::disconnect() {
  connected_ = false;
}

bool Worker::connected() {
  return connected_;
}

// TODO(rkn): Should we be using pointers or references? And should they be const?
void Worker::scheduler_info(ClientContext &context, SchedulerInfoRequest &request, SchedulerInfoReply &reply) {
  RAY_CHECK(connected_, "Attempted to get scheduler info but failed.");
  scheduler_stub_->SchedulerInfo(&context, request, &reply);
}

// Communication between the WorkerServer and the Worker happens via a message
// queue. This is because the Python interpreter needs to be single threaded
// (in our case running in the main thread), whereas the WorkerService will
// run in a separate thread and potentially utilize multiple threads.
void Worker::start_worker_service() {
  const char* service_addr = worker_address_.c_str();
  worker_server_thread_ = std::thread([service_addr]() {
    std::string service_address(service_addr);
    std::string::iterator split_point = split_ip_address(service_address);
    std::string port;
    port.assign(split_point, service_address.end());
    WorkerServiceImpl service(service_address);
    ServerBuilder builder;
    builder.AddListeningPort(std::string("0.0.0.0:") + port, grpc::InsecureServerCredentials());
    builder.RegisterService(&service);
    std::unique_ptr<Server> server(builder.BuildAndStart());
    RAY_LOG(RAY_INFO, "worker server listening on " << service_address);
    server->Wait();
  });
}
<commit_msg>Downgrading reference count logging to DEBUG<commit_after>#include "worker.h"

#include "utils.h"

#include <pynumbuf/serialize.h>

extern "C" {
  static PyObject *RayError;
}

Status WorkerServiceImpl::ExecuteTask(ServerContext* context, const ExecuteTaskRequest* request, ExecuteTaskReply* reply) {
  task_ = request->task(); // Copy task
  RAY_LOG(RAY_INFO, "invoked task " << request->task().name());
  Task* taskptr = &task_;
  send_queue_.send(&taskptr);
  return Status::OK;
}

Worker::Worker(const std::string& worker_address, std::shared_ptr<Channel> scheduler_channel, std::shared_ptr<Channel> objstore_channel)
    : worker_address_(worker_address),
      scheduler_stub_(Scheduler::NewStub(scheduler_channel)),
      objstore_stub_(ObjStore::NewStub(objstore_channel)) {
  receive_queue_.connect(worker_address_, true);
  connected_ = true;
}

SubmitTaskReply Worker::submit_task(SubmitTaskRequest* request) {
  RAY_CHECK(connected_, "Attempted to perform submit_task but failed.");
  SubmitTaskReply reply;
  ClientContext context;
  Status status = scheduler_stub_->SubmitTask(&context, *request, &reply);
  return reply;
}

void Worker::register_worker(const std::string& worker_address, const std::string& objstore_address) {
  RegisterWorkerRequest request;
  request.set_worker_address(worker_address);
  request.set_objstore_address(objstore_address);
  RegisterWorkerReply reply;
  ClientContext context;
  Status status = scheduler_stub_->RegisterWorker(&context, request, &reply);
  workerid_ = reply.workerid();
  objstoreid_ = reply.objstoreid();
  segmentpool_ = std::make_shared<MemorySegmentPool>(objstoreid_, false);
  request_obj_queue_.connect(std::string("queue:") + objstore_address + std::string(":obj"), false);
  std::string queue_name = std::string("queue:") + objstore_address + std::string(":worker:") + std::to_string(workerid_) + std::string(":obj");
  receive_obj_queue_.connect(queue_name, true);
  return;
}

void Worker::request_object(ObjRef objref) {
  RAY_CHECK(connected_, "Attempted to perform request_object but failed.");
  RequestObjRequest request;
  request.set_workerid(workerid_);
  request.set_objref(objref);
  AckReply reply;
  ClientContext context;
  Status status = scheduler_stub_->RequestObj(&context, request, &reply);
  return;
}

ObjRef Worker::get_objref() {
  // first get objref for the new object
  RAY_CHECK(connected_, "Attempted to perform get_objref but failed.");
  PushObjRequest push_request;
  PushObjReply push_reply;
  ClientContext push_context;
  Status push_status = scheduler_stub_->PushObj(&push_context, push_request, &push_reply);
  return push_reply.objref();
}

slice Worker::get_object(ObjRef objref) {
  // get_object assumes that objref is a canonical objref
  RAY_CHECK(connected_, "Attempted to perform get_object but failed.");
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::GET;
  request.objref = objref;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  slice slice;
  slice.data = segmentpool_->get_address(result);
  slice.len = result.size();
  return slice;
}

// TODO(pcm): More error handling
// contained_objrefs is a vector of all the objrefs contained in obj
void Worker::put_object(ObjRef objref, const Obj* obj, std::vector<ObjRef> &contained_objrefs) {
  RAY_CHECK(connected_, "Attempted to perform put_object but failed.");
  std::string data;
  obj->SerializeToString(&data); // TODO(pcm): get rid of this serialization
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::ALLOC;
  request.objref = objref;
  request.size = data.size();
  request_obj_queue_.send(&request);
  if (contained_objrefs.size() > 0) {
    RAY_LOG(RAY_REFCOUNT, "In put_object, calling increment_reference_count for contained objrefs");
    increment_reference_count(contained_objrefs); // Notify the scheduler that some object references are serialized in the objstore.
  }
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  uint8_t* target = segmentpool_->get_address(result);
  std::memcpy(target, &data[0], data.size());
  request.type = ObjRequestType::WORKER_DONE;
  request.metadata_offset = 0;
  request_obj_queue_.send(&request);

  // Notify the scheduler about the objrefs that we are serializing in the objstore.
  AddContainedObjRefsRequest contained_objrefs_request;
  contained_objrefs_request.set_objref(objref);
  for (int i = 0; i < contained_objrefs.size(); ++i) {
    contained_objrefs_request.add_contained_objref(contained_objrefs[i]); // TODO(rkn): The naming here is bad
  }
  AckReply reply;
  ClientContext context;
  scheduler_stub_->AddContainedObjRefs(&context, contained_objrefs_request, &reply);
}

#define CHECK_ARROW_STATUS(s, msg)                              \
  do {                                                          \
    arrow::Status _s = (s);                                     \
    if (!_s.ok()) {                                             \
      std::string _errmsg = std::string(msg) + _s.ToString();   \
      PyErr_SetString(RayError, _errmsg.c_str());            \
      return NULL;                                              \
    }                                                           \
  } while (0);

PyObject* Worker::put_arrow(ObjRef objref, PyObject* value) {
  RAY_CHECK(connected_, "Attempted to perform put_arrow but failed.");
  ObjRequest request;
  pynumbuf::PythonObjectWriter writer;
  int64_t size;
  CHECK_ARROW_STATUS(writer.AssemblePayload(value), "error during AssemblePayload: ");
  CHECK_ARROW_STATUS(writer.GetTotalSize(&size), "error during GetTotalSize: ");
  request.workerid = workerid_;
  request.type = ObjRequestType::ALLOC;
  request.objref = objref;
  request.size = size;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  int64_t metadata_offset;
  uint8_t* address = segmentpool_->get_address(result);
  auto source = std::make_shared<BufferMemorySource>(address, size);
  CHECK_ARROW_STATUS(writer.Write(source.get(), &metadata_offset), "error during Write: ");
  request.type = ObjRequestType::WORKER_DONE;
  request.metadata_offset = metadata_offset;
  request_obj_queue_.send(&request);
  Py_RETURN_NONE;
}

PyObject* Worker::get_arrow(ObjRef objref) {
  RAY_CHECK(connected_, "Attempted to perform get_arrow but failed.");
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::GET;
  request.objref = objref;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  uint8_t* address = segmentpool_->get_address(result);
  auto source = std::make_shared<BufferMemorySource>(address, result.size());
  PyObject* value;
  CHECK_ARROW_STATUS(pynumbuf::ReadPythonObjectFrom(source.get(), result.metadata_offset(), &value), "error during ReadPythonObjectFrom: ");
  return value;
}

bool Worker::is_arrow(ObjRef objref) {
  RAY_CHECK(connected_, "Attempted to perform is_arrow but failed.");
  ObjRequest request;
  request.workerid = workerid_;
  request.type = ObjRequestType::GET;
  request.objref = objref;
  request_obj_queue_.send(&request);
  ObjHandle result;
  receive_obj_queue_.receive(&result);
  return result.metadata_offset() != 0;
}

void Worker::alias_objrefs(ObjRef alias_objref, ObjRef target_objref) {
  RAY_CHECK(connected_, "Attempted to perform alias_objrefs but failed.");
  ClientContext context;
  AliasObjRefsRequest request;
  request.set_alias_objref(alias_objref);
  request.set_target_objref(target_objref);
  AckReply reply;
  scheduler_stub_->AliasObjRefs(&context, request, &reply);
}

void Worker::increment_reference_count(std::vector<ObjRef> &objrefs) {
  if (!connected_) {
    RAY_LOG(RAY_DEBUG, "Attempting to increment_reference_count for objrefs, but connected_ = " << connected_ << " so returning instead.");
    return;
  }
  if (objrefs.size() > 0) {
    ClientContext context;
    IncrementRefCountRequest request;
    for (int i = 0; i < objrefs.size(); ++i) {
      RAY_LOG(RAY_REFCOUNT, "Incrementing reference count for objref " << objrefs[i]);
      request.add_objref(objrefs[i]);
    }
    AckReply reply;
    scheduler_stub_->IncrementRefCount(&context, request, &reply);
  }
}

void Worker::decrement_reference_count(std::vector<ObjRef> &objrefs) {
  if (!connected_) {
    RAY_LOG(RAY_DEBUG, "Attempting to decrement_reference_count, but connected_ = " << connected_ << " so returning instead.");
    return;
  }
  if (objrefs.size() > 0) {
    ClientContext context;
    DecrementRefCountRequest request;
    for (int i = 0; i < objrefs.size(); ++i) {
      RAY_LOG(RAY_REFCOUNT, "Decrementing reference count for objref " << objrefs[i]);
      request.add_objref(objrefs[i]);
    }
    AckReply reply;
    scheduler_stub_->DecrementRefCount(&context, request, &reply);
  }
}

void Worker::register_function(const std::string& name, size_t num_return_vals) {
  RAY_CHECK(connected_, "Attempted to perform register_function but failed.");
  ClientContext context;
  RegisterFunctionRequest request;
  request.set_fnname(name);
  request.set_num_return_vals(num_return_vals);
  request.set_workerid(workerid_);
  AckReply reply;
  scheduler_stub_->RegisterFunction(&context, request, &reply);
}

Task* Worker::receive_next_task() {
  Task* task;
  receive_queue_.receive(&task);
  return task;
}

void Worker::notify_task_completed(bool task_succeeded, std::string error_message) {
  RAY_CHECK(connected_, "Attempted to perform notify_task_completed but failed.");
  ClientContext context;
  NotifyTaskCompletedRequest request;
  request.set_workerid(workerid_);
  request.set_task_succeeded(task_succeeded);
  request.set_error_message(error_message);
  AckReply reply;
  scheduler_stub_->NotifyTaskCompleted(&context, request, &reply);
}

void Worker::disconnect() {
  connected_ = false;
}

bool Worker::connected() {
  return connected_;
}

// TODO(rkn): Should we be using pointers or references? And should they be const?
void Worker::scheduler_info(ClientContext &context, SchedulerInfoRequest &request, SchedulerInfoReply &reply) {
  RAY_CHECK(connected_, "Attempted to get scheduler info but failed.");
  scheduler_stub_->SchedulerInfo(&context, request, &reply);
}

// Communication between the WorkerServer and the Worker happens via a message
// queue. This is because the Python interpreter needs to be single threaded
// (in our case running in the main thread), whereas the WorkerService will
// run in a separate thread and potentially utilize multiple threads.
void Worker::start_worker_service() {
  const char* service_addr = worker_address_.c_str();
  worker_server_thread_ = std::thread([service_addr]() {
    std::string service_address(service_addr);
    std::string::iterator split_point = split_ip_address(service_address);
    std::string port;
    port.assign(split_point, service_address.end());
    WorkerServiceImpl service(service_address);
    ServerBuilder builder;
    builder.AddListeningPort(std::string("0.0.0.0:") + port, grpc::InsecureServerCredentials());
    builder.RegisterService(&service);
    std::unique_ptr<Server> server(builder.BuildAndStart());
    RAY_LOG(RAY_INFO, "worker server listening on " << service_address);
    server->Wait();
  });
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
 *
 * 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 <dali/integration-api/debug.h>
#include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
#include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>

#if defined(DEBUG_ENABLED)
extern Dali::Integration::Log::Filter* gFontClientLogFilter;
#endif

namespace Dali::TextAbstraction::Internal
{
const float FROM_266        = 1.0f / 64.0f;
const float POINTS_PER_INCH = 72.f;

FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
                                     FT_Face            ftFace,
                                     const FontPath&    path,
                                     PointSize26Dot6    requestedPointSize,
                                     FaceIndex          face,
                                     const FontMetrics& metrics)
: mFreeTypeLibrary(freeTypeLibrary),
  mFreeTypeFace(ftFace),
  mPath(path),
  mRequestedPointSize(requestedPointSize),
  mFaceIndex(face),
  mMetrics(metrics),
  mCharacterSet(nullptr),
  mFixedSizeIndex(0),
  mFixedWidthPixels(0.f),
  mFixedHeightPixels(0.f),
  mVectorFontId(0u),
  mFontId(0u),
  mIsFixedSizeBitmap(false),
  mHasColorTables(false)
{
}

FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
                                     FT_Face            ftFace,
                                     const FontPath&    path,
                                     PointSize26Dot6    requestedPointSize,
                                     FaceIndex          face,
                                     const FontMetrics& metrics,
                                     int                fixedSizeIndex,
                                     float              fixedWidth,
                                     float              fixedHeight,
                                     bool               hasColorTables)
: mFreeTypeLibrary(freeTypeLibrary),
  mFreeTypeFace(ftFace),
  mPath(path),
  mRequestedPointSize(requestedPointSize),
  mFaceIndex(face),
  mMetrics(metrics),
  mCharacterSet(nullptr),
  mFixedSizeIndex(fixedSizeIndex),
  mFixedWidthPixels(fixedWidth),
  mFixedHeightPixels(fixedHeight),
  mVectorFontId(0u),
  mFontId(0u),
  mIsFixedSizeBitmap(true),
  mHasColorTables(hasColorTables)
{
}

void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
{
  metrics = mMetrics;

  // Adjust the metrics if the fixed-size font should be down-scaled
  if(mIsFixedSizeBitmap)
  {
    const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;

    if(desiredFixedSize > 0.f)
    {
      const float scaleFactor = desiredFixedSize / mFixedHeightPixels;

      metrics.ascender           = metrics.ascender * scaleFactor;
      metrics.descender          = metrics.descender * scaleFactor;
      metrics.height             = metrics.height * scaleFactor;
      metrics.underlinePosition  = metrics.underlinePosition * scaleFactor;
      metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
    }
  }
}

bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const
{
  bool success(true);

  FT_Face ftFace = mFreeTypeFace;

#ifdef FREETYPE_BITMAP_SUPPORT
  // Check to see if we should be loading a Fixed Size bitmap?
  if(mIsFixedSizeBitmap)
  {
    FT_Select_Size(ftFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
    int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
    if(FT_Err_Ok == error)
    {
      glyph.width    = mFixedWidthPixels;
      glyph.height   = mFixedHeightPixels;
      glyph.advance  = mFixedWidthPixels;
      glyph.xBearing = 0.0f;
      glyph.yBearing = mFixedHeightPixels;

      // Adjust the metrics if the fixed-size font should be down-scaled
      const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;

      if(desiredFixedSize > 0.f)
      {
        const float scaleFactor = desiredFixedSize / mFixedHeightPixels;

        glyph.width    = glyph.width * scaleFactor;
        glyph.height   = glyph.height * scaleFactor;
        glyph.advance  = glyph.advance * scaleFactor;
        glyph.xBearing = glyph.xBearing * scaleFactor;
        glyph.yBearing = glyph.yBearing * scaleFactor;

        glyph.scaleFactor = scaleFactor;
      }
    }
    else
    {
      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
      success = false;
    }
  }
  else
#endif
  {
    // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
    // i.e. with the SNum-3R font.
    // @todo: add an option to use the FT_LOAD_DEFAULT if required?
    int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);

    // Keep the width of the glyph before doing the software emboldening.
    // It will be used to calculate a scale factor to be applied to the
    // advance as Harfbuzz doesn't apply any SW emboldening to calculate
    // the advance of the glyph.
    const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;

    if(FT_Err_Ok == error)
    {
      const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
      if(isEmboldeningRequired)
      {
        // Does the software bold.
        FT_GlyphSlot_Embolden(ftFace->glyph);
      }

      glyph.width  = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
      glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
      if(horizontal)
      {
        glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
      }
      else
      {
        glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
      }

      if(isEmboldeningRequired && !Dali::EqualsZero(width))
      {
        // If the glyph is emboldened by software, the advance is multiplied by a
        // scale factor to make it slightly bigger.
        glyph.advance *= (glyph.width / width);
      }

      // Use the bounding box of the bitmap to correct the metrics.
      // For some fonts i.e the SNum-3R the metrics need to be corrected,
      // otherwise the glyphs 'dance' up and down depending on the
      // font's point size.

      FT_Glyph ftGlyph;
      error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);

      FT_BBox bbox;
      FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);

      const float descender = glyph.height - glyph.yBearing;
      glyph.height          = (bbox.yMax - bbox.yMin) * FROM_266;
      glyph.yBearing        = glyph.height - round(descender);

      // Created FT_Glyph object must be released with FT_Done_Glyph
      FT_Done_Glyph(ftGlyph);
    }
    else
    {
      success = false;
    }
  }
  return success;
}

/**
 * @brief Create a bitmap representation of a glyph from a face font
 *
 * @param[in]  glyphIndex        The index of a glyph within the specified font.
 * @param[in]  isItalicRequired  Whether the glyph requires italic style.
 * @param[in]  isBoldRequired    Whether the glyph requires bold style.
 * @param[out] data              The bitmap data.
 * @param[in]  outlineWidth      The width of the glyph outline in pixels.
 */
void FontFaceCacheItem::CreateBitmap(
  GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
{
  FT_Face  ftFace = mFreeTypeFace;
  FT_Error error;
  // For the software italics.
  bool isShearRequired = false;

#ifdef FREETYPE_BITMAP_SUPPORT
  // Check to see if this is fixed size bitmap
  if(mIsFixedSizeBitmap)
  {
    error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
  }
  else
#endif
  {
    // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
    // i.e. with the SNum-3R font.
    // @todo: add an option to use the FT_LOAD_DEFAULT if required?
    error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
  }
  if(FT_Err_Ok == error)
  {
    if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
    {
      // Does the software bold.
      FT_GlyphSlot_Embolden(ftFace->glyph);
    }

    if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
    {
      // Will do the software italic.
      isShearRequired = true;
    }

    FT_Glyph glyph;
    error = FT_Get_Glyph(ftFace->glyph, &glyph);

    // Convert to bitmap if necessary
    if(FT_Err_Ok == error)
    {
      if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
      {
        int  offsetX = 0, offsetY = 0;
        bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);

        // Create a bitmap for the outline
        if(isOutlineGlyph)
        {
          // Retrieve the horizontal and vertical distance from the current pen position to the
          // left and top border of the glyph bitmap for a normal glyph before applying the outline.
          if(FT_Err_Ok == error)
          {
            FT_Glyph normalGlyph;
            error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);

            error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
            if(FT_Err_Ok == error)
            {
              FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);

              offsetX = bitmapGlyph->left;
              offsetY = bitmapGlyph->top;
            }

            // Created FT_Glyph object must be released with FT_Done_Glyph
            FT_Done_Glyph(normalGlyph);
          }

          // Now apply the outline

          // Set up a stroker
          FT_Stroker stroker;
          error = FT_Stroker_New(mFreeTypeLibrary, &stroker);

          if(FT_Err_Ok == error)
          {
            FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
            error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);

            if(FT_Err_Ok == error)
            {
              FT_Stroker_Done(stroker);
            }
            else
            {
              DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
            }
          }
          else
          {
            DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
          }
        }

        error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
        if(FT_Err_Ok == error)
        {
          FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);

          if(isOutlineGlyph)
          {
            // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
            data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
            data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
          }

          ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
        }
        else
        {
          DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
        }
      }
      else
      {
        ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
      }

      data.isColorEmoji = mIsFixedSizeBitmap;

      // Created FT_Glyph object must be released with FT_Done_Glyph
      FT_Done_Glyph(glyph);
    }
  }
  else
  {
    DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
  }
}

bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
{
  FT_Error error = -1;

#ifdef FREETYPE_BITMAP_SUPPORT
  // Check to see if this is fixed size bitmap
  if(mHasColorTables)
  {
    error = FT_Load_Glyph(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR);
  }
#endif
  return FT_Err_Ok == error;
}

/**
 * Check if the character is supported by this font
 * @param[in] character The character to test
 */
bool FontFaceCacheItem::IsCharacterSupported(Character character)
{
  if(nullptr == mCharacterSet)
  {
    // Create again the character set.
    // It can be null if the ResetSystemDefaults() method has been called.

    FontDescription description;
    description.path   = mPath;
    description.family = std::move(FontFamily(mFreeTypeFace->family_name));
    description.weight = FontWeight::NONE;
    description.width  = FontWidth::NONE;
    description.slant  = FontSlant::NONE;

    // Note FreeType doesn't give too much info to build a proper font style.
    if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
    {
      description.slant = FontSlant::ITALIC;
    }
    if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
    {
      description.weight = FontWeight::BOLD;
    }

    mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
  }

  return FcCharSetHasChar(mCharacterSet, character);
}

GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
{
  return FT_Get_Char_Index(mFreeTypeFace, character);
}

} // namespace Dali::TextAbstraction::Internal
<commit_msg>Adjust the yBearing value of the emoji.<commit_after>/*
 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
 *
 * 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 <dali/integration-api/debug.h>
#include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
#include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>

#if defined(DEBUG_ENABLED)
extern Dali::Integration::Log::Filter* gFontClientLogFilter;
#endif

namespace Dali::TextAbstraction::Internal
{
const float FROM_266        = 1.0f / 64.0f;
const float POINTS_PER_INCH = 72.f;

FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
                                     FT_Face            ftFace,
                                     const FontPath&    path,
                                     PointSize26Dot6    requestedPointSize,
                                     FaceIndex          face,
                                     const FontMetrics& metrics)
: mFreeTypeLibrary(freeTypeLibrary),
  mFreeTypeFace(ftFace),
  mPath(path),
  mRequestedPointSize(requestedPointSize),
  mFaceIndex(face),
  mMetrics(metrics),
  mCharacterSet(nullptr),
  mFixedSizeIndex(0),
  mFixedWidthPixels(0.f),
  mFixedHeightPixels(0.f),
  mVectorFontId(0u),
  mFontId(0u),
  mIsFixedSizeBitmap(false),
  mHasColorTables(false)
{
}

FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
                                     FT_Face            ftFace,
                                     const FontPath&    path,
                                     PointSize26Dot6    requestedPointSize,
                                     FaceIndex          face,
                                     const FontMetrics& metrics,
                                     int                fixedSizeIndex,
                                     float              fixedWidth,
                                     float              fixedHeight,
                                     bool               hasColorTables)
: mFreeTypeLibrary(freeTypeLibrary),
  mFreeTypeFace(ftFace),
  mPath(path),
  mRequestedPointSize(requestedPointSize),
  mFaceIndex(face),
  mMetrics(metrics),
  mCharacterSet(nullptr),
  mFixedSizeIndex(fixedSizeIndex),
  mFixedWidthPixels(fixedWidth),
  mFixedHeightPixels(fixedHeight),
  mVectorFontId(0u),
  mFontId(0u),
  mIsFixedSizeBitmap(true),
  mHasColorTables(hasColorTables)
{
}

void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
{
  metrics = mMetrics;

  // Adjust the metrics if the fixed-size font should be down-scaled
  if(mIsFixedSizeBitmap)
  {
    const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;

    if(desiredFixedSize > 0.f)
    {
      const float scaleFactor = desiredFixedSize / mFixedHeightPixels;

      metrics.ascender           = metrics.ascender * scaleFactor;
      metrics.descender          = metrics.descender * scaleFactor;
      metrics.height             = metrics.height * scaleFactor;
      metrics.underlinePosition  = metrics.underlinePosition * scaleFactor;
      metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
    }
  }
}

bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const
{
  bool success(true);

  FT_Face ftFace = mFreeTypeFace;

#ifdef FREETYPE_BITMAP_SUPPORT
  // Check to see if we should be loading a Fixed Size bitmap?
  if(mIsFixedSizeBitmap)
  {
    FT_Select_Size(ftFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
    int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
    if(FT_Err_Ok == error)
    {
      glyph.width    = mFixedWidthPixels;
      glyph.height   = mFixedHeightPixels;
      glyph.advance  = mFixedWidthPixels;
      glyph.xBearing = 0.0f;
      if(horizontal)
      {
        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
      }
      else
      {
        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
      }

      // Adjust the metrics if the fixed-size font should be down-scaled
      const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;

      if(desiredFixedSize > 0.f)
      {
        const float scaleFactor = desiredFixedSize / mFixedHeightPixels;

        glyph.width    = glyph.width * scaleFactor;
        glyph.height   = glyph.height * scaleFactor;
        glyph.advance  = glyph.advance * scaleFactor;
        glyph.xBearing = glyph.xBearing * scaleFactor;
        glyph.yBearing = glyph.yBearing * scaleFactor;

        glyph.scaleFactor = scaleFactor;
      }
    }
    else
    {
      DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
      success = false;
    }
  }
  else
#endif
  {
    // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
    // i.e. with the SNum-3R font.
    // @todo: add an option to use the FT_LOAD_DEFAULT if required?
    int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);

    // Keep the width of the glyph before doing the software emboldening.
    // It will be used to calculate a scale factor to be applied to the
    // advance as Harfbuzz doesn't apply any SW emboldening to calculate
    // the advance of the glyph.
    const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;

    if(FT_Err_Ok == error)
    {
      const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
      if(isEmboldeningRequired)
      {
        // Does the software bold.
        FT_GlyphSlot_Embolden(ftFace->glyph);
      }

      glyph.width  = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
      glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
      if(horizontal)
      {
        glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
      }
      else
      {
        glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
        glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
      }

      if(isEmboldeningRequired && !Dali::EqualsZero(width))
      {
        // If the glyph is emboldened by software, the advance is multiplied by a
        // scale factor to make it slightly bigger.
        glyph.advance *= (glyph.width / width);
      }

      // Use the bounding box of the bitmap to correct the metrics.
      // For some fonts i.e the SNum-3R the metrics need to be corrected,
      // otherwise the glyphs 'dance' up and down depending on the
      // font's point size.

      FT_Glyph ftGlyph;
      error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);

      FT_BBox bbox;
      FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);

      const float descender = glyph.height - glyph.yBearing;
      glyph.height          = (bbox.yMax - bbox.yMin) * FROM_266;
      glyph.yBearing        = glyph.height - round(descender);

      // Created FT_Glyph object must be released with FT_Done_Glyph
      FT_Done_Glyph(ftGlyph);
    }
    else
    {
      success = false;
    }
  }
  return success;
}

/**
 * @brief Create a bitmap representation of a glyph from a face font
 *
 * @param[in]  glyphIndex        The index of a glyph within the specified font.
 * @param[in]  isItalicRequired  Whether the glyph requires italic style.
 * @param[in]  isBoldRequired    Whether the glyph requires bold style.
 * @param[out] data              The bitmap data.
 * @param[in]  outlineWidth      The width of the glyph outline in pixels.
 */
void FontFaceCacheItem::CreateBitmap(
  GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
{
  FT_Face  ftFace = mFreeTypeFace;
  FT_Error error;
  // For the software italics.
  bool isShearRequired = false;

#ifdef FREETYPE_BITMAP_SUPPORT
  // Check to see if this is fixed size bitmap
  if(mIsFixedSizeBitmap)
  {
    error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
  }
  else
#endif
  {
    // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
    // i.e. with the SNum-3R font.
    // @todo: add an option to use the FT_LOAD_DEFAULT if required?
    error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
  }
  if(FT_Err_Ok == error)
  {
    if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
    {
      // Does the software bold.
      FT_GlyphSlot_Embolden(ftFace->glyph);
    }

    if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
    {
      // Will do the software italic.
      isShearRequired = true;
    }

    FT_Glyph glyph;
    error = FT_Get_Glyph(ftFace->glyph, &glyph);

    // Convert to bitmap if necessary
    if(FT_Err_Ok == error)
    {
      if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
      {
        int  offsetX = 0, offsetY = 0;
        bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);

        // Create a bitmap for the outline
        if(isOutlineGlyph)
        {
          // Retrieve the horizontal and vertical distance from the current pen position to the
          // left and top border of the glyph bitmap for a normal glyph before applying the outline.
          if(FT_Err_Ok == error)
          {
            FT_Glyph normalGlyph;
            error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);

            error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
            if(FT_Err_Ok == error)
            {
              FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);

              offsetX = bitmapGlyph->left;
              offsetY = bitmapGlyph->top;
            }

            // Created FT_Glyph object must be released with FT_Done_Glyph
            FT_Done_Glyph(normalGlyph);
          }

          // Now apply the outline

          // Set up a stroker
          FT_Stroker stroker;
          error = FT_Stroker_New(mFreeTypeLibrary, &stroker);

          if(FT_Err_Ok == error)
          {
            FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
            error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);

            if(FT_Err_Ok == error)
            {
              FT_Stroker_Done(stroker);
            }
            else
            {
              DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
            }
          }
          else
          {
            DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
          }
        }

        error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
        if(FT_Err_Ok == error)
        {
          FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);

          if(isOutlineGlyph)
          {
            // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
            data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
            data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
          }

          ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
        }
        else
        {
          DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
        }
      }
      else
      {
        ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
      }

      data.isColorEmoji = mIsFixedSizeBitmap;

      // Created FT_Glyph object must be released with FT_Done_Glyph
      FT_Done_Glyph(glyph);
    }
  }
  else
  {
    DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
  }
}

bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
{
  FT_Error error = -1;

#ifdef FREETYPE_BITMAP_SUPPORT
  // Check to see if this is fixed size bitmap
  if(mHasColorTables)
  {
    error = FT_Load_Glyph(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR);
  }
#endif
  return FT_Err_Ok == error;
}

/**
 * Check if the character is supported by this font
 * @param[in] character The character to test
 */
bool FontFaceCacheItem::IsCharacterSupported(Character character)
{
  if(nullptr == mCharacterSet)
  {
    // Create again the character set.
    // It can be null if the ResetSystemDefaults() method has been called.

    FontDescription description;
    description.path   = mPath;
    description.family = std::move(FontFamily(mFreeTypeFace->family_name));
    description.weight = FontWeight::NONE;
    description.width  = FontWidth::NONE;
    description.slant  = FontSlant::NONE;

    // Note FreeType doesn't give too much info to build a proper font style.
    if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
    {
      description.slant = FontSlant::ITALIC;
    }
    if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
    {
      description.weight = FontWeight::BOLD;
    }

    mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
  }

  return FcCharSetHasChar(mCharacterSet, character);
}

GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
{
  return FT_Get_Char_Index(mFreeTypeFace, character);
}

} // namespace Dali::TextAbstraction::Internal
<|endoftext|>


<commit_before>/*
Copyright (c) 2012 Carsten Burstedde, Donna Calhoun
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.

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

#include "bump_user.h"

#include <fclaw2d_clawpatch.h>

#include <fc2d_clawpack46.h>
#include <fc2d_clawpack5.h>
#include <fc2d_cudaclaw.h>

#include "../rp/shallow_user_fort.h"

void bump_link_solvers(fclaw2d_global_t *glob)
{
    fclaw2d_vtable_t *vt = fclaw2d_vt();

    vt->problem_setup = &bump_problem_setup;  /* Version-independent */

    const user_options_t* user = bump_get_options(glob);
    if(user->cuda)
    {
        fc2d_cudaclaw_vtable_t *cuclaw_vt = fc2d_cudaclaw_vt();

        cuclaw_vt->fort_qinit     = &CLAWPACK46_QINIT;
        // cuclaw_vt->fort_rpn2      = &CLAWPACK46_RPN2;
        // cuclaw_vt->fort_rpt2      = &CLAWPACK46_RPT2;

        bump_assign_rpn2(&cuclaw_vt->cuda_rpn2);
        FCLAW_ASSERT(cuclaw_vt->cuda_rpn2 != NULL);

        bump_assign_rpt2(&cuclaw_vt->cuda_rpt2);
        FCLAW_ASSERT(cuclaw_vt->cuda_rpt2 != NULL);
    }
    else
    {
        if (user->claw_version == 4)
        {
            fclaw2d_clawpatch_vtable_t *clawpatch_vt = fclaw2d_clawpatch_vt();
            fc2d_clawpack46_vtable_t *claw46_vt = fc2d_clawpack46_vt();

            claw46_vt->fort_qinit     = &CLAWPACK46_QINIT;
            claw46_vt->fort_rpn2      = &CLAWPACK46_RPN2;
            claw46_vt->fort_rpt2      = &CLAWPACK46_RPT2;

            /* Avoid tagging block corners in 5 patch example*/
            clawpatch_vt->fort_tag4refinement = &TAG4REFINEMENT;
            clawpatch_vt->fort_tag4coarsening = &TAG4COARSENING;
        }
    }
    else if (user->claw_version == 5)
    {
        fc2d_clawpack5_vtable_t    *claw5_vt = fc2d_clawpack5_vt();

        claw5_vt->fort_qinit     = &CLAWPACK5_QINIT;

        if (user->example == 0)
        {
            claw5_vt->fort_rpn2 = &CLAWPACK5_RPN2;
            claw5_vt->fort_rpt2 = &CLAWPACK5_RPT2;
        }
        else if (user->example == 1)
        {
            fclaw2d_clawpatch_vtable_t *clawpatch_vt = fclaw2d_clawpatch_vt();
            fclaw2d_patch_vtable_t         *patch_vt = fclaw2d_patch_vt();

            patch_vt->setup = &bump_patch_setup;

            claw5_vt->fort_rpn2  = &CLAWPACK5_RPN2_MANIFOLD;
            claw5_vt->fort_rpt2  = &CLAWPACK5_RPT2_MANIFOLD;

            /* Avoid tagging block corners in 5 patch example*/
            clawpatch_vt->fort_tag4refinement = &CLAWPACK5_TAG4REFINEMENT;
            clawpatch_vt->fort_tag4coarsening = &CLAWPACK5_TAG4COARSENING;
        }
    }
}

void bump_problem_setup(fclaw2d_global_t* glob)
{
    const user_options_t* user = bump_get_options(glob);

    if (glob->mpirank == 0)
    {
        FILE *f = fopen("setprob.data","w");
        fprintf(f,  "%-24d   %s",user->example,"\% example\n");
        fprintf(f,  "%-24.16f   %s",user->gravity,"\% gravity\n");
        fclose(f);
    }
    fclaw2d_domain_barrier (glob->domain);
    BUMP_SETPROB();

    if (user->cuda == 1)
    {
        bump_setprob_cuda(user->gravity);
    }
}

void bump_patch_setup(fclaw2d_global_t *glob,
                           fclaw2d_patch_t *this_patch,
                           int this_block_idx,
                           int this_patch_idx)
{
    int mx,my,mbc,maux;
    double xlower,ylower,dx,dy;
    double *aux,*xd,*yd,*zd,*area;
    double *xp,*yp,*zp;
    double *xnormals,*ynormals,*xtangents,*ytangents;
    double *surfnormals,*edgelengths,*curvature;

    if (fclaw2d_patch_is_ghost(this_patch))
    {
        /* Mapped info is needed only for an update */
        return;
    }

    fclaw2d_clawpatch_grid_data(glob,this_patch,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    fclaw2d_clawpatch_metric_data(glob,this_patch,&xp,&yp,&zp,
                                  &xd,&yd,&zd,&area);

    fclaw2d_clawpatch_metric_data2(glob,this_patch,
                                   &xnormals,&ynormals,
                                   &xtangents,&ytangents,
                                   &surfnormals,&edgelengths,
                                   &curvature);

    fclaw2d_clawpatch_aux_data(glob,this_patch,&aux,&maux);

    USER5_SETAUX_MANIFOLD(&mbc,&mx,&my,&xlower,&ylower,
                          &dx,&dy,&maux,aux,
                          xnormals,xtangents,
                          ynormals,ytangents,
                          surfnormals,area);
}
<commit_msg>(clawpack/shallow/bump_cuda) Fix syntax error<commit_after>/*
Copyright (c) 2012 Carsten Burstedde, Donna Calhoun
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.

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

#include "bump_user.h"

#include <fclaw2d_clawpatch.h>

#include <fc2d_clawpack46.h>
#include <fc2d_clawpack5.h>
#include <fc2d_cudaclaw.h>

#include "../rp/shallow_user_fort.h"

void bump_link_solvers(fclaw2d_global_t *glob)
{
    fclaw2d_vtable_t *vt = fclaw2d_vt();

    vt->problem_setup = &bump_problem_setup;  /* Version-independent */

    const user_options_t* user = bump_get_options(glob);
    if(user->cuda)
    {
        fc2d_cudaclaw_vtable_t *cuclaw_vt = fc2d_cudaclaw_vt();

        cuclaw_vt->fort_qinit     = &CLAWPACK46_QINIT;
        // cuclaw_vt->fort_rpn2      = &CLAWPACK46_RPN2;
        // cuclaw_vt->fort_rpt2      = &CLAWPACK46_RPT2;

        bump_assign_rpn2(&cuclaw_vt->cuda_rpn2);
        FCLAW_ASSERT(cuclaw_vt->cuda_rpn2 != NULL);

        bump_assign_rpt2(&cuclaw_vt->cuda_rpt2);
        FCLAW_ASSERT(cuclaw_vt->cuda_rpt2 != NULL);
    }
    else
    {
        if (user->claw_version == 4)
        {
            fclaw2d_clawpatch_vtable_t *clawpatch_vt = fclaw2d_clawpatch_vt();
            fc2d_clawpack46_vtable_t *claw46_vt = fc2d_clawpack46_vt();

            claw46_vt->fort_qinit     = &CLAWPACK46_QINIT;
            claw46_vt->fort_rpn2      = &CLAWPACK46_RPN2;
            claw46_vt->fort_rpt2      = &CLAWPACK46_RPT2;

            /* Avoid tagging block corners in 5 patch example*/
            clawpatch_vt->fort_tag4refinement = &TAG4REFINEMENT;
            clawpatch_vt->fort_tag4coarsening = &TAG4COARSENING;
        }
        else if (user->claw_version == 5)
        {
            fc2d_clawpack5_vtable_t    *claw5_vt = fc2d_clawpack5_vt();

            claw5_vt->fort_qinit     = &CLAWPACK5_QINIT;

            if (user->example == 0)
            {
                claw5_vt->fort_rpn2 = &CLAWPACK5_RPN2;
                claw5_vt->fort_rpt2 = &CLAWPACK5_RPT2;
            }
            else if (user->example == 1)
            {
                fclaw2d_clawpatch_vtable_t *clawpatch_vt = fclaw2d_clawpatch_vt();
                fclaw2d_patch_vtable_t         *patch_vt = fclaw2d_patch_vt();

                patch_vt->setup = &bump_patch_setup;

                claw5_vt->fort_rpn2  = &CLAWPACK5_RPN2_MANIFOLD;
                claw5_vt->fort_rpt2  = &CLAWPACK5_RPT2_MANIFOLD;

                /* Avoid tagging block corners in 5 patch example*/
                clawpatch_vt->fort_tag4refinement = &CLAWPACK5_TAG4REFINEMENT;
                clawpatch_vt->fort_tag4coarsening = &CLAWPACK5_TAG4COARSENING;
            }
        }
    }
}

void bump_problem_setup(fclaw2d_global_t* glob)
{
    const user_options_t* user = bump_get_options(glob);

    if (glob->mpirank == 0)
    {
        FILE *f = fopen("setprob.data","w");
        fprintf(f,  "%-24d   %s",user->example,"\% example\n");
        fprintf(f,  "%-24.16f   %s",user->gravity,"\% gravity\n");
        fclose(f);
    }
    fclaw2d_domain_barrier (glob->domain);
    BUMP_SETPROB();

    if (user->cuda == 1)
    {
        bump_setprob_cuda(user->gravity);
    }
}

void bump_patch_setup(fclaw2d_global_t *glob,
                           fclaw2d_patch_t *this_patch,
                           int this_block_idx,
                           int this_patch_idx)
{
    int mx,my,mbc,maux;
    double xlower,ylower,dx,dy;
    double *aux,*xd,*yd,*zd,*area;
    double *xp,*yp,*zp;
    double *xnormals,*ynormals,*xtangents,*ytangents;
    double *surfnormals,*edgelengths,*curvature;

    if (fclaw2d_patch_is_ghost(this_patch))
    {
        /* Mapped info is needed only for an update */
        return;
    }

    fclaw2d_clawpatch_grid_data(glob,this_patch,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    fclaw2d_clawpatch_metric_data(glob,this_patch,&xp,&yp,&zp,
                                  &xd,&yd,&zd,&area);

    fclaw2d_clawpatch_metric_data2(glob,this_patch,
                                   &xnormals,&ynormals,
                                   &xtangents,&ytangents,
                                   &surfnormals,&edgelengths,
                                   &curvature);

    fclaw2d_clawpatch_aux_data(glob,this_patch,&aux,&maux);

    USER5_SETAUX_MANIFOLD(&mbc,&mx,&my,&xlower,&ylower,
                          &dx,&dy,&maux,aux,
                          xnormals,xtangents,
                          ynormals,ytangents,
                          surfnormals,area);
}
<|endoftext|>